diff --git a/CREDITS b/CREDITS index 9bf714a..66b9e7a 100644 --- a/CREDITS +++ b/CREDITS @@ -24,6 +24,11 @@ S: C. Negri 6, bl. D3 S: Iasi 6600 S: Romania +N: Mark Adler +E: madler@alumni.caltech.edu +W: http://alumnus.caltech.edu/~madler/ +D: zlib decompression + N: Monalisa Agrawal E: magrawal@nortelnetworks.com D: Basic Interphase 5575 driver with UBR and ABR support. @@ -1573,12 +1578,8 @@ S: 160 00 Praha 6 S: Czech Republic N: Niels Kristian Bech Jensen -E: nkbj@image.dk -W: http://www.image.dk/~nkbj +E: nkbj1970@hotmail.com D: Miscellaneous kernel updates and fixes. -S: Dr. Holsts Vej 34, lejl. 164 -S: DK-8230 Åbyhøj -S: Denmark N: Michael K. Johnson E: johnsonm@redhat.com @@ -3400,10 +3401,10 @@ S: Czech Republic N: Thibaut Varene E: T-Bone@parisc-linux.org -W: http://www.parisc-linux.org/ +W: http://www.parisc-linux.org/~varenet/ P: 1024D/B7D2F063 E67C 0D43 A75E 12A5 BB1C FA2F 1E32 C3DA B7D2 F063 D: PA-RISC port minion, PDC and GSCPS2 drivers, debuglocks and other bits -D: Some bits in an ARM port, S1D13XXX FB driver, random patches here and there +D: Some ARM at91rm9200 bits, S1D13XXX FB driver, random patches here and there D: AD1889 sound driver S: Paris, France diff --git a/Documentation/ABI/README b/Documentation/ABI/README new file mode 100644 index 0000000..9feaf16 --- /dev/null +++ b/Documentation/ABI/README @@ -0,0 +1,77 @@ +This directory attempts to document the ABI between the Linux kernel and +userspace, and the relative stability of these interfaces. Due to the +everchanging nature of Linux, and the differing maturity levels, these +interfaces should be used by userspace programs in different ways. + +We have four different levels of ABI stability, as shown by the four +different subdirectories in this location. Interfaces may change levels +of stability according to the rules described below. + +The different levels of stability are: + + stable/ + This directory documents the interfaces that the developer has + defined to be stable. Userspace programs are free to use these + interfaces with no restrictions, and backward compatibility for + them will be guaranteed for at least 2 years. Most interfaces + (like syscalls) are expected to never change and always be + available. + + testing/ + This directory documents interfaces that are felt to be stable, + as the main development of this interface has been completed. + The interface can be changed to add new features, but the + current interface will not break by doing this, unless grave + errors or security problems are found in them. Userspace + programs can start to rely on these interfaces, but they must be + aware of changes that can occur before these interfaces move to + be marked stable. Programs that use these interfaces are + strongly encouraged to add their name to the description of + these interfaces, so that the kernel developers can easily + notify them if any changes occur (see the description of the + layout of the files below for details on how to do this.) + + obsolete/ + This directory documents interfaces that are still remaining in + the kernel, but are marked to be removed at some later point in + time. The description of the interface will document the reason + why it is obsolete and when it can be expected to be removed. + The file Documentation/feature-removal-schedule.txt may describe + some of these interfaces, giving a schedule for when they will + be removed. + + removed/ + This directory contains a list of the old interfaces that have + been removed from the kernel. + +Every file in these directories will contain the following information: + +What: Short description of the interface +Date: Date created +KernelVersion: Kernel version this feature first showed up in. +Contact: Primary contact for this interface (may be a mailing list) +Description: Long description of the interface and how to use it. +Users: All users of this interface who wish to be notified when + it changes. This is very important for interfaces in + the "testing" stage, so that kernel developers can work + with userspace developers to ensure that things do not + break in ways that are unacceptable. It is also + important to get feedback for these interfaces to make + sure they are working in a proper way and do not need to + be changed further. + + +How things move between levels: + +Interfaces in stable may move to obsolete, as long as the proper +notification is given. + +Interfaces may be removed from obsolete and the kernel as long as the +documented amount of time has gone by. + +Interfaces in the testing state can move to the stable state when the +developers feel they are finished. They cannot be removed from the +kernel tree without going through the obsolete state first. + +It's up to the developer to place their interfaces in the category they +wish for it to start out in. diff --git a/Documentation/ABI/obsolete/devfs b/Documentation/ABI/obsolete/devfs new file mode 100644 index 0000000..b8b8739 --- /dev/null +++ b/Documentation/ABI/obsolete/devfs @@ -0,0 +1,13 @@ +What: devfs +Date: July 2005 +Contact: Greg Kroah-Hartman +Description: + devfs has been unmaintained for a number of years, has unfixable + races, contains a naming policy within the kernel that is + against the LSB, and can be replaced by using udev. + The files fs/devfs/*, include/linux/devfs_fs*.h will be removed, + along with the the assorted devfs function calls throughout the + kernel tree. + +Users: + diff --git a/Documentation/ABI/stable/syscalls b/Documentation/ABI/stable/syscalls new file mode 100644 index 0000000..c3ae3e7 --- /dev/null +++ b/Documentation/ABI/stable/syscalls @@ -0,0 +1,10 @@ +What: The kernel syscall interface +Description: + This interface matches much of the POSIX interface and is based + on it and other Unix based interfaces. It will only be added to + over time, and not have things removed from it. + + Note that this interface is different for every architecture + that Linux supports. Please see the architecture-specific + documentation for details on the syscall numbers that are to be + mapped to each syscall. diff --git a/Documentation/ABI/stable/sysfs-module b/Documentation/ABI/stable/sysfs-module new file mode 100644 index 0000000..75be431 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-module @@ -0,0 +1,30 @@ +What: /sys/module +Description: + The /sys/module tree consists of the following structure: + + /sys/module/MODULENAME + The name of the module that is in the kernel. This + module name will show up either if the module is built + directly into the kernel, or if it is loaded as a + dyanmic module. + + /sys/module/MODULENAME/parameters + This directory contains individual files that are each + individual parameters of the module that are able to be + changed at runtime. See the individual module + documentation as to the contents of these parameters and + what they accomplish. + + Note: The individual parameter names and values are not + considered stable, only the fact that they will be + placed in this location within sysfs. See the + individual driver documentation for details as to the + stability of the different parameters. + + /sys/module/MODULENAME/refcnt + If the module is able to be unloaded from the kernel, this file + will contain the current reference count of the module. + + Note: If the module is built into the kernel, or if the + CONFIG_MODULE_UNLOAD kernel configuration value is not enabled, + this file will not be present. diff --git a/Documentation/ABI/testing/sysfs-class b/Documentation/ABI/testing/sysfs-class new file mode 100644 index 0000000..4b0cb89 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class @@ -0,0 +1,16 @@ +What: /sys/class/ +Date: Febuary 2006 +Contact: Greg Kroah-Hartman +Description: + The /sys/class directory will consist of a group of + subdirectories describing individual classes of devices + in the kernel. The individual directories will consist + of either subdirectories, or symlinks to other + directories. + + All programs that use this directory tree must be able + to handle both subdirectories or symlinks in order to + work properly. + +Users: + udev diff --git a/Documentation/ABI/testing/sysfs-devices b/Documentation/ABI/testing/sysfs-devices new file mode 100644 index 0000000..6a25671 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices @@ -0,0 +1,25 @@ +What: /sys/devices +Date: February 2006 +Contact: Greg Kroah-Hartman +Description: + The /sys/devices tree contains a snapshot of the + internal state of the kernel device tree. Devices will + be added and removed dynamically as the machine runs, + and between different kernel versions, the layout of the + devices within this tree will change. + + Please do not rely on the format of this tree because of + this. If a program wishes to find different things in + the tree, please use the /sys/class structure and rely + on the symlinks there to point to the proper location + within the /sys/devices tree of the individual devices. + Or rely on the uevent messages to notify programs of + devices being added and removed from this tree to find + the location of those devices. + + Note that sometimes not all devices along the directory + chain will have emitted uevent messages, so userspace + programs must be able to handle such occurrences. + +Users: + udev diff --git a/Documentation/Changes b/Documentation/Changes index b02f476..4882720 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -181,8 +181,8 @@ Intel IA32 microcode -------------------- A driver has been added to allow updating of Intel IA32 microcode, -accessible as both a devfs regular file and as a normal (misc) -character device. If you are not using devfs you may need to: +accessible as a normal (misc) character device. If you are not using +udev you may need to: mkdir /dev/cpu mknod /dev/cpu/microcode c 10 184 @@ -201,7 +201,9 @@ with programs using shared memory. udev ---- udev is a userspace application for populating /dev dynamically with -only entries for devices actually present. udev replaces devfs. +only entries for devices actually present. udev replaces the basic +functionality of devfs, while allowing persistant device naming for +devices. FUSE ---- @@ -231,18 +233,13 @@ The PPP driver has been restructured to enable it to operate over diverse media layers. If you use PPP, upgrade pppd to at least 2.4.0. -If you are not using devfs, you must have the device file /dev/ppp +If you are not using udev, you must have the device file /dev/ppp which can be made by: mknod /dev/ppp c 108 0 as root. -If you use devfsd and build ppp support as modules, you will need -the following in your /etc/devfsd.conf file: - -LOOKUP PPP MODLOAD - Isdn4k-utils ------------ diff --git a/Documentation/CodingStyle b/Documentation/CodingStyle index ce5d2c0..6d2412e 100644 --- a/Documentation/CodingStyle +++ b/Documentation/CodingStyle @@ -155,7 +155,83 @@ problem, which is called the function-gr See next chapter. - Chapter 5: Functions + Chapter 5: Typedefs + +Please don't use things like "vps_t". + +It's a _mistake_ to use typedef for structures and pointers. When you see a + + vps_t a; + +in the source, what does it mean? + +In contrast, if it says + + struct virtual_container *a; + +you can actually tell what "a" is. + +Lots of people think that typedefs "help readability". Not so. They are +useful only for: + + (a) totally opaque objects (where the typedef is actively used to _hide_ + what the object is). + + Example: "pte_t" etc. opaque objects that you can only access using + the proper accessor functions. + + NOTE! Opaqueness and "accessor functions" are not good in themselves. + The reason we have them for things like pte_t etc. is that there + really is absolutely _zero_ portably accessible information there. + + (b) Clear integer types, where the abstraction _helps_ avoid confusion + whether it is "int" or "long". + + u8/u16/u32 are perfectly fine typedefs, although they fit into + category (d) better than here. + + NOTE! Again - there needs to be a _reason_ for this. If something is + "unsigned long", then there's no reason to do + + typedef unsigned long myflags_t; + + but if there is a clear reason for why it under certain circumstances + might be an "unsigned int" and under other configurations might be + "unsigned long", then by all means go ahead and use a typedef. + + (c) when you use sparse to literally create a _new_ type for + type-checking. + + (d) New types which are identical to standard C99 types, in certain + exceptional circumstances. + + Although it would only take a short amount of time for the eyes and + brain to become accustomed to the standard types like 'uint32_t', + some people object to their use anyway. + + Therefore, the Linux-specific 'u8/u16/u32/u64' types and their + signed equivalents which are identical to standard types are + permitted -- although they are not mandatory in new code of your + own. + + When editing existing code which already uses one or the other set + of types, you should conform to the existing choices in that code. + + (e) Types safe for use in userspace. + + In certain structures which are visible to userspace, we cannot + require C99 types and cannot use the 'u32' form above. Thus, we + use __u32 and similar types in all structures which are shared + with userspace. + +Maybe there are other cases too, but the rule should basically be to NEVER +EVER use a typedef unless you can clearly match one of those rules. + +In general, a pointer, or a struct that has elements that can reasonably +be directly accessed should _never_ be a typedef. + + + Chapter 6: Functions Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, @@ -183,7 +259,7 @@ and it gets confused. You know you're b to understand what you did 2 weeks from now. - Chapter 6: Centralized exiting of functions + Chapter 7: Centralized exiting of functions Albeit deprecated by some people, the equivalent of the goto statement is used frequently by compilers in form of the unconditional jump instruction. @@ -220,7 +296,7 @@ out: return result; } - Chapter 7: Commenting + Chapter 8: Commenting Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to @@ -240,7 +316,7 @@ When commenting the kernel API functions See the files Documentation/kernel-doc-nano-HOWTO.txt and scripts/kernel-doc for details. - Chapter 8: You've made a mess of it + Chapter 9: You've made a mess of it That's OK, we all do. You've probably been told by your long-time Unix user helper that "GNU emacs" automatically formats the C sources for @@ -288,7 +364,7 @@ re-formatting you may want to take a loo remember: "indent" is not a fix for bad programming. - Chapter 9: Configuration-files + Chapter 10: Configuration-files For configuration options (arch/xxx/Kconfig, and all the Kconfig files), somewhat different indentation is used. @@ -313,7 +389,7 @@ support for file-systems, for instance) experimental options should be denoted (EXPERIMENTAL). - Chapter 10: Data structures + Chapter 11: Data structures Data structures that have visibility outside the single-threaded environment they are created and destroyed in should always have @@ -344,7 +420,7 @@ Remember: if another thread can find you have a reference count on it, you almost certainly have a bug. - Chapter 11: Macros, Enums and RTL + Chapter 12: Macros, Enums and RTL Names of macros defining constants and labels in enums are capitalized. @@ -399,7 +475,7 @@ The cpp manual deals with macros exhaust covers RTL which is used frequently with assembly language in the kernel. - Chapter 12: Printing kernel messages + Chapter 13: Printing kernel messages Kernel developers like to be seen as literate. Do mind the spelling of kernel messages to make a good impression. Do not use crippled @@ -410,7 +486,7 @@ Kernel messages do not have to be termin Printing numbers in parentheses (%d) adds no value and should be avoided. - Chapter 13: Allocating memory + Chapter 14: Allocating memory The kernel provides the following general purpose memory allocators: kmalloc(), kzalloc(), kcalloc(), and vmalloc(). Please refer to the API @@ -429,7 +505,7 @@ from void pointer to any other pointer t language. - Chapter 14: The inline disease + Chapter 15: The inline disease There appears to be a common misperception that gcc has a magic "make me faster" speedup option called "inline". While the use of inlines can be @@ -457,7 +533,7 @@ something it would have done anyway. - Chapter 15: References + Appendix I: References The C Programming Language, Second Edition by Brian W. Kernighan and Dennis M. Ritchie. @@ -481,4 +557,4 @@ Kernel CodingStyle, by greg@kroah.com at http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ -- -Last updated on 30 December 2005 by a community effort on LKML. +Last updated on 30 April 2006. diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 5a2882d..66e1cf7 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -10,7 +10,8 @@ DOCBOOKS := wanbook.xml z8530book.xml mc kernel-hacking.xml kernel-locking.xml deviceiobook.xml \ procfs-guide.xml writing_usb_driver.xml \ kernel-api.xml journal-api.xml lsm.xml usb.xml \ - gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml + gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ + genericirq.xml ### # The build process is as follows (targets): diff --git a/Documentation/DocBook/genericirq.tmpl b/Documentation/DocBook/genericirq.tmpl new file mode 100644 index 0000000..0f4a4b6 --- /dev/null +++ b/Documentation/DocBook/genericirq.tmpl @@ -0,0 +1,474 @@ + + + + + + Linux generic IRQ handling + + + + Thomas + Gleixner + +
+ tglx@linutronix.de +
+
+
+ + Ingo + Molnar + +
+ mingo@elte.hu +
+
+
+
+ + + 2005-2006 + Thomas Gleixner + + + 2005-2006 + Ingo Molnar + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License version 2 as published by the Free Software Foundation. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + The generic interrupt handling layer is designed to provide a + complete abstraction of interrupt handling for device drivers. + It is able to handle all the different types of interrupt controller + hardware. Device drivers use generic API functions to request, enable, + disable and free interrupts. The drivers do not have to know anything + about interrupt hardware details, so they can be used on different + platforms without code changes. + + + This documentation is provided to developers who want to implement + an interrupt subsystem based for their architecture, with the help + of the generic IRQ handling layer. + + + + + Rationale + + The original implementation of interrupt handling in Linux is using + the __do_IRQ() super-handler, which is able to deal with every + type of interrupt logic. + + + Originally, Russell King identified different types of handlers to + build a quite universal set for the ARM interrupt handler + implementation in Linux 2.5/2.6. He distinguished between: + + Level type + Edge type + Simple type + + In the SMP world of the __do_IRQ() super-handler another type + was identified: + + Per CPU type + + + + This split implementation of highlevel IRQ handlers allows us to + optimize the flow of the interrupt handling for each specific + interrupt type. This reduces complexity in that particular codepath + and allows the optimized handling of a given type. + + + The original general IRQ implementation used hw_interrupt_type + structures and their ->ack(), ->end() [etc.] callbacks to + differentiate the flow control in the super-handler. This leads to + a mix of flow logic and lowlevel hardware logic, and it also leads + to unnecessary code duplication: for example in i386, there is a + ioapic_level_irq and a ioapic_edge_irq irq-type which share many + of the lowlevel details but have different flow handling. + + + A more natural abstraction is the clean separation of the + 'irq flow' and the 'chip details'. + + + Analysing a couple of architecture's IRQ subsystem implementations + reveals that most of them can use a generic set of 'irq flow' + methods and only need to add the chip level specific code. + The separation is also valuable for (sub)architectures + which need specific quirks in the irq flow itself but not in the + chip-details - and thus provides a more transparent IRQ subsystem + design. + + + Each interrupt descriptor is assigned its own highlevel flow + handler, which is normally one of the generic + implementations. (This highlevel flow handler implementation also + makes it simple to provide demultiplexing handlers which can be + found in embedded platforms on various architectures.) + + + The separation makes the generic interrupt handling layer more + flexible and extensible. For example, an (sub)architecture can + use a generic irq-flow implementation for 'level type' interrupts + and add a (sub)architecture specific 'edge type' implementation. + + + To make the transition to the new model easier and prevent the + breakage of existing implementations, the __do_IRQ() super-handler + is still available. This leads to a kind of duality for the time + being. Over time the new model should be used in more and more + architectures, as it enables smaller and cleaner IRQ subsystems. + + + + Known Bugs And Assumptions + + None (knock on wood). + + + + + Abstraction layers + + There are three main levels of abstraction in the interrupt code: + + Highlevel driver API + Highlevel IRQ flow handlers + Chiplevel hardware encapsulation + + + + Interrupt control flow + + Each interrupt is described by an interrupt descriptor structure + irq_desc. The interrupt is referenced by an 'unsigned int' numeric + value which selects the corresponding interrupt decription structure + in the descriptor structures array. + The descriptor structure contains status information and pointers + to the interrupt flow method and the interrupt chip structure + which are assigned to this interrupt. + + + Whenever an interrupt triggers, the lowlevel arch code calls into + the generic interrupt code by calling desc->handle_irq(). + This highlevel IRQ handling function only uses desc->chip primitives + referenced by the assigned chip descriptor structure. + + + + Highlevel Driver API + + The highlevel Driver API consists of following functions: + + request_irq() + free_irq() + disable_irq() + enable_irq() + disable_irq_nosync() (SMP only) + synchronize_irq() (SMP only) + set_irq_type() + set_irq_wake() + set_irq_data() + set_irq_chip() + set_irq_chip_data() + + See the autogenerated function documentation for details. + + + + Highlevel IRQ flow handlers + + The generic layer provides a set of pre-defined irq-flow methods: + + handle_level_irq + handle_edge_irq + handle_simple_irq + handle_percpu_irq + + The interrupt flow handlers (either predefined or architecture + specific) are assigned to specific interrupts by the architecture + either during bootup or during device initialization. + + + Default flow implementations + + Helper functions + + The helper functions call the chip primitives and + are used by the default flow implementations. + The following helper functions are implemented (simplified excerpt): + +default_enable(irq) +{ + desc->chip->unmask(irq); +} + +default_disable(irq) +{ + if (!delay_disable(irq)) + desc->chip->mask(irq); +} + +default_ack(irq) +{ + chip->ack(irq); +} + +default_mask_ack(irq) +{ + if (chip->mask_ack) { + chip->mask_ack(irq); + } else { + chip->mask(irq); + chip->ack(irq); + } +} + +noop(irq) +{ +} + + + + + + + Default flow handler implementations + + Default Level IRQ flow handler + + handle_level_irq provides a generic implementation + for level-triggered interrupts. + + + The following control flow is implemented (simplified excerpt): + +desc->chip->start(); +handle_IRQ_event(desc->action); +desc->chip->end(); + + + + + Default Edge IRQ flow handler + + handle_edge_irq provides a generic implementation + for edge-triggered interrupts. + + + The following control flow is implemented (simplified excerpt): + +if (desc->status & running) { + desc->chip->hold(); + desc->status |= pending | masked; + return; +} +desc->chip->start(); +desc->status |= running; +do { + if (desc->status & masked) + desc->chip->enable(); + desc-status &= ~pending; + handle_IRQ_event(desc->action); +} while (status & pending); +desc-status &= ~running; +desc->chip->end(); + + + + + Default simple IRQ flow handler + + handle_simple_irq provides a generic implementation + for simple interrupts. + + + Note: The simple flow handler does not call any + handler/chip primitives. + + + The following control flow is implemented (simplified excerpt): + +handle_IRQ_event(desc->action); + + + + + Default per CPU flow handler + + handle_percpu_irq provides a generic implementation + for per CPU interrupts. + + + Per CPU interrupts are only available on SMP and + the handler provides a simplified version without + locking. + + + The following control flow is implemented (simplified excerpt): + +desc->chip->start(); +handle_IRQ_event(desc->action); +desc->chip->end(); + + + + + + Quirks and optimizations + + The generic functions are intended for 'clean' architectures and chips, + which have no platform-specific IRQ handling quirks. If an architecture + needs to implement quirks on the 'flow' level then it can do so by + overriding the highlevel irq-flow handler. + + + + Delayed interrupt disable + + This per interrupt selectable feature, which was introduced by Russell + King in the ARM interrupt implementation, does not mask an interrupt + at the hardware level when disable_irq() is called. The interrupt is + kept enabled and is masked in the flow handler when an interrupt event + happens. This prevents losing edge interrupts on hardware which does + not store an edge interrupt event while the interrupt is disabled at + the hardware level. When an interrupt arrives while the IRQ_DISABLED + flag is set, then the interrupt is masked at the hardware level and + the IRQ_PENDING bit is set. When the interrupt is re-enabled by + enable_irq() the pending bit is checked and if it is set, the + interrupt is resent either via hardware or by a software resend + mechanism. (It's necessary to enable CONFIG_HARDIRQS_SW_RESEND when + you want to use the delayed interrupt disable feature and your + hardware is not capable of retriggering an interrupt.) + The delayed interrupt disable can be runtime enabled, per interrupt, + by setting the IRQ_DELAYED_DISABLE flag in the irq_desc status field. + + + + + Chiplevel hardware encapsulation + + The chip level hardware descriptor structure irq_chip + contains all the direct chip relevant functions, which + can be utilized by the irq flow implementations. + + ack() + mask_ack() - Optional, recommended for performance + mask() + unmask() + retrigger() - Optional + set_type() - Optional + set_wake() - Optional + + These primitives are strictly intended to mean what they say: ack means + ACK, masking means masking of an IRQ line, etc. It is up to the flow + handler(s) to use these basic units of lowlevel functionality. + + + + + + __do_IRQ entry point + + The original implementation __do_IRQ() is an alternative entry + point for all types of interrupts. + + + This handler turned out to be not suitable for all + interrupt hardware and was therefore reimplemented with split + functionality for egde/level/simple/percpu interrupts. This is not + only a functional optimization. It also shortens code paths for + interrupts. + + + To make use of the split implementation, replace the call to + __do_IRQ by a call to desc->chip->handle_irq() and associate + the appropriate handler function to desc->chip->handle_irq(). + In most cases the generic handler implementations should + be sufficient. + + + + + Locking on SMP + + The locking of chip registers is up to the architecture that + defines the chip primitives. There is a chip->lock field that can be used + for serialization, but the generic layer does not touch it. The per-irq + structure is protected via desc->lock, by the generic layer. + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the generic IRQ layer. + +!Iinclude/linux/irq.h + + + + Public Functions Provided + + This chapter contains the autogenerated documentation of the kernel API functions + which are exported. + +!Ekernel/irq/manage.c +!Ekernel/irq/chip.c + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Ikernel/irq/handle.c +!Ikernel/irq/chip.c + + + + Credits + + The following people have contributed to this document: + + Thomas Gleixnertglx@linutronix.de + Ingo Molnarmingo@elte.hu + + + +
diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index ca02e04..1ae4dc0 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -62,6 +62,8 @@ Internal Functions !Ikernel/exit.c !Ikernel/signal.c +!Iinclude/linux/kthread.h +!Ekernel/kthread.c Kernel objects manipulation @@ -114,9 +116,33 @@ X!Ilib/string.c + + Basic Kernel Library Functions + + + The Linux kernel provides more basic utility functions. + + + Bitmap Operations +!Elib/bitmap.c +!Ilib/bitmap.c + + + Command-line Parsing +!Elib/cmdline.c + + + CRC Functions +!Elib/crc16.c +!Elib/crc32.c +!Elib/crc-ccitt.c + + + Memory Management in Linux The Slab Cache +!Iinclude/linux/slab.h !Emm/slab.c User Space Memory Access @@ -280,12 +306,13 @@ X!Ekernel/module.c MTRR Handling !Earch/i386/kernel/cpu/mtrr/main.c + PCI Support Library !Edrivers/pci/pci.c !Edrivers/pci/pci-driver.c !Edrivers/pci/remove.c !Edrivers/pci/pci-acpi.c - !Edrivers/pci/msi.c @@ -314,9 +341,11 @@ X!Earch/i386/kernel/mca.c - - The Device File System -!Efs/devfs/base.c + + Firmware Interfaces + DMI Interfaces +!Edrivers/firmware/dmi_scan.c + @@ -331,6 +360,18 @@ X!Earch/i386/kernel/mca.c !Esecurity/security.c + + Audit Interfaces +!Ekernel/audit.c +!Ikernel/auditsc.c +!Ikernel/auditfilter.c + + + + Accounting Framework +!Ikernel/acct.c + + Power Management !Ekernel/power/pm.c @@ -390,7 +431,6 @@ X!Edrivers/pnp/system.c - Block Devices !Eblock/ll_rw_blk.c @@ -401,6 +441,14 @@ X!Edrivers/pnp/system.c !Edrivers/char/misc.c + + Parallel Port Devices +!Iinclude/linux/parport.h +!Edrivers/parport/ieee1284.c +!Edrivers/parport/share.c +!Idrivers/parport/daisy.c + + Video4Linux !Edrivers/media/video/videodev.c diff --git a/Documentation/DocBook/kernel-locking.tmpl b/Documentation/DocBook/kernel-locking.tmpl index 158ffe9..644c388 100644 --- a/Documentation/DocBook/kernel-locking.tmpl +++ b/Documentation/DocBook/kernel-locking.tmpl @@ -1590,7 +1590,7 @@ the amount of locking which needs to be Our final dilemma is this: when can we actually destroy the removed element? Remember, a reader might be stepping through - this element in the list right now: it we free this element and + this element in the list right now: if we free this element and the next pointer changes, the reader will jump off into garbage and crash. We need to wait until we know that all the readers who were traversing the list when we deleted the diff --git a/Documentation/DocBook/libata.tmpl b/Documentation/DocBook/libata.tmpl index f869b03..e97c323 100644 --- a/Documentation/DocBook/libata.tmpl +++ b/Documentation/DocBook/libata.tmpl @@ -169,6 +169,22 @@ void (*tf_read) (struct ata_port *ap, st + PIO data read/write + +void (*data_xfer) (struct ata_device *, unsigned char *, unsigned int, int); + + + +All bmdma-style drivers must implement this hook. This is the low-level +operation that actually copies the data bytes during a PIO data +transfer. +Typically the driver +will choose one of ata_pio_data_xfer_noirq(), ata_pio_data_xfer(), or +ata_mmio_data_xfer(). + + + + ATA command execute void (*exec_command)(struct ata_port *ap, struct ata_taskfile *tf); @@ -204,11 +220,10 @@ command. u8 (*check_status)(struct ata_port *ap); u8 (*check_altstatus)(struct ata_port *ap); -u8 (*check_err)(struct ata_port *ap); - Reads the Status/AltStatus/Error ATA shadow register from + Reads the Status/AltStatus ATA shadow register from hardware. On some hardware, reading the Status register has the side effect of clearing the interrupt condition. Most drivers for taskfile-based hardware use @@ -269,23 +284,6 @@ void (*set_mode) (struct ata_port *ap); - Reset ATA bus - -void (*phy_reset) (struct ata_port *ap); - - - - The very first step in the probe phase. Actions vary depending - on the bus type, typically. After waking up the device and probing - for device presence (PATA and SATA), typically a soft reset - (SRST) will be performed. Drivers typically use the helper - functions ata_bus_reset() or sata_phy_reset() for this hook. - Many SATA drivers use sata_phy_reset() or call it from within - their own phy_reset() functions. - - - - Control PCI IDE BMDMA engine void (*bmdma_setup) (struct ata_queued_cmd *qc); @@ -354,16 +352,74 @@ int (*qc_issue) (struct ata_queued_cmd * - Timeout (error) handling + Exception and probe handling (EH) void (*eng_timeout) (struct ata_port *ap); +void (*phy_reset) (struct ata_port *ap); + + + +Deprecated. Use ->error_handler() instead. + + + +void (*freeze) (struct ata_port *ap); +void (*thaw) (struct ata_port *ap); + + + +ata_port_freeze() is called when HSM violations or some other +condition disrupts normal operation of the port. A frozen port +is not allowed to perform any operation until the port is +thawed, which usually follows a successful reset. + + + +The optional ->freeze() callback can be used for freezing the port +hardware-wise (e.g. mask interrupt and stop DMA engine). If a +port cannot be frozen hardware-wise, the interrupt handler +must ack and clear interrupts unconditionally while the port +is frozen. + + +The optional ->thaw() callback is called to perform the opposite of ->freeze(): +prepare the port for normal operation once again. Unmask interrupts, +start DMA engine, etc. + + + +void (*error_handler) (struct ata_port *ap); + + + +->error_handler() is a driver's hook into probe, hotplug, and recovery +and other exceptional conditions. The primary responsibility of an +implementation is to call ata_do_eh() or ata_bmdma_drive_eh() with a set +of EH hooks as arguments: + + + +'prereset' hook (may be NULL) is called during an EH reset, before any other actions +are taken. + + + +'postreset' hook (may be NULL) is called after the EH reset is performed. Based on +existing conditions, severity of the problem, and hardware capabilities, + + + +Either 'softreset' (may be NULL) or 'hardreset' (may be NULL) will be +called to perform the low-level EH reset. + + + +void (*post_internal_cmd) (struct ata_queued_cmd *qc); -This is a high level error handling function, called from the -error handling thread, when a command times out. Most newer -hardware will implement its own error handling code here. IDE BMDMA -drivers may use the helper function ata_eng_timeout(). +Perform any hardware-specific actions necessary to finish processing +after executing a probe-time or EH-time command via ata_exec_internal(). diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index 6e463d0..a8c8cce 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl @@ -109,7 +109,7 @@ for most of the implementations. These functions can be replaced by the board driver if neccecary. Those functions are called via pointers in the NAND chip description structure. The board driver can set the functions which - should be replaced by board dependend functions before calling nand_scan(). + should be replaced by board dependent functions before calling nand_scan(). If the function pointer is NULL on entry to nand_scan() then the pointer is set to the default function which is suitable for the detected chip type. @@ -133,7 +133,7 @@ [REPLACEABLE] Replaceable members hold hardware related functions which can be provided by the board driver. The board driver can set the functions which - should be replaced by board dependend functions before calling nand_scan(). + should be replaced by board dependent functions before calling nand_scan(). If the function pointer is NULL on entry to nand_scan() then the pointer is set to the default function which is suitable for the detected chip type. @@ -156,9 +156,8 @@ Basic board driver For most boards it will be sufficient to provide just the - basic functions and fill out some really board dependend + basic functions and fill out some really board dependent members in the nand chip description structure. - See drivers/mtd/nand/skeleton for reference. Basic defines @@ -189,9 +188,9 @@ static unsigned long baseaddr; Partition defines - If you want to divide your device into parititions, then - enable the configuration switch CONFIG_MTD_PARITIONS and define - a paritioning scheme suitable to your board. + If you want to divide your device into partitions, then + enable the configuration switch CONFIG_MTD_PARTITIONS and define + a partitioning scheme suitable to your board. #define NUM_PARTITIONS 2 @@ -1295,7 +1294,9 @@ #define NAND_BBT_SAVECONTENT 0x00002000 !Idrivers/mtd/nand/nand_base.c !Idrivers/mtd/nand/nand_bbt.c -!Idrivers/mtd/nand/nand_ecc.c + diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl index fdff984..b629da3 100644 --- a/Documentation/DocBook/videobook.tmpl +++ b/Documentation/DocBook/videobook.tmpl @@ -976,7 +976,7 @@ static int camera_close(struct video_dev Interrupt Handling Our example handler is for an ISA bus device. If it was PCI you would be - able to share the interrupt and would have set SA_SHIRQ to indicate a + able to share the interrupt and would have set IRQF_SHARED to indicate a shared IRQ. We pass the device pointer as the interrupt routine argument. We don't need to since we only support one card but doing this will make it easier to upgrade the driver for multiple devices in the future. diff --git a/Documentation/IPMI.txt b/Documentation/IPMI.txt index bf1cf98..0256805 100644 --- a/Documentation/IPMI.txt +++ b/Documentation/IPMI.txt @@ -10,7 +10,7 @@ standard for controlling intelligent dev It provides for dynamic discovery of sensors in the system and the ability to monitor the sensors and be informed when the sensor's values change or go outside certain boundaries. It also has a -standardized database for field-replacable units (FRUs) and a watchdog +standardized database for field-replaceable units (FRUs) and a watchdog timer. To use this, you need an interface to an IPMI controller in your @@ -64,7 +64,7 @@ situation, you need to read the section IPMI defines a standard watchdog timer. You can enable this with the 'IPMI Watchdog Timer' config option. If you compile the driver into the kernel, then via a kernel command-line option you can have the -watchdog timer start as soon as it intitializes. It also have a lot +watchdog timer start as soon as it initializes. It also have a lot of other options, see the 'Watchdog' section below for more details. Note that you can also have the watchdog continue to run if it is closed (by default it is disabled on close). Go into the 'Watchdog diff --git a/Documentation/IRQ.txt b/Documentation/IRQ.txt new file mode 100644 index 0000000..1011e71 --- /dev/null +++ b/Documentation/IRQ.txt @@ -0,0 +1,22 @@ +What is an IRQ? + +An IRQ is an interrupt request from a device. +Currently they can come in over a pin, or over a packet. +Several devices may be connected to the same pin thus +sharing an IRQ. + +An IRQ number is a kernel identifier used to talk about a hardware +interrupt source. Typically this is an index into the global irq_desc +array, but except for what linux/interrupt.h implements the details +are architecture specific. + +An IRQ number is an enumeration of the possible interrupt sources on a +machine. Typically what is enumerated is the number of input pins on +all of the interrupt controller in the system. In the case of ISA +what is enumerated are the 16 input pins on the two i8259 interrupt +controllers. + +Architectures can assign additional meaning to the IRQ numbers, and +are encouraged to in the case where there is any manual configuration +of the hardware involved. The ISA IRQs are a classic example of +assigning this kind of additional meaning. diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt index 49e27cc..1d50cf0 100644 --- a/Documentation/RCU/checklist.txt +++ b/Documentation/RCU/checklist.txt @@ -144,9 +144,47 @@ over a rather long period of time, but i whether the increased speed is worth it. 8. Although synchronize_rcu() is a bit slower than is call_rcu(), - it usually results in simpler code. So, unless update performance - is important or the updaters cannot block, synchronize_rcu() - should be used in preference to call_rcu(). + it usually results in simpler code. So, unless update + performance is critically important or the updaters cannot block, + synchronize_rcu() should be used in preference to call_rcu(). + + An especially important property of the synchronize_rcu() + primitive is that it automatically self-limits: if grace periods + are delayed for whatever reason, then the synchronize_rcu() + primitive will correspondingly delay updates. In contrast, + code using call_rcu() should explicitly limit update rate in + cases where grace periods are delayed, as failing to do so can + result in excessive realtime latencies or even OOM conditions. + + Ways of gaining this self-limiting property when using call_rcu() + include: + + a. Keeping a count of the number of data-structure elements + used by the RCU-protected data structure, including those + waiting for a grace period to elapse. Enforce a limit + on this number, stalling updates as needed to allow + previously deferred frees to complete. + + Alternatively, limit only the number awaiting deferred + free rather than the total number of elements. + + b. Limiting update rate. For example, if updates occur only + once per hour, then no explicit rate limiting is required, + unless your system is already badly broken. The dcache + subsystem takes this approach -- updates are guarded + by a global lock, limiting their rate. + + c. Trusted update -- if updates can only be done manually by + superuser or some other trusted user, then it might not + be necessary to automatically limit them. The theory + here is that superuser already has lots of ways to crash + the machine. + + d. Use call_rcu_bh() rather than call_rcu(), in order to take + advantage of call_rcu_bh()'s faster grace periods. + + e. Periodically invoke synchronize_rcu(), permitting a limited + number of updates per grace period. 9. All RCU list-traversal primitives, which include list_for_each_rcu(), list_for_each_entry_rcu(), diff --git a/Documentation/RCU/torture.txt b/Documentation/RCU/torture.txt index e4c3815..a494859 100644 --- a/Documentation/RCU/torture.txt +++ b/Documentation/RCU/torture.txt @@ -7,7 +7,7 @@ The CONFIG_RCU_TORTURE_TEST config optio implementations. It creates an rcutorture kernel module that can be loaded to run a torture test. The test periodically outputs status messages via printk(), which can be examined via the dmesg -command (perhaps grepping for "rcutorture"). The test is started +command (perhaps grepping for "torture"). The test is started when the module is loaded, and stops when the module is unloaded. However, actually setting this config option to "y" results in the system @@ -35,6 +35,19 @@ stat_interval The number of seconds betw be printed -only- when the module is unloaded, and this is the default. +shuffle_interval + The number of seconds to keep the test threads affinitied + to a particular subset of the CPUs. Used in conjunction + with test_no_idle_hz. + +test_no_idle_hz Whether or not to test the ability of RCU to operate in + a kernel that disables the scheduling-clock interrupt to + idle CPUs. Boolean parameter, "1" to test, "0" otherwise. + +torture_type The type of RCU to test: "rcu" for the rcu_read_lock() + API, "rcu_bh" for the rcu_read_lock_bh() API, and "srcu" + for the "srcu_read_lock()" API. + verbose Enable debug printk()s. Default is disabled. @@ -42,14 +55,14 @@ OUTPUT The statistics output is as follows: - rcutorture: --- Start of test: nreaders=16 stat_interval=0 verbose=0 - rcutorture: rtc: 0000000000000000 ver: 1916 tfle: 0 rta: 1916 rtaf: 0 rtf: 1915 - rcutorture: Reader Pipe: 1466408 9747 0 0 0 0 0 0 0 0 0 - rcutorture: Reader Batch: 1464477 11678 0 0 0 0 0 0 0 0 - rcutorture: Free-Block Circulation: 1915 1915 1915 1915 1915 1915 1915 1915 1915 1915 0 - rcutorture: --- End of test + rcu-torture: --- Start of test: nreaders=16 stat_interval=0 verbose=0 + rcu-torture: rtc: 0000000000000000 ver: 1916 tfle: 0 rta: 1916 rtaf: 0 rtf: 1915 + rcu-torture: Reader Pipe: 1466408 9747 0 0 0 0 0 0 0 0 0 + rcu-torture: Reader Batch: 1464477 11678 0 0 0 0 0 0 0 0 + rcu-torture: Free-Block Circulation: 1915 1915 1915 1915 1915 1915 1915 1915 1915 1915 0 + rcu-torture: --- End of test -The command "dmesg | grep rcutorture:" will extract this information on +The command "dmesg | grep torture:" will extract this information on most systems. On more esoteric configurations, it may be necessary to use other commands to access the output of the printk()s used by the RCU torture test. The printk()s use KERN_ALERT, so they should @@ -115,8 +128,9 @@ The following script may be used to tort modprobe rcutorture sleep 100 rmmod rcutorture - dmesg | grep rcutorture: + dmesg | grep torture: The output can be manually inspected for the error flag of "!!!". One could of course create a more elaborate script that automatically -checked for such errors. +checked for such errors. The "rmmod" command forces a "SUCCESS" or +"FAILURE" indication to be printk()ed. diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index 07cb93b..4f41a60 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -184,7 +184,17 @@ synchronize_rcu() blocking, it registers a function and argument which are invoked after all ongoing RCU read-side critical sections have completed. This callback variant is particularly useful in situations where - it is illegal to block. + it is illegal to block or where update-side performance is + critically important. + + However, the call_rcu() API should not be used lightly, as use + of the synchronize_rcu() API generally results in simpler code. + In addition, the synchronize_rcu() API has the nice property + of automatically limiting update rate should grace periods + be delayed. This property results in system resilience in face + of denial-of-service attacks. Code using call_rcu() should limit + update rate in order to gain this same sort of resilience. See + checklist.txt for some approaches to limiting the update rate. rcu_assign_pointer() @@ -790,7 +800,6 @@ RCU pointer update: RCU grace period: - synchronize_kernel (deprecated) synchronize_net synchronize_sched synchronize_rcu diff --git a/Documentation/README.DAC960 b/Documentation/README.DAC960 index 98ea617..0e8f618 100644 --- a/Documentation/README.DAC960 +++ b/Documentation/README.DAC960 @@ -78,9 +78,9 @@ also known as "System Drives", and Drive terms are in use in the Mylex documentation; I have chosen to standardize on the more generic "Logical Drive" and "Drive Group". -DAC960 RAID disk devices are named in the style of the Device File System -(DEVFS). The device corresponding to Logical Drive D on Controller C is -referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1 +DAC960 RAID disk devices are named in the style of the obsolete Device File +System (DEVFS). The device corresponding to Logical Drive D on Controller C +is referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1 through /dev/rd/cCdDp7. For example, partition 3 of Logical Drive 5 on Controller 2 is referred to as /dev/rd/c2d5p3. Note that unlike with SCSI disks the device names will not change in the event of a disk drive failure. diff --git a/Documentation/SubmitChecklist b/Documentation/SubmitChecklist new file mode 100644 index 0000000..8230098 --- /dev/null +++ b/Documentation/SubmitChecklist @@ -0,0 +1,57 @@ +Linux Kernel patch sumbittal checklist +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Here are some basic things that developers should do if they +want to see their kernel patch submittals accepted quicker. + +These are all above and beyond the documentation that is provided +in Documentation/SubmittingPatches and elsewhere about submitting +Linux kernel patches. + + + +- Builds cleanly with applicable or modified CONFIG options =y, =m, and =n. + No gcc warnings/errors, no linker warnings/errors. + +- Passes allnoconfig, allmodconfig + +- Builds on multiple CPU arch-es by using local cross-compile tools + or something like PLM at OSDL. + +- ppc64 is a good architecture for cross-compilation checking because it + tends to use `unsigned long' for 64-bit quantities. + +- Matches kernel coding style(!) + +- Any new or modified CONFIG options don't muck up the config menu. + +- All new Kconfig options have help text. + +- Has been carefully reviewed with respect to relevant Kconfig + combinations. This is very hard to get right with testing -- + brainpower pays off here. + +- Check cleanly with sparse. + +- Use 'make checkstack' and 'make namespacecheck' and fix any + problems that they find. Note: checkstack does not point out + problems explicitly, but any one function that uses more than + 512 bytes on the stack is a candidate for change. + +- Include kernel-doc to document global kernel APIs. (Not required + for static functions, but OK there also.) Use 'make htmldocs' + or 'make mandocs' to check the kernel-doc and fix any issues. + +- Has been tested with CONFIG_PREEMPT, CONFIG_DEBUG_PREEMPT, + CONFIG_DEBUG_SLAB, CONFIG_DEBUG_PAGEALLOC, CONFIG_DEBUG_MUTEXES, + CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_SPINLOCK_SLEEP all simultaneously + enabled. + +- Has been build- and runtime tested with and without CONFIG_SMP and + CONFIG_PREEMPT. + +- If the patch affects IO/Disk, etc: has been tested with and without + CONFIG_LBD. + + +2006-APR-27 diff --git a/Documentation/arm/IXP4xx b/Documentation/arm/IXP4xx index d4c6d3a..43edb4e 100644 --- a/Documentation/arm/IXP4xx +++ b/Documentation/arm/IXP4xx @@ -85,7 +85,7 @@ IXP4xx provides two methods of accessing 2) If > 64MB of memory space is required, the IXP4xx can be configured to use indirect registers to access PCI This allows for up to 128MB (0x48000000 to 0x4fffffff) of memory on the bus. - The disadvantadge of this is that every PCI access requires + The disadvantage of this is that every PCI access requires three local register accesses plus a spinlock, but in some cases the performance hit is acceptable. In addition, you cannot mmap() PCI devices in this case due to the indirect nature diff --git a/Documentation/arm/Samsung-S3C24XX/Overview.txt b/Documentation/arm/Samsung-S3C24XX/Overview.txt index 8c6ee68..3e46d2a 100644 --- a/Documentation/arm/Samsung-S3C24XX/Overview.txt +++ b/Documentation/arm/Samsung-S3C24XX/Overview.txt @@ -7,11 +7,13 @@ Introduction ------------ The Samsung S3C24XX range of ARM9 System-on-Chip CPUs are supported - by the 's3c2410' architecture of ARM Linux. Currently the S3C2410 and - the S3C2440 are supported CPUs. + by the 's3c2410' architecture of ARM Linux. Currently the S3C2410, + S3C2440 and S3C2442 devices are supported. Support for the S3C2400 series is in progress. + Support for the S3C2412 and S3C2413 CPUs is being merged. + Configuration ------------- @@ -43,9 +45,18 @@ Machines Samsung's own development board, geared for PDA work. + Samsung/Aiji SMDK2412 + + The S3C2412 version of the SMDK2440. + + Samsung/Aiji SMDK2413 + + The S3C2412 version of the SMDK2440. + Samsung/Meritech SMDK2440 - The S3C2440 compatible version of the SMDK2440 + The S3C2440 compatible version of the SMDK2440, which has the + option of an S3C2440 or S3C2442 CPU module. Thorcom VR1000 @@ -211,24 +222,6 @@ Port Contributors Lucas Correia Villa Real (S3C2400 port) -Document Changes ----------------- - - 05 Sep 2004 - BJD - Added Document Changes section - 05 Sep 2004 - BJD - Added Klaus Fetscher to list of contributors - 25 Oct 2004 - BJD - Added Dimitry Andric to list of contributors - 25 Oct 2004 - BJD - Updated the MTD from the 2.6.9 merge - 21 Jan 2005 - BJD - Added rx3715, added Shannon to contributors - 10 Feb 2005 - BJD - Added Guillaume Gourat to contributors - 02 Mar 2005 - BJD - Added SMDK2440 to list of machines - 06 Mar 2005 - BJD - Added Christer Weinigel - 08 Mar 2005 - BJD - Added LCVR to list of people, updated introduction - 08 Mar 2005 - BJD - Added section on adding machines - 09 Sep 2005 - BJD - Added section on platform data - 11 Feb 2006 - BJD - Added I2C, RTC and Watchdog sections - 11 Feb 2006 - BJD - Added Osiris machine, and S3C2400 information - - Document Author --------------- diff --git a/Documentation/arm/Samsung-S3C24XX/S3C2412.txt b/Documentation/arm/Samsung-S3C24XX/S3C2412.txt new file mode 100644 index 0000000..cb82a7f --- /dev/null +++ b/Documentation/arm/Samsung-S3C24XX/S3C2412.txt @@ -0,0 +1,120 @@ + S3C2412 ARM Linux Overview + ========================== + +Introduction +------------ + + The S3C2412 is part of the S3C24XX range of ARM9 System-on-Chip CPUs + from Samsung. This part has an ARM926-EJS core, capable of running up + to 266MHz (see data-sheet for more information) + + +Clock +----- + + The core clock code provides a set of clocks to the drivers, and allows + for source selection and a number of other features. + + +Power +----- + + No support for suspend/resume to RAM in the current system. + + +DMA +--- + + No current support for DMA. + + +GPIO +---- + + There is support for setting the GPIO to input/output/special function + and reading or writing to them. + + +UART +---- + + The UART hardware is similar to the S3C2440, and is supported by the + s3c2410 driver in the drivers/serial directory. + + +NAND +---- + + The NAND hardware is similar to the S3C2440, and is supported by the + s3c2410 driver in the drivers/mtd/nand directory. + + +USB Host +-------- + + The USB hardware is similar to the S3C2410, with extended clock source + control. The OHCI portion is supported by the ohci-s3c2410 driver, and + the clock control selection is supported by the core clock code. + + +USB Device +---------- + + No current support in the kernel + + +IRQs +---- + + All the standard, and external interrupt sources are supported. The + extra sub-sources are not yet supported. + + +RTC +--- + + The RTC hardware is similar to the S3C2410, and is supported by the + s3c2410-rtc driver. + + +Watchdog +-------- + + The watchdog harware is the same as the S3C2410, and is supported by + the s3c2410_wdt driver. + + +MMC/SD/SDIO +----------- + + No current support for the MMC/SD/SDIO block. + +IIC +--- + + The IIC hardware is the same as the S3C2410, and is supported by the + i2c-s3c24xx driver. + + +IIS +--- + + No current support for the IIS interface. + + +SPI +--- + + No current support for the SPI interfaces. + + +ATA +--- + + No current support for the on-board ATA block. + + +Document Author +--------------- + +Ben Dooks, (c) 2006 Simtec Electronics diff --git a/Documentation/arm/Samsung-S3C24XX/S3C2413.txt b/Documentation/arm/Samsung-S3C24XX/S3C2413.txt new file mode 100644 index 0000000..ab2a888 --- /dev/null +++ b/Documentation/arm/Samsung-S3C24XX/S3C2413.txt @@ -0,0 +1,21 @@ + S3C2413 ARM Linux Overview + ========================== + +Introduction +------------ + + The S3C2413 is an extended version of the S3C2412, with an camera + interface and mobile DDR memory support. See the S3C2412 support + documentation for more information. + + +Camera Interface +--------------- + + This block is currently not supported. + + +Document Author +--------------- + +Ben Dooks, (c) 2006 Simtec Electronics diff --git a/Documentation/arm/Sharp-LH/ADC-LH7-Touchscreen b/Documentation/arm/Sharp-LH/ADC-LH7-Touchscreen new file mode 100644 index 0000000..1e6a23f --- /dev/null +++ b/Documentation/arm/Sharp-LH/ADC-LH7-Touchscreen @@ -0,0 +1,61 @@ +README on the ADC/Touchscreen Controller +======================================== + +The LH79524 and LH7A404 include a built-in Analog to Digital +controller (ADC) that is used to process input from a touchscreen. +The driver only implements a four-wire touch panel protocol. + +The touchscreen driver is maintenance free except for the pen-down or +touch threshold. Some resistive displays and board combinations may +require tuning of this threshold. The driver exposes some of it's +internal state in the sys filesystem. If the kernel is configured +with it, CONFIG_SYSFS, and sysfs is mounted at /sys, there will be a +directory + + /sys/devices/platform/adc-lh7.0 + +containing these files. + + -r--r--r-- 1 root root 4096 Jan 1 00:00 samples + -rw-r--r-- 1 root root 4096 Jan 1 00:00 threshold + -r--r--r-- 1 root root 4096 Jan 1 00:00 threshold_range + +The threshold is the current touch threshold. It defaults to 750 on +most targets. + + # cat threshold + 750 + +The threshold_range contains the range of valid values for the +threshold. Values outside of this range will be silently ignored. + + # cat threshold_range + 0 1023 + +To change the threshold, write a value to the threshold file. + + # echo 500 > threshold + # cat threshold + 500 + +The samples file contains the most recently sampled values from the +ADC. There are 12. Below are typical of the last sampled values when +the pen has been released. The first two and last two samples are for +detecting whether or not the pen is down. The third through sixth are +X coordinate samples. The seventh through tenth are Y coordinate +samples. + + # cat samples + 1023 1023 0 0 0 0 530 529 530 529 1023 1023 + +To determine a reasonable threshold, press on the touch panel with an +appropriate stylus and read the values from samples. + + # cat samples + 1023 676 92 103 101 102 855 919 922 922 1023 679 + +The first and eleventh samples are discarded. Thus, the important +values are the second and twelfth which are used to determine if the +pen is down. When both are below the threshold, the driver registers +that the pen is down. When either is above the threshold, it +registers then pen is up. diff --git a/Documentation/arm/Sharp-LH/LCDPanels b/Documentation/arm/Sharp-LH/LCDPanels new file mode 100644 index 0000000..fb1b21c --- /dev/null +++ b/Documentation/arm/Sharp-LH/LCDPanels @@ -0,0 +1,59 @@ +README on the LCD Panels +======================== + +Configuration options for several LCD panels, available from Logic PD, +are included in the kernel source. This README will help you +understand the configuration data and give you some guidance for +adding support for other panels if you wish. + + +lcd-panels.h +------------ + +There is no way, at present, to detect which panel is attached to the +system at runtime. Thus the kernel configuration is static. The file +arch/arm/mach-ld7a40x/lcd-panels.h (or similar) defines all of the +panel specific parameters. + +It should be possible for this data to be shared among several device +families. The current layout may be insufficiently general, but it is +amenable to improvement. + + +PIXEL_CLOCK +----------- + +The panel data sheets will give a range of acceptable pixel clocks. +The fundamental LCDCLK input frequency is divided down by a PCD +constant in field '.tim2'. It may happen that it is impossible to set +the pixel clock within this range. A clock which is too slow will +tend to flicker. For the highest quality image, set the clock as high +as possible. + + +MARGINS +------- + +These values may be difficult to glean from the panel data sheet. In +the case of the Sharp panels, the upper margin is explicitly called +out as a specific number of lines from the top of the frame. The +other values may not matter as much as the panels tend to +automatically center the image. + + +Sync Sense +---------- + +The sense of the hsync and vsync pulses may be called out in the data +sheet. On one panel, the sense of these pulses determine the height +of the visible region on the panel. Most of the Sharp panels use +negative sense sync pulses set by the TIM2_IHS and TIM2_IVS bits in +'.tim2'. + + +Pel Layout +---------- + +The Sharp color TFT panels are all configured for 16 bit direct color +modes. The amba-lcd driver sets the pel mode to 565 for 5 bits of +each red and blue and 6 bits of green. diff --git a/Documentation/atomic_ops.txt b/Documentation/atomic_ops.txt index 23a1c24..2a63d56 100644 --- a/Documentation/atomic_ops.txt +++ b/Documentation/atomic_ops.txt @@ -157,13 +157,13 @@ For example, smp_mb__before_atomic_dec() smp_mb__before_atomic_dec(); atomic_dec(&obj->ref_count); -It makes sure that all memory operations preceeding the atomic_dec() +It makes sure that all memory operations preceding the atomic_dec() call are strongly ordered with respect to the atomic counter -operation. In the above example, it guarentees that the assignment of +operation. In the above example, it guarantees that the assignment of "1" to obj->dead will be globally visible to other cpus before the atomic counter decrement. -Without the explicitl smp_mb__before_atomic_dec() call, the +Without the explicit smp_mb__before_atomic_dec() call, the implementation could legally allow the atomic counter update visible to other cpus before the "obj->dead = 1;" assignment. @@ -173,11 +173,11 @@ (smp_mb__after_atomic_dec()) and around (smp_mb__{before,after}_atomic_inc()). A missing memory barrier in the cases where they are required by the -atomic_t implementation above can have disasterous results. Here is -an example, which follows a pattern occuring frequently in the Linux +atomic_t implementation above can have disastrous results. Here is +an example, which follows a pattern occurring frequently in the Linux kernel. It is the use of atomic counters to implement reference counting, and it works such that once the counter falls to zero it can -be guarenteed that no other entity can be accessing the object: +be guaranteed that no other entity can be accessing the object: static void obj_list_add(struct obj *obj) { @@ -291,9 +291,9 @@ to the size of an "unsigned long" C data size. The endianness of the bits within each "unsigned long" are the native endianness of the cpu. - void set_bit(unsigned long nr, volatils unsigned long *addr); - void clear_bit(unsigned long nr, volatils unsigned long *addr); - void change_bit(unsigned long nr, volatils unsigned long *addr); + void set_bit(unsigned long nr, volatile unsigned long *addr); + void clear_bit(unsigned long nr, volatile unsigned long *addr); + void change_bit(unsigned long nr, volatile unsigned long *addr); These routines set, clear, and change, respectively, the bit number indicated by "nr" on the bit mask pointed to by "ADDR". @@ -301,9 +301,9 @@ indicated by "nr" on the bit mask pointe They must execute atomically, yet there are no implicit memory barrier semantics required of these interfaces. - int test_and_set_bit(unsigned long nr, volatils unsigned long *addr); - int test_and_clear_bit(unsigned long nr, volatils unsigned long *addr); - int test_and_change_bit(unsigned long nr, volatils unsigned long *addr); + int test_and_set_bit(unsigned long nr, volatile unsigned long *addr); + int test_and_clear_bit(unsigned long nr, volatile unsigned long *addr); + int test_and_change_bit(unsigned long nr, volatile unsigned long *addr); Like the above, except that these routines return a boolean which indicates whether the changed bit was set _BEFORE_ the atomic bit @@ -335,7 +335,7 @@ subsequent memory operation is made visi /* ... */; obj->killed = 1; -The implementation of test_and_set_bit() must guarentee that +The implementation of test_and_set_bit() must guarantee that "obj->dead = 1;" is visible to cpus before the atomic memory operation done by test_and_set_bit() becomes visible. Likewise, the atomic memory operation done by test_and_set_bit() must become visible before @@ -474,7 +474,7 @@ Now, as far as memory barriers go, as lo strictly orders all subsequent memory operations (including the cas()) with respect to itself, things will be fine. -Said another way, _atomic_dec_and_lock() must guarentee that +Said another way, _atomic_dec_and_lock() must guarantee that a counter dropping to zero is never made visible before the spinlock being acquired. diff --git a/Documentation/console/console.txt b/Documentation/console/console.txt new file mode 100644 index 0000000..d3e1744 --- /dev/null +++ b/Documentation/console/console.txt @@ -0,0 +1,144 @@ +Console Drivers +=============== + +The linux kernel has 2 general types of console drivers. The first type is +assigned by the kernel to all the virtual consoles during the boot process. +This type will be called 'system driver', and only one system driver is allowed +to exist. The system driver is persistent and it can never be unloaded, though +it may become inactive. + +The second type has to be explicitly loaded and unloaded. This will be called +'modular driver' by this document. Multiple modular drivers can coexist at +any time with each driver sharing the console with other drivers including +the system driver. However, modular drivers cannot take over the console +that is currently occupied by another modular driver. (Exception: Drivers that +call take_over_console() will succeed in the takeover regardless of the type +of driver occupying the consoles.) They can only take over the console that is +occupied by the system driver. In the same token, if the modular driver is +released by the console, the system driver will take over. + +Modular drivers, from the programmer's point of view, has to call: + + take_over_console() - load and bind driver to console layer + give_up_console() - unbind and unload driver + +In newer kernels, the following are also available: + + register_con_driver() + unregister_con_driver() + +If sysfs is enabled, the contents of /sys/class/vtconsole can be +examined. This shows the console backends currently registered by the +system which are named vtcon where is an integer fro 0 to 15. Thus: + + ls /sys/class/vtconsole + . .. vtcon0 vtcon1 + +Each directory in /sys/class/vtconsole has 3 files: + + ls /sys/class/vtconsole/vtcon0 + . .. bind name uevent + +What do these files signify? + + 1. bind - this is a read/write file. It shows the status of the driver if + read, or acts to bind or unbind the driver to the virtual consoles + when written to. The possible values are: + + 0 - means the driver is not bound and if echo'ed, commands the driver + to unbind + + 1 - means the driver is bound and if echo'ed, commands the driver to + bind + + 2. name - read-only file. Shows the name of the driver in this format: + + cat /sys/class/vtconsole/vtcon0/name + (S) VGA+ + + '(S)' stands for a (S)ystem driver, ie, it cannot be directly + commanded to bind or unbind + + 'VGA+' is the name of the driver + + cat /sys/class/vtconsole/vtcon1/name + (M) frame buffer device + + In this case, '(M)' stands for a (M)odular driver, one that can be + directly commanded to bind or unbind. + + 3. uevent - ignore this file + +When unbinding, the modular driver is detached first, and then the system +driver takes over the consoles vacated by the driver. Binding, on the other +hand, will bind the driver to the consoles that are currently occupied by a +system driver. + +NOTE1: Binding and binding must be selected in Kconfig. It's under: + +Device Drivers -> Character devices -> Support for binding and unbinding +console drivers + +NOTE2: If any of the virtual consoles are in KD_GRAPHICS mode, then binding or +unbinding will not succeed. An example of an application that sets the console +to KD_GRAPHICS is X. + +How useful is this feature? This is very useful for console driver +developers. By unbinding the driver from the console layer, one can unload the +driver, make changes, recompile, reload and rebind the driver without any need +for rebooting the kernel. For regular users who may want to switch from +framebuffer console to VGA console and vice versa, this feature also makes +this possible. (NOTE NOTE NOTE: Please read fbcon.txt under Documentation/fb +for more details). + +Notes for developers: +===================== + +take_over_console() is now broken up into: + + register_con_driver() + bind_con_driver() - private function + +give_up_console() is a wrapper to unregister_con_driver(), and a driver must +be fully unbound for this call to succeed. con_is_bound() will check if the +driver is bound or not. + +Guidelines for console driver writers: +===================================== + +In order for binding to and unbinding from the console to properly work, +console drivers must follow these guidelines: + +1. All drivers, except system drivers, must call either register_con_driver() + or take_over_console(). register_con_driver() will just add the driver to + the console's internal list. It won't take over the + console. take_over_console(), as it name implies, will also take over (or + bind to) the console. + +2. All resources allocated during con->con_init() must be released in + con->con_deinit(). + +3. All resources allocated in con->con_startup() must be released when the + driver, which was previously bound, becomes unbound. The console layer + does not have a complementary call to con->con_startup() so it's up to the + driver to check when it's legal to release these resources. Calling + con_is_bound() in con->con_deinit() will help. If the call returned + false(), then it's safe to release the resources. This balance has to be + ensured because con->con_startup() can be called again when a request to + rebind the driver to the console arrives. + +4. Upon exit of the driver, ensure that the driver is totally unbound. If the + condition is satisfied, then the driver must call unregister_con_driver() + or give_up_console(). + +5. unregister_con_driver() can also be called on conditions which make it + impossible for the driver to service console requests. This can happen + with the framebuffer console that suddenly lost all of its drivers. + +The current crop of console drivers should still work correctly, but binding +and unbinding them may cause problems. With minimal fixes, these drivers can +be made to work correctly. + +========================== +Antonino Daplas + diff --git a/Documentation/devices.txt b/Documentation/devices.txt index b369a8c..4aaf68f 100644 --- a/Documentation/devices.txt +++ b/Documentation/devices.txt @@ -3,7 +3,7 @@ Maintained by Torben Mathiasen - Last revised: 25 January 2005 + Last revised: 15 May 2006 This list is the Linux Device List, the official registry of allocated device numbers and /dev directory nodes for the Linux operating @@ -94,7 +94,6 @@ Your cooperation is appreciated. 9 = /dev/urandom Faster, less secure random number gen. 10 = /dev/aio Asyncronous I/O notification interface 11 = /dev/kmsg Writes to this come out as printk's - 12 = /dev/oldmem Access to crash dump from kexec kernel 1 block RAM disk 0 = /dev/ram0 First RAM disk 1 = /dev/ram1 Second RAM disk @@ -262,13 +261,13 @@ Your cooperation is appreciated. NOTE: These devices permit both read and write access. 7 block Loopback devices - 0 = /dev/loop0 First loopback device - 1 = /dev/loop1 Second loopback device + 0 = /dev/loop0 First loop device + 1 = /dev/loop1 Second loop device ... - The loopback devices are used to mount filesystems not + The loop devices are used to mount filesystems not associated with block devices. The binding to the - loopback devices is handled by mount(8) or losetup(8). + loop devices is handled by mount(8) or losetup(8). 8 block SCSI disk devices (0-15) 0 = /dev/sda First SCSI disk whole disk @@ -943,7 +942,7 @@ Your cooperation is appreciated. 240 = /dev/ftlp FTL on 16th Memory Technology Device Partitions are handled in the same way as for IDE - disks (see major number 3) expect that the partition + disks (see major number 3) except that the partition limit is 15 rather than 63 per disk (same as SCSI.) 45 char isdn4linux ISDN BRI driver @@ -1168,7 +1167,7 @@ Your cooperation is appreciated. The filename of the encrypted container and the passwords are sent via ioctls (using the sdmount tool) to the master node which then activates them via one of the - /dev/scramdisk/x nodes for loopback mounting (all handled + /dev/scramdisk/x nodes for loop mounting (all handled through the sdmount tool). Requested by: andy@scramdisklinux.org @@ -2538,18 +2537,32 @@ Your cooperation is appreciated. 0 = /dev/usb/lp0 First USB printer ... 15 = /dev/usb/lp15 16th USB printer - 16 = /dev/usb/mouse0 First USB mouse - ... - 31 = /dev/usb/mouse15 16th USB mouse - 32 = /dev/usb/ez0 First USB firmware loader - ... - 47 = /dev/usb/ez15 16th USB firmware loader 48 = /dev/usb/scanner0 First USB scanner ... 63 = /dev/usb/scanner15 16th USB scanner 64 = /dev/usb/rio500 Diamond Rio 500 65 = /dev/usb/usblcd USBLCD Interface (info@usblcd.de) 66 = /dev/usb/cpad0 Synaptics cPad (mouse/LCD) + 96 = /dev/usb/hiddev0 1st USB HID device + ... + 111 = /dev/usb/hiddev15 16th USB HID device + 112 = /dev/usb/auer0 1st auerswald ISDN device + ... + 127 = /dev/usb/auer15 16th auerswald ISDN device + 128 = /dev/usb/brlvgr0 First Braille Voyager device + ... + 131 = /dev/usb/brlvgr3 Fourth Braille Voyager device + 132 = /dev/usb/idmouse ID Mouse (fingerprint scanner) device + 133 = /dev/usb/sisusbvga1 First SiSUSB VGA device + ... + 140 = /dev/usb/sisusbvga8 Eigth SISUSB VGA device + 144 = /dev/usb/lcd USB LCD device + 160 = /dev/usb/legousbtower0 1st USB Legotower device + ... + 175 = /dev/usb/legousbtower15 16th USB Legotower device + 240 = /dev/usb/dabusb0 First daubusb device + ... + 243 = /dev/usb/dabusb3 Fourth dabusb device 180 block USB block devices 0 = /dev/uba First USB block device @@ -2710,6 +2723,17 @@ Your cooperation is appreciated. 1 = /dev/cpu/1/msr MSRs on CPU 1 ... +202 block Xen Virtual Block Device + 0 = /dev/xvda First Xen VBD whole disk + 16 = /dev/xvdb Second Xen VBD whole disk + 32 = /dev/xvdc Third Xen VBD whole disk + ... + 240 = /dev/xvdp Sixteenth Xen VBD whole disk + + Partitions are handled in the same way as for IDE + disks (see major number 3) except that the limit on + partitions is 15. + 203 char CPU CPUID information 0 = /dev/cpu/0/cpuid CPUID on CPU 0 1 = /dev/cpu/1/cpuid CPUID on CPU 1 @@ -2747,11 +2771,27 @@ Your cooperation is appreciated. 46 = /dev/ttyCPM0 PPC CPM (SCC or SMC) - port 0 ... 47 = /dev/ttyCPM5 PPC CPM (SCC or SMC) - port 5 - 50 = /dev/ttyIOC40 Altix serial card + 50 = /dev/ttyIOC0 Altix serial card + ... + 81 = /dev/ttyIOC31 Altix serial card + 82 = /dev/ttyVR0 NEC VR4100 series SIU + 83 = /dev/ttyVR1 NEC VR4100 series DSIU + 84 = /dev/ttyIOC84 Altix ioc4 serial card + ... + 115 = /dev/ttyIOC115 Altix ioc4 serial card + 116 = /dev/ttySIOC0 Altix ioc3 serial card + ... + 147 = /dev/ttySIOC31 Altix ioc3 serial card + 148 = /dev/ttyPSC0 PPC PSC - port 0 + ... + 153 = /dev/ttyPSC5 PPC PSC - port 5 + 154 = /dev/ttyAT0 ATMEL serial port 0 + ... + 169 = /dev/ttyAT15 ATMEL serial port 15 + 170 = /dev/ttyNX0 Hilscher netX serial port 0 ... - 81 = /dev/ttyIOC431 Altix serial card - 82 = /dev/ttyVR0 NEC VR4100 series SIU - 83 = /dev/ttyVR1 NEC VR4100 series DSIU + 185 = /dev/ttyNX15 Hilscher netX serial port 15 + 186 = /dev/ttyJ0 JTAG1 DCC protocol based serial port emulation 205 char Low-density serial ports (alternate device) 0 = /dev/culu0 Callout device for ttyLU0 @@ -2786,8 +2826,8 @@ Your cooperation is appreciated. 50 = /dev/cuioc40 Callout device for ttyIOC40 ... 81 = /dev/cuioc431 Callout device for ttyIOC431 - 82 = /dev/cuvr0 Callout device for ttyVR0 - 83 = /dev/cuvr1 Callout device for ttyVR1 + 82 = /dev/cuvr0 Callout device for ttyVR0 + 83 = /dev/cuvr1 Callout device for ttyVR1 206 char OnStream SC-x0 tape devices @@ -2897,7 +2937,6 @@ Your cooperation is appreciated. ... 196 = /dev/dvb/adapter3/video0 first video decoder of fourth card - 216 char Bluetooth RFCOMM TTY devices 0 = /dev/rfcomm0 First Bluetooth RFCOMM TTY device 1 = /dev/rfcomm1 Second Bluetooth RFCOMM TTY device @@ -3002,12 +3041,43 @@ Your cooperation is appreciated. ioctl()'s can be used to rewind the tape regardless of the device used to access it. -231 char InfiniBand MAD +231 char InfiniBand 0 = /dev/infiniband/umad0 1 = /dev/infiniband/umad1 - ... + ... + 63 = /dev/infiniband/umad63 63rd InfiniBandMad device + 64 = /dev/infiniband/issm0 First InfiniBand IsSM device + 65 = /dev/infiniband/issm1 Second InfiniBand IsSM device + ... + 127 = /dev/infiniband/issm63 63rd InfiniBand IsSM device + 128 = /dev/infiniband/uverbs0 First InfiniBand verbs device + 129 = /dev/infiniband/uverbs1 Second InfiniBand verbs device + ... + 159 = /dev/infiniband/uverbs31 31st InfiniBand verbs device + +232 char Biometric Devices + 0 = /dev/biometric/sensor0/fingerprint first fingerprint sensor on first device + 1 = /dev/biometric/sensor0/iris first iris sensor on first device + 2 = /dev/biometric/sensor0/retina first retina sensor on first device + 3 = /dev/biometric/sensor0/voiceprint first voiceprint sensor on first device + 4 = /dev/biometric/sensor0/facial first facial sensor on first device + 5 = /dev/biometric/sensor0/hand first hand sensor on first device + ... + 10 = /dev/biometric/sensor1/fingerprint first fingerprint sensor on second device + ... + 20 = /dev/biometric/sensor2/fingerprint first fingerprint sensor on third device + ... + +233 char PathScale InfiniPath interconnect + 0 = /dev/ipath Primary device for programs (any unit) + 1 = /dev/ipath0 Access specifically to unit 0 + 2 = /dev/ipath1 Access specifically to unit 1 + ... + 4 = /dev/ipath3 Access specifically to unit 3 + 129 = /dev/ipath_sma Device used by Subnet Management Agent + 130 = /dev/ipath_diag Device used by diagnostics programs -232-239 UNASSIGNED +234-239 UNASSIGNED 240-254 char LOCAL/EXPERIMENTAL USE 240-254 block LOCAL/EXPERIMENTAL USE @@ -3021,6 +3091,28 @@ Your cooperation is appreciated. This major is reserved to assist the expansion to a larger number space. No device nodes with this major should ever be created on the filesystem. + (This is probaly not true anymore, but I'll leave it + for now /Torben) + +---LARGE MAJORS!!!!!--- + +256 char Equinox SST multi-port serial boards + 0 = /dev/ttyEQ0 First serial port on first Equinox SST board + 127 = /dev/ttyEQ127 Last serial port on first Equinox SST board + 128 = /dev/ttyEQ128 First serial port on second Equinox SST board + ... + 1027 = /dev/ttyEQ1027 Last serial port on eighth Equinox SST board + +256 block Resident Flash Disk Flash Translation Layer + 0 = /dev/rfda First RFD FTL layer + 16 = /dev/rfdb Second RFD FTL layer + ... + 240 = /dev/rfdp 16th RFD FTL layer + +257 char Phoenix Technologies Cryptographic Services Driver + 0 = /dev/ptlsec Crypto Services Driver + + **** ADDITIONAL /dev DIRECTORY ENTRIES diff --git a/Documentation/digiepca.txt b/Documentation/digiepca.txt index 88820fe..f2560e2 100644 --- a/Documentation/digiepca.txt +++ b/Documentation/digiepca.txt @@ -2,7 +2,7 @@ NOTE: This driver is obsolete. Digi pr http://www.digi.com for PCI cards. They no longer maintain this driver, and have no 2.6 driver for ISA cards. -This driver requires a number of user-space tools. They can be aquired from +This driver requires a number of user-space tools. They can be acquired from http://www.digi.com, but only works with 2.4 kernels. diff --git a/Documentation/driver-model/overview.txt b/Documentation/driver-model/overview.txt index ac4a7a7..2050c9f 100644 --- a/Documentation/driver-model/overview.txt +++ b/Documentation/driver-model/overview.txt @@ -18,7 +18,7 @@ Traditional driver models implemented so (sometimes just a list) for the devices they control. There wasn't any uniformity across the different bus types. -The current driver model provides a comon, uniform data model for describing +The current driver model provides a common, uniform data model for describing a bus and the devices that can appear under the bus. The unified bus model includes a set of common attributes which all busses carry, and a set of common callbacks, such as device discovery during bus probing, bus diff --git a/Documentation/fb/fbcon.txt b/Documentation/fb/fbcon.txt index 08dce0f..f373df1 100644 --- a/Documentation/fb/fbcon.txt +++ b/Documentation/fb/fbcon.txt @@ -135,10 +135,10 @@ C. Boot options The angle can be changed anytime afterwards by 'echoing' the same numbers to any one of the 2 attributes found in - /sys/class/graphics/fb{x} + /sys/class/graphics/fbcon - con_rotate - rotate the display of the active console - con_rotate_all - rotate the display of all consoles + rotate - rotate the display of the active console + rotate_all - rotate the display of all consoles Console rotation will only become available if Console Rotation Support is compiled in your kernel. @@ -148,5 +148,177 @@ C. Boot options Actually, the underlying fb driver is totally ignorant of console rotation. ---- +C. Attaching, Detaching and Unloading + +Before going on on how to attach, detach and unload the framebuffer console, an +illustration of the dependencies may help. + +The console layer, as with most subsystems, needs a driver that interfaces with +the hardware. Thus, in a VGA console: + +console ---> VGA driver ---> hardware. + +Assuming the VGA driver can be unloaded, one must first unbind the VGA driver +from the console layer before unloading the driver. The VGA driver cannot be +unloaded if it is still bound to the console layer. (See +Documentation/console/console.txt for more information). + +This is more complicated in the case of the the framebuffer console (fbcon), +because fbcon is an intermediate layer between the console and the drivers: + +console ---> fbcon ---> fbdev drivers ---> hardware + +The fbdev drivers cannot be unloaded if it's bound to fbcon, and fbcon cannot +be unloaded if it's bound to the console layer. + +So to unload the fbdev drivers, one must first unbind fbcon from the console, +then unbind the fbdev drivers from fbcon. Fortunately, unbinding fbcon from +the console layer will automatically unbind framebuffer drivers from +fbcon. Thus, there is no need to explicitly unbind the fbdev drivers from +fbcon. + +So, how do we unbind fbcon from the console? Part of the answer is in +Documentation/console/console.txt. To summarize: + +Echo a value to the bind file that represents the framebuffer console +driver. So assuming vtcon1 represents fbcon, then: + +echo 1 > sys/class/vtconsole/vtcon1/bind - attach framebuffer console to + console layer +echo 0 > sys/class/vtconsole/vtcon1/bind - detach framebuffer console from + console layer + +If fbcon is detached from the console layer, your boot console driver (which is +usually VGA text mode) will take over. A few drivers (rivafb and i810fb) will +restore VGA text mode for you. With the rest, before detaching fbcon, you +must take a few additional steps to make sure that your VGA text mode is +restored properly. The following is one of the several methods that you can do: + +1. Download or install vbetool. This utility is included with most + distributions nowadays, and is usually part of the suspend/resume tool. + +2. In your kernel configuration, ensure that CONFIG_FRAMEBUFFER_CONSOLE is set + to 'y' or 'm'. Enable one or more of your favorite framebuffer drivers. + +3. Boot into text mode and as root run: + + vbetool vbestate save > + + The above command saves the register contents of your graphics + hardware to . You need to do this step only once as + the state file can be reused. + +4. If fbcon is compiled as a module, load fbcon by doing: + + modprobe fbcon + +5. Now to detach fbcon: + + vbetool vbestate restore < && \ + echo 0 > /sys/class/vtconsole/vtcon1/bind + +6. That's it, you're back to VGA mode. And if you compiled fbcon as a module, + you can unload it by 'rmmod fbcon' + +7. To reattach fbcon: + + echo 1 > /sys/class/vtconsole/vtcon1/bind + +8. Once fbcon is unbound, all drivers registered to the system will also +become unbound. This means that fbcon and individual framebuffer drivers +can be unloaded or reloaded at will. Reloading the drivers or fbcon will +automatically bind the console, fbcon and the drivers together. Unloading +all the drivers without unloading fbcon will make it impossible for the +console to bind fbcon. + +Notes for vesafb users: +======================= + +Unfortunately, if your bootline includes a vga=xxx parameter that sets the +hardware in graphics mode, such as when loading vesafb, vgacon will not load. +Instead, vgacon will replace the default boot console with dummycon, and you +won't get any display after detaching fbcon. Your machine is still alive, so +you can reattach vesafb. However, to reattach vesafb, you need to do one of +the following: + +Variation 1: + + a. Before detaching fbcon, do + + vbetool vbemode save > # do once for each vesafb mode, + # the file can be reused + + b. Detach fbcon as in step 5. + + c. Attach fbcon + + vbetool vbestate restore < && \ + echo 1 > /sys/class/vtconsole/vtcon1/bind + +Variation 2: + + a. Before detaching fbcon, do: + echo > /sys/class/tty/console/bind + + + vbetool vbemode get + + b. Take note of the mode number + + b. Detach fbcon as in step 5. + + c. Attach fbcon: + + vbetool vbemode set && \ + echo 1 > /sys/class/vtconsole/vtcon1/bind + +Samples: +======== + +Here are 2 sample bash scripts that you can use to bind or unbind the +framebuffer console driver if you are in an X86 box: + +--------------------------------------------------------------------------- +#!/bin/bash +# Unbind fbcon + +# Change this to where your actual vgastate file is located +# Or Use VGASTATE=$1 to indicate the state file at runtime +VGASTATE=/tmp/vgastate + +# path to vbetool +VBETOOL=/usr/local/bin + + +for (( i = 0; i < 16; i++)) +do + if test -x /sys/class/vtconsole/vtcon$i; then + if [ `cat /sys/class/vtconsole/vtcon$i/name | grep -c "frame buffer"` \ + = 1 ]; then + if test -x $VBETOOL/vbetool; then + echo Unbinding vtcon$i + $VBETOOL/vbetool vbestate restore < $VGASTATE + echo 0 > /sys/class/vtconsole/vtcon$i/bind + fi + fi + fi +done + +--------------------------------------------------------------------------- +#!/bin/bash +# Bind fbcon + +for (( i = 0; i < 16; i++)) +do + if test -x /sys/class/vtconsole/vtcon$i; then + if [ `cat /sys/class/vtconsole/vtcon$i/name | grep -c "frame buffer"` \ + = 1 ]; then + echo Unbinding vtcon$i + echo 1 > /sys/class/vtconsole/vtcon$i/bind + fi + fi +done +--------------------------------------------------------------------------- + +-- Antonino Daplas diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 43ab119..99f219a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -6,17 +6,6 @@ be removed from this file. --------------------------- -What: devfs -When: July 2005 -Files: fs/devfs/*, include/linux/devfs_fs*.h and assorted devfs - function calls throughout the kernel tree -Why: It has been unmaintained for a number of years, has unfixable - races, contains a naming policy within the kernel that is - against the LSB, and can be replaced by using udev. -Who: Greg Kroah-Hartman - ---------------------------- - What: RAW driver (CONFIG_RAW_DRIVER) When: December 2005 Why: declared obsolete since kernel 2.6.3 @@ -33,27 +22,12 @@ Who: Adrian Bunk --------------------------- -What: RCU API moves to EXPORT_SYMBOL_GPL -When: April 2006 -Files: include/linux/rcupdate.h, kernel/rcupdate.c -Why: Outside of Linux, the only implementations of anything even - vaguely resembling RCU that I am aware of are in DYNIX/ptx, - VM/XA, Tornado, and K42. I do not expect anyone to port binary - drivers or kernel modules from any of these, since the first two - are owned by IBM and the last two are open-source research OSes. - So these will move to GPL after a grace period to allow - people, who might be using implementations that I am not aware - of, to adjust to this upcoming change. -Who: Paul E. McKenney - ---------------------------- - What: raw1394: requests of type RAW1394_REQ_ISO_SEND, RAW1394_REQ_ISO_LISTEN -When: November 2005 +When: November 2006 Why: Deprecated in favour of the new ioctl-based rawiso interface, which is more efficient. You should really be using libraw1394 for raw1394 access anyway. -Who: Jody McIntyre +Who: Jody McIntyre --------------------------- @@ -147,16 +121,6 @@ Who: NeilBrown --------------------------- -What: au1x00_uart driver -When: January 2006 -Why: The 8250 serial driver now has the ability to deal with the differences - between the standard 8250 family of UARTs and their slightly strange - brother on Alchemy SOCs. The loss of features is not considered an - issue. -Who: Ralf Baechle - ---------------------------- - What: eepro100 network driver When: January 2007 Why: replaced by the e100 driver @@ -192,6 +156,16 @@ Who: Jean Delvare --------------------------- +What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports + (temporary transition config option provided until then) + The transition config option will also be removed at the same time. +When: before 2.6.19 +Why: Unused symbols are both increasing the size of the kernel binary + and are often a sign of "wrong API" +Who: Arjan van de Ven + +--------------------------- + What: remove EXPORT_SYMBOL(tasklist_lock) When: August 2006 Files: kernel/fork.c @@ -212,15 +186,6 @@ Who: Greg Kroah-Hartman --------------------------- -What: Support for NEC DDB5074 and DDB5476 evaluation boards. -When: June 2006 -Why: Board specific code doesn't build anymore since ~2.6.0 and no - users have complained indicating there is no more need for these - boards. This should really be considered a last call. -Who: Ralf Baechle - ---------------------------- - What: USB driver API moves to EXPORT_SYMBOL_GPL When: Febuary 2008 Files: include/linux/usb.h, drivers/usb/core/driver.c @@ -248,3 +213,56 @@ Why: The interface no longer has any cal Who: Nick Piggin --------------------------- + +What: Support for the MIPS EV96100 evaluation board +When: September 2006 +Why: Does no longer build since at least November 15, 2003, apparently + no userbase left. +Who: Ralf Baechle + +--------------------------- + +What: Support for the Momentum / PMC-Sierra Jaguar ATX evaluation board +When: September 2006 +Why: Does no longer build since quite some time, and was never popular, + due to the platform being replaced by successor models. Apparently + no user base left. It also is one of the last users of + WANT_PAGE_VIRTUAL. +Who: Ralf Baechle + +--------------------------- + +What: Support for the Momentum Ocelot, Ocelot 3, Ocelot C and Ocelot G +When: September 2006 +Why: Some do no longer build and apparently there is no user base left + for these platforms. +Who: Ralf Baechle + +--------------------------- + +What: Support for MIPS Technologies' Altas and SEAD evaluation board +When: September 2006 +Why: Some do no longer build and apparently there is no user base left + for these platforms. Hardware out of production since several years. +Who: Ralf Baechle + +--------------------------- + +What: Support for the IT8172-based platforms, ITE 8172G and Globespan IVR +When: September 2006 +Why: Code does no longer build since at least 2.6.0, apparently there is + no user base left for these platforms. Hardware out of production + since several years and hardly a trace of the manufacturer left on + the net. +Who: Ralf Baechle + +--------------------------- + +What: Interrupt only SA_* flags +When: Januar 2007 +Why: The interrupt related SA_* flags are replaced by IRQF_* to move them + out of the signal namespace. + +Who: Thomas Gleixner + +--------------------------- diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 1045da5..d31efbb 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -99,7 +99,7 @@ prototypes: int (*sync_fs)(struct super_block *sb, int wait); void (*write_super_lockfs) (struct super_block *); void (*unlockfs) (struct super_block *); - int (*statfs) (struct super_block *, struct kstatfs *); + int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); @@ -142,15 +142,16 @@ see also dquot_operations section. --------------------------- file_system_type --------------------------- prototypes: - struct super_block *(*get_sb) (struct file_system_type *, int, - const char *, void *); + struct int (*get_sb) (struct file_system_type *, int, + const char *, void *, struct vfsmount *); void (*kill_sb) (struct super_block *); locking rules: may block BKL get_sb yes yes kill_sb yes yes -->get_sb() returns error or a locked superblock (exclusive on ->s_umount). +->get_sb() returns error or 0 with locked superblock attached to the vfsmount +(exclusive on ->s_umount). ->kill_sb() takes a write-locked superblock, does all shutdown work on it, unlocks and drops the reference. diff --git a/Documentation/filesystems/automount-support.txt b/Documentation/filesystems/automount-support.txt index 58c65a1..7cac200 100644 --- a/Documentation/filesystems/automount-support.txt +++ b/Documentation/filesystems/automount-support.txt @@ -19,7 +19,7 @@ following procedure: (2) Have the follow_link() op do the following steps: - (a) Call do_kern_mount() to call the appropriate filesystem to set up a + (a) Call vfs_kern_mount() to call the appropriate filesystem to set up a superblock and gain a vfsmount structure representing it. (b) Copy the nameidata provided as an argument and substitute the dentry diff --git a/Documentation/filesystems/configfs/configfs_example.c b/Documentation/filesystems/configfs/configfs_example.c index 3d4713a..2d6a14a 100644 --- a/Documentation/filesystems/configfs/configfs_example.c +++ b/Documentation/filesystems/configfs/configfs_example.c @@ -264,6 +264,15 @@ static struct config_item_type simple_ch }; +struct simple_children { + struct config_group group; +}; + +static inline struct simple_children *to_simple_children(struct config_item *item) +{ + return item ? container_of(to_config_group(item), struct simple_children, group) : NULL; +} + static struct config_item *simple_children_make_item(struct config_group *group, const char *name) { struct simple_child *simple_child; @@ -304,7 +313,13 @@ static ssize_t simple_children_attr_show "items have only one attribute that is readable and writeable.\n"); } +static void simple_children_release(struct config_item *item) +{ + kfree(to_simple_children(item)); +} + static struct configfs_item_operations simple_children_item_ops = { + .release = simple_children_release, .show_attribute = simple_children_attr_show, }; @@ -345,10 +360,6 @@ static struct configfs_subsystem simple_ * children of its own. */ -struct simple_children { - struct config_group group; -}; - static struct config_group *group_children_make_group(struct config_group *group, const char *name) { struct simple_children *simple_children; diff --git a/Documentation/filesystems/devfs/ChangeLog b/Documentation/filesystems/devfs/ChangeLog deleted file mode 100644 index e5aba52..0000000 --- a/Documentation/filesystems/devfs/ChangeLog +++ /dev/null @@ -1,1977 +0,0 @@ -/* -*- auto-fill -*- */ -=============================================================================== -Changes for patch v1 - -- creation of devfs - -- modified miscellaneous character devices to support devfs -=============================================================================== -Changes for patch v2 - -- bug fix with manual inode creation -=============================================================================== -Changes for patch v3 - -- bugfixes - -- documentation improvements - -- created a couple of scripts (one to save&restore a devfs and the - other to set up compatibility symlinks) - -- devfs support for SCSI discs. New name format is: sd_hHcCiIlL -=============================================================================== -Changes for patch v4 - -- bugfix for the directory reading code - -- bugfix for compilation with kerneld - -- devfs support for generic hard discs - -- rationalisation of the various watchdog drivers -=============================================================================== -Changes for patch v5 - -- support for mounting directly from entries in the devfs (it doesn't - need to be mounted to do this), including the root filesystem. - Mounting of swap partitions also works. Hence, now if you set - CONFIG_DEVFS_ONLY to 'Y' then you won't be able to access your discs - via ordinary device nodes. Naturally, the default is 'N' so that you - can still use your old device nodes. If you want to mount from devfs - entries, make sure you use: append = "root=/dev/sd_..." in your - lilo.conf. It seems LILO looks for the device number (major&minor) - and writes that into the kernel image :-( - -- support for character memory devices (/dev/null, /dev/zero, /dev/full - and so on). Thanks to C. Scott Ananian -=============================================================================== -Changes for patch v6 - -- support for subdirectories - -- support for symbolic links (created by devfs_mk_symlink(), no - support yet for creation via symlink(2)) - -- SCSI disc naming now cast in stone, with the format: - /dev/sd/c0b1t2u3 controller=0, bus=1, ID=2, LUN=3, whole disc - /dev/sd/c0b1t2u3p4 controller=0, bus=1, ID=2, LUN=3, 4th partition - -- loop devices now appear in devfs - -- tty devices, console, serial ports, etc. now appear in devfs - Thanks to C. Scott Ananian - -- bugs with mounting devfs-only devices now fixed -=============================================================================== -Changes for patch v7 - -- SCSI CD-ROMS, tapes and generic devices now appear in devfs -=============================================================================== -Changes for patch v8 - -- bugfix with no-rewind SCSI tapes - -- RAMDISCs now appear in devfs - -- better cleaning up of devfs entries created by various modules - -- interface change to -=============================================================================== -Changes for patch v9 - -- the v8 patch was corrupted somehow, which would affect the patch for - linux/fs/filesystems.c - I've also fixed the v8 patch file on the WWW - -- MetaDevices (/dev/md*) should now appear in devfs -=============================================================================== -Changes for patch v10 - -- bugfix in meta device support for devfs - -- created this ChangeLog file - -- added devfs support to the floppy driver - -- added support for creating sockets in a devfs -=============================================================================== -Changes for patch v11 - -- added DEVFS_FL_HIDE_UNREG flag - -- incorporated better patch for ttyname() in libc 5.4.43 from H.J. Lu. - -- interface change to - -- support for creating symlinks with symlink(2) - -- parallel port printer (/dev/lp*) now appears in devfs -=============================================================================== -Changes for patch v12 - -- added inode check to function - -- improved devfs support when mounting from devfs - -- added call to <> operation when removing swap areas on - devfs devices - -- increased NR_SUPER to 128 to support large numbers of devfs mounts - (for chroot(2) gaols) - -- fixed bug in SCSI disc support: was generating incorrect minors if - SCSI ID's did not start at 0 and increase by 1 - -- support symlink traversal when mounting root -=============================================================================== -Changes for patch v13 - -- added devfs support to soundcard driver - Thanks to Eric Dumas and - C. Scott Ananian - -- added devfs support to the joystick driver - -- loop driver now has it's own subdirectory "/dev/loop/" - -- created and functions - -- fix problem with SCSI disc compatibility names (sd{a,b,c,d,e,f}) - which assumes ID's start at 0 and increase by 1. Also only create - devfs entries for SCSI disc partitions which actually exist - Show new names in partition check - Thanks to Jakub Jelinek -=============================================================================== -Changes for patch v14 - -- bug fix in floppy driver: would not compile without - CONFIG_DEVFS_FS='Y' - Thanks to Jurgen Botz - -- bug fix in loop driver - Thanks to C. Scott Ananian - -- do not create devfs entries for printers not configured - Thanks to C. Scott Ananian - -- do not create devfs entries for serial ports not present - Thanks to C. Scott Ananian - -- ensure is exported from tty_io.c - Thanks to C. Scott Ananian - -- allow unregistering of devfs symlink entries - -- fixed bug in SCSI disc naming introduced in last patch version -=============================================================================== -Changes for patch v15 - -- ported to kernel 2.1.81 -=============================================================================== -Changes for patch v16 - -- created function - -- moved DEVFS_SUPER_MAGIC into header file - -- added DEVFS_FL_HIDE flag - -- created - -- created - -- fixed bugs in searching by major&minor - -- changed interface to , and - - -- fixed inode times when symlink created with symlink(2) - -- change tty driver to do auto-creation of devfs entries - Thanks to C. Scott Ananian - -- fixed bug in genhd.c: whole disc (non-SCSI) was not registered to - devfs - -- updated libc 5.4.43 patch for ttyname() -=============================================================================== -Changes for patch v17 - -- added CONFIG_DEVFS_TTY_COMPAT - Thanks to C. Scott Ananian - -- bugfix in devfs support for drivers/char/lp.c - Thanks to C. Scott Ananian - -- clean up serial driver so that PCMCIA devices unregister correctly - Thanks to C. Scott Ananian - -- fixed bug in genhd.c: whole disc (non-SCSI) was not registered to - devfs [was missing in patch v16] - -- updated libc 5.4.43 patch for ttyname() [was missing in patch v16] - -- all SCSI devices now registered in /dev/sg - -- support removal of devfs entries via unlink(2) -=============================================================================== -Changes for patch v18 - -- added floppy/?u720 floppy entry - -- fixed kerneld support for entries in devfs subdirectories - -- incorporated latest patch for ttyname() in libc 5.4.43 from H.J. Lu. -=============================================================================== -Changes for patch v19 - -- bug fix when looking up unregistered entries: kerneld was not called - -- fixes for kernel 2.1.86 (now requires 2.1.86) -=============================================================================== -Changes for patch v20 - -- only create available floppy entries - Thanks to Andrzej Krzysztofowicz - -- new IDE naming scheme following SCSI format (i.e. /dev/id/c0b0t0u0p1 - instead of /dev/hda1) - Thanks to Andrzej Krzysztofowicz - -- new XT disc naming scheme following SCSI format (i.e. /dev/xd/c0t0p1 - instead of /dev/xda1) - Thanks to Andrzej Krzysztofowicz - -- new non-standard CD-ROM names (i.e. /dev/sbp/c#t#) - Thanks to Andrzej Krzysztofowicz - -- allow symlink traversal when mounting the root filesystem - -- Create entries for MD devices at MD init - Thanks to Christophe Leroy -=============================================================================== -Changes for patch v21 - -- ported to kernel 2.1.91 -=============================================================================== -Changes for patch v22 - -- SCSI host number patch ("scsihosts=" kernel option) - Thanks to Andrzej Krzysztofowicz -=============================================================================== -Changes for patch v23 - -- Fixed persistence bug with device numbers for manually created - device files - -- Fixed problem with recreating symlinks with different content - -- Added CONFIG_DEVFS_MOUNT (mount devfs on /dev at boot time) -=============================================================================== -Changes for patch v24 - -- Switched from CONFIG_KERNELD to CONFIG_KMOD: module autoloading - should now work again - -- Hide entries which are manually unlinked - -- Always invalidate devfs dentry cache when registering entries - -- Support removal of devfs directories via rmdir(2) - -- Ensure directories created by are visible - -- Default no access for "other" for floppy device -=============================================================================== -Changes for patch v25 - -- Updates to CREDITS file and minor IDE numbering change - Thanks to Andrzej Krzysztofowicz - -- Invalidate devfs dentry cache when making directories - -- Invalidate devfs dentry cache when removing entries - -- More informative message if root FS mount fails when devfs - configured - -- Fixed persistence bug with fifos -=============================================================================== -Changes for patch v26 - -- ported to kernel 2.1.97 - -- Changed serial directory from "/dev/serial" to "/dev/tts" and - "/dev/consoles" to "/dev/vc" to be more friendly to new procps -=============================================================================== -Changes for patch v27 - -- Added support for IDE4 and IDE5 - Thanks to Andrzej Krzysztofowicz - -- Documented "scsihosts=" boot parameter - -- Print process command when debugging kerneld/kmod - -- Added debugging for register/unregister/change operations - -- Added "devfs=" boot options - -- Hide unregistered entries by default -=============================================================================== -Changes for patch v28 - -- No longer lock/unlock superblock in (cope with - recent VFS interface change) - -- Do not automatically change ownership/protection of /dev/tty - -- Drop negative dentries when they are released - -- Manage dcache more efficiently -=============================================================================== -Changes for patch v29 - -- Added DEVFS_FL_AUTO_DEVNUM flag -=============================================================================== -Changes for patch v30 - -- No longer set unnecessary methods - -- Ported to kernel 2.1.99-pre3 -=============================================================================== -Changes for patch v31 - -- Added PID display to debugging message - -- Added "diread" and "diwrite" options - -- Ported to kernel 2.1.102 - -- Fixed persistence problem with permissions -=============================================================================== -Changes for patch v32 - -- Fixed devfs support in drivers/block/md.c -=============================================================================== -Changes for patch v33 - -- Support legacy device nodes - -- Fixed bug where recreated inodes were hidden - -- New IDE naming scheme: everything is under /dev/ide -=============================================================================== -Changes for patch v34 - -- Improved debugging in - -- Prevent duplicate calls to in SCSI layer - -- No longer free old dentries in - -- Free all dentries for a given entry when deleting inodes -=============================================================================== -Changes for patch v35 - -- Ported to kernel 2.1.105 (sound driver changes) -=============================================================================== -Changes for patch v36 - -- Fixed sound driver port -=============================================================================== -Changes for patch v37 - -- Minor documentation tweaks -=============================================================================== -Changes for patch v38 - -- More documentation tweaks - -- Fix for sound driver port - -- Removed ttyname-patch (grab libc 5.4.44 instead) - -- Ported to kernel 2.1.107-pre2 (loop driver fix) -=============================================================================== -Changes for patch v39 - -- Ported to kernel 2.1.107 (hd.c hunk broke due to spelling "fixes"). Sigh - -- Removed many #ifdef's, replaced with trickery in include/devfs_fs.h -=============================================================================== -Changes for patch v40 - -- Fix for sound driver port - -- Limit auto-device numbering to majors 128 to 239 -=============================================================================== -Changes for patch v41 - -- Fixed inode times persistence problem -=============================================================================== -Changes for patch v42 - -- Ported to kernel 2.1.108 (drivers/scsi/hosts.c hunk broke) -=============================================================================== -Changes for patch v43 - -- Fixed spelling in debug - -- Fixed bug in parsing "dilookup" - -- More #ifdef's removed - -- Supported Sparc keyboard (/dev/kbd) - -- Supported DSP56001 digital signal processor (/dev/dsp56k) - -- Supported Apple Desktop Bus (/dev/adb) - -- Supported Coda network file system (/dev/cfs*) -=============================================================================== -Changes for patch v44 - -- Fixed devfs inode leak when manually recreating inodes - -- Fixed permission persistence problem when recreating inodes -=============================================================================== -Changes for patch v45 - -- Ported to kernel 2.1.110 -=============================================================================== -Changes for patch v46 - -- Ported to kernel 2.1.112-pre1 - -- Removed harmless "unused variable" compiler warning - -- Fixed modes for manually recreated device nodes -=============================================================================== -Changes for patch v47 - -- Added NULL devfs inode warning in - -- Force all inode nlink values to 1 -=============================================================================== -Changes for patch v48 - -- Added "dimknod" option - -- Set inode nlink to 0 when freeing dentries - -- Added support for virtual console capture devices (/dev/vcs*) - Thanks to Dennis Hou - -- Fixed modes for manually recreated symlinks -=============================================================================== -Changes for patch v49 - -- Ported to kernel 2.1.113 -=============================================================================== -Changes for patch v50 - -- Fixed bugs in recreated directories and symlinks -=============================================================================== -Changes for patch v51 - -- Improved robustness of rc.devfs script - Thanks to Roderich Schupp - -- Fixed bugs in recreated device nodes - -- Fixed bug in currently unused - -- Defined new type - -- Improved debugging when getting entries - -- Fixed bug where directories could be emptied - -- Ported to kernel 2.1.115 -=============================================================================== -Changes for patch v52 - -- Replaced dummy .epoch inode with .devfsd character device - -- Modified rc.devfs to take account of above change - -- Removed spurious driver warning messages when CONFIG_DEVFS_FS=n - -- Implemented devfsd protocol revision 0 -=============================================================================== -Changes for patch v53 - -- Ported to kernel 2.1.116 (kmod change broke hunk) - -- Updated Documentation/Configure.help - -- Test and tty pattern patch for rc.devfs script - Thanks to Roderich Schupp - -- Added soothing message to warning in -=============================================================================== -Changes for patch v54 - -- Ported to kernel 2.1.117 - -- Fixed default permissions in sound driver - -- Added support for frame buffer devices (/dev/fb*) -=============================================================================== -Changes for patch v55 - -- Ported to kernel 2.1.119 - -- Use GCC extensions for structure initialisations - -- Implemented async open notification - -- Incremented devfsd protocol revision to 1 -=============================================================================== -Changes for patch v56 - -- Ported to kernel 2.1.120-pre3 - -- Moved async open notification to end of -=============================================================================== -Changes for patch v57 - -- Ported to kernel 2.1.121 - -- Prepended "/dev/" to module load request - -- Renamed to - -- Created sample modules.conf file -=============================================================================== -Changes for patch v58 - -- Fixed typo "AYSNC" -> "ASYNC" -=============================================================================== -Changes for patch v59 - -- Added open flag for files -=============================================================================== -Changes for patch v60 - -- Ported to kernel 2.1.123-pre2 -=============================================================================== -Changes for patch v61 - -- Set i_blocks=0 and i_blksize=1024 in -=============================================================================== -Changes for patch v62 - -- Ported to kernel 2.1.123 -=============================================================================== -Changes for patch v63 - -- Ported to kernel 2.1.124-pre2 -=============================================================================== -Changes for patch v64 - -- Fixed Unix98 pty support - -- Increased buffer size in to avoid crash and - burn -=============================================================================== -Changes for patch v65 - -- More Unix98 pty support fixes - -- Added test for empty <> in - -- Renamed to and published - -- Created /dev/root symlink - Thanks to Roderich Schupp - with further modifications by me -=============================================================================== -Changes for patch v66 - -- Yet more Unix98 pty support fixes (now tested) - -- Created - -- Support media change checks when CONFIG_DEVFS_ONLY=y - -- Abolished Unix98-style PTY names for old PTY devices -=============================================================================== -Changes for patch v67 - -- Added inline declaration for dummy - -- Removed spurious "unable to register... in devfs" messages when - CONFIG_DEVFS_FS=n - -- Fixed misc. devices when CONFIG_DEVFS_FS=n - -- Limit auto-device numbering to majors 144 to 239 -=============================================================================== -Changes for patch v68 - -- Hide unopened virtual consoles from directory listings - -- Added support for video capture devices - -- Ported to kernel 2.1.125 -=============================================================================== -Changes for patch v69 - -- Fix for CONFIG_VT=n -=============================================================================== -Changes for patch v70 - -- Added support for non-OSS/Free sound cards -=============================================================================== -Changes for patch v71 - -- Ported to kernel 2.1.126-pre2 -=============================================================================== -Changes for patch v72 - -- #ifdef's for CONFIG_DEVFS_DISABLE_OLD_NAMES removed -=============================================================================== -Changes for patch v73 - -- CONFIG_DEVFS_DISABLE_OLD_NAMES replaced with "nocompat" boot option - -- CONFIG_DEVFS_BOOT_OPTIONS removed: boot options always available -=============================================================================== -Changes for patch v74 - -- Removed CONFIG_DEVFS_MOUNT and "mount" boot option and replaced with - "nomount" boot option - -- Documentation updates - -- Updated sample modules.conf -=============================================================================== -Changes for patch v75 - -- Updated sample modules.conf - -- Remount devfs after initrd finishes - -- Ported to kernel 2.1.127 - -- Added support for ISDN - Thanks to Christophe Leroy -=============================================================================== -Changes for patch v76 - -- Updated an email address in ChangeLog - -- CONFIG_DEVFS_ONLY replaced with "only" boot option -=============================================================================== -Changes for patch v77 - -- Added DEVFS_FL_REMOVABLE flag - -- Check for disc change when listing directories with removable media - devices - -- Use DEVFS_FL_REMOVABLE in sd.c - -- Ported to kernel 2.1.128 -=============================================================================== -Changes for patch v78 - -- Only call on first call to - -- Ported to kernel 2.1.129-pre5 - -- ISDN support improvements - Thanks to Christophe Leroy -=============================================================================== -Changes for patch v79 - -- Ported to kernel 2.1.130 - -- Renamed miscdevice "apm" to "apm_bios" to be consistent with - devices.txt -=============================================================================== -Changes for patch v80 - -- Ported to kernel 2.1.131 - -- Updated for VFS change in 2.1.131 -=============================================================================== -Changes for patch v81 - -- Fixed permissions on /dev/ptmx -=============================================================================== -Changes for patch v82 - -- Ported to kernel 2.1.132-pre4 - -- Changed initial permissions on /dev/pts/* - -- Created - -- Added "symlinks" boot option - -- Changed devfs_register_blkdev() back to register_blkdev() for IDE - -- Check for partitions on removable media in -=============================================================================== -Changes for patch v83 - -- Fixed support for ramdisc when using string-based root FS name - -- Ported to kernel 2.2.0-pre1 -=============================================================================== -Changes for patch v84 - -- Ported to kernel 2.2.0-pre7 -=============================================================================== -Changes for patch v85 - -- Compile fixes for driver/sound/sound_common.c (non-module) and - drivers/isdn/isdn_common.c - Thanks to Christophe Leroy - -- Added support for registering regular files - -- Created - -- Added /dev/cpu/mtrr as an alternative interface to /proc/mtrr - -- Update devfs inodes from entries if not changed through FS -=============================================================================== -Changes for patch v86 - -- Ported to kernel 2.2.0-pre9 -=============================================================================== -Changes for patch v87 - -- Fixed bug when mounting non-devfs devices in a devfs -=============================================================================== -Changes for patch v88 - -- Fixed to only initialise temporary inodes - -- Trap for NULL fops in - -- Return -ENODEV in for non-driver inodes - -- Fixed bug when unswapping non-devfs devices in a devfs -=============================================================================== -Changes for patch v89 - -- Switched to C data types in include/linux/devfs_fs.h - -- Switched from PATH_MAX to DEVFS_PATHLEN - -- Updated Documentation/filesystems/devfs/modules.conf to take account - of reverse scanning (!) by modprobe - -- Ported to kernel 2.2.0 -=============================================================================== -Changes for patch v90 - -- CONFIG_DEVFS_DISABLE_OLD_TTY_NAMES replaced with "nottycompat" boot - option - -- CONFIG_DEVFS_TTY_COMPAT removed: existing "symlinks" boot option now - controls this. This means you must have libc 5.4.44 or later, or a - recent version of libc 6 if you use the "symlinks" option -=============================================================================== -Changes for patch v91 - -- Switch from to in - drivers/char/vc_screen.c to fix problems with Midnight Commander -=============================================================================== -Changes for patch v92 - -- Ported to kernel 2.2.2-pre5 -=============================================================================== -Changes for patch v93 - -- Modified in drivers/scsi/sd.c to cope with devices that - don't exist (which happens with new RAID autostart code printk()s) -=============================================================================== -Changes for patch v94 - -- Fixed bug in joystick driver: only first joystick was registered -=============================================================================== -Changes for patch v95 - -- Fixed another bug in joystick driver - -- Fixed to not overrun event buffer -=============================================================================== -Changes for patch v96 - -- Ported to kernel 2.2.5-2 - -- Created - -- Fixed bugs: compatibility entries were not unregistered for: - loop driver - floppy driver - RAMDISC driver - IDE tape driver - SCSI CD-ROM driver - SCSI HDD driver -=============================================================================== -Changes for patch v97 - -- Fixed bugs: compatibility entries were not unregistered for: - ALSA sound driver - partitions in generic disc driver - -- Don't return unregistred entries in - -- Panic in if entry unregistered - -- Don't panic in for duplicates -=============================================================================== -Changes for patch v98 - -- Don't unregister already unregistered entries in - -- Register entry in - -- Unregister entry in - -- Changed to in drivers/char/tty_io.c - -- Ported to kernel 2.2.7 -=============================================================================== -Changes for patch v99 - -- Ported to kernel 2.2.8 - -- Fixed bug in drivers/scsi/sd.c when >16 SCSI discs - -- Disable warning messages when unable to read partition table for - removable media -=============================================================================== -Changes for patch v100 - -- Ported to kernel 2.3.1-pre5 - -- Added "oops-on-panic" boot option - -- Improved debugging in and - -- Register entry in - -- Unregister entry in - -- Register entry in - -- Unregister entry in - -- Added support for ALSA drivers -=============================================================================== -Changes for patch v101 - -- Ported to kernel 2.3.2 -=============================================================================== -Changes for patch v102 - -- Update serial driver to register PCMCIA entries - Thanks to Roch-Alexandre Nomine-Beguin - -- Updated an email address in ChangeLog - -- Hide virtual console capture entries from directory listings when - corresponding console device is not open -=============================================================================== -Changes for patch v103 - -- Ported to kernel 2.3.3 -=============================================================================== -Changes for patch v104 - -- Added documentation for some functions - -- Added "doc" target to fs/devfs/Makefile - -- Added "v4l" directory for video4linux devices - -- Replaced call to in with call to - - -- Moved registration for sr and sg drivers from detect() to attach() - methods - -- Register entries in and unregister in - -- Work around IDE driver treating CD-ROM as gendisk - -- Use instead of in rc.devfs - -- Updated ToDo list - -- Removed "oops-on-panic" boot option: now always Oops -=============================================================================== -Changes for patch v105 - -- Unregister SCSI host from in - Thanks to Zoltán Böszörményi - -- Don't save /dev/log in rc.devfs - -- Ported to kernel 2.3.4-pre1 -=============================================================================== -Changes for patch v106 - -- Fixed silly typo in drivers/scsi/st.c - -- Improved debugging in -=============================================================================== -Changes for patch v107 - -- Added "diunlink" and "nokmod" boot options - -- Removed superfluous warning message in -=============================================================================== -Changes for patch v108 - -- Remove entries when unloading sound module -=============================================================================== -Changes for patch v109 - -- Ported to kernel 2.3.6-pre2 -=============================================================================== -Changes for patch v110 - -- Took account of change to -=============================================================================== -Changes for patch v111 - -- Created separate event queue for each mounted devfs - -- Removed - -- Created new ioctl()s for devfsd - -- Incremented devfsd protocol revision to 3 - -- Fixed bug when re-creating directories: contents were lost - -- Block access to inodes until devfsd updates permissions -=============================================================================== -Changes for patch v112 - -- Modified patch so it applies against 2.3.5 and 2.3.6 - -- Updated an email address in ChangeLog - -- Do not automatically change ownership/protection of /dev/tty - -- Updated sample modules.conf - -- Switched to sending process uid/gid to devfsd - -- Renamed to - -- Added DEVFSD_NOTIFY_LOOKUP event - -- Added DEVFSD_NOTIFY_CHANGE event - -- Added DEVFSD_NOTIFY_CREATE event - -- Incremented devfsd protocol revision to 4 - -- Moved kernel-specific stuff to include/linux/devfs_fs_kernel.h -=============================================================================== -Changes for patch v113 - -- Ported to kernel 2.3.9 - -- Restricted permissions on some block devices -=============================================================================== -Changes for patch v114 - -- Added support for /dev/netlink - Thanks to Dennis Hou - -- Return EISDIR rather than EINVAL for read(2) on directories - -- Ported to kernel 2.3.10 -=============================================================================== -Changes for patch v115 - -- Added support for all remaining character devices - Thanks to Dennis Hou - -- Cleaned up netlink support -=============================================================================== -Changes for patch v116 - -- Added support for /dev/parport%d - Thanks to Tim Waugh - -- Fixed parallel port ATAPI tape driver - -- Fixed Atari SLM laser printer driver -=============================================================================== -Changes for patch v117 - -- Added support for COSA card - Thanks to Dennis Hou - -- Fixed drivers/char/ppdev.c: missing #include - -- Fixed drivers/char/ftape/zftape/zftape-init.c - Thanks to Vladimir Popov -=============================================================================== -Changes for patch v118 - -- Ported to kernel 2.3.15-pre3 - -- Fixed bug in loop driver - -- Unregister /dev/lp%d entries in drivers/char/lp.c - Thanks to Maciej W. Rozycki -=============================================================================== -Changes for patch v119 - -- Ported to kernel 2.3.16 -=============================================================================== -Changes for patch v120 - -- Fixed bug in drivers/scsi/scsi.c - -- Added /dev/ppp - Thanks to Dennis Hou - -- Ported to kernel 2.3.17 -=============================================================================== -Changes for patch v121 - -- Fixed bug in drivers/block/loop.c - -- Ported to kernel 2.3.18 -=============================================================================== -Changes for patch v122 - -- Ported to kernel 2.3.19 -=============================================================================== -Changes for patch v123 - -- Ported to kernel 2.3.20 -=============================================================================== -Changes for patch v124 - -- Ported to kernel 2.3.21 -=============================================================================== -Changes for patch v125 - -- Created , , - and - Added <> parameter to , , - and - Work sponsored by SGI - -- Fixed apparent bug in COSA driver - -- Re-instated "scsihosts=" boot option -=============================================================================== -Changes for patch v126 - -- Always create /dev/pts if CONFIG_UNIX98_PTYS=y - -- Fixed call to in drivers/block/ide-disk.c - Thanks to Dennis Hou - -- Allow multiple unregistrations - -- Created /dev/scsi hierarchy - Work sponsored by SGI -=============================================================================== -Changes for patch v127 - -Work sponsored by SGI - -- No longer disable devpts if devfs enabled (caveat emptor) - -- Added flags array to struct gendisk and removed code from - drivers/scsi/sd.c - -- Created /dev/discs hierarchy -=============================================================================== -Changes for patch v128 - -Work sponsored by SGI - -- Created /dev/cdroms hierarchy -=============================================================================== -Changes for patch v129 - -Work sponsored by SGI - -- Removed compatibility entries for sound devices - -- Removed compatibility entries for printer devices - -- Removed compatibility entries for video4linux devices - -- Removed compatibility entries for parallel port devices - -- Removed compatibility entries for frame buffer devices -=============================================================================== -Changes for patch v130 - -Work sponsored by SGI - -- Added major and minor number to devfsd protocol - -- Incremented devfsd protocol revision to 5 - -- Removed compatibility entries for SoundBlaster CD-ROMs - -- Removed compatibility entries for netlink devices - -- Removed compatibility entries for SCSI generic devices - -- Removed compatibility entries for SCSI tape devices -=============================================================================== -Changes for patch v131 - -Work sponsored by SGI - -- Support info pointer for all devfs entry types - -- Added <> parameter to and - -- Removed /dev/st hierarchy - -- Removed /dev/sg hierarchy - -- Removed compatibility entries for loop devices - -- Removed compatibility entries for IDE tape devices - -- Removed compatibility entries for SCSI CD-ROMs - -- Removed /dev/sr hierarchy -=============================================================================== -Changes for patch v132 - -Work sponsored by SGI - -- Removed compatibility entries for floppy devices - -- Removed compatibility entries for RAMDISCs - -- Removed compatibility entries for meta-devices - -- Removed compatibility entries for SCSI discs - -- Created - -- Removed /dev/sd hierarchy - -- Support "../" when searching devfs namespace - -- Created /dev/ide/host* hierarchy - -- Supported IDE hard discs in /dev/ide/host* hierarchy - -- Removed compatibility entries for IDE discs - -- Removed /dev/ide/hd hierarchy - -- Supported IDE CD-ROMs in /dev/ide/host* hierarchy - -- Removed compatibility entries for IDE CD-ROMs - -- Removed /dev/ide/cd hierarchy -=============================================================================== -Changes for patch v133 - -Work sponsored by SGI - -- Created - -- Fixed bug in fs/partitions/check.c when rescanning -=============================================================================== -Changes for patch v134 - -Work sponsored by SGI - -- Removed /dev/sd, /dev/sr, /dev/st and /dev/sg directories - -- Removed /dev/ide/hd directory - -- Exported - -- Created and /dev/tapes hierarchy - -- Removed /dev/ide/mt hierarchy - -- Removed /dev/ide/fd hierarchy - -- Ported to kernel 2.3.25 -=============================================================================== -Changes for patch v135 - -Work sponsored by SGI - -- Removed compatibility entries for virtual console capture devices - -- Removed unused - -- Removed compatibility entries for serial devices - -- Removed compatibility entries for console devices - -- Do not hide entries from devfsd or children - -- Removed DEVFS_FL_TTY_COMPAT flag - -- Removed "nottycompat" boot option - -- Removed -=============================================================================== -Changes for patch v136 - -Work sponsored by SGI - -- Moved BSD pty devices to /dev/pty - -- Added DEVFS_FL_WAIT flag -=============================================================================== -Changes for patch v137 - -Work sponsored by SGI - -- Really fixed bug in fs/partitions/check.c when rescanning - -- Support new "disc" naming scheme in - -- Allow NULL fops in - -- Removed redundant name functions in SCSI disc and IDE drivers -=============================================================================== -Changes for patch v138 - -Work sponsored by SGI - -- Fixed old bugs in drivers/block/paride/pt.c, drivers/char/tpqic02.c, - drivers/net/wan/cosa.c and drivers/scsi/scsi.c - Thanks to Sergey Kubushin - -- Fall back to major table if NULL fops given to -=============================================================================== -Changes for patch v139 - -Work sponsored by SGI - -- Corrected and moved and declarations - from arch/alpha/kernel/osf_sys.c to include/linux/fs.h - -- Removed name function from struct gendisk - -- Updated devfs FAQ -=============================================================================== -Changes for patch v140 - -Work sponsored by SGI - -- Ported to kernel 2.3.27 -=============================================================================== -Changes for patch v141 - -Work sponsored by SGI - -- Bug fix in arch/m68k/atari/joystick.c - -- Moved ISDN and capi devices to /dev/isdn -=============================================================================== -Changes for patch v142 - -Work sponsored by SGI - -- Bug fix in drivers/block/ide-probe.c (patch confusion) -=============================================================================== -Changes for patch v143 - -Work sponsored by SGI - -- Bug fix in drivers/block/blkpg.c:partition_name() -=============================================================================== -Changes for patch v144 - -Work sponsored by SGI - -- Ported to kernel 2.3.29 - -- Removed calls to from cdu31a, cm206, mcd and mcdx - CD-ROM drivers: generic driver handles this now - -- Moved joystick devices to /dev/joysticks -=============================================================================== -Changes for patch v145 - -Work sponsored by SGI - -- Ported to kernel 2.3.30-pre3 - -- Register whole-disc entry even for invalid partition tables - -- Fixed bug in mounting root FS when initrd enabled - -- Fixed device entry leak with IDE CD-ROMs - -- Fixed compile problem with drivers/isdn/isdn_common.c - -- Moved COSA devices to /dev/cosa - -- Support fifos when unregistering - -- Created and used in many drivers - -- Moved Coda devices to /dev/coda - -- Moved parallel port IDE tapes to /dev/pt - -- Moved parallel port IDE generic devices to /dev/pg -=============================================================================== -Changes for patch v146 - -Work sponsored by SGI - -- Removed obsolete DEVFS_FL_COMPAT and DEVFS_FL_TOLERANT flags - -- Fixed compile problem with fs/coda/psdev.c - -- Reinstate change to in - drivers/block/ide-probe.c now that fs/isofs/inode.c is fixed - -- Switched to in drivers/block/floppy.c, - drivers/scsi/sr.c and drivers/block/md.c - -- Moved DAC960 devices to /dev/dac960 -=============================================================================== -Changes for patch v147 - -Work sponsored by SGI - -- Ported to kernel 2.3.32-pre4 -=============================================================================== -Changes for patch v148 - -Work sponsored by SGI - -- Removed kmod support: use devfsd instead - -- Moved miscellaneous character devices to /dev/misc -=============================================================================== -Changes for patch v149 - -Work sponsored by SGI - -- Ensure include/linux/joystick.h is OK for user-space - -- Improved debugging in - -- Ensure dentries created by devfsd will be cleaned up -=============================================================================== -Changes for patch v150 - -Work sponsored by SGI - -- Ported to kernel 2.3.34 -=============================================================================== -Changes for patch v151 - -Work sponsored by SGI - -- Ported to kernel 2.3.35-pre1 - -- Created -=============================================================================== -Changes for patch v152 - -Work sponsored by SGI - -- Updated sample modules.conf - -- Ported to kernel 2.3.36-pre1 -=============================================================================== -Changes for patch v153 - -Work sponsored by SGI - -- Ported to kernel 2.3.42 - -- Removed -=============================================================================== -Changes for patch v154 - -Work sponsored by SGI - -- Took account of device number changes for /dev/fb* -=============================================================================== -Changes for patch v155 - -Work sponsored by SGI - -- Ported to kernel 2.3.43-pre8 - -- Moved /dev/tty0 to /dev/vc/0 - -- Moved sequence number formatting from <_tty_make_name> to drivers -=============================================================================== -Changes for patch v156 - -Work sponsored by SGI - -- Fixed breakage in drivers/scsi/sd.c due to recent SCSI changes -=============================================================================== -Changes for patch v157 - -Work sponsored by SGI - -- Ported to kernel 2.3.45 -=============================================================================== -Changes for patch v158 - -Work sponsored by SGI - -- Ported to kernel 2.3.46-pre2 -=============================================================================== -Changes for patch v159 - -Work sponsored by SGI - -- Fixed drivers/block/md.c - Thanks to Mike Galbraith - -- Documentation fixes - -- Moved device registration from to - Thanks to Tim Waugh -=============================================================================== -Changes for patch v160 - -Work sponsored by SGI - -- Fixed drivers/char/joystick/joystick.c - Thanks to Vojtech Pavlik - -- Documentation updates - -- Fixed arch/i386/kernel/mtrr.c if procfs and devfs not enabled - -- Fixed drivers/char/stallion.c -=============================================================================== -Changes for patch v161 - -Work sponsored by SGI - -- Remove /dev/ide when ide-mod is unloaded - -- Fixed bug in drivers/block/ide-probe.c when secondary but no primary - -- Added DEVFS_FL_NO_PERSISTENCE flag - -- Used new DEVFS_FL_NO_PERSISTENCE flag for Unix98 pty slaves - -- Removed unnecessary call to in - - -- Only set auto-ownership for /dev/pty/s* -=============================================================================== -Changes for patch v162 - -Work sponsored by SGI - -- Set inode->i_size to correct size for symlinks - Thanks to Jeremy Fitzhardinge - -- Only give lookup() method to directories to comply with new VFS - assumptions - -- Remove unnecessary tests in symlink methods - -- Don't kill existing block ops in - -- Restore auto-ownership for /dev/pty/m* -=============================================================================== -Changes for patch v163 - -Work sponsored by SGI - -- Don't create missing directories in - -- Removed Documentation/filesystems/devfs/mk-devlinks - -- Updated Documentation/filesystems/devfs/README -=============================================================================== -Changes for patch v164 - -Work sponsored by SGI - -- Fixed CONFIG_DEVFS breakage in drivers/char/serial.c introduced in - linux-2.3.99-pre6-7 -=============================================================================== -Changes for patch v165 - -Work sponsored by SGI - -- Ported to kernel 2.3.99-pre6 -=============================================================================== -Changes for patch v166 - -Work sponsored by SGI - -- Added CONFIG_DEVFS_MOUNT -=============================================================================== -Changes for patch v167 - -Work sponsored by SGI - -- Updated Documentation/filesystems/devfs/README - -- Updated sample modules.conf -=============================================================================== -Changes for patch v168 - -Work sponsored by SGI - -- Disabled multi-mount capability (use VFS bindings instead) - -- Updated README from master HTML file -=============================================================================== -Changes for patch v169 - -Work sponsored by SGI - -- Removed multi-mount code - -- Removed compatibility macros: VFS has changed too much -=============================================================================== -Changes for patch v170 - -Work sponsored by SGI - -- Updated README from master HTML file - -- Merged devfs inode into devfs entry -=============================================================================== -Changes for patch v171 - -Work sponsored by SGI - -- Updated sample modules.conf - -- Removed dead code in which used to call - - -- Ported to kernel 2.4.0-test2-pre3 -=============================================================================== -Changes for patch v172 - -Work sponsored by SGI - -- Changed interface to - -- Changed interface to -=============================================================================== -Changes for patch v173 - -Work sponsored by SGI - -- Simplified interface to - -- Simplified interface to - -- Simplified interface to -=============================================================================== -Changes for patch v174 - -Work sponsored by SGI - -- Updated README from master HTML file -=============================================================================== -Changes for patch v175 - -Work sponsored by SGI - -- DocBook update for fs/devfs/base.c - Thanks to Tim Waugh - -- Removed stale fs/tunnel.c (was never used or completed) -=============================================================================== -Changes for patch v176 - -Work sponsored by SGI - -- Updated ToDo list - -- Removed sample modules.conf: now distributed with devfsd - -- Updated README from master HTML file - -- Ported to kernel 2.4.0-test3-pre4 (which had devfs-patch-v174) -=============================================================================== -Changes for patch v177 - -- Updated README from master HTML file - -- Documentation cleanups - -- Ensure terminates string for root entry - Thanks to Tim Jansen - -- Exported to modules - -- Make send events to devfsd - -- Cleaned up option processing in - -- Fixed bugs in handling symlinks: could leak or cause Oops - -- Cleaned up directory handling by separating fops - Thanks to Alexander Viro -=============================================================================== -Changes for patch v178 - -- Fixed handling of inverted options in -=============================================================================== -Changes for patch v179 - -- Adjusted to account for fix -=============================================================================== -Changes for patch v180 - -- Fixed !CONFIG_DEVFS_FS stub declaration of -=============================================================================== -Changes for patch v181 - -- Answered question posed by Al Viro and removed his comments from - -- Moved setting of registered flag after other fields are changed - -- Fixed race between and - -- Global VFS changes added bogus BKL to devfsd_close(): removed - -- Widened locking in and - -- Replaced stack usage with kmalloc - -- Simplified locking in and fixed memory leak -=============================================================================== -Changes for patch v182 - -- Created and - -- Removed broken devnum allocation and use - -- Fixed old devnum leak by calling new - -- Created - -- Fixed number leak for /dev/cdroms/cdrom%d - -- Fixed number leak for /dev/discs/disc%d -=============================================================================== -Changes for patch v183 - -- Fixed bug in which could hang boot process -=============================================================================== -Changes for patch v184 - -- Documentation typo fix for fs/devfs/util.c - -- Fixed drivers/char/stallion.c for devfs - -- Added DEVFSD_NOTIFY_DELETE event - -- Updated README from master HTML file - -- Removed #include from fs/devfs/base.c -=============================================================================== -Changes for patch v185 - -- Made and in fs/devfs/util.c - private - -- Fixed inode table races by removing it and using inode->u.generic_ip - instead - -- Moved into - -- Moved into -=============================================================================== -Changes for patch v186 - -- Fixed race in for uni-processor - -- Updated README from master HTML file -=============================================================================== -Changes for patch v187 - -- Fixed drivers/char/stallion.c for devfs - -- Fixed drivers/char/rocket.c for devfs - -- Fixed bug in : limited to 128 numbers -=============================================================================== -Changes for patch v188 - -- Updated major masks in fs/devfs/util.c up to Linus' "no new majors" - proclamation. Block: were 126 now 122 free, char: were 26 now 19 free - -- Updated README from master HTML file - -- Removed remnant of multi-mount support in - -- Removed unused DEVFS_FL_SHOW_UNREG flag -=============================================================================== -Changes for patch v189 - -- Removed nlink field from struct devfs_inode - -- Removed auto-ownership for /dev/pty/* (BSD ptys) and used - DEVFS_FL_CURRENT_OWNER|DEVFS_FL_NO_PERSISTENCE for /dev/pty/s* (just - like Unix98 pty slaves) and made /dev/pty/m* rw-rw-rw- access -=============================================================================== -Changes for patch v190 - -- Updated README from master HTML file - -- Replaced BKL with global rwsem to protect symlink data (quick and - dirty hack) -=============================================================================== -Changes for patch v191 - -- Replaced global rwsem for symlink with per-link refcount -=============================================================================== -Changes for patch v192 - -- Removed unnecessary #ifdef CONFIG_DEVFS_FS from arch/i386/kernel/mtrr.c - -- Ported to kernel 2.4.10-pre11 - -- Set inode->i_mapping->a_ops for block nodes in -=============================================================================== -Changes for patch v193 - -- Went back to global rwsem for symlinks (refcount scheme no good) -=============================================================================== -Changes for patch v194 - -- Fixed overrun in by removing function (not needed) - -- Updated README from master HTML file -=============================================================================== -Changes for patch v195 - -- Fixed buffer underrun in - -- Moved down_read() from to -=============================================================================== -Changes for patch v196 - -- Fixed race in when setting event mask - Thanks to Kari Hurtta - -- Avoid deadlock in by using temporary buffer -=============================================================================== -Changes for patch v197 - -- First release of new locking code for devfs core (v1.0) - -- Fixed bug in drivers/cdrom/cdrom.c -=============================================================================== -Changes for patch v198 - -- Discard temporary buffer, now use "%s" for dentry names - -- Don't generate path in : use fake entry instead - -- Use "existing" directory in <_devfs_make_parent_for_leaf> - -- Use slab cache rather than fixed buffer for devfsd events -=============================================================================== -Changes for patch v199 - -- Removed obsolete usage of DEVFS_FL_NO_PERSISTENCE - -- Send DEVFSD_NOTIFY_REGISTERED events in - -- Fixed locking bug in due to typo - -- Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from devfsd - or children -=============================================================================== -Changes for patch v200 - -- Ported to kernel 2.5.1-pre2 -=============================================================================== -Changes for patch v201 - -- Fixed bug in : was dereferencing freed pointer -=============================================================================== -Changes for patch v202 - -- Fixed bug in : was dereferencing freed pointer - -- Added process group check for devfsd privileges -=============================================================================== -Changes for patch v203 - -- Use SLAB_ATOMIC in from -=============================================================================== -Changes for patch v204 - -- Removed long obsolete rc.devfs - -- Return old entry in for 2.4.x kernels - -- Updated README from master HTML file - -- Increment refcount on module in - -- Created and exported - -- Increment refcount on module in - -- Created and used where needed to fix races - -- Added clarifying comments in response to preliminary EMC code review - -- Added poisoning to - -- Improved debugging messages - -- Fixed unregister bugs in drivers/md/lvm-fs.c -=============================================================================== -Changes for patch v205 - -- Corrected (made useful) debugging message in - -- Moved in to - -- Fixed drivers/md/lvm-fs.c to create "lvm" entry - -- Added magic number to guard against scribbling drivers - -- Only return old entry in if a directory - -- Defined macros for error and debug messages - -- Updated README from master HTML file -=============================================================================== -Changes for patch v206 - -- Added support for multiple Compaq cpqarray controllers - -- Fixed (rare, old) race in -=============================================================================== -Changes for patch v207 - -- Fixed deadlock bug in - -- Tag VFS deletable in if handle ignored - -- Updated README from master HTML file -=============================================================================== -Changes for patch v208 - -- Added KERN_* to remaining messages - -- Cleaned up declaration of - -- Updated README from master HTML file -=============================================================================== -Changes for patch v209 - -- Updated README from master HTML file - -- Removed silently introduced calls to lock_kernel() and - unlock_kernel() due to recent VFS locking changes. BKL isn't - required in devfs - -- Changed to allow later additions if not yet empty - -- Added calls to in drivers/block/blkpc.c - and - -- Fixed bug in : was clearing beyond - bitfield - -- Fixed bitfield data type for - -- Made major bitfield type and initialiser 64 bit safe -=============================================================================== -Changes for patch v210 - -- Updated fs/devfs/util.c to fix shift warning on 64 bit machines - Thanks to Anton Blanchard - -- Updated README from master HTML file -=============================================================================== -Changes for patch v211 - -- Do not put miscellaneous character devices in /dev/misc if they - specify their own directory (i.e. contain a '/' character) - -- Copied macro for error messages from fs/devfs/base.c to - fs/devfs/util.c and made use of this macro - -- Removed 2.4.x compatibility code from fs/devfs/base.c -=============================================================================== -Changes for patch v212 - -- Added BKL to because drivers still need it -=============================================================================== -Changes for patch v213 - -- Protected and - from changing directory contents -=============================================================================== -Changes for patch v214 - -- Switched to ISO C structure field initialisers - -- Switch to set_current_state() and move before add_wait_queue() - -- Updated README from master HTML file - -- Fixed devfs entry leak in when *readdir fails -=============================================================================== -Changes for patch v215 - -- Created - -- Switched many functions from to - - -- Switched many functions from to -=============================================================================== -Changes for patch v216 - -- Switched arch/ia64/sn/io/hcl.c from to - - -- Removed deprecated -=============================================================================== -Changes for patch v217 - -- Exported and to modules - -- Updated README from master HTML file - -- Fixed module unload race in -=============================================================================== -Changes for patch v218 - -- Removed DEVFS_FL_AUTO_OWNER flag - -- Switched lingering structure field initialiser to ISO C - -- Added locking when setting/clearing flags - -- Documentation fix in fs/devfs/util.c diff --git a/Documentation/filesystems/devfs/README b/Documentation/filesystems/devfs/README deleted file mode 100644 index aabfba2..0000000 --- a/Documentation/filesystems/devfs/README +++ /dev/null @@ -1,1959 +0,0 @@ -Devfs (Device File System) FAQ - - -Linux Devfs (Device File System) FAQ -Richard Gooch -20-AUG-2002 - - -Document languages: - - - - - - - ------------------------------------------------------------------------------ - -NOTE: the master copy of this document is available online at: - -http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html -and looks much better than the text version distributed with the -kernel sources. A mirror site is available at: - -http://www.ras.ucalgary.ca/~rgooch/linux/docs/devfs.html - -There is also an optional daemon that may be used with devfs. You can -find out more about it at: - -http://www.atnf.csiro.au/~rgooch/linux/ - -A mailing list is available which you may subscribe to. Send -email -to majordomo@oss.sgi.com with the following line in the -body of the message: -subscribe devfs -To unsubscribe, send the message body: -unsubscribe devfs -instead. The list is archived at - -http://oss.sgi.com/projects/devfs/archive/. - ------------------------------------------------------------------------------ - -Contents - - -What is it? - -Why do it? - -Who else does it? - -How it works - -Operational issues (essential reading) - -Instructions for the impatient -Permissions persistence across reboots -Dealing with drivers without devfs support -All the way with Devfs -Other Issues -Kernel Naming Scheme -Devfsd Naming Scheme -Old Compatibility Names -SCSI Host Probing Issues - - - -Device drivers currently ported - -Allocation of Device Numbers - -Questions and Answers - -Making things work -Alternatives to devfs -What I don't like about devfs -How to report bugs -Strange kernel messages -Compilation problems with devfsd - - -Other resources - -Translations of this document - - ------------------------------------------------------------------------------ - - -What is it? - -Devfs is an alternative to "real" character and block special devices -on your root filesystem. Kernel device drivers can register devices by -name rather than major and minor numbers. These devices will appear in -devfs automatically, with whatever default ownership and -protection the driver specified. A daemon (devfsd) can be used to -override these defaults. Devfs has been in the kernel since 2.3.46. - -NOTE that devfs is entirely optional. If you prefer the old -disc-based device nodes, then simply leave CONFIG_DEVFS_FS=n (the -default). In this case, nothing will change. ALSO NOTE that if you do -enable devfs, the defaults are such that full compatibility is -maintained with the old devices names. - -There are two aspects to devfs: one is the underlying device -namespace, which is a namespace just like any mounted filesystem. The -other aspect is the filesystem code which provides a view of the -device namespace. The reason I make a distinction is because devfs -can be mounted many times, with each mount showing the same device -namespace. Changes made are global to all mounted devfs filesystems. -Also, because the devfs namespace exists without any devfs mounts, you -can easily mount the root filesystem by referring to an entry in the -devfs namespace. - - -The cost of devfs is a small increase in kernel code size and memory -usage. About 7 pages of code (some of that in __init sections) and 72 -bytes for each entry in the namespace. A modest system has only a -couple of hundred device entries, so this costs a few more -pages. Compare this with the suggestion to put /dev on a ramdisc. - -On a typical machine, the cost is under 0.2 percent. On a modest -system with 64 MBytes of RAM, the cost is under 0.1 percent. The -accusations of "bloatware" levelled at devfs are not justified. - ------------------------------------------------------------------------------ - - -Why do it? - -There are several problems that devfs addresses. Some of these -problems are more serious than others (depending on your point of -view), and some can be solved without devfs. However, the totality of -these problems really calls out for devfs. - -The choice is a patchwork of inefficient user space solutions, which -are complex and likely to be fragile, or to use a simple and efficient -devfs which is robust. - -There have been many counter-proposals to devfs, all seeking to -provide some of the benefits without actually implementing devfs. So -far there has been an absence of code and no proposed alternative has -been able to provide all the features that devfs does. Further, -alternative proposals require far more complexity in user-space (and -still deliver less functionality than devfs). Some people have the -mantra of reducing "kernel bloat", but don't consider the effects on -user-space. - -A good solution limits the total complexity of kernel-space and -user-space. - - -Major&minor allocation - -The existing scheme requires the allocation of major and minor device -numbers for each and every device. This means that a central -co-ordinating authority is required to issue these device numbers -(unless you're developing a "private" device driver), in order to -preserve uniqueness. Devfs shifts the burden to a namespace. This may -not seem like a huge benefit, but actually it is. Since driver authors -will naturally choose a device name which reflects the functionality -of the device, there is far less potential for namespace conflict. -Solving this requires a kernel change. - -/dev management - -Because you currently access devices through device nodes, these must -be created by the system administrator. For standard devices you can -usually find a MAKEDEV programme which creates all these (hundreds!) -of nodes. This means that changes in the kernel must be reflected by -changes in the MAKEDEV programme, or else the system administrator -creates device nodes by hand. - -The basic problem is that there are two separate databases of -major and minor numbers. One is in the kernel and one is in /dev (or -in a MAKEDEV programme, if you want to look at it that way). This is -duplication of information, which is not good practice. -Solving this requires a kernel change. - -/dev growth - -A typical /dev has over 1200 nodes! Most of these devices simply don't -exist because the hardware is not available. A huge /dev increases the -time to access devices (I'm just referring to the dentry lookup times -and the time taken to read inodes off disc: the next subsection shows -some more horrors). - -An example of how big /dev can grow is if we consider SCSI devices: - -host 6 bits (say up to 64 hosts on a really big machine) -channel 4 bits (say up to 16 SCSI buses per host) -id 4 bits -lun 3 bits -partition 6 bits -TOTAL 23 bits - - -This requires 8 Mega (1024*1024) inodes if we want to store all -possible device nodes. Even if we scrap everything but id,partition -and assume a single host adapter with a single SCSI bus and only one -logical unit per SCSI target (id), that's still 10 bits or 1024 -inodes. Each VFS inode takes around 256 bytes (kernel 2.1.78), so -that's 256 kBytes of inode storage on disc (assuming real inodes take -a similar amount of space as VFS inodes). This is actually not so bad, -because disc is cheap these days. Embedded systems would care about -256 kBytes of /dev inodes, but you could argue that embedded systems -would have hand-tuned /dev directories. I've had to do just that on my -embedded systems, but I would rather just leave it to devfs. - -Another issue is the time taken to lookup an inode when first -referenced. Not only does this take time in scanning through a list in -memory, but also the seek times to read the inodes off disc. -This could be solved in user-space using a clever programme which -scanned the kernel logs and deleted /dev entries which are not -available and created them when they were available. This programme -would need to be run every time a new module was loaded, which would -slow things down a lot. - -There is an existing programme called scsidev which will automatically -create device nodes for SCSI devices. It can do this by scanning files -in /proc/scsi. Unfortunately, to extend this idea to other device -nodes would require significant modifications to existing drivers (so -they too would provide information in /proc). This is a non-trivial -change (I should know: devfs has had to do something similar). Once -you go to this much effort, you may as well use devfs itself (which -also provides this information). Furthermore, such a system would -likely be implemented in an ad-hoc fashion, as different drivers will -provide their information in different ways. - -Devfs is much cleaner, because it (naturally) has a uniform mechanism -to provide this information: the device nodes themselves! - - -Node to driver file_operations translation - -There is an important difference between the way disc-based character -and block nodes and devfs entries make the connection between an entry -in /dev and the actual device driver. - -With the current 8 bit major and minor numbers the connection between -disc-based c&b nodes and per-major drivers is done through a -fixed-length table of 128 entries. The various filesystem types set -the inode operations for c&b nodes to {chr,blk}dev_inode_operations, -so when a device is opened a few quick levels of indirection bring us -to the driver file_operations. - -For miscellaneous character devices a second step is required: there -is a scan for the driver entry with the same minor number as the file -that was opened, and the appropriate minor open method is called. This -scanning is done *every time* you open a device node. Potentially, you -may be searching through dozens of misc. entries before you find your -open method. While not an enormous performance overhead, this does -seem pointless. - -Linux *must* move beyond the 8 bit major and minor barrier, -somehow. If we simply increase each to 16 bits, then the indexing -scheme used for major driver lookup becomes untenable, because the -major tables (one each for character and block devices) would need to -be 64 k entries long (512 kBytes on x86, 1 MByte for 64 bit -systems). So we would have to use a scheme like that used for -miscellaneous character devices, which means the search time goes up -linearly with the average number of major device drivers on your -system. Not all "devices" are hardware, some are higher-level drivers -like KGI, so you can get more "devices" without adding hardware -You can improve this by creating an ordered (balanced:-) -binary tree, in which case your search time becomes log(N). -Alternatively, you can use hashing to speed up the search. -But why do that search at all if you don't have to? Once again, it -seems pointless. - -Note that devfs doesn't use the major&minor system. For devfs -entries, the connection is done when you lookup the /dev entry. When -devfs_register() is called, an internal table is appended which has -the entry name and the file_operations. If the dentry cache doesn't -have the /dev entry already, this internal table is scanned to get the -file_operations, and an inode is created. If the dentry cache already -has the entry, there is *no lookup time* (other than the dentry scan -itself, but we can't avoid that anyway, and besides Linux dentries -cream other OS's which don't have them:-). Furthermore, the number of -node entries in a devfs is only the number of available device -entries, not the number of *conceivable* entries. Even if you remove -unnecessary entries in a disc-based /dev, the number of conceivable -entries remains the same: you just limit yourself in order to save -space. - -Devfs provides a fast connection between a VFS node and the device -driver, in a scalable way. - -/dev as a system administration tool - -Right now /dev contains a list of conceivable devices, most of which I -don't have. Devfs only shows those devices available on my -system. This means that listing /dev is a handy way of checking what -devices are available. - -Major&minor size - -Existing major and minor numbers are limited to 8 bits each. This is -now a limiting factor for some drivers, particularly the SCSI disc -driver, which consumes a single major number. Only 16 discs are -supported, and each disc may have only 15 partitions. Maybe this isn't -a problem for you, but some of us are building huge Linux systems with -disc arrays. With devfs an arbitrary pointer can be associated with -each device entry, which can be used to give an effective 32 bit -device identifier (i.e. that's like having a 32 bit minor -number). Since this is private to the kernel, there are no C library -compatibility issues which you would have with increasing major and -minor number sizes. See the section on "Allocation of Device Numbers" -for details on maintaining compatibility with userspace. - -Solving this requires a kernel change. - -Since writing this, the kernel has been modified so that the SCSI disc -driver has more major numbers allocated to it and now supports up to -128 discs. Since these major numbers are non-contiguous (a result of -unplanned expansion), the implementation is a little more cumbersome -than originally. - -Just like the changes to IPv4 to fix impending limitations in the -address space, people find ways around the limitations. In the long -run, however, solutions like IPv6 or devfs can't be put off forever. - -Read-only root filesystem - -Having your device nodes on the root filesystem means that you can't -operate properly with a read-only root filesystem. This is because you -want to change ownerships and protections of tty devices. Existing -practice prevents you using a CD-ROM as your root filesystem for a -*real* system. Sure, you can boot off a CD-ROM, but you can't change -tty ownerships, so it's only good for installing. - -Also, you can't use a shared NFS root filesystem for a cluster of -discless Linux machines (having tty ownerships changed on a common -/dev is not good). Nor can you embed your root filesystem in a -ROM-FS. - -You can get around this by creating a RAMDISC at boot time, making -an ext2 filesystem in it, mounting it somewhere and copying the -contents of /dev into it, then unmounting it and mounting it over -/dev. - -A devfs is a cleaner way of solving this. - -Non-Unix root filesystem - -Non-Unix filesystems (such as NTFS) can't be used for a root -filesystem because they variously don't support character and block -special files or symbolic links. You can't have a separate disc-based -or RAMDISC-based filesystem mounted on /dev because you need device -nodes before you can mount these. Devfs can be mounted without any -device nodes. Devlinks won't work because symlinks aren't supported. -An alternative solution is to use initrd to mount a RAMDISC initial -root filesystem (which is populated with a minimal set of device -nodes), and then construct a new /dev in another RAMDISC, and finally -switch to your non-Unix root filesystem. This requires clever boot -scripts and a fragile and conceptually complex boot procedure. - -Devfs solves this in a robust and conceptually simple way. - -PTY security - -Current pseudo-tty (pty) devices are owned by root and read-writable -by everyone. The user of a pty-pair cannot change -ownership/protections without being suid-root. - -This could be solved with a secure user-space daemon which runs as -root and does the actual creation of pty-pairs. Such a daemon would -require modification to *every* programme that wants to use this new -mechanism. It also slows down creation of pty-pairs. - -An alternative is to create a new open_pty() syscall which does much -the same thing as the user-space daemon. Once again, this requires -modifications to pty-handling programmes. - -The devfs solution allows a device driver to "tag" certain device -files so that when an unopened device is opened, the ownerships are -changed to the current euid and egid of the opening process, and the -protections are changed to the default registered by the driver. When -the device is closed ownership is set back to root and protections are -set back to read-write for everybody. No programme need be changed. -The devpts filesystem provides this auto-ownership feature for Unix98 -ptys. It doesn't support old-style pty devices, nor does it have all -the other features of devfs. - -Intelligent device management - -Devfs implements a simple yet powerful protocol for communication with -a device management daemon (devfsd) which runs in user space. It is -possible to send a message (either synchronously or asynchronously) to -devfsd on any event, such as registration/unregistration of device -entries, opening and closing devices, looking up inodes, scanning -directories and more. This has many possibilities. Some of these are -already implemented. See: - - -http://www.atnf.csiro.au/~rgooch/linux/ - -Device entry registration events can be used by devfsd to change -permissions of newly-created device nodes. This is one mechanism to -control device permissions. - -Device entry registration/unregistration events can be used to run -programmes or scripts. This can be used to provide automatic mounting -of filesystems when a new block device media is inserted into the -drive. - -Asynchronous device open and close events can be used to implement -clever permissions management. For example, the default permissions on -/dev/dsp do not allow everybody to read from the device. This is -sensible, as you don't want some remote user recording what you say at -your console. However, the console user is also prevented from -recording. This behaviour is not desirable. With asynchronous device -open and close events, you can have devfsd run a programme or script -when console devices are opened to change the ownerships for *other* -device nodes (such as /dev/dsp). On closure, you can run a different -script to restore permissions. An advantage of this scheme over -modifying the C library tty handling is that this works even if your -programme crashes (how many times have you seen the utmp database with -lingering entries for non-existent logins?). - -Synchronous device open events can be used to perform intelligent -device access protections. Before the device driver open() method is -called, the daemon must first validate the open attempt, by running an -external programme or script. This is far more flexible than access -control lists, as access can be determined on the basis of other -system conditions instead of just the UID and GID. - -Inode lookup events can be used to authenticate module autoload -requests. Instead of using kmod directly, the event is sent to -devfsd which can implement an arbitrary authentication before loading -the module itself. - -Inode lookup events can also be used to construct arbitrary -namespaces, without having to resort to populating devfs with symlinks -to devices that don't exist. - -Speculative Device Scanning - -Consider an application (like cdparanoia) that wants to find all -CD-ROM devices on the system (SCSI, IDE and other types), whether or -not their respective modules are loaded. The application must -speculatively open certain device nodes (such as /dev/sr0 for the SCSI -CD-ROMs) in order to make sure the module is loaded. This requires -that all Linux distributions follow the standard device naming scheme -(last time I looked RedHat did things differently). Devfs solves the -naming problem. - -The same application also wants to see which devices are actually -available on the system. With the existing system it needs to read the -/dev directory and speculatively open each /dev/sr* device to -determine if the device exists or not. With a large /dev this is an -inefficient operation, especially if there are many /dev/sr* nodes. A -solution like scsidev could reduce the number of /dev/sr* entries (but -of course that also requires all that inefficient directory scanning). - -With devfs, the application can open the /dev/sr directory -(which triggers the module autoloading if required), and proceed to -read /dev/sr. Since only the available devices will have -entries, there are no inefficencies in directory scanning or device -openings. - ------------------------------------------------------------------------------ - -Who else does it? - -FreeBSD has a devfs implementation. Solaris and AIX each have a -pseudo-devfs (something akin to scsidev but for all devices, with some -unspecified kernel support). BeOS, Plan9 and QNX also have it. SGI's -IRIX 6.4 and above also have a device filesystem. - -While we shouldn't just automatically do something because others do -it, we should not ignore the work of others either. FreeBSD has a lot -of competent people working on it, so their opinion should not be -blithely ignored. - ------------------------------------------------------------------------------ - - -How it works - -Registering device entries - -For every entry (device node) in a devfs-based /dev a driver must call -devfs_register(). This adds the name of the device entry, the -file_operations structure pointer and a few other things to an -internal table. Device entries may be added and removed at any -time. When a device entry is registered, it automagically appears in -any mounted devfs'. - -Inode lookup - -When a lookup operation on an entry is performed and if there is no -driver information for that entry devfs will attempt to call -devfsd. If still no driver information can be found then a negative -dentry is yielded and the next stage operation will be called by the -VFS (such as create() or mknod() inode methods). If driver information -can be found, an inode is created (if one does not exist already) and -all is well. - -Manually creating device nodes - -The mknod() method allows you to create an ordinary named pipe in the -devfs, or you can create a character or block special inode if one -does not already exist. You may wish to create a character or block -special inode so that you can set permissions and ownership. Later, if -a device driver registers an entry with the same name, the -permissions, ownership and times are retained. This is how you can set -the protections on a device even before the driver is loaded. Once you -create an inode it appears in the directory listing. - -Unregistering device entries - -A device driver calls devfs_unregister() to unregister an entry. - -Chroot() gaols - -2.2.x kernels - -The semantics of inode creation are different when devfs is mounted -with the "explicit" option. Now, when a device entry is registered, it -will not appear until you use mknod() to create the device. It doesn't -matter if you mknod() before or after the device is registered with -devfs_register(). The purpose of this behaviour is to support -chroot(2) gaols, where you want to mount a minimal devfs inside the -gaol. Only the devices you specifically want to be available (through -your mknod() setup) will be accessible. - -2.4.x kernels - -As of kernel 2.3.99, the VFS has had the ability to rebind parts of -the global filesystem namespace into another part of the namespace. -This now works even at the leaf-node level, which means that -individual files and device nodes may be bound into other parts of the -namespace. This is like making links, but better, because it works -across filesystems (unlike hard links) and works through chroot() -gaols (unlike symbolic links). - -Because of these improvements to the VFS, the multi-mount capability -in devfs is no longer needed. The administrator may create a minimal -device tree inside a chroot(2) gaol by using VFS bindings. As this -provides most of the features of the devfs multi-mount capability, I -removed the multi-mount support code (after issuing an RFC). This -yielded code size reductions and simplifications. - -If you want to construct a minimal chroot() gaol, the following -command should suffice: - -mount --bind /dev/null /gaol/dev/null - - -Repeat for other device nodes you want to expose. Simple! - ------------------------------------------------------------------------------ - - -Operational issues - - -Instructions for the impatient - -Nobody likes reading documentation. People just want to get in there -and play. So this section tells you quickly the steps you need to take -to run with devfs mounted over /dev. Skip these steps and you will end -up with a nearly unbootable system. Subsequent sections describe the -issues in more detail, and discuss non-essential configuration -options. - -Devfsd -OK, if you're reading this, I assume you want to play with -devfs. First you should ensure that /usr/src/linux contains a -recent kernel source tree. Then you need to compile devfsd, the device -management daemon, available at - -http://www.atnf.csiro.au/~rgooch/linux/. -Because the kernel has a naming scheme -which is quite different from the old naming scheme, you need to -install devfsd so that software and configuration files that use the -old naming scheme will not break. - -Compile and install devfsd. You will be provided with a default -configuration file /etc/devfsd.conf which will provide -compatibility symlinks for the old naming scheme. Don't change this -config file unless you know what you're doing. Even if you think you -do know what you're doing, don't change it until you've followed all -the steps below and booted a devfs-enabled system and verified that it -works. - -Now edit your main system boot script so that devfsd is started at the -very beginning (before any filesystem -checks). /etc/rc.d/rc.sysinit is often the main boot script -on systems with SysV-style boot scripts. On systems with BSD-style -boot scripts it is often /etc/rc. Also check -/sbin/rc. - -NOTE that the line you put into the boot -script should be exactly: - -/sbin/devfsd /dev - -DO NOT use some special daemon-launching -programme, otherwise the boot script may not wait for devfsd to finish -initialising. - -System Libraries -There may still be some problems because of broken software making -assumptions about device names. In particular, some software does not -handle devices which are symbolic links. If you are running a libc 5 -based system, install libc 5.4.44 (if you have libc 5.4.46, go back to -libc 5.4.44, which is actually correct). If you are running a glibc -based system, make sure you have glibc 2.1.3 or later. - -/etc/securetty -PAM (Pluggable Authentication Modules) is supposed to be a flexible -mechanism for providing better user authentication and access to -services. Unfortunately, it's also fragile, complex and undocumented -(check out RedHat 6.1, and probably other distributions as well). PAM -has problems with symbolic links. Append the following lines to your -/etc/securetty file: - -vc/1 -vc/2 -vc/3 -vc/4 -vc/5 -vc/6 -vc/7 -vc/8 - -This will not weaken security. If you have a version of util-linux -earlier than 2.10.h, please upgrade to 2.10.h or later. If you -absolutely cannot upgrade, then also append the following lines to -your /etc/securetty file: - -1 -2 -3 -4 -5 -6 -7 -8 - -This may potentially weaken security by allowing root logins over the -network (a password is still required, though). However, since there -are problems with dealing with symlinks, I'm suspicious of the level -of security offered in any case. - -XFree86 -While not essential, it's probably a good idea to upgrade to XFree86 -4.0, as patches went in to make it more devfs-friendly. If you don't, -you'll probably need to apply the following patch to -/etc/security/console.perms so that ordinary users can run -startx. Note that not all distributions have this file (e.g. Debian), -so if it's not present, don't worry about it. - ---- /etc/security/console.perms.orig Sat Apr 17 16:26:47 1999 -+++ /etc/security/console.perms Fri Feb 25 23:53:55 2000 -@@ -14,7 +14,7 @@ - # man 5 console.perms - - # file classes -- these are regular expressions --=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9] -+=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9] - - # device classes -- these are shell-style globs - =/dev/fd[0-1]* - -If the patch does not apply, then change the line: - -=tty[0-9][0-9]* :[0-9]\.[0-9] :[0-9] - -with: - -=tty[0-9][0-9]* vc/[0-9][0-9]* :[0-9]\.[0-9] :[0-9] - - -Disable devpts -I've had a report of devpts mounted on /dev/pts not working -correctly. Since devfs will also manage /dev/pts, there is no -need to mount devpts as well. You should either edit your -/etc/fstab so devpts is not mounted, or disable devpts from -your kernel configuration. - -Unsupported drivers -Not all drivers have devfs support. If you depend on one of these -drivers, you will need to create a script or tarfile that you can use -at boot time to create device nodes as appropriate. There is a -section which describes this. Another -section lists the drivers which have -devfs support. - -/dev/mouse - -Many disributions configure /dev/mouse to be the mouse device -for XFree86 and GPM. I actually think this is a bad idea, because it -adds another level of indirection. When looking at a config file, if -you see /dev/mouse you're left wondering which mouse -is being referred to. Hence I recommend putting the actual mouse -device (for example /dev/psaux) into your -/etc/X11/XF86Config file (and similarly for the GPM -configuration file). - -Alternatively, use the same technique used for unsupported drivers -described above. - -The Kernel -Finally, you need to make sure devfs is compiled into your kernel. Set -CONFIG_EXPERIMENTAL=y, CONFIG_DEVFS_FS=y and CONFIG_DEVFS_MOUNT=y by -using favourite configuration tool (i.e. make config or -make xconfig) and then make clean and then recompile your kernel and -modules. At boot, devfs will be mounted onto /dev. - -If you encounter problems booting (for example if you forgot a -configuration step), you can pass devfs=nomount at the kernel -boot command line. This will prevent the kernel from mounting devfs at -boot time onto /dev. - -In general, a kernel built with CONFIG_DEVFS_FS=y but without mounting -devfs onto /dev is completely safe, and requires no -configuration changes. One exception to take note of is when -LABEL= directives are used in /etc/fstab. In this -case you will be unable to boot properly. This is because the -mount(8) programme uses /proc/partitions as part of -the volume label search process, and the device names it finds are not -available, because setting CONFIG_DEVFS_FS=y changes the names in -/proc/partitions, irrespective of whether devfs is mounted. - -Now you've finished all the steps required. You're now ready to boot -your shiny new kernel. Enjoy. - -Changing the configuration - -OK, you've now booted a devfs-enabled system, and everything works. -Now you may feel like changing the configuration (common targets are -/etc/fstab and /etc/devfsd.conf). Since you have a -system that works, if you make any changes and it doesn't work, you -now know that you only have to restore your configuration files to the -default and it will work again. - - -Permissions persistence across reboots - -If you don't use mknod(2) to create a device file, nor use chmod(2) or -chown(2) to change the ownerships/permissions, the inode ctime will -remain at 0 (the epoch, 12 am, 1-JAN-1970, GMT). Anything with a ctime -later than this has had it's ownership/permissions changed. Hence, a -simple script or programme may be used to tar up all changed inodes, -prior to shutdown. Although effective, many consider this approach a -kludge. - -A much better approach is to use devfsd to save and restore -permissions. It may be configured to record changes in permissions and -will save them in a database (in fact a directory tree), and restore -these upon boot. This is an efficient method and results in immediate -saving of current permissions (unlike the tar approach, which saves -permissions at some unspecified future time). - -The default configuration file supplied with devfsd has config entries -which you may uncomment to enable persistence management. - -If you decide to use the tar approach anyway, be aware that tar will -first unlink(2) an inode before creating a new device node. The -unlink(2) has the effect of breaking the connection between a devfs -entry and the device driver. If you use the "devfs=only" boot option, -you lose access to the device driver, requiring you to reload the -module. I consider this a bug in tar (there is no real need to -unlink(2) the inode first). - -Alternatively, you can use devfsd to provide more sophisticated -management of device permissions. You can use devfsd to store -permissions for whole groups of devices with a single configuration -entry, rather than the conventional single entry per device entry. - -Permissions database stored in mounted-over /dev - -If you wish to save and restore your device permissions into the -disc-based /dev while still mounting devfs onto /dev -you may do so. This requires a 2.4.x kernel (in fact, 2.3.99 or -later), which has the VFS binding facility. You need to do the -following to set this up: - - - -make sure the kernel does not mount devfs at boot time - - -make sure you have a correct /dev/console entry in your -root file-system (where your disc-based /dev lives) - -create the /dev-state directory - - -add the following lines near the very beginning of your boot -scripts: - -mount --bind /dev /dev-state -mount -t devfs none /dev -devfsd /dev - - - - -add the following lines to your /etc/devfsd.conf file: - -REGISTER ^pt[sy] IGNORE -CREATE ^pt[sy] IGNORE -CHANGE ^pt[sy] IGNORE -DELETE ^pt[sy] IGNORE -REGISTER .* COPY /dev-state/$devname $devpath -CREATE .* COPY $devpath /dev-state/$devname -CHANGE .* COPY $devpath /dev-state/$devname -DELETE .* CFUNCTION GLOBAL unlink /dev-state/$devname -RESTORE /dev-state - -Note that the sample devfsd.conf file contains these lines, -as well as other sample configurations you may find useful. See the -devfsd distribution - - -reboot. - - - - -Permissions database stored in normal directory - -If you are using an older kernel which doesn't support VFS binding, -then you won't be able to have the permissions database in a -mounted-over /dev. However, you can still use a regular -directory to store the database. The sample /etc/devfsd.conf -file above may still be used. You will need to create the -/dev-state directory prior to installing devfsd. If you have -old permissions in /dev, then just copy (or move) the device -nodes over to the new directory. - -Which method is better? - -The best method is to have the permissions database stored in the -mounted-over /dev. This is because you will not need to copy -device nodes over to /dev-state, and because it allows you to -switch between devfs and non-devfs kernels, without requiring you to -copy permissions between /dev-state (for devfs) and -/dev (for non-devfs). - - -Dealing with drivers without devfs support - -Currently, not all device drivers in the kernel have been modified to -use devfs. Device drivers which do not yet have devfs support will not -automagically appear in devfs. The simplest way to create device nodes -for these drivers is to unpack a tarfile containing the required -device nodes. You can do this in your boot scripts. All your drivers -will now work as before. - -Hopefully for most people devfs will have enough support so that they -can mount devfs directly over /dev without losing most functionality -(i.e. losing access to various devices). As of 22-JAN-1998 (devfs -patch version 10) I am now running this way. All the devices I have -are available in devfs, so I don't lose anything. - -WARNING: if your configuration requires the old-style device names -(i.e. /dev/hda1 or /dev/sda1), you must install devfsd and configure -it to maintain compatibility entries. It is almost certain that you -will require this. Note that the kernel creates a compatibility entry -for the root device, so you don't need initrd. - -Note that you no longer need to mount devpts if you use Unix98 PTYs, -as devfs can manage /dev/pts itself. This saves you some RAM, as you -don't need to compile and install devpts. Note that some versions of -glibc have a bug with Unix98 pty handling on devfs systems. Contact -the glibc maintainers for a fix. Glibc 2.1.3 has the fix. - -Note also that apart from editing /etc/fstab, other things will need -to be changed if you *don't* install devfsd. Some software (like the X -server) hard-wire device names in their source. It really is much -easier to install devfsd so that compatibility entries are created. -You can then slowly migrate your system to using the new device names -(for example, by starting with /etc/fstab), and then limiting the -compatibility entries that devfsd creates. - -IF YOU CONFIGURE TO MOUNT DEVFS AT BOOT, MAKE SURE YOU INSTALL DEVFSD -BEFORE YOU BOOT A DEVFS-ENABLED KERNEL! - -Now that devfs has gone into the 2.3.46 kernel, I'm getting a lot of -reports back. Many of these are because people are trying to run -without devfsd, and hence some things break. Please just run devfsd if -things break. I want to concentrate on real bugs rather than -misconfiguration problems at the moment. If people are willing to fix -bugs/false assumptions in other code (i.e. glibc, X server) and submit -that to the respective maintainers, that would be great. - - -All the way with Devfs - -The devfs kernel patch creates a rationalised device tree. As stated -above, if you want to keep using the old /dev naming scheme, -you just need to configure devfsd appopriately (see the man -page). People who prefer the old names can ignore this section. For -those of us who like the rationalised names and an uncluttered -/dev, read on. - -If you don't run devfsd, or don't enable compatibility entry -management, then you will have to configure your system to use the new -names. For example, you will then need to edit your -/etc/fstab to use the new disc naming scheme. If you want to -be able to boot non-devfs kernels, you will need compatibility -symlinks in the underlying disc-based /dev pointing back to -the old-style names for when you boot a kernel without devfs. - -You can selectively decide which devices you want compatibility -entries for. For example, you may only want compatibility entries for -BSD pseudo-terminal devices (otherwise you'll have to patch you C -library or use Unix98 ptys instead). It's just a matter of putting in -the correct regular expression into /dev/devfsd.conf. - -There are other choices of naming schemes that you may prefer. For -example, I don't use the kernel-supplied -names, because they are too verbose. A common misconception is -that the kernel-supplied names are meant to be used directly in -configuration files. This is not the case. They are designed to -reflect the layout of the devices attached and to provide easy -classification. - -If you like the kernel-supplied names, that's fine. If you don't then -you should be using devfsd to construct a namespace more to your -liking. Devfsd has built-in code to construct a -namespace that is both logical and easy to -manage. In essence, it creates a convenient abbreviation of the -kernel-supplied namespace. - -You are of course free to build your own namespace. Devfsd has all the -infrastructure required to make this easy for you. All you need do is -write a script. You can even write some C code and devfsd can load the -shared object as a callable extension. - - -Other Issues - -The init programme -Another thing to take note of is whether your init programme -creates a Unix socket /dev/telinit. Some versions of init -create /dev/telinit so that the telinit programme can -communicate with the init process. If you have such a system you need -to make sure that devfs is mounted over /dev *before* init -starts. In other words, you can't leave the mounting of devfs to -/etc/rc, since this is executed after init. Other -versions of init require a named pipe /dev/initctl -which must exist *before* init starts. Once again, you need to -mount devfs and then create the named pipe *before* init -starts. - -The default behaviour now is not to mount devfs onto /dev at -boot time for 2.3.x and later kernels. You can correct this with the -"devfs=mount" boot option. This solves any problems with init, -and also prevents the dreaded: - -Cannot open initial console - -message. For 2.2.x kernels where you need to apply the devfs patch, -the default is to mount. - -If you have automatic mounting of devfs onto /dev then you -may need to create /dev/initctl in your boot scripts. The -following lines should suffice: - -mknod /dev/initctl p -kill -SIGUSR1 1 # tell init that /dev/initctl now exists - -Alternatively, if you don't want the kernel to mount devfs onto -/dev then you could use the following procedure is a -guideline for how to get around /dev/initctl problems: - -# cd /sbin -# mv init init.real -# cat > init -#! /bin/sh -mount -n -t devfs none /dev -mknod /dev/initctl p -exec /sbin/init.real $* -[control-D] -# chmod a+x init - -Note that newer versions of init create /dev/initctl -automatically, so you don't have to worry about this. - -Module autoloading -You will need to configure devfsd to enable module -autoloading. The following lines should be placed in your -/etc/devfsd.conf file: - -LOOKUP .* MODLOAD - - -As of devfsd-v1.3.10, a generic /etc/modules.devfs -configuration file is installed, which is used by the MODLOAD -action. This should be sufficient for most configurations. If you -require further configuration, edit your /etc/modules.conf -file. The way module autoloading work with devfs is: - - -a process attempts to lookup a device node (e.g. /dev/fred) - - -if that device node does not exist, the full pathname is passed to -devfsd as a string - - -devfsd will pass the string to the modprobe programme (provided the -configuration line shown above is present), and specifies that -/etc/modules.devfs is the configuration file - - -/etc/modules.devfs includes /etc/modules.conf to -access local configurations - -modprobe will search it's configuration files, looking for an alias -that translates the pathname into a module name - - -the translated pathname is then used to load the module. - - -If you wanted a lookup of /dev/fred to load the -mymod module, you would require the following configuration -line in /etc/modules.conf: - -alias /dev/fred mymod - -The /etc/modules.devfs configuration file provides many such -aliases for standard device names. If you look closely at this file, -you will note that some modules require multiple alias configuration -lines. This is required to support module autoloading for old and new -device names. - -Mounting root off a devfs device -If you wish to mount root off a devfs device when you pass the -"devfs=only" boot option, then you need to pass in the -"root=" option to the kernel when booting. If you use -LILO, then you must have this in lilo.conf: - -append = "root=" - -Surprised? Yep, so was I. It turns out if you have (as most people -do): - -root = - - -then LILO will determine the device number of and will -write that device number into a special place in the kernel image -before starting the kernel, and the kernel will use that device number -to mount the root filesystem. So, using the "append" variety ensures -that LILO passes the root filesystem device as a string, which devfs -can then use. - -Note that this isn't an issue if you don't pass "devfs=only". - -TTY issues -The ttyname(3) function in some versions of the C library makes -false assumptions about device entries which are symbolic links. The -tty(1) programme is one that depends on this function. I've -written a patch to libc 5.4.43 which fixes this. This has been -included in libc 5.4.44 and a similar fix is in glibc 2.1.3. - - -Kernel Naming Scheme - -The kernel provides a default naming scheme. This scheme is designed -to make it easy to search for specific devices or device types, and to -view the available devices. Some device types (such as hard discs), -have a directory of entries, making it easy to see what devices of -that class are available. Often, the entries are symbolic links into a -directory tree that reflects the topology of available devices. The -topological tree is useful for finding how your devices are arranged. - -Below is a list of the naming schemes for the most common drivers. A -list of reserved device names is -available for reference. Please send email to -rgooch@atnf.csiro.au to obtain an allocation. Please be -patient (the maintainer is busy). An alternative name may be allocated -instead of the requested name, at the discretion of the maintainer. - -Disc Devices - -All discs, whether SCSI, IDE or whatever, are placed under the -/dev/discs hierarchy: - - /dev/discs/disc0 first disc - /dev/discs/disc1 second disc - - -Each of these entries is a symbolic link to the directory for that -device. The device directory contains: - - disc for the whole disc - part* for individual partitions - - -CD-ROM Devices - -All CD-ROMs, whether SCSI, IDE or whatever, are placed under the -/dev/cdroms hierarchy: - - /dev/cdroms/cdrom0 first CD-ROM - /dev/cdroms/cdrom1 second CD-ROM - - -Each of these entries is a symbolic link to the real device entry for -that device. - -Tape Devices - -All tapes, whether SCSI, IDE or whatever, are placed under the -/dev/tapes hierarchy: - - /dev/tapes/tape0 first tape - /dev/tapes/tape1 second tape - - -Each of these entries is a symbolic link to the directory for that -device. The device directory contains: - - mt for mode 0 - mtl for mode 1 - mtm for mode 2 - mta for mode 3 - mtn for mode 0, no rewind - mtln for mode 1, no rewind - mtmn for mode 2, no rewind - mtan for mode 3, no rewind - - -SCSI Devices - -To uniquely identify any SCSI device requires the following -information: - - controller (host adapter) - bus (SCSI channel) - target (SCSI ID) - unit (Logical Unit Number) - - -All SCSI devices are placed under /dev/scsi (assuming devfs -is mounted on /dev). Hence, a SCSI device with the following -parameters: c=1,b=2,t=3,u=4 would appear as: - - /dev/scsi/host1/bus2/target3/lun4 device directory - - -Inside this directory, a number of device entries may be created, -depending on which SCSI device-type drivers were installed. - -See the section on the disc naming scheme to see what entries the SCSI -disc driver creates. - -See the section on the tape naming scheme to see what entries the SCSI -tape driver creates. - -The SCSI CD-ROM driver creates: - - cd - - -The SCSI generic driver creates: - - generic - - -IDE Devices - -To uniquely identify any IDE device requires the following -information: - - controller - bus (aka. primary/secondary) - target (aka. master/slave) - unit - - -All IDE devices are placed under /dev/ide, and uses a similar -naming scheme to the SCSI subsystem. - -XT Hard Discs - -All XT discs are placed under /dev/xd. The first XT disc has -the directory /dev/xd/disc0. - -TTY devices - -The tty devices now appear as: - - New name Old-name Device Type - -------- -------- ----------- - /dev/tts/{0,1,...} /dev/ttyS{0,1,...} Serial ports - /dev/cua/{0,1,...} /dev/cua{0,1,...} Call out devices - /dev/vc/0 /dev/tty Current virtual console - /dev/vc/{1,2,...} /dev/tty{1...63} Virtual consoles - /dev/vcc/{0,1,...} /dev/vcs{1...63} Virtual consoles - /dev/pty/m{0,1,...} /dev/ptyp?? PTY masters - /dev/pty/s{0,1,...} /dev/ttyp?? PTY slaves - - -RAMDISCS - -The RAMDISCS are placed in their own directory, and are named thus: - - /dev/rd/{0,1,2,...} - - -Meta Devices - -The meta devices are placed in their own directory, and are named -thus: - - /dev/md/{0,1,2,...} - - -Floppy discs - -Floppy discs are placed in the /dev/floppy directory. - -Loop devices - -Loop devices are placed in the /dev/loop directory. - -Sound devices - -Sound devices are placed in the /dev/sound directory -(audio, sequencer, ...). - - -Devfsd Naming Scheme - -Devfsd provides a naming scheme which is a convenient abbreviation of -the kernel-supplied namespace. In some -cases, the kernel-supplied naming scheme is quite convenient, so -devfsd does not provide another naming scheme. The convenience names -that devfsd creates are in fact the same names as the original devfs -kernel patch created (before Linus mandated the Big Name -Change). These are referred to as "new compatibility entries". - -In order to configure devfsd to create these convenience names, the -following lines should be placed in your /etc/devfsd.conf: - -REGISTER .* MKNEWCOMPAT -UNREGISTER .* RMNEWCOMPAT - -This will cause devfsd to create (and destroy) symbolic links which -point to the kernel-supplied names. - -SCSI Hard Discs - -All SCSI discs are placed under /dev/sd (assuming devfs is -mounted on /dev). Hence, a SCSI disc with the following -parameters: c=1,b=2,t=3,u=4 would appear as: - - /dev/sd/c1b2t3u4 for the whole disc - /dev/sd/c1b2t3u4p5 for the 5th partition - /dev/sd/c1b2t3u4p5s6 for the 6th slice in the 5th partition - - -SCSI Tapes - -All SCSI tapes are placed under /dev/st. A similar naming -scheme is used as for SCSI discs. A SCSI tape with the -parameters:c=1,b=2,t=3,u=4 would appear as: - - /dev/st/c1b2t3u4m0 for mode 0 - /dev/st/c1b2t3u4m1 for mode 1 - /dev/st/c1b2t3u4m2 for mode 2 - /dev/st/c1b2t3u4m3 for mode 3 - /dev/st/c1b2t3u4m0n for mode 0, no rewind - /dev/st/c1b2t3u4m1n for mode 1, no rewind - /dev/st/c1b2t3u4m2n for mode 2, no rewind - /dev/st/c1b2t3u4m3n for mode 3, no rewind - - -SCSI CD-ROMs - -All SCSI CD-ROMs are placed under /dev/sr. A similar naming -scheme is used as for SCSI discs. A SCSI CD-ROM with the -parameters:c=1,b=2,t=3,u=4 would appear as: - - /dev/sr/c1b2t3u4 - - -SCSI Generic Devices - -The generic (aka. raw) interface for all SCSI devices are placed under -/dev/sg. A similar naming scheme is used as for SCSI discs. A -SCSI generic device with the parameters:c=1,b=2,t=3,u=4 would appear -as: - - /dev/sg/c1b2t3u4 - - -IDE Hard Discs - -All IDE discs are placed under /dev/ide/hd, using a similar -convention to SCSI discs. The following mappings exist between the new -and the old names: - - /dev/hda /dev/ide/hd/c0b0t0u0 - /dev/hdb /dev/ide/hd/c0b0t1u0 - /dev/hdc /dev/ide/hd/c0b1t0u0 - /dev/hdd /dev/ide/hd/c0b1t1u0 - - -IDE Tapes - -A similar naming scheme is used as for IDE discs. The entries will -appear in the /dev/ide/mt directory. - -IDE CD-ROM - -A similar naming scheme is used as for IDE discs. The entries will -appear in the /dev/ide/cd directory. - -IDE Floppies - -A similar naming scheme is used as for IDE discs. The entries will -appear in the /dev/ide/fd directory. - -XT Hard Discs - -All XT discs are placed under /dev/xd. The first XT disc -would appear as /dev/xd/c0t0. - - -Old Compatibility Names - -The old compatibility names are the legacy device names, such as -/dev/hda, /dev/sda, /dev/rtc and so on. -Devfsd can be configured to create compatibility symlinks so that you -may continue to use the old names in your configuration files and so -that old applications will continue to function correctly. - -In order to configure devfsd to create these legacy names, the -following lines should be placed in your /etc/devfsd.conf: - -REGISTER .* MKOLDCOMPAT -UNREGISTER .* RMOLDCOMPAT - -This will cause devfsd to create (and destroy) symbolic links which -point to the kernel-supplied names. - - ------------------------------------------------------------------------------ - - -Device drivers currently ported - -- All miscellaneous character devices support devfs (this is done - transparently through misc_register()) - -- SCSI discs and generic hard discs - -- Character memory devices (null, zero, full and so on) - Thanks to C. Scott Ananian - -- Loop devices (/dev/loop?) - -- TTY devices (console, serial ports, terminals and pseudo-terminals) - Thanks to C. Scott Ananian - -- SCSI tapes (/dev/scsi and /dev/tapes) - -- SCSI CD-ROMs (/dev/scsi and /dev/cdroms) - -- SCSI generic devices (/dev/scsi) - -- RAMDISCS (/dev/ram?) - -- Meta Devices (/dev/md*) - -- Floppy discs (/dev/floppy) - -- Parallel port printers (/dev/printers) - -- Sound devices (/dev/sound) - Thanks to Eric Dumas and - C. Scott Ananian - -- Joysticks (/dev/joysticks) - -- Sparc keyboard (/dev/kbd) - -- DSP56001 digital signal processor (/dev/dsp56k) - -- Apple Desktop Bus (/dev/adb) - -- Coda network file system (/dev/cfs*) - -- Virtual console capture devices (/dev/vcc) - Thanks to Dennis Hou - -- Frame buffer devices (/dev/fb) - -- Video capture devices (/dev/v4l) - - ------------------------------------------------------------------------------ - - -Allocation of Device Numbers - -Devfs allows you to write a driver which doesn't need to allocate a -device number (major&minor numbers) for the internal operation of the -kernel. However, there are a number of userspace programmes that use -the device number as a unique handle for a device. An example is the -find programme, which uses device numbers to determine whether -an inode is on a different filesystem than another inode. The device -number used is the one for the block device which a filesystem is -using. To preserve compatibility with userspace programmes, block -devices using devfs need to have unique device numbers allocated to -them. Furthermore, POSIX specifies device numbers, so some kind of -device number needs to be presented to userspace. - -The simplest option (especially when porting drivers to devfs) is to -keep using the old major and minor numbers. Devfs will take whatever -values are given for major&minor and pass them onto userspace. - -This device number is a 16 bit number, so this leaves plenty of space -for large numbers of discs and partitions. This scheme can also be -used for character devices, in particular the tty devices, which are -currently limited to 256 pseudo-ttys (this limits the total number of -simultaneous xterms and remote logins). Note that the device number -is limited to the range 36864-61439 (majors 144-239), in order to -avoid any possible conflicts with existing official allocations. - -Please note that using dynamically allocated block device numbers may -break the NFS daemons (both user and kernel mode), which expect dev_t -for a given device to be constant over the lifetime of remote mounts. - -A final note on this scheme: since it doesn't increase the size of -device numbers, there are no compatibility issues with userspace. - ------------------------------------------------------------------------------ - - -Questions and Answers - - -Making things work -Alternatives to devfs -What I don't like about devfs -How to report bugs -Strange kernel messages -Compilation problems with devfsd - - - -Making things work - -Here are some common questions and answers. - - - -Devfsd doesn't start - -Make sure you have compiled and installed devfsd -Make sure devfsd is being started from your boot -scripts -Make sure you have configured your kernel to enable devfs (see -below) -Make sure devfs is mounted (see below) - - -Devfsd is not managing all my permissions - -Make sure you are capturing the appropriate events. For example, -device entries created by the kernel generate REGISTER events, -but those created by devfsd generate CREATE events. - - -Devfsd is not capturing all REGISTER events - -See the previous entry: you may need to capture CREATE events. - - -X will not start - -Make sure you followed the steps -outlined above. - - -Why don't my network devices appear in devfs? - -This is not a bug. Network devices have their own, completely separate -namespace. They are accessed via socket(2) and -setsockopt(2) calls, and thus require no device nodes. I have -raised the possibilty of moving network devices into the device -namespace, but have had no response. - - -How can I test if I have devfs compiled into my kernel? - -All filesystems built-in or currently loaded are listed in -/proc/filesystems. If you see a devfs entry, then -you know that devfs was compiled into your kernel. If you have -correctly configured and rebuilt your kernel, then devfs will be -built-in. If you think you've configured it in, but -/proc/filesystems doesn't show it, you've made a mistake. -Common mistakes include: - -Using a 2.2.x kernel without applying the devfs patch (if you -don't know how to patch your kernel, use 2.4.x instead, don't bother -asking me how to patch) -Forgetting to set CONFIG_EXPERIMENTAL=y -Forgetting to set CONFIG_DEVFS_FS=y -Forgetting to set CONFIG_DEVFS_MOUNT=y (if you want devfs -to be automatically mounted at boot) -Editing your .config manually, instead of using make -config or make xconfig -Forgetting to run make dep; make clean after changing the -configuration and before compiling -Forgetting to compile your kernel and modules -Forgetting to install your kernel -Forgetting to install your modules - -Please check twice that you've done all these steps before sending in -a bug report. - - - -How can I test if devfs is mounted on /dev? - -The device filesystem will always create an entry called -".devfsd", which is used to communicate with the daemon. Even -if the daemon is not running, this entry will exist. Testing for the -existence of this entry is the approved method of determining if devfs -is mounted or not. Note that the type of entry (i.e. regular file, -character device, named pipe, etc.) may change without notice. Only -the existence of the entry should be relied upon. - - -When I start devfsd, I see the error: -Error opening file: ".devfsd" No such file or directory? - -This means that devfs is not mounted. Make sure you have devfs mounted. - - -How do I mount devfs? - -First make sure you have devfs compiled into your kernel (see -above). Then you will either need to: - -set CONFIG_DEVFS_MOUNT=y in your kernel config -pass devfs=mount to your boot loader -mount devfs manually in your boot scripts with: -mount -t none devfs /dev - - - -Mount by volume LABEL=> parameter to , , - and . - Work sponsored by SGI. - v0.76 - 19991017 Richard Gooch - Allow multiple unregistrations. - Work sponsored by SGI. - v0.77 - 19991026 Richard Gooch - Added major and minor number to devfsd protocol. - Incremented devfsd protocol revision to 5. - Work sponsored by SGI. - v0.78 - 19991030 Richard Gooch - Support info pointer for all devfs entry types. - Added <> parameter to and - . - Work sponsored by SGI. - v0.79 - 19991031 Richard Gooch - Support "../" when searching devfs namespace. - Work sponsored by SGI. - v0.80 - 19991101 Richard Gooch - Created . - Work sponsored by SGI. - v0.81 - 19991103 Richard Gooch - Exported . - Work sponsored by SGI. - v0.82 - 19991104 Richard Gooch - Removed unused . - 19991105 Richard Gooch - Do not hide entries from devfsd or children. - Removed DEVFS_ FL_TTY_COMPAT flag. - Removed "nottycompat" boot option. - Removed . - Work sponsored by SGI. - v0.83 - 19991107 Richard Gooch - Added DEVFS_FL_WAIT flag. - Work sponsored by SGI. - v0.84 - 19991107 Richard Gooch - Support new "disc" naming scheme in . - Allow NULL fops in . - Work sponsored by SGI. - v0.85 - 19991110 Richard Gooch - Fall back to major table if NULL fops given to . - Work sponsored by SGI. - v0.86 - 19991204 Richard Gooch - Support fifos when unregistering. - Work sponsored by SGI. - v0.87 - 19991209 Richard Gooch - Removed obsolete DEVFS_ FL_COMPAT and DEVFS_ FL_TOLERANT flags. - Work sponsored by SGI. - v0.88 - 19991214 Richard Gooch - Removed kmod support. - Work sponsored by SGI. - v0.89 - 19991216 Richard Gooch - Improved debugging in . - Ensure dentries created by devfsd will be cleaned up. - Work sponsored by SGI. - v0.90 - 19991223 Richard Gooch - Created . - Work sponsored by SGI. - v0.91 - 20000203 Richard Gooch - Ported to kernel 2.3.42. - Removed . - Work sponsored by SGI. - v0.92 - 20000306 Richard Gooch - Added DEVFS_ FL_NO_PERSISTENCE flag. - Removed unnecessary call to in - . - Work sponsored by SGI. - v0.93 - 20000413 Richard Gooch - Set inode->i_size to correct size for symlinks. - 20000414 Richard Gooch - Only give lookup() method to directories to comply with new VFS - assumptions. - Work sponsored by SGI. - 20000415 Richard Gooch - Remove unnecessary tests in symlink methods. - Don't kill existing block ops in . - Work sponsored by SGI. - v0.94 - 20000424 Richard Gooch - Don't create missing directories in . - Work sponsored by SGI. - v0.95 - 20000430 Richard Gooch - Added CONFIG_DEVFS_MOUNT. - Work sponsored by SGI. - v0.96 - 20000608 Richard Gooch - Disabled multi-mount capability (use VFS bindings instead). - Work sponsored by SGI. - v0.97 - 20000610 Richard Gooch - Switched to FS_SINGLE to disable multi-mounts. - 20000612 Richard Gooch - Removed module support. - Removed multi-mount code. - Removed compatibility macros: VFS has changed too much. - Work sponsored by SGI. - v0.98 - 20000614 Richard Gooch - Merged devfs inode into devfs entry. - Work sponsored by SGI. - v0.99 - 20000619 Richard Gooch - Removed dead code in which used to call - . - Work sponsored by SGI. - v0.100 - 20000621 Richard Gooch - Changed interface to . - Work sponsored by SGI. - v0.101 - 20000622 Richard Gooch - Simplified interface to and . - Simplified interface to . - Work sponsored by SGI. - v0.102 - 20010519 Richard Gooch - Ensure terminates string for root entry. - Exported to modules. - 20010520 Richard Gooch - Make send events to devfsd. - Cleaned up option processing in . - 20010521 Richard Gooch - Fixed bugs in handling symlinks: could leak or cause Oops. - 20010522 Richard Gooch - Cleaned up directory handling by separating fops. - v0.103 - 20010601 Richard Gooch - Fixed handling of inverted options in . - v0.104 - 20010604 Richard Gooch - Adjusted to account for fix. - v0.105 - 20010617 Richard Gooch - Answered question posed by Al Viro and removed his comments. - Moved setting of registered flag after other fields are changed. - Fixed race between and . - Global VFS changes added bogus BKL to : removed. - Widened locking in and . - Replaced stack usage with kmalloc. - Simplified locking in and fixed memory leak. - v0.106 - 20010709 Richard Gooch - Removed broken devnum allocation and use . - Fixed old devnum leak by calling new . - v0.107 - 20010712 Richard Gooch - Fixed bug in which could hang boot process. - v0.108 - 20010730 Richard Gooch - Added DEVFSD_NOTIFY_DELETE event. - 20010801 Richard Gooch - Removed #include . - v0.109 - 20010807 Richard Gooch - Fixed inode table races by removing it and using - inode->u.generic_ip instead. - Moved into . - Moved into . - v0.110 - 20010808 Richard Gooch - Fixed race in for uni-processor. - v0.111 - 20010818 Richard Gooch - Removed remnant of multi-mount support in . - Removed unused DEVFS_FL_SHOW_UNREG flag. - v0.112 - 20010820 Richard Gooch - Removed nlink field from struct devfs_inode. - v0.113 - 20010823 Richard Gooch - Replaced BKL with global rwsem to protect symlink data (quick - and dirty hack). - v0.114 - 20010827 Richard Gooch - Replaced global rwsem for symlink with per-link refcount. - v0.115 - 20010919 Richard Gooch - Set inode->i_mapping->a_ops for block nodes in . - v0.116 - 20011008 Richard Gooch - Fixed overrun in by removing function (not needed). - 20011009 Richard Gooch - Fixed buffer underrun in . - 20011029 Richard Gooch - Fixed race in when setting event mask. - 20011114 Richard Gooch - First release of new locking code. - v1.0 - 20011117 Richard Gooch - Discard temporary buffer, now use "%s" for dentry names. - 20011118 Richard Gooch - Don't generate path in : use fake entry instead. - Use "existing" directory in <_devfs_make_parent_for_leaf>. - 20011122 Richard Gooch - Use slab cache rather than fixed buffer for devfsd events. - v1.1 - 20011125 Richard Gooch - Send DEVFSD_NOTIFY_REGISTERED events in . - 20011127 Richard Gooch - Fixed locking bug in due to typo. - Do not send CREATE, CHANGE, ASYNC_OPEN or DELETE events from - devfsd or children. - v1.2 - 20011202 Richard Gooch - Fixed bug in : was dereferencing freed pointer. - v1.3 - 20011203 Richard Gooch - Fixed bug in : was dereferencing freed pointer. - Added process group check for devfsd privileges. - v1.4 - 20011204 Richard Gooch - Use SLAB_ATOMIC in from . - v1.5 - 20011211 Richard Gooch - Return old entry in for 2.4.x kernels. - 20011212 Richard Gooch - Increment refcount on module in . - 20011215 Richard Gooch - Created and exported . - Increment refcount on module in . - Created . - v1.6 - 20011216 Richard Gooch - Added poisoning to . - Improved debugging messages. - v1.7 - 20011221 Richard Gooch - Corrected (made useful) debugging message in . - Moved in to - 20011224 Richard Gooch - Added magic number to guard against scribbling drivers. - 20011226 Richard Gooch - Only return old entry in if a directory. - Defined macros for error and debug messages. - v1.8 - 20020113 Richard Gooch - Fixed (rare, old) race in . - v1.9 - 20020120 Richard Gooch - Fixed deadlock bug in . - Tag VFS deletable in if handle ignored. - v1.10 - 20020129 Richard Gooch - Added KERN_* to remaining messages. - Cleaned up declaration of . - v1.11 - 20020219 Richard Gooch - Changed to allow later additions if not yet empty. - v1.12 - 20020406 Richard Gooch - Removed silently introduced calls to lock_kernel() and - unlock_kernel() due to recent VFS locking changes. BKL isn't - required in devfs. - v1.13 - 20020428 Richard Gooch - Removed 2.4.x compatibility code. - v1.14 - 20020510 Richard Gooch - Added BKL to because drivers still need it. - v1.15 - 20020512 Richard Gooch - Protected and - from changing directory contents. - v1.16 - 20020514 Richard Gooch - Minor cleanup of . - v1.17 - 20020721 Richard Gooch - Switched to ISO C structure field initialisers. - Switch to set_current_state() and move before add_wait_queue(). - 20020722 Richard Gooch - Fixed devfs entry leak in when *readdir fails. - v1.18 - 20020725 Richard Gooch - Created . - v1.19 - 20020728 Richard Gooch - Removed deprecated . - v1.20 - 20020820 Richard Gooch - Fixed module unload race in . - v1.21 - 20021013 Richard Gooch - Removed DEVFS_ FL_AUTO_OWNER. - Switched lingering structure field initialiser to ISO C. - Added locking when updating FCB flags. - v1.22 -*/ -#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 - -#define DEVFS_VERSION "2004-01-31" - -#define DEVFS_NAME "devfs" - -#define FIRST_INODE 1 - -#define STRING_LENGTH 256 -#define FAKE_BLOCK_SIZE 1024 -#define POISON_PTR ( *(void **) poison_array ) -#define MAGIC_VALUE 0x327db823 - -#ifndef TRUE -# define TRUE 1 -# define FALSE 0 -#endif - -#define MODE_DIR (S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO) - -#define DEBUG_NONE 0x0000000 -#define DEBUG_MODULE_LOAD 0x0000001 -#define DEBUG_REGISTER 0x0000002 -#define DEBUG_UNREGISTER 0x0000004 -#define DEBUG_FREE 0x0000008 -#define DEBUG_SET_FLAGS 0x0000010 -#define DEBUG_S_READ 0x0000100 /* Break */ -#define DEBUG_I_LOOKUP 0x0001000 /* Break */ -#define DEBUG_I_CREATE 0x0002000 -#define DEBUG_I_GET 0x0004000 -#define DEBUG_I_CHANGE 0x0008000 -#define DEBUG_I_UNLINK 0x0010000 -#define DEBUG_I_RLINK 0x0020000 -#define DEBUG_I_FLINK 0x0040000 -#define DEBUG_I_MKNOD 0x0080000 -#define DEBUG_F_READDIR 0x0100000 /* Break */ -#define DEBUG_D_DELETE 0x1000000 /* Break */ -#define DEBUG_D_RELEASE 0x2000000 -#define DEBUG_D_IPUT 0x4000000 -#define DEBUG_ALL 0xfffffff -#define DEBUG_DISABLED DEBUG_NONE - -#define OPTION_NONE 0x00 -#define OPTION_MOUNT 0x01 - -#define PRINTK(format, args...) \ - {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);} - -#define OOPS(format, args...) \ - {printk (KERN_CRIT "%s" format, __FUNCTION__ , ## args); \ - printk ("Forcing Oops\n"); \ - BUG();} - -#ifdef CONFIG_DEVFS_DEBUG -# define VERIFY_ENTRY(de) \ - {if ((de) && (de)->magic_number != MAGIC_VALUE) \ - OOPS ("(%p): bad magic value: %x\n", (de), (de)->magic_number);} -# define WRITE_ENTRY_MAGIC(de,magic) (de)->magic_number = (magic) -# define DPRINTK(flag, format, args...) \ - {if (devfs_debug & flag) \ - printk (KERN_INFO "%s" format, __FUNCTION__ , ## args);} -#else -# define VERIFY_ENTRY(de) -# define WRITE_ENTRY_MAGIC(de,magic) -# define DPRINTK(flag, format, args...) -#endif - -typedef struct devfs_entry *devfs_handle_t; - -struct directory_type { - rwlock_t lock; /* Lock for searching(R)/updating(W) */ - struct devfs_entry *first; - struct devfs_entry *last; - unsigned char no_more_additions:1; -}; - -struct symlink_type { - unsigned int length; /* Not including the NULL-termimator */ - char *linkname; /* This is NULL-terminated */ -}; - -struct devfs_inode { /* This structure is for "persistent" inode storage */ - struct dentry *dentry; - struct timespec atime; - struct timespec mtime; - struct timespec ctime; - unsigned int ino; /* Inode number as seen in the VFS */ - uid_t uid; - gid_t gid; -}; - -struct devfs_entry { -#ifdef CONFIG_DEVFS_DEBUG - unsigned int magic_number; -#endif - void *info; - atomic_t refcount; /* When this drops to zero, it's unused */ - union { - struct directory_type dir; - dev_t dev; - struct symlink_type symlink; - const char *name; /* Only used for (mode == 0) */ - } u; - struct devfs_entry *prev; /* Previous entry in the parent directory */ - struct devfs_entry *next; /* Next entry in the parent directory */ - struct devfs_entry *parent; /* The parent directory */ - struct devfs_inode inode; - umode_t mode; - unsigned short namelen; /* I think 64k+ filenames are a way off... */ - unsigned char vfs:1; /* Whether the VFS may delete the entry */ - char name[1]; /* This is just a dummy: the allocated array - is bigger. This is NULL-terminated */ -}; - -/* The root of the device tree */ -static struct devfs_entry *root_entry; - -struct devfsd_buf_entry { - struct devfs_entry *de; /* The name is generated with this */ - unsigned short type; /* The type of event */ - umode_t mode; - uid_t uid; - gid_t gid; - struct devfsd_buf_entry *next; -}; - -struct fs_info { /* This structure is for the mounted devfs */ - struct super_block *sb; - spinlock_t devfsd_buffer_lock; /* Lock when inserting/deleting events */ - struct devfsd_buf_entry *devfsd_first_event; - struct devfsd_buf_entry *devfsd_last_event; - volatile int devfsd_sleeping; - volatile struct task_struct *devfsd_task; - volatile pid_t devfsd_pgrp; - volatile struct file *devfsd_file; - struct devfsd_notify_struct *devfsd_info; - volatile unsigned long devfsd_event_mask; - atomic_t devfsd_overrun_count; - wait_queue_head_t devfsd_wait_queue; /* Wake devfsd on input */ - wait_queue_head_t revalidate_wait_queue; /* Wake when devfsd sleeps */ -}; - -static struct fs_info fs_info = {.devfsd_buffer_lock = SPIN_LOCK_UNLOCKED }; -static kmem_cache_t *devfsd_buf_cache; -#ifdef CONFIG_DEVFS_DEBUG -static unsigned int devfs_debug_init __initdata = DEBUG_NONE; -static unsigned int devfs_debug = DEBUG_NONE; -static DEFINE_SPINLOCK(stat_lock); -static unsigned int stat_num_entries; -static unsigned int stat_num_bytes; -#endif -static unsigned char poison_array[8] = - { 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a }; - -#ifdef CONFIG_DEVFS_MOUNT -static unsigned int boot_options = OPTION_MOUNT; -#else -static unsigned int boot_options = OPTION_NONE; -#endif - -/* Forward function declarations */ -static devfs_handle_t _devfs_walk_path(struct devfs_entry *dir, - const char *name, int namelen, - int traverse_symlink); -static ssize_t devfsd_read(struct file *file, char __user *buf, size_t len, - loff_t * ppos); -static int devfsd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); -static int devfsd_close(struct inode *inode, struct file *file); -#ifdef CONFIG_DEVFS_DEBUG -static ssize_t stat_read(struct file *file, char __user *buf, size_t len, - loff_t * ppos); -static const struct file_operations stat_fops = { - .open = nonseekable_open, - .read = stat_read, -}; -#endif - -/* Devfs daemon file operations */ -static const struct file_operations devfsd_fops = { - .open = nonseekable_open, - .read = devfsd_read, - .ioctl = devfsd_ioctl, - .release = devfsd_close, -}; - -/* Support functions follow */ - -/** - * devfs_get - Get a reference to a devfs entry. - * @de: The devfs entry. - */ - -static struct devfs_entry *devfs_get(struct devfs_entry *de) -{ - VERIFY_ENTRY(de); - if (de) - atomic_inc(&de->refcount); - return de; -} /* End Function devfs_get */ - -/** - * devfs_put - Put (release) a reference to a devfs entry. - * @de: The handle to the devfs entry. - */ - -static void devfs_put(devfs_handle_t de) -{ - if (!de) - return; - VERIFY_ENTRY(de); - if (de->info == POISON_PTR) - OOPS("(%p): poisoned pointer\n", de); - if (!atomic_dec_and_test(&de->refcount)) - return; - if (de == root_entry) - OOPS("(%p): root entry being freed\n", de); - DPRINTK(DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n", - de->name, de, de->parent, - de->parent ? de->parent->name : "no parent"); - if (S_ISLNK(de->mode)) - kfree(de->u.symlink.linkname); - WRITE_ENTRY_MAGIC(de, 0); -#ifdef CONFIG_DEVFS_DEBUG - spin_lock(&stat_lock); - --stat_num_entries; - stat_num_bytes -= sizeof *de + de->namelen; - if (S_ISLNK(de->mode)) - stat_num_bytes -= de->u.symlink.length + 1; - spin_unlock(&stat_lock); -#endif - de->info = POISON_PTR; - kfree(de); -} /* End Function devfs_put */ - -/** - * _devfs_search_dir - Search for a devfs entry in a directory. - * @dir: The directory to search. - * @name: The name of the entry to search for. - * @namelen: The number of characters in @name. - * - * Search for a devfs entry in a directory and returns a pointer to the entry - * on success, else %NULL. The directory must be locked already. - * An implicit devfs_get() is performed on the returned entry. - */ - -static struct devfs_entry *_devfs_search_dir(struct devfs_entry *dir, - const char *name, - unsigned int namelen) -{ - struct devfs_entry *curr; - - if (!S_ISDIR(dir->mode)) { - PRINTK("(%s): not a directory\n", dir->name); - return NULL; - } - for (curr = dir->u.dir.first; curr != NULL; curr = curr->next) { - if (curr->namelen != namelen) - continue; - if (memcmp(curr->name, name, namelen) == 0) - break; - /* Not found: try the next one */ - } - return devfs_get(curr); -} /* End Function _devfs_search_dir */ - -/** - * _devfs_alloc_entry - Allocate a devfs entry. - * @name: the name of the entry - * @namelen: the number of characters in @name - * @mode: the mode for the entry - * - * Allocate a devfs entry and returns a pointer to the entry on success, else - * %NULL. - */ - -static struct devfs_entry *_devfs_alloc_entry(const char *name, - unsigned int namelen, - umode_t mode) -{ - struct devfs_entry *new; - static unsigned long inode_counter = FIRST_INODE; - static DEFINE_SPINLOCK(counter_lock); - - if (name && (namelen < 1)) - namelen = strlen(name); - if ((new = kmalloc(sizeof *new + namelen, GFP_KERNEL)) == NULL) - return NULL; - memset(new, 0, sizeof *new + namelen); /* Will set '\0' on name */ - new->mode = mode; - if (S_ISDIR(mode)) - rwlock_init(&new->u.dir.lock); - atomic_set(&new->refcount, 1); - spin_lock(&counter_lock); - new->inode.ino = inode_counter++; - spin_unlock(&counter_lock); - if (name) - memcpy(new->name, name, namelen); - new->namelen = namelen; - WRITE_ENTRY_MAGIC(new, MAGIC_VALUE); -#ifdef CONFIG_DEVFS_DEBUG - spin_lock(&stat_lock); - ++stat_num_entries; - stat_num_bytes += sizeof *new + namelen; - spin_unlock(&stat_lock); -#endif - return new; -} /* End Function _devfs_alloc_entry */ - -/** - * _devfs_append_entry - Append a devfs entry to a directory's child list. - * @dir: The directory to add to. - * @de: The devfs entry to append. - * @old_de: If an existing entry exists, it will be written here. This may - * be %NULL. An implicit devfs_get() is performed on this entry. - * - * Append a devfs entry to a directory's list of children, checking first to - * see if an entry of the same name exists. The directory will be locked. - * The value 0 is returned on success, else a negative error code. - * On failure, an implicit devfs_put() is performed on %de. - */ - -static int _devfs_append_entry(devfs_handle_t dir, devfs_handle_t de, - devfs_handle_t * old_de) -{ - int retval; - - if (old_de) - *old_de = NULL; - if (!S_ISDIR(dir->mode)) { - PRINTK("(%s): dir: \"%s\" is not a directory\n", de->name, - dir->name); - devfs_put(de); - return -ENOTDIR; - } - write_lock(&dir->u.dir.lock); - if (dir->u.dir.no_more_additions) - retval = -ENOENT; - else { - struct devfs_entry *old; - - old = _devfs_search_dir(dir, de->name, de->namelen); - if (old_de) - *old_de = old; - else - devfs_put(old); - if (old == NULL) { - de->parent = dir; - de->prev = dir->u.dir.last; - /* Append to the directory's list of children */ - if (dir->u.dir.first == NULL) - dir->u.dir.first = de; - else - dir->u.dir.last->next = de; - dir->u.dir.last = de; - retval = 0; - } else - retval = -EEXIST; - } - write_unlock(&dir->u.dir.lock); - if (retval) - devfs_put(de); - return retval; -} /* End Function _devfs_append_entry */ - -/** - * _devfs_get_root_entry - Get the root devfs entry. - * - * Returns the root devfs entry on success, else %NULL. - * - * TODO it must be called asynchronously due to the fact - * that devfs is initialized relatively late. Proper way - * is to remove module_init from init_devfs_fs and manually - * call it early enough during system init - */ - -static struct devfs_entry *_devfs_get_root_entry(void) -{ - struct devfs_entry *new; - static DEFINE_SPINLOCK(root_lock); - - if (root_entry) - return root_entry; - - new = _devfs_alloc_entry(NULL, 0, MODE_DIR); - if (new == NULL) - return NULL; - - spin_lock(&root_lock); - if (root_entry) { - spin_unlock(&root_lock); - devfs_put(new); - return root_entry; - } - root_entry = new; - spin_unlock(&root_lock); - - return root_entry; -} /* End Function _devfs_get_root_entry */ - -/** - * _devfs_descend - Descend down a tree using the next component name. - * @dir: The directory to search. - * @name: The component name to search for. - * @namelen: The length of %name. - * @next_pos: The position of the next '/' or '\0' is written here. - * - * Descend into a directory, searching for a component. This function forms - * the core of a tree-walking algorithm. The directory will be locked. - * The devfs entry corresponding to the component is returned. If there is - * no matching entry, %NULL is returned. - * An implicit devfs_get() is performed on the returned entry. - */ - -static struct devfs_entry *_devfs_descend(struct devfs_entry *dir, - const char *name, int namelen, - int *next_pos) -{ - const char *stop, *ptr; - struct devfs_entry *entry; - - if ((namelen >= 3) && (strncmp(name, "../", 3) == 0)) { /* Special-case going to parent directory */ - *next_pos = 3; - return devfs_get(dir->parent); - } - stop = name + namelen; - /* Search for a possible '/' */ - for (ptr = name; (ptr < stop) && (*ptr != '/'); ++ptr) ; - *next_pos = ptr - name; - read_lock(&dir->u.dir.lock); - entry = _devfs_search_dir(dir, name, *next_pos); - read_unlock(&dir->u.dir.lock); - return entry; -} /* End Function _devfs_descend */ - -static devfs_handle_t _devfs_make_parent_for_leaf(struct devfs_entry *dir, - const char *name, - int namelen, int *leaf_pos) -{ - int next_pos = 0; - - if (dir == NULL) - dir = _devfs_get_root_entry(); - if (dir == NULL) - return NULL; - devfs_get(dir); - /* Search for possible trailing component and ignore it */ - for (--namelen; (namelen > 0) && (name[namelen] != '/'); --namelen) ; - *leaf_pos = (name[namelen] == '/') ? (namelen + 1) : 0; - for (; namelen > 0; name += next_pos, namelen -= next_pos) { - struct devfs_entry *de, *old = NULL; - - if ((de = - _devfs_descend(dir, name, namelen, &next_pos)) == NULL) { - de = _devfs_alloc_entry(name, next_pos, MODE_DIR); - devfs_get(de); - if (!de || _devfs_append_entry(dir, de, &old)) { - devfs_put(de); - if (!old || !S_ISDIR(old->mode)) { - devfs_put(old); - devfs_put(dir); - return NULL; - } - de = old; /* Use the existing directory */ - } - } - if (de == dir->parent) { - devfs_put(dir); - devfs_put(de); - return NULL; - } - devfs_put(dir); - dir = de; - if (name[next_pos] == '/') - ++next_pos; - } - return dir; -} /* End Function _devfs_make_parent_for_leaf */ - -static devfs_handle_t _devfs_prepare_leaf(devfs_handle_t * dir, - const char *name, umode_t mode) -{ - int namelen, leaf_pos; - struct devfs_entry *de; - - namelen = strlen(name); - if ((*dir = _devfs_make_parent_for_leaf(*dir, name, namelen, - &leaf_pos)) == NULL) { - PRINTK("(%s): could not create parent path\n", name); - return NULL; - } - if ((de = _devfs_alloc_entry(name + leaf_pos, namelen - leaf_pos, mode)) - == NULL) { - PRINTK("(%s): could not allocate entry\n", name); - devfs_put(*dir); - return NULL; - } - return de; -} /* End Function _devfs_prepare_leaf */ - -static devfs_handle_t _devfs_walk_path(struct devfs_entry *dir, - const char *name, int namelen, - int traverse_symlink) -{ - int next_pos = 0; - - if (dir == NULL) - dir = _devfs_get_root_entry(); - if (dir == NULL) - return NULL; - devfs_get(dir); - for (; namelen > 0; name += next_pos, namelen -= next_pos) { - struct devfs_entry *de, *link; - - if (!S_ISDIR(dir->mode)) { - devfs_put(dir); - return NULL; - } - - if ((de = - _devfs_descend(dir, name, namelen, &next_pos)) == NULL) { - devfs_put(dir); - return NULL; - } - if (S_ISLNK(de->mode) && traverse_symlink) { /* Need to follow the link: this is a stack chomper */ - /* FIXME what if it puts outside of mounted tree? */ - link = _devfs_walk_path(dir, de->u.symlink.linkname, - de->u.symlink.length, TRUE); - devfs_put(de); - if (!link) { - devfs_put(dir); - return NULL; - } - de = link; - } - devfs_put(dir); - dir = de; - if (name[next_pos] == '/') - ++next_pos; - } - return dir; -} /* End Function _devfs_walk_path */ - -/** - * _devfs_find_entry - Find a devfs entry. - * @dir: The handle to the parent devfs directory entry. If this is %NULL the - * name is relative to the root of the devfs. - * @name: The name of the entry. This may be %NULL. - * @traverse_symlink: If %TRUE then symbolic links are traversed. - * - * Returns the devfs_entry pointer on success, else %NULL. An implicit - * devfs_get() is performed. - */ - -static struct devfs_entry *_devfs_find_entry(devfs_handle_t dir, - const char *name, - int traverse_symlink) -{ - unsigned int namelen = strlen(name); - - if (name[0] == '/') { - /* Skip leading pathname component */ - if (namelen < 2) { - PRINTK("(%s): too short\n", name); - return NULL; - } - for (++name, --namelen; (*name != '/') && (namelen > 0); - ++name, --namelen) ; - if (namelen < 2) { - PRINTK("(%s): too short\n", name); - return NULL; - } - ++name; - --namelen; - } - return _devfs_walk_path(dir, name, namelen, traverse_symlink); -} /* End Function _devfs_find_entry */ - -static struct devfs_entry *get_devfs_entry_from_vfs_inode(struct inode *inode) -{ - if (inode == NULL) - return NULL; - VERIFY_ENTRY((struct devfs_entry *)inode->u.generic_ip); - return inode->u.generic_ip; -} /* End Function get_devfs_entry_from_vfs_inode */ - -/** - * free_dentry - Free the dentry for a device entry and invalidate inode. - * @de: The entry. - * - * This must only be called after the entry has been unhooked from its - * parent directory. - */ - -static void free_dentry(struct devfs_entry *de) -{ - struct dentry *dentry = de->inode.dentry; - - if (!dentry) - return; - spin_lock(&dcache_lock); - dget_locked(dentry); - spin_unlock(&dcache_lock); - /* Forcefully remove the inode */ - if (dentry->d_inode != NULL) - dentry->d_inode->i_nlink = 0; - d_drop(dentry); - dput(dentry); -} /* End Function free_dentry */ - -/** - * is_devfsd_or_child - Test if the current process is devfsd or one of its children. - * @fs_info: The filesystem information. - * - * Returns %TRUE if devfsd or child, else %FALSE. - */ - -static int is_devfsd_or_child(struct fs_info *fs_info) -{ - struct task_struct *p = current; - - if (p == fs_info->devfsd_task) - return (TRUE); - if (process_group(p) == fs_info->devfsd_pgrp) - return (TRUE); - read_lock(&tasklist_lock); - for (; p != &init_task; p = p->real_parent) { - if (p == fs_info->devfsd_task) { - read_unlock(&tasklist_lock); - return (TRUE); - } - } - read_unlock(&tasklist_lock); - return (FALSE); -} /* End Function is_devfsd_or_child */ - -/** - * devfsd_queue_empty - Test if devfsd has work pending in its event queue. - * @fs_info: The filesystem information. - * - * Returns %TRUE if the queue is empty, else %FALSE. - */ - -static inline int devfsd_queue_empty(struct fs_info *fs_info) -{ - return (fs_info->devfsd_last_event) ? FALSE : TRUE; -} /* End Function devfsd_queue_empty */ - -/** - * wait_for_devfsd_finished - Wait for devfsd to finish processing its event queue. - * @fs_info: The filesystem information. - * - * Returns %TRUE if no more waiting will be required, else %FALSE. - */ - -static int wait_for_devfsd_finished(struct fs_info *fs_info) -{ - DECLARE_WAITQUEUE(wait, current); - - if (fs_info->devfsd_task == NULL) - return (TRUE); - if (devfsd_queue_empty(fs_info) && fs_info->devfsd_sleeping) - return TRUE; - if (is_devfsd_or_child(fs_info)) - return (FALSE); - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&fs_info->revalidate_wait_queue, &wait); - if (!devfsd_queue_empty(fs_info) || !fs_info->devfsd_sleeping) - if (fs_info->devfsd_task) - schedule(); - remove_wait_queue(&fs_info->revalidate_wait_queue, &wait); - __set_current_state(TASK_RUNNING); - return (TRUE); -} /* End Function wait_for_devfsd_finished */ - -/** - * devfsd_notify_de - Notify the devfsd daemon of a change. - * @de: The devfs entry that has changed. This and all parent entries will - * have their reference counts incremented if the event was queued. - * @type: The type of change. - * @mode: The mode of the entry. - * @uid: The user ID. - * @gid: The group ID. - * @fs_info: The filesystem info. - * - * Returns %TRUE if an event was queued and devfsd woken up, else %FALSE. - */ - -static int devfsd_notify_de(struct devfs_entry *de, - unsigned short type, umode_t mode, - uid_t uid, gid_t gid, struct fs_info *fs_info) -{ - struct devfsd_buf_entry *entry; - struct devfs_entry *curr; - - if (!(fs_info->devfsd_event_mask & (1 << type))) - return (FALSE); - if ((entry = kmem_cache_alloc(devfsd_buf_cache, SLAB_KERNEL)) == NULL) { - atomic_inc(&fs_info->devfsd_overrun_count); - return (FALSE); - } - for (curr = de; curr != NULL; curr = curr->parent) - devfs_get(curr); - entry->de = de; - entry->type = type; - entry->mode = mode; - entry->uid = uid; - entry->gid = gid; - entry->next = NULL; - spin_lock(&fs_info->devfsd_buffer_lock); - if (!fs_info->devfsd_first_event) - fs_info->devfsd_first_event = entry; - if (fs_info->devfsd_last_event) - fs_info->devfsd_last_event->next = entry; - fs_info->devfsd_last_event = entry; - spin_unlock(&fs_info->devfsd_buffer_lock); - wake_up_interruptible(&fs_info->devfsd_wait_queue); - return (TRUE); -} /* End Function devfsd_notify_de */ - -/** - * devfsd_notify - Notify the devfsd daemon of a change. - * @de: The devfs entry that has changed. - * @type: The type of change event. - * @wait: If TRUE, the function waits for the daemon to finish processing - * the event. - */ - -static void devfsd_notify(struct devfs_entry *de, unsigned short type) -{ - devfsd_notify_de(de, type, de->mode, current->euid, - current->egid, &fs_info); -} - -static int devfs_mk_dev(dev_t dev, umode_t mode, const char *fmt, va_list args) -{ - struct devfs_entry *dir = NULL, *de; - char buf[64]; - int error, n; - - n = vsnprintf(buf, sizeof(buf), fmt, args); - if (n >= sizeof(buf) || !buf[0]) { - printk(KERN_WARNING "%s: invalid format string %s\n", - __FUNCTION__, fmt); - return -EINVAL; - } - - de = _devfs_prepare_leaf(&dir, buf, mode); - if (!de) { - printk(KERN_WARNING "%s: could not prepare leaf for %s\n", - __FUNCTION__, buf); - return -ENOMEM; /* could be more accurate... */ - } - - de->u.dev = dev; - - error = _devfs_append_entry(dir, de, NULL); - if (error) { - printk(KERN_WARNING "%s: could not append to parent for %s\n", - __FUNCTION__, buf); - goto out; - } - - devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED); - out: - devfs_put(dir); - return error; -} - -int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...) -{ - va_list args; - - if (!S_ISBLK(mode)) { - printk(KERN_WARNING "%s: invalide mode (%u) for %s\n", - __FUNCTION__, mode, fmt); - return -EINVAL; - } - - va_start(args, fmt); - return devfs_mk_dev(dev, mode, fmt, args); -} - -EXPORT_SYMBOL(devfs_mk_bdev); - -int devfs_mk_cdev(dev_t dev, umode_t mode, const char *fmt, ...) -{ - va_list args; - - if (!S_ISCHR(mode)) { - printk(KERN_WARNING "%s: invalide mode (%u) for %s\n", - __FUNCTION__, mode, fmt); - return -EINVAL; - } - - va_start(args, fmt); - return devfs_mk_dev(dev, mode, fmt, args); -} - -EXPORT_SYMBOL(devfs_mk_cdev); - -/** - * _devfs_unhook - Unhook a device entry from its parents list - * @de: The entry to unhook. - * - * Returns %TRUE if the entry was unhooked, else %FALSE if it was - * previously unhooked. - * The caller must have a write lock on the parent directory. - */ - -static int _devfs_unhook(struct devfs_entry *de) -{ - struct devfs_entry *parent; - - if (!de || (de->prev == de)) - return FALSE; - parent = de->parent; - if (de->prev == NULL) - parent->u.dir.first = de->next; - else - de->prev->next = de->next; - if (de->next == NULL) - parent->u.dir.last = de->prev; - else - de->next->prev = de->prev; - de->prev = de; /* Indicate we're unhooked */ - de->next = NULL; /* Force early termination for */ - return TRUE; -} /* End Function _devfs_unhook */ - -/** - * _devfs_unregister - Unregister a device entry from its parent. - * @dir: The parent directory. - * @de: The entry to unregister. - * - * The caller must have a write lock on the parent directory, which is - * unlocked by this function. - */ - -static void _devfs_unregister(struct devfs_entry *dir, struct devfs_entry *de) -{ - int unhooked = _devfs_unhook(de); - - write_unlock(&dir->u.dir.lock); - if (!unhooked) - return; - devfs_get(dir); - devfsd_notify(de, DEVFSD_NOTIFY_UNREGISTERED); - free_dentry(de); - devfs_put(dir); - if (!S_ISDIR(de->mode)) - return; - while (TRUE) { /* Recursively unregister: this is a stack chomper */ - struct devfs_entry *child; - - write_lock(&de->u.dir.lock); - de->u.dir.no_more_additions = TRUE; - child = de->u.dir.first; - VERIFY_ENTRY(child); - _devfs_unregister(de, child); - if (!child) - break; - DPRINTK(DEBUG_UNREGISTER, "(%s): child: %p refcount: %d\n", - child->name, child, atomic_read(&child->refcount)); - devfs_put(child); - } -} /* End Function _devfs_unregister */ - -static int devfs_do_symlink(devfs_handle_t dir, const char *name, - const char *link, devfs_handle_t * handle) -{ - int err; - unsigned int linklength; - char *newlink; - struct devfs_entry *de; - - if (handle != NULL) - *handle = NULL; - if (name == NULL) { - PRINTK("(): NULL name pointer\n"); - return -EINVAL; - } - if (link == NULL) { - PRINTK("(%s): NULL link pointer\n", name); - return -EINVAL; - } - linklength = strlen(link); - if ((newlink = kmalloc(linklength + 1, GFP_KERNEL)) == NULL) - return -ENOMEM; - memcpy(newlink, link, linklength); - newlink[linklength] = '\0'; - if ((de = _devfs_prepare_leaf(&dir, name, S_IFLNK | S_IRUGO | S_IXUGO)) - == NULL) { - PRINTK("(%s): could not prepare leaf\n", name); - kfree(newlink); - return -ENOTDIR; - } - de->info = NULL; - de->u.symlink.linkname = newlink; - de->u.symlink.length = linklength; - if ((err = _devfs_append_entry(dir, de, NULL)) != 0) { - PRINTK("(%s): could not append to parent, err: %d\n", name, - err); - devfs_put(dir); - return err; - } - devfs_put(dir); -#ifdef CONFIG_DEVFS_DEBUG - spin_lock(&stat_lock); - stat_num_bytes += linklength + 1; - spin_unlock(&stat_lock); -#endif - if (handle != NULL) - *handle = de; - return 0; -} /* End Function devfs_do_symlink */ - -/** - * devfs_mk_symlink Create a symbolic link in the devfs namespace. - * @from: The name of the entry. - * @to: Name of the destination - * - * Returns 0 on success, else a negative error code is returned. - */ - -int devfs_mk_symlink(const char *from, const char *to) -{ - devfs_handle_t de; - int err; - - err = devfs_do_symlink(NULL, from, to, &de); - if (!err) { - de->vfs = TRUE; - devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED); - } - - return err; -} - -/** - * devfs_mk_dir - Create a directory in the devfs namespace. - * new name is relative to the root of the devfs. - * @fmt: The name of the entry. - * - * Use of this function is optional. The devfs_register() function - * will automatically create intermediate directories as needed. This function - * is provided for efficiency reasons, as it provides a handle to a directory. - * On failure %NULL is returned. - */ - -int devfs_mk_dir(const char *fmt, ...) -{ - struct devfs_entry *dir = NULL, *de = NULL, *old; - char buf[64]; - va_list args; - int error, n; - - va_start(args, fmt); - n = vsnprintf(buf, 64, fmt, args); - if (n >= 64 || !buf[0]) { - printk(KERN_WARNING "%s: invalid argument.", __FUNCTION__); - return -EINVAL; - } - - de = _devfs_prepare_leaf(&dir, buf, MODE_DIR); - if (!de) { - PRINTK("(%s): could not prepare leaf\n", buf); - return -EINVAL; - } - - error = _devfs_append_entry(dir, de, &old); - if (error == -EEXIST && S_ISDIR(old->mode)) { - /* - * devfs_mk_dir() of an already-existing directory will - * return success. - */ - error = 0; - goto out_put; - } else if (error) { - PRINTK("(%s): could not append to dir: %p \"%s\"\n", - buf, dir, dir->name); - devfs_put(old); - goto out_put; - } - - devfsd_notify(de, DEVFSD_NOTIFY_REGISTERED); - - out_put: - devfs_put(dir); - return error; -} - -void devfs_remove(const char *fmt, ...) -{ - char buf[64]; - va_list args; - int n; - - va_start(args, fmt); - n = vsnprintf(buf, sizeof(buf), fmt, args); - if (n < sizeof(buf) && buf[0]) { - devfs_handle_t de = _devfs_find_entry(NULL, buf, 0); - - if (!de) { - printk(KERN_ERR "%s: %s not found, cannot remove\n", - __FUNCTION__, buf); - dump_stack(); - return; - } - - write_lock(&de->parent->u.dir.lock); - _devfs_unregister(de->parent, de); - devfs_put(de); - devfs_put(de); - } -} - -/** - * devfs_generate_path - Generate a pathname for an entry, relative to the devfs root. - * @de: The devfs entry. - * @path: The buffer to write the pathname to. The pathname and '\0' - * terminator will be written at the end of the buffer. - * @buflen: The length of the buffer. - * - * Returns the offset in the buffer where the pathname starts on success, - * else a negative error code. - */ - -static int devfs_generate_path(devfs_handle_t de, char *path, int buflen) -{ - int pos; -#define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name ) - - if (de == NULL) - return -EINVAL; - VERIFY_ENTRY(de); - if (de->namelen >= buflen) - return -ENAMETOOLONG; /* Must be first */ - path[buflen - 1] = '\0'; - if (de->parent == NULL) - return buflen - 1; /* Don't prepend root */ - pos = buflen - de->namelen - 1; - memcpy(path + pos, NAMEOF(de), de->namelen); - for (de = de->parent; de->parent != NULL; de = de->parent) { - if (pos - de->namelen - 1 < 0) - return -ENAMETOOLONG; - path[--pos] = '/'; - pos -= de->namelen; - memcpy(path + pos, NAMEOF(de), de->namelen); - } - return pos; -} /* End Function devfs_generate_path */ - -/** - * devfs_setup - Process kernel boot options. - * @str: The boot options after the "devfs=". - */ - -static int __init devfs_setup(char *str) -{ - static struct { - char *name; - unsigned int mask; - unsigned int *opt; - } devfs_options_tab[] __initdata = { -#ifdef CONFIG_DEVFS_DEBUG - { - "dall", DEBUG_ALL, &devfs_debug_init}, { - "dmod", DEBUG_MODULE_LOAD, &devfs_debug_init}, { - "dreg", DEBUG_REGISTER, &devfs_debug_init}, { - "dunreg", DEBUG_UNREGISTER, &devfs_debug_init}, { - "dfree", DEBUG_FREE, &devfs_debug_init}, { - "diget", DEBUG_I_GET, &devfs_debug_init}, { - "dchange", DEBUG_SET_FLAGS, &devfs_debug_init}, { - "dsread", DEBUG_S_READ, &devfs_debug_init}, { - "dichange", DEBUG_I_CHANGE, &devfs_debug_init}, { - "dimknod", DEBUG_I_MKNOD, &devfs_debug_init}, { - "dilookup", DEBUG_I_LOOKUP, &devfs_debug_init}, { - "diunlink", DEBUG_I_UNLINK, &devfs_debug_init}, -#endif /* CONFIG_DEVFS_DEBUG */ - { - "mount", OPTION_MOUNT, &boot_options}, { - NULL, 0, NULL} - }; - - while ((*str != '\0') && !isspace(*str)) { - int i, found = 0, invert = 0; - - if (strncmp(str, "no", 2) == 0) { - invert = 1; - str += 2; - } - for (i = 0; devfs_options_tab[i].name != NULL; i++) { - int len = strlen(devfs_options_tab[i].name); - - if (strncmp(str, devfs_options_tab[i].name, len) == 0) { - if (invert) - *devfs_options_tab[i].opt &= - ~devfs_options_tab[i].mask; - else - *devfs_options_tab[i].opt |= - devfs_options_tab[i].mask; - str += len; - found = 1; - break; - } - } - if (!found) - return 0; /* No match */ - if (*str != ',') - return 0; /* No more options */ - ++str; - } - return 1; -} /* End Function devfs_setup */ - -__setup("devfs=", devfs_setup); - -EXPORT_SYMBOL(devfs_mk_dir); -EXPORT_SYMBOL(devfs_remove); - -/** - * try_modload - Notify devfsd of an inode lookup by a non-devfsd process. - * @parent: The parent devfs entry. - * @fs_info: The filesystem info. - * @name: The device name. - * @namelen: The number of characters in @name. - * @buf: A working area that will be used. This must not go out of scope - * until devfsd is idle again. - * - * Returns 0 on success (event was queued), else a negative error code. - */ - -static int try_modload(struct devfs_entry *parent, struct fs_info *fs_info, - const char *name, unsigned namelen, - struct devfs_entry *buf) -{ - if (!(fs_info->devfsd_event_mask & (1 << DEVFSD_NOTIFY_LOOKUP))) - return -ENOENT; - if (is_devfsd_or_child(fs_info)) - return -ENOENT; - memset(buf, 0, sizeof *buf); - atomic_set(&buf->refcount, 1); - buf->parent = parent; - buf->namelen = namelen; - buf->u.name = name; - WRITE_ENTRY_MAGIC(buf, MAGIC_VALUE); - if (!devfsd_notify_de(buf, DEVFSD_NOTIFY_LOOKUP, 0, - current->euid, current->egid, fs_info)) - return -ENOENT; - /* Possible success: event has been queued */ - return 0; -} /* End Function try_modload */ - -/* Superblock operations follow */ - -static struct inode_operations devfs_iops; -static struct inode_operations devfs_dir_iops; -static const struct file_operations devfs_fops; -static const struct file_operations devfs_dir_fops; -static struct inode_operations devfs_symlink_iops; - -static int devfs_notify_change(struct dentry *dentry, struct iattr *iattr) -{ - int retval; - struct devfs_entry *de; - struct inode *inode = dentry->d_inode; - struct fs_info *fs_info = inode->i_sb->s_fs_info; - - de = get_devfs_entry_from_vfs_inode(inode); - if (de == NULL) - return -ENODEV; - retval = inode_change_ok(inode, iattr); - if (retval != 0) - return retval; - retval = inode_setattr(inode, iattr); - if (retval != 0) - return retval; - DPRINTK(DEBUG_I_CHANGE, "(%d): VFS inode: %p devfs_entry: %p\n", - (int)inode->i_ino, inode, de); - DPRINTK(DEBUG_I_CHANGE, "(): mode: 0%o uid: %d gid: %d\n", - (int)inode->i_mode, (int)inode->i_uid, (int)inode->i_gid); - /* Inode is not on hash chains, thus must save permissions here rather - than in a write_inode() method */ - de->mode = inode->i_mode; - de->inode.uid = inode->i_uid; - de->inode.gid = inode->i_gid; - de->inode.atime = inode->i_atime; - de->inode.mtime = inode->i_mtime; - de->inode.ctime = inode->i_ctime; - if ((iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) && - !is_devfsd_or_child(fs_info)) - devfsd_notify_de(de, DEVFSD_NOTIFY_CHANGE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_notify_change */ - -static struct super_operations devfs_sops = { - .drop_inode = generic_delete_inode, - .statfs = simple_statfs, -}; - -/** - * _devfs_get_vfs_inode - Get a VFS inode. - * @sb: The super block. - * @de: The devfs inode. - * @dentry: The dentry to register with the devfs inode. - * - * Returns the inode on success, else %NULL. An implicit devfs_get() is - * performed if the inode is created. - */ - -static struct inode *_devfs_get_vfs_inode(struct super_block *sb, - struct devfs_entry *de, - struct dentry *dentry) -{ - struct inode *inode; - - if (de->prev == de) - return NULL; /* Quick check to see if unhooked */ - if ((inode = new_inode(sb)) == NULL) { - PRINTK("(%s): new_inode() failed, de: %p\n", de->name, de); - return NULL; - } - if (de->parent) { - read_lock(&de->parent->u.dir.lock); - if (de->prev != de) - de->inode.dentry = dentry; /* Not unhooked */ - read_unlock(&de->parent->u.dir.lock); - } else - de->inode.dentry = dentry; /* Root: no locking needed */ - if (de->inode.dentry != dentry) { /* Must have been unhooked */ - iput(inode); - return NULL; - } - /* FIXME where is devfs_put? */ - inode->u.generic_ip = devfs_get(de); - inode->i_ino = de->inode.ino; - DPRINTK(DEBUG_I_GET, "(%d): VFS inode: %p devfs_entry: %p\n", - (int)inode->i_ino, inode, de); - inode->i_blocks = 0; - inode->i_blksize = FAKE_BLOCK_SIZE; - inode->i_op = &devfs_iops; - inode->i_mode = de->mode; - if (S_ISDIR(de->mode)) { - inode->i_op = &devfs_dir_iops; - inode->i_fop = &devfs_dir_fops; - } else if (S_ISLNK(de->mode)) { - inode->i_op = &devfs_symlink_iops; - inode->i_size = de->u.symlink.length; - } else if (S_ISCHR(de->mode) || S_ISBLK(de->mode)) { - init_special_inode(inode, de->mode, de->u.dev); - } else if (S_ISFIFO(de->mode) || S_ISSOCK(de->mode)) { - init_special_inode(inode, de->mode, 0); - } else { - PRINTK("(%s): unknown mode %o de: %p\n", - de->name, de->mode, de); - iput(inode); - devfs_put(de); - return NULL; - } - - inode->i_uid = de->inode.uid; - inode->i_gid = de->inode.gid; - inode->i_atime = de->inode.atime; - inode->i_mtime = de->inode.mtime; - inode->i_ctime = de->inode.ctime; - DPRINTK(DEBUG_I_GET, "(): mode: 0%o uid: %d gid: %d\n", - (int)inode->i_mode, (int)inode->i_uid, (int)inode->i_gid); - return inode; -} /* End Function _devfs_get_vfs_inode */ - -/* File operations for device entries follow */ - -static int devfs_readdir(struct file *file, void *dirent, filldir_t filldir) -{ - int err, count; - int stored = 0; - struct fs_info *fs_info; - struct devfs_entry *parent, *de, *next = NULL; - struct inode *inode = file->f_dentry->d_inode; - - fs_info = inode->i_sb->s_fs_info; - parent = get_devfs_entry_from_vfs_inode(file->f_dentry->d_inode); - if ((long)file->f_pos < 0) - return -EINVAL; - DPRINTK(DEBUG_F_READDIR, "(%s): fs_info: %p pos: %ld\n", - parent->name, fs_info, (long)file->f_pos); - switch ((long)file->f_pos) { - case 0: - err = (*filldir) (dirent, "..", 2, file->f_pos, - parent_ino(file->f_dentry), DT_DIR); - if (err == -EINVAL) - break; - if (err < 0) - return err; - file->f_pos++; - ++stored; - /* Fall through */ - case 1: - err = - (*filldir) (dirent, ".", 1, file->f_pos, inode->i_ino, - DT_DIR); - if (err == -EINVAL) - break; - if (err < 0) - return err; - file->f_pos++; - ++stored; - /* Fall through */ - default: - /* Skip entries */ - count = file->f_pos - 2; - read_lock(&parent->u.dir.lock); - for (de = parent->u.dir.first; de && (count > 0); de = de->next) - --count; - devfs_get(de); - read_unlock(&parent->u.dir.lock); - /* Now add all remaining entries */ - while (de) { - err = (*filldir) (dirent, de->name, de->namelen, - file->f_pos, de->inode.ino, - de->mode >> 12); - if (err < 0) - devfs_put(de); - else { - file->f_pos++; - ++stored; - } - if (err == -EINVAL) - break; - if (err < 0) - return err; - read_lock(&parent->u.dir.lock); - next = devfs_get(de->next); - read_unlock(&parent->u.dir.lock); - devfs_put(de); - de = next; - } - break; - } - return stored; -} /* End Function devfs_readdir */ - -/* Open devfs specific special files */ -static int devfs_open(struct inode *inode, struct file *file) -{ - int err; - int minor = MINOR(inode->i_rdev); - struct file_operations *old_fops, *new_fops; - - switch (minor) { - case 0: /* /dev/.devfsd */ - new_fops = fops_get(&devfsd_fops); - break; -#ifdef CONFIG_DEVFS_DEBUG - case 1: /* /dev/.stat */ - new_fops = fops_get(&stat_fops); - break; -#endif - default: - return -ENODEV; - } - - if (new_fops == NULL) - return -ENODEV; - old_fops = file->f_op; - file->f_op = new_fops; - err = new_fops->open ? new_fops->open(inode, file) : 0; - if (err) { - file->f_op = old_fops; - fops_put(new_fops); - } else - fops_put(old_fops); - return err; -} /* End Function devfs_open */ - -static const struct file_operations devfs_fops = { - .open = devfs_open, -}; - -static const struct file_operations devfs_dir_fops = { - .read = generic_read_dir, - .readdir = devfs_readdir, -}; - -/* Dentry operations for device entries follow */ - -/** - * devfs_d_release - Callback for when a dentry is freed. - * @dentry: The dentry. - */ - -static void devfs_d_release(struct dentry *dentry) -{ - DPRINTK(DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, dentry->d_inode); -} /* End Function devfs_d_release */ - -/** - * devfs_d_iput - Callback for when a dentry loses its inode. - * @dentry: The dentry. - * @inode: The inode. - */ - -static void devfs_d_iput(struct dentry *dentry, struct inode *inode) -{ - struct devfs_entry *de; - - de = get_devfs_entry_from_vfs_inode(inode); - DPRINTK(DEBUG_D_IPUT, - "(%s): dentry: %p inode: %p de: %p de->dentry: %p\n", de->name, - dentry, inode, de, de->inode.dentry); - if (de->inode.dentry && (de->inode.dentry != dentry)) - OOPS("(%s): de: %p dentry: %p de->dentry: %p\n", - de->name, de, dentry, de->inode.dentry); - de->inode.dentry = NULL; - iput(inode); - devfs_put(de); -} /* End Function devfs_d_iput */ - -static int devfs_d_delete(struct dentry *dentry); - -static struct dentry_operations devfs_dops = { - .d_delete = devfs_d_delete, - .d_release = devfs_d_release, - .d_iput = devfs_d_iput, -}; - -static int devfs_d_revalidate_wait(struct dentry *dentry, struct nameidata *); - -static struct dentry_operations devfs_wait_dops = { - .d_delete = devfs_d_delete, - .d_release = devfs_d_release, - .d_iput = devfs_d_iput, - .d_revalidate = devfs_d_revalidate_wait, -}; - -/** - * devfs_d_delete - Callback for when all files for a dentry are closed. - * @dentry: The dentry. - */ - -static int devfs_d_delete(struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - - if (dentry->d_op == &devfs_wait_dops) - dentry->d_op = &devfs_dops; - /* Unhash dentry if negative (has no inode) */ - if (inode == NULL) { - DPRINTK(DEBUG_D_DELETE, "(%p): dropping negative dentry\n", - dentry); - return 1; - } - return 0; -} /* End Function devfs_d_delete */ - -struct devfs_lookup_struct { - devfs_handle_t de; - wait_queue_head_t wait_queue; -}; - -/* XXX: this doesn't handle the case where we got a negative dentry - but a devfs entry has been registered in the meanwhile */ -static int devfs_d_revalidate_wait(struct dentry *dentry, struct nameidata *nd) -{ - struct inode *dir = dentry->d_parent->d_inode; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - devfs_handle_t parent = get_devfs_entry_from_vfs_inode(dir); - struct devfs_lookup_struct *lookup_info = dentry->d_fsdata; - DECLARE_WAITQUEUE(wait, current); - int need_lock; - - /* - * FIXME HACK - * - * make sure that - * d_instantiate always runs under lock - * we release i_mutex lock before going to sleep - * - * unfortunately sometimes d_revalidate is called with - * and sometimes without i_mutex lock held. The following checks - * attempt to deduce when we need to add (and drop resp.) lock - * here. This relies on current (2.6.2) calling coventions: - * - * lookup_hash is always run under i_mutex and is passing NULL - * as nd - * - * open(...,O_CREATE,...) calls _lookup_hash under i_mutex - * and sets flags to LOOKUP_OPEN|LOOKUP_CREATE - * - * all other invocations of ->d_revalidate seem to happen - * outside of i_mutex - */ - need_lock = nd && - (!(nd->flags & LOOKUP_CREATE) || (nd->flags & LOOKUP_PARENT)); - - if (need_lock) - mutex_lock(&dir->i_mutex); - - if (is_devfsd_or_child(fs_info)) { - devfs_handle_t de = lookup_info->de; - struct inode *inode; - - DPRINTK(DEBUG_I_LOOKUP, - "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n", - dentry->d_name.name, dentry, dentry->d_inode, de, - current->comm); - if (dentry->d_inode) - goto out; - if (de == NULL) { - read_lock(&parent->u.dir.lock); - de = _devfs_search_dir(parent, dentry->d_name.name, - dentry->d_name.len); - read_unlock(&parent->u.dir.lock); - if (de == NULL) - goto out; - lookup_info->de = de; - } - /* Create an inode, now that the driver information is available */ - inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry); - if (!inode) - goto out; - DPRINTK(DEBUG_I_LOOKUP, - "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", - de->name, de->inode.ino, inode, de, current->comm); - d_instantiate(dentry, inode); - goto out; - } - if (lookup_info == NULL) - goto out; /* Early termination */ - read_lock(&parent->u.dir.lock); - if (dentry->d_fsdata) { - set_current_state(TASK_UNINTERRUPTIBLE); - add_wait_queue(&lookup_info->wait_queue, &wait); - read_unlock(&parent->u.dir.lock); - /* at this point it is always (hopefully) locked */ - mutex_unlock(&dir->i_mutex); - schedule(); - mutex_lock(&dir->i_mutex); - /* - * This does not need nor should remove wait from wait_queue. - * Wait queue head is never reused - nothing is ever added to it - * after all waiters have been waked up and head itself disappears - * very soon after it. Moreover it is local variable on stack that - * is likely to have already disappeared so any reference to it - * at this point is buggy. - */ - - } else - read_unlock(&parent->u.dir.lock); - - out: - if (need_lock) - mutex_unlock(&dir->i_mutex); - return 1; -} /* End Function devfs_d_revalidate_wait */ - -/* Inode operations for device entries follow */ - -static struct dentry *devfs_lookup(struct inode *dir, struct dentry *dentry, - struct nameidata *nd) -{ - struct devfs_entry tmp; /* Must stay in scope until devfsd idle again */ - struct devfs_lookup_struct lookup_info; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - struct dentry *retval = NULL; - - /* Set up the dentry operations before anything else, to ensure cleaning - up on any error */ - dentry->d_op = &devfs_dops; - /* First try to get the devfs entry for this directory */ - parent = get_devfs_entry_from_vfs_inode(dir); - DPRINTK(DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n", - dentry->d_name.name, dentry, parent, current->comm); - if (parent == NULL) - return ERR_PTR(-ENOENT); - read_lock(&parent->u.dir.lock); - de = _devfs_search_dir(parent, dentry->d_name.name, dentry->d_name.len); - read_unlock(&parent->u.dir.lock); - lookup_info.de = de; - init_waitqueue_head(&lookup_info.wait_queue); - dentry->d_fsdata = &lookup_info; - if (de == NULL) { /* Try with devfsd. For any kind of failure, leave a negative dentry - so someone else can deal with it (in the case where the sysadmin - does a mknod()). It's important to do this before hashing the - dentry, so that the devfsd queue is filled before revalidates - can start */ - if (try_modload(parent, fs_info, dentry->d_name.name, dentry->d_name.len, &tmp) < 0) { /* Lookup event was not queued to devfsd */ - d_add(dentry, NULL); - return NULL; - } - } - dentry->d_op = &devfs_wait_dops; - d_add(dentry, NULL); /* Open the floodgates */ - /* Unlock directory semaphore, which will release any waiters. They - will get the hashed dentry, and may be forced to wait for - revalidation */ - mutex_unlock(&dir->i_mutex); - wait_for_devfsd_finished(fs_info); /* If I'm not devfsd, must wait */ - mutex_lock(&dir->i_mutex); /* Grab it again because them's the rules */ - de = lookup_info.de; - /* If someone else has been so kind as to make the inode, we go home - early */ - if (dentry->d_inode) - goto out; - if (de == NULL) { - read_lock(&parent->u.dir.lock); - de = _devfs_search_dir(parent, dentry->d_name.name, - dentry->d_name.len); - read_unlock(&parent->u.dir.lock); - if (de == NULL) - goto out; - /* OK, there's an entry now, but no VFS inode yet */ - } - /* Create an inode, now that the driver information is available */ - inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry); - if (!inode) { - retval = ERR_PTR(-ENOMEM); - goto out; - } - DPRINTK(DEBUG_I_LOOKUP, - "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n", de->name, - de->inode.ino, inode, de, current->comm); - d_instantiate(dentry, inode); - out: - write_lock(&parent->u.dir.lock); - dentry->d_op = &devfs_dops; - dentry->d_fsdata = NULL; - wake_up(&lookup_info.wait_queue); - write_unlock(&parent->u.dir.lock); - devfs_put(de); - return retval; -} /* End Function devfs_lookup */ - -static int devfs_unlink(struct inode *dir, struct dentry *dentry) -{ - int unhooked; - struct devfs_entry *de; - struct inode *inode = dentry->d_inode; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - - de = get_devfs_entry_from_vfs_inode(inode); - DPRINTK(DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de); - if (de == NULL) - return -ENOENT; - if (!de->vfs) - return -EPERM; - write_lock(&de->parent->u.dir.lock); - unhooked = _devfs_unhook(de); - write_unlock(&de->parent->u.dir.lock); - if (!unhooked) - return -ENOENT; - if (!is_devfsd_or_child(fs_info)) - devfsd_notify_de(de, DEVFSD_NOTIFY_DELETE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - free_dentry(de); - devfs_put(de); - return 0; -} /* End Function devfs_unlink */ - -static int devfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int err; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - - /* First try to get the devfs entry for this directory */ - parent = get_devfs_entry_from_vfs_inode(dir); - if (parent == NULL) - return -ENOENT; - err = devfs_do_symlink(parent, dentry->d_name.name, symname, &de); - DPRINTK(DEBUG_DISABLED, "(%s): errcode from : %d\n", - dentry->d_name.name, err); - if (err < 0) - return err; - de->vfs = TRUE; - de->inode.uid = current->euid; - de->inode.gid = current->egid; - de->inode.atime = CURRENT_TIME; - de->inode.mtime = CURRENT_TIME; - de->inode.ctime = CURRENT_TIME; - if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL) - return -ENOMEM; - DPRINTK(DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n", - dentry->d_name.name, de->inode.ino, inode, dentry); - d_instantiate(dentry, inode); - if (!is_devfsd_or_child(fs_info)) - devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_symlink */ - -static int devfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) -{ - int err; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - - mode = (mode & ~S_IFMT) | S_IFDIR; /* VFS doesn't pass S_IFMT part */ - parent = get_devfs_entry_from_vfs_inode(dir); - if (parent == NULL) - return -ENOENT; - de = _devfs_alloc_entry(dentry->d_name.name, dentry->d_name.len, mode); - if (!de) - return -ENOMEM; - de->vfs = TRUE; - if ((err = _devfs_append_entry(parent, de, NULL)) != 0) - return err; - de->inode.uid = current->euid; - de->inode.gid = current->egid; - de->inode.atime = CURRENT_TIME; - de->inode.mtime = CURRENT_TIME; - de->inode.ctime = CURRENT_TIME; - if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL) - return -ENOMEM; - DPRINTK(DEBUG_DISABLED, "(%s): new VFS inode(%u): %p dentry: %p\n", - dentry->d_name.name, de->inode.ino, inode, dentry); - d_instantiate(dentry, inode); - if (!is_devfsd_or_child(fs_info)) - devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_mkdir */ - -static int devfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - int err = 0; - struct devfs_entry *de; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct inode *inode = dentry->d_inode; - - if (dir->i_sb->s_fs_info != inode->i_sb->s_fs_info) - return -EINVAL; - de = get_devfs_entry_from_vfs_inode(inode); - if (de == NULL) - return -ENOENT; - if (!S_ISDIR(de->mode)) - return -ENOTDIR; - if (!de->vfs) - return -EPERM; - /* First ensure the directory is empty and will stay that way */ - write_lock(&de->u.dir.lock); - if (de->u.dir.first) - err = -ENOTEMPTY; - else - de->u.dir.no_more_additions = TRUE; - write_unlock(&de->u.dir.lock); - if (err) - return err; - /* Now unhook the directory from its parent */ - write_lock(&de->parent->u.dir.lock); - if (!_devfs_unhook(de)) - err = -ENOENT; - write_unlock(&de->parent->u.dir.lock); - if (err) - return err; - if (!is_devfsd_or_child(fs_info)) - devfsd_notify_de(de, DEVFSD_NOTIFY_DELETE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - free_dentry(de); - devfs_put(de); - return 0; -} /* End Function devfs_rmdir */ - -static int devfs_mknod(struct inode *dir, struct dentry *dentry, int mode, - dev_t rdev) -{ - int err; - struct fs_info *fs_info = dir->i_sb->s_fs_info; - struct devfs_entry *parent, *de; - struct inode *inode; - - DPRINTK(DEBUG_I_MKNOD, "(%s): mode: 0%o dev: %u:%u\n", - dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); - parent = get_devfs_entry_from_vfs_inode(dir); - if (parent == NULL) - return -ENOENT; - de = _devfs_alloc_entry(dentry->d_name.name, dentry->d_name.len, mode); - if (!de) - return -ENOMEM; - de->vfs = TRUE; - if (S_ISCHR(mode) || S_ISBLK(mode)) - de->u.dev = rdev; - if ((err = _devfs_append_entry(parent, de, NULL)) != 0) - return err; - de->inode.uid = current->euid; - de->inode.gid = current->egid; - de->inode.atime = CURRENT_TIME; - de->inode.mtime = CURRENT_TIME; - de->inode.ctime = CURRENT_TIME; - if ((inode = _devfs_get_vfs_inode(dir->i_sb, de, dentry)) == NULL) - return -ENOMEM; - DPRINTK(DEBUG_I_MKNOD, ": new VFS inode(%u): %p dentry: %p\n", - de->inode.ino, inode, dentry); - d_instantiate(dentry, inode); - if (!is_devfsd_or_child(fs_info)) - devfsd_notify_de(de, DEVFSD_NOTIFY_CREATE, inode->i_mode, - inode->i_uid, inode->i_gid, fs_info); - return 0; -} /* End Function devfs_mknod */ - -static void *devfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - struct devfs_entry *p = get_devfs_entry_from_vfs_inode(dentry->d_inode); - nd_set_link(nd, p ? p->u.symlink.linkname : ERR_PTR(-ENODEV)); - return NULL; -} /* End Function devfs_follow_link */ - -static struct inode_operations devfs_iops = { - .setattr = devfs_notify_change, -}; - -static struct inode_operations devfs_dir_iops = { - .lookup = devfs_lookup, - .unlink = devfs_unlink, - .symlink = devfs_symlink, - .mkdir = devfs_mkdir, - .rmdir = devfs_rmdir, - .mknod = devfs_mknod, - .setattr = devfs_notify_change, -}; - -static struct inode_operations devfs_symlink_iops = { - .readlink = generic_readlink, - .follow_link = devfs_follow_link, - .setattr = devfs_notify_change, -}; - -static int devfs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct inode *root_inode = NULL; - - if (_devfs_get_root_entry() == NULL) - goto out_no_root; - atomic_set(&fs_info.devfsd_overrun_count, 0); - init_waitqueue_head(&fs_info.devfsd_wait_queue); - init_waitqueue_head(&fs_info.revalidate_wait_queue); - fs_info.sb = sb; - sb->s_fs_info = &fs_info; - sb->s_blocksize = 1024; - sb->s_blocksize_bits = 10; - sb->s_magic = DEVFS_SUPER_MAGIC; - sb->s_op = &devfs_sops; - sb->s_time_gran = 1; - if ((root_inode = _devfs_get_vfs_inode(sb, root_entry, NULL)) == NULL) - goto out_no_root; - sb->s_root = d_alloc_root(root_inode); - if (!sb->s_root) - goto out_no_root; - DPRINTK(DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->s_fs_info); - return 0; - - out_no_root: - PRINTK("(): get root inode failed\n"); - if (root_inode) - iput(root_inode); - return -EINVAL; -} /* End Function devfs_fill_super */ - -static struct super_block *devfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) -{ - return get_sb_single(fs_type, flags, data, devfs_fill_super); -} - -static struct file_system_type devfs_fs_type = { - .name = DEVFS_NAME, - .get_sb = devfs_get_sb, - .kill_sb = kill_anon_super, -}; - -/* File operations for devfsd follow */ - -static ssize_t devfsd_read(struct file *file, char __user *buf, size_t len, - loff_t * ppos) -{ - int done = FALSE; - int ival; - loff_t pos, devname_offset, tlen, rpos; - devfs_handle_t de; - struct devfsd_buf_entry *entry; - struct fs_info *fs_info = file->f_dentry->d_inode->i_sb->s_fs_info; - struct devfsd_notify_struct *info = fs_info->devfsd_info; - DECLARE_WAITQUEUE(wait, current); - - /* Verify the task has grabbed the queue */ - if (fs_info->devfsd_task != current) - return -EPERM; - info->major = 0; - info->minor = 0; - /* Block for a new entry */ - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&fs_info->devfsd_wait_queue, &wait); - while (devfsd_queue_empty(fs_info)) { - fs_info->devfsd_sleeping = TRUE; - wake_up(&fs_info->revalidate_wait_queue); - schedule(); - fs_info->devfsd_sleeping = FALSE; - if (signal_pending(current)) { - remove_wait_queue(&fs_info->devfsd_wait_queue, &wait); - __set_current_state(TASK_RUNNING); - return -EINTR; - } - set_current_state(TASK_INTERRUPTIBLE); - } - remove_wait_queue(&fs_info->devfsd_wait_queue, &wait); - __set_current_state(TASK_RUNNING); - /* Now play with the data */ - ival = atomic_read(&fs_info->devfsd_overrun_count); - info->overrun_count = ival; - entry = fs_info->devfsd_first_event; - info->type = entry->type; - info->mode = entry->mode; - info->uid = entry->uid; - info->gid = entry->gid; - de = entry->de; - if (S_ISCHR(de->mode) || S_ISBLK(de->mode)) { - info->major = MAJOR(de->u.dev); - info->minor = MINOR(de->u.dev); - } - pos = devfs_generate_path(de, info->devname, DEVFS_PATHLEN); - if (pos < 0) - return pos; - info->namelen = DEVFS_PATHLEN - pos - 1; - if (info->mode == 0) - info->mode = de->mode; - devname_offset = info->devname - (char *)info; - rpos = *ppos; - if (rpos < devname_offset) { - /* Copy parts of the header */ - tlen = devname_offset - rpos; - if (tlen > len) - tlen = len; - if (copy_to_user(buf, (char *)info + rpos, tlen)) { - return -EFAULT; - } - rpos += tlen; - buf += tlen; - len -= tlen; - } - if ((rpos >= devname_offset) && (len > 0)) { - /* Copy the name */ - tlen = info->namelen + 1; - if (tlen > len) - tlen = len; - else - done = TRUE; - if (copy_to_user - (buf, info->devname + pos + rpos - devname_offset, tlen)) { - return -EFAULT; - } - rpos += tlen; - } - tlen = rpos - *ppos; - if (done) { - devfs_handle_t parent; - - spin_lock(&fs_info->devfsd_buffer_lock); - fs_info->devfsd_first_event = entry->next; - if (entry->next == NULL) - fs_info->devfsd_last_event = NULL; - spin_unlock(&fs_info->devfsd_buffer_lock); - for (; de != NULL; de = parent) { - parent = de->parent; - devfs_put(de); - } - kmem_cache_free(devfsd_buf_cache, entry); - if (ival > 0) - atomic_sub(ival, &fs_info->devfsd_overrun_count); - *ppos = 0; - } else - *ppos = rpos; - return tlen; -} /* End Function devfsd_read */ - -static int devfsd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ival; - struct fs_info *fs_info = inode->i_sb->s_fs_info; - - switch (cmd) { - case DEVFSDIOC_GET_PROTO_REV: - ival = DEVFSD_PROTOCOL_REVISION_KERNEL; - if (copy_to_user((void __user *)arg, &ival, sizeof ival)) - return -EFAULT; - break; - case DEVFSDIOC_SET_EVENT_MASK: - /* Ensure only one reader has access to the queue. This scheme will - work even if the global kernel lock were to be removed, because it - doesn't matter who gets in first, as long as only one gets it */ - if (fs_info->devfsd_task == NULL) { - static DEFINE_SPINLOCK(lock); - - if (!spin_trylock(&lock)) - return -EBUSY; - if (fs_info->devfsd_task != NULL) { /* We lost the race... */ - spin_unlock(&lock); - return -EBUSY; - } - fs_info->devfsd_task = current; - spin_unlock(&lock); - fs_info->devfsd_pgrp = - (process_group(current) == - current->pid) ? process_group(current) : 0; - fs_info->devfsd_file = file; - fs_info->devfsd_info = - kmalloc(sizeof *fs_info->devfsd_info, GFP_KERNEL); - if (!fs_info->devfsd_info) { - devfsd_close(inode, file); - return -ENOMEM; - } - } else if (fs_info->devfsd_task != current) - return -EBUSY; - fs_info->devfsd_event_mask = arg; /* Let the masses come forth */ - break; - case DEVFSDIOC_RELEASE_EVENT_QUEUE: - if (fs_info->devfsd_file != file) - return -EPERM; - return devfsd_close(inode, file); - /*break; */ -#ifdef CONFIG_DEVFS_DEBUG - case DEVFSDIOC_SET_DEBUG_MASK: - if (copy_from_user(&ival, (void __user *)arg, sizeof ival)) - return -EFAULT; - devfs_debug = ival; - break; -#endif - default: - return -ENOIOCTLCMD; - } - return 0; -} /* End Function devfsd_ioctl */ - -static int devfsd_close(struct inode *inode, struct file *file) -{ - struct devfsd_buf_entry *entry, *next; - struct fs_info *fs_info = inode->i_sb->s_fs_info; - - if (fs_info->devfsd_file != file) - return 0; - fs_info->devfsd_event_mask = 0; - fs_info->devfsd_file = NULL; - spin_lock(&fs_info->devfsd_buffer_lock); - entry = fs_info->devfsd_first_event; - fs_info->devfsd_first_event = NULL; - fs_info->devfsd_last_event = NULL; - kfree(fs_info->devfsd_info); - fs_info->devfsd_info = NULL; - spin_unlock(&fs_info->devfsd_buffer_lock); - fs_info->devfsd_pgrp = 0; - fs_info->devfsd_task = NULL; - wake_up(&fs_info->revalidate_wait_queue); - for (; entry; entry = next) { - next = entry->next; - kmem_cache_free(devfsd_buf_cache, entry); - } - return 0; -} /* End Function devfsd_close */ - -#ifdef CONFIG_DEVFS_DEBUG -static ssize_t stat_read(struct file *file, char __user *buf, size_t len, - loff_t * ppos) -{ - ssize_t num; - char txt[80]; - - num = sprintf(txt, "Number of entries: %u number of bytes: %u\n", - stat_num_entries, stat_num_bytes) + 1; - if (*ppos >= num) - return 0; - if (*ppos + len > num) - len = num - *ppos; - if (copy_to_user(buf, txt + *ppos, len)) - return -EFAULT; - *ppos += len; - return len; -} /* End Function stat_read */ -#endif - -static int __init init_devfs_fs(void) -{ - int err; - int major; - struct devfs_entry *devfsd; -#ifdef CONFIG_DEVFS_DEBUG - struct devfs_entry *stat; -#endif - - if (_devfs_get_root_entry() == NULL) - return -ENOMEM; - - printk(KERN_INFO "%s: %s Richard Gooch (rgooch@atnf.csiro.au)\n", - DEVFS_NAME, DEVFS_VERSION); - devfsd_buf_cache = kmem_cache_create("devfsd_event", - sizeof(struct devfsd_buf_entry), - 0, 0, NULL, NULL); - if (!devfsd_buf_cache) - OOPS("(): unable to allocate event slab\n"); -#ifdef CONFIG_DEVFS_DEBUG - devfs_debug = devfs_debug_init; - printk(KERN_INFO "%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug); -#endif - printk(KERN_INFO "%s: boot_options: 0x%0x\n", DEVFS_NAME, boot_options); - - /* register special device for devfsd communication */ - major = register_chrdev(0, "devfs", &devfs_fops); - if (major < 0) - return major; - - /* And create the entry for ".devfsd" */ - devfsd = _devfs_alloc_entry(".devfsd", 0, S_IFCHR | S_IRUSR | S_IWUSR); - if (devfsd == NULL) - return -ENOMEM; - devfsd->u.dev = MKDEV(major, 0); - _devfs_append_entry(root_entry, devfsd, NULL); - -#ifdef CONFIG_DEVFS_DEBUG - stat = _devfs_alloc_entry(".stat", 0, S_IFCHR | S_IRUGO); - if (stat == NULL) - return -ENOMEM; - stat->u.dev = MKDEV(major, 1); - _devfs_append_entry(root_entry, stat, NULL); -#endif - - err = register_filesystem(&devfs_fs_type); - return err; -} /* End Function init_devfs_fs */ - -void __init mount_devfs_fs(void) -{ - int err; - - if (!(boot_options & OPTION_MOUNT)) - return; - err = do_mount("none", "/dev", "devfs", 0, NULL); - if (err == 0) - printk(KERN_INFO "Mounted devfs on /dev\n"); - else - PRINTK("(): unable to mount devfs, err: %d\n", err); -} /* End Function mount_devfs_fs */ - -module_init(init_devfs_fs) diff --git a/fs/devfs/util.c b/fs/devfs/util.c deleted file mode 100644 index db06d38..0000000 --- a/fs/devfs/util.c +++ /dev/null @@ -1,97 +0,0 @@ -/* devfs (Device FileSystem) utilities. - - Copyright (C) 1999-2002 Richard Gooch - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Richard Gooch may be reached by email at rgooch@atnf.csiro.au - The postal address is: - Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. - - ChangeLog - - 19991031 Richard Gooch - Created. - 19991103 Richard Gooch - Created <_devfs_convert_name> and supported SCSI and IDE CD-ROMs - 20000203 Richard Gooch - Changed operations pointer type to void *. - 20000621 Richard Gooch - Changed interface to . - 20000622 Richard Gooch - Took account of interface change to . - Took account of interface change to . - 20010519 Richard Gooch - Documentation cleanup. - 20010709 Richard Gooch - Created and . - 20010710 Richard Gooch - Created . - 20010730 Richard Gooch - Documentation typo fix. - 20010806 Richard Gooch - Made and private. - 20010813 Richard Gooch - Fixed bug in : limited to 128 numbers - 20010818 Richard Gooch - Updated major masks up to Linus' "no new majors" proclamation. - Block: were 126 now 122 free, char: were 26 now 19 free. - 20020324 Richard Gooch - Fixed bug in : was clearing beyond - bitfield. - 20020326 Richard Gooch - Fixed bitfield data type for . - Made major bitfield type and initialiser 64 bit safe. - 20020413 Richard Gooch - Fixed shift warning on 64 bit machines. - 20020428 Richard Gooch - Copied and used macro for error messages from fs/devfs/base.c - 20021013 Richard Gooch - Documentation fix. - 20030101 Adam J. Richter - Eliminate DEVFS_SPECIAL_{CHR,BLK}. Use mode_t instead. - 20030106 Christoph Hellwig - Rewrite devfs_{,de}alloc_devnum to look like C code. -*/ -#include -#include -#include -#include -#include -#include -#include - -int devfs_register_tape(const char *name) -{ - char tname[32], dest[64]; - static unsigned int tape_counter; - unsigned int n = tape_counter++; - - sprintf(dest, "../%s", name); - sprintf(tname, "tapes/tape%u", n); - devfs_mk_symlink(tname, dest); - - return n; -} - -EXPORT_SYMBOL(devfs_register_tape); - -void devfs_unregister_tape(int num) -{ - if (num >= 0) - devfs_remove("tapes/tape%u", num); -} - -EXPORT_SYMBOL(devfs_unregister_tape); diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 14c5620..f7aef5b 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -130,10 +130,10 @@ fail: return -ENOMEM; } -static struct super_block *devpts_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int devpts_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, devpts_fill_super); + return get_sb_single(fs_type, flags, data, devpts_fill_super, mnt); } static struct file_system_type devpts_fs_type = { diff --git a/fs/direct-io.c b/fs/direct-io.c index b05d1b2..5981e17 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -162,7 +162,7 @@ static int dio_refill_pages(struct dio * NULL); /* vmas */ up_read(¤t->mm->mmap_sem); - if (ret < 0 && dio->blocks_available && (dio->rw == WRITE)) { + if (ret < 0 && dio->blocks_available && (dio->rw & WRITE)) { struct page *page = ZERO_PAGE(dio->curr_user_address); /* * A memory fault, but the filesystem has some outstanding @@ -220,7 +220,8 @@ static void dio_complete(struct dio *dio if (dio->end_io && dio->result) dio->end_io(dio->iocb, offset, bytes, dio->map_bh.b_private); if (dio->lock_type == DIO_LOCKING) - up_read(&dio->inode->i_alloc_sem); + /* lockdep: non-owner release */ + up_read_non_owner(&dio->inode->i_alloc_sem); } /* @@ -535,7 +536,7 @@ static int get_more_blocks(struct dio *d map_bh->b_state = 0; map_bh->b_size = fs_count << dio->inode->i_blkbits; - create = dio->rw == WRITE; + create = dio->rw & WRITE; if (dio->lock_type == DIO_LOCKING) { if (dio->block_in_file < (i_size_read(dio->inode) >> dio->blkbits)) @@ -867,7 +868,7 @@ do_holes: loff_t i_size_aligned; /* AKPM: eargh, -ENOTBLK is a hack */ - if (dio->rw == WRITE) { + if (dio->rw & WRITE) { page_cache_release(page); return -ENOTBLK; } @@ -1045,7 +1046,7 @@ direct_io_worker(int rw, struct kiocb *i } } /* end iovec loop */ - if (ret == -ENOTBLK && rw == WRITE) { + if (ret == -ENOTBLK && (rw & WRITE)) { /* * The remaining part of the request will be * be handled by buffered I/O when we return @@ -1089,7 +1090,7 @@ direct_io_worker(int rw, struct kiocb *i if (dio->is_async) { int should_wait = 0; - if (dio->result < dio->size && rw == WRITE) { + if (dio->result < dio->size && (rw & WRITE)) { dio->waiter = current; should_wait = 1; } @@ -1142,7 +1143,7 @@ direct_io_worker(int rw, struct kiocb *i ret = transferred; /* We could have also come here on an AIO file extend */ - if (!is_sync_kiocb(iocb) && rw == WRITE && + if (!is_sync_kiocb(iocb) && (rw & WRITE) && ret >= 0 && dio->result == dio->size) /* * For AIO writes where we have completed the @@ -1194,7 +1195,7 @@ __blockdev_direct_IO(int rw, struct kioc int acquire_i_mutex = 0; if (rw & WRITE) - current->flags |= PF_SYNCWRITE; + rw = WRITE_SYNC; if (bdev) bdev_blkbits = blksize_bits(bdev_hardsect_size(bdev)); @@ -1261,7 +1262,8 @@ __blockdev_direct_IO(int rw, struct kioc } if (dio_lock_type == DIO_LOCKING) - down_read(&inode->i_alloc_sem); + /* lockdep: not the owner will release it */ + down_read_non_owner(&inode->i_alloc_sem); } /* @@ -1270,7 +1272,7 @@ __blockdev_direct_IO(int rw, struct kioc * even for AIO, we need to wait for i/o to complete before * returning in this case. */ - dio->is_async = !is_sync_kiocb(iocb) && !((rw == WRITE) && + dio->is_async = !is_sync_kiocb(iocb) && !((rw & WRITE) && (end > i_size_read(inode))); retval = direct_io_worker(rw, iocb, inode, iov, offset, @@ -1284,8 +1286,6 @@ out: mutex_unlock(&inode->i_mutex); else if (acquire_i_mutex) mutex_lock(&inode->i_mutex); - if (rw & WRITE) - current->flags &= ~PF_SYNCWRITE; return retval; } EXPORT_SYMBOL(__blockdev_direct_IO); diff --git a/fs/dquot.c b/fs/dquot.c index 81d87a4..0122a27 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -250,7 +250,7 @@ static inline struct dquot *find_dquot(u /* Add a dquot to the tail of the free list */ static inline void put_dquot_last(struct dquot *dquot) { - list_add(&dquot->dq_free, free_dquots.prev); + list_add_tail(&dquot->dq_free, &free_dquots); dqstats.free_dquots++; } @@ -266,7 +266,7 @@ static inline void put_inuse(struct dquo { /* We add to the back of inuse list so we don't have to restart * when traversing this list and we block */ - list_add(&dquot->dq_inuse, inuse_list.prev); + list_add_tail(&dquot->dq_inuse, &inuse_list); dqstats.allocated_dquots++; } diff --git a/fs/efs/inode.c b/fs/efs/inode.c index 180607f..174696f 100644 --- a/fs/efs/inode.c +++ b/fs/efs/inode.c @@ -21,7 +21,7 @@ static sector_t _efs_bmap(struct address { return generic_block_bmap(mapping,block,efs_get_block); } -static struct address_space_operations efs_aops = { +static const struct address_space_operations efs_aops = { .readpage = efs_readpage, .sync_page = block_sync_page, .bmap = _efs_bmap diff --git a/fs/efs/super.c b/fs/efs/super.c index dff623e..8ac2462 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -15,13 +15,13 @@ #include #include #include -static int efs_statfs(struct super_block *s, struct kstatfs *buf); +static int efs_statfs(struct dentry *dentry, struct kstatfs *buf); static int efs_fill_super(struct super_block *s, void *d, int silent); -static struct super_block *efs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int efs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, efs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, efs_fill_super, mnt); } static struct file_system_type efs_fs_type = { @@ -322,8 +322,8 @@ out_no_fs: return -EINVAL; } -static int efs_statfs(struct super_block *s, struct kstatfs *buf) { - struct efs_sb_info *sb = SUPER_INFO(s); +static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct efs_sb_info *sb = SUPER_INFO(dentry->d_sb); buf->f_type = EFS_SUPER_MAGIC; /* efs magic number */ buf->f_bsize = EFS_BLOCKSIZE; /* blocksize */ diff --git a/fs/efs/symlink.c b/fs/efs/symlink.c index 3d9a350..e249cf7 100644 --- a/fs/efs/symlink.c +++ b/fs/efs/symlink.c @@ -53,6 +53,6 @@ fail: return err; } -struct address_space_operations efs_symlink_aops = { +const struct address_space_operations efs_symlink_aops = { .readpage = efs_symlink_readpage }; diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 1b4491c..19ffb04 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1,6 +1,6 @@ /* * fs/eventpoll.c ( Efficent event polling implementation ) - * Copyright (C) 2001,...,2003 Davide Libenzi + * Copyright (C) 2001,...,2006 Davide Libenzi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -120,7 +120,7 @@ struct epoll_filefd { */ struct wake_task_node { struct list_head llink; - task_t *task; + struct task_struct *task; wait_queue_head_t *wq; }; @@ -268,9 +268,9 @@ static int ep_poll(struct eventpoll *ep, int maxevents, long timeout); static int eventpollfs_delete_dentry(struct dentry *dentry); static struct inode *ep_eventpoll_inode(void); -static struct super_block *eventpollfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data); +static int eventpollfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt); /* * This semaphore is used to serialize ep_free() and eventpoll_release_file(). @@ -337,20 +337,20 @@ static inline int ep_cmp_ffd(struct epol /* Special initialization for the rb-tree node to detect linkage */ static inline void ep_rb_initnode(struct rb_node *n) { - n->rb_parent = n; + rb_set_parent(n, n); } /* Removes a node from the rb-tree and marks it for a fast is-linked check */ static inline void ep_rb_erase(struct rb_node *n, struct rb_root *r) { rb_erase(n, r); - n->rb_parent = n; + rb_set_parent(n, n); } /* Fast check to verify that the item is linked to the main rb-tree */ static inline int ep_rb_linked(struct rb_node *n) { - return n->rb_parent != n; + return rb_parent(n) != n; } /* @@ -413,7 +413,7 @@ static void ep_poll_safewake(struct poll { int wake_nests = 0; unsigned long flags; - task_t *this_task = current; + struct task_struct *this_task = current; struct list_head *lsthead = &psw->wake_task_list, *lnk; struct wake_task_node *tncur; struct wake_task_node tnode; @@ -1004,7 +1004,7 @@ static int ep_insert(struct eventpoll *e /* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq)) - wake_up(&ep->wq); + __wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE); if (waitqueue_active(&ep->poll_wait)) pwake++; } @@ -1083,7 +1083,8 @@ static int ep_modify(struct eventpoll *e /* Notify waiting tasks that events are available */ if (waitqueue_active(&ep->wq)) - wake_up(&ep->wq); + __wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE | + TASK_INTERRUPTIBLE); if (waitqueue_active(&ep->poll_wait)) pwake++; } @@ -1260,7 +1261,8 @@ is_linked: * wait list. */ if (waitqueue_active(&ep->wq)) - wake_up(&ep->wq); + __wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE | + TASK_INTERRUPTIBLE); if (waitqueue_active(&ep->poll_wait)) pwake++; @@ -1444,7 +1446,8 @@ static void ep_reinject_items(struct eve * wait list. */ if (waitqueue_active(&ep->wq)) - wake_up(&ep->wq); + __wake_up_locked(&ep->wq, TASK_UNINTERRUPTIBLE | + TASK_INTERRUPTIBLE); if (waitqueue_active(&ep->poll_wait)) pwake++; } @@ -1516,7 +1519,7 @@ retry: * ep_poll_callback() when events will become available. */ init_waitqueue_entry(&wait, current); - add_wait_queue(&ep->wq, &wait); + __add_wait_queue(&ep->wq, &wait); for (;;) { /* @@ -1536,7 +1539,7 @@ retry: jtimeout = schedule_timeout(jtimeout); write_lock_irqsave(&ep->lock, flags); } - remove_wait_queue(&ep->wq, &wait); + __remove_wait_queue(&ep->wq, &wait); set_current_state(TASK_RUNNING); } @@ -1595,11 +1598,12 @@ eexit_1: } -static struct super_block * +static int eventpollfs_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) + const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_pseudo(fs_type, "eventpoll:", NULL, EVENTPOLLFS_MAGIC); + return get_sb_pseudo(fs_type, "eventpoll:", NULL, EVENTPOLLFS_MAGIC, + mnt); } diff --git a/fs/exec.c b/fs/exec.c index 3a79d97..8344ba7 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -22,7 +22,6 @@ * formats. */ -#include #include #include #include @@ -49,6 +48,7 @@ #include #include #include #include +#include #include #include @@ -665,8 +665,6 @@ static int de_thread(struct task_struct * and to assume its PID: */ if (!thread_group_leader(current)) { - struct dentry *proc_dentry1, *proc_dentry2; - /* * Wait for the thread group leader to be a zombie. * It should already be zombie at this point, most @@ -688,10 +686,6 @@ static int de_thread(struct task_struct */ current->start_time = leader->start_time; - spin_lock(&leader->proc_lock); - spin_lock(¤t->proc_lock); - proc_dentry1 = proc_pid_unhash(current); - proc_dentry2 = proc_pid_unhash(leader); write_lock_irq(&tasklist_lock); BUG_ON(leader->tgid != current->tgid); @@ -712,7 +706,7 @@ static int de_thread(struct task_struct attach_pid(current, PIDTYPE_PID, current->pid); attach_pid(current, PIDTYPE_PGID, current->signal->pgrp); attach_pid(current, PIDTYPE_SID, current->signal->session); - list_add_tail_rcu(¤t->tasks, &init_task.tasks); + list_replace_rcu(&leader->tasks, ¤t->tasks); current->group_leader = current; leader->group_leader = current; @@ -720,7 +714,6 @@ static int de_thread(struct task_struct /* Reduce leader to a thread */ detach_pid(leader, PIDTYPE_PGID); detach_pid(leader, PIDTYPE_SID); - list_del_init(&leader->tasks); current->exit_signal = SIGCHLD; @@ -728,10 +721,6 @@ static int de_thread(struct task_struct leader->exit_state = EXIT_DEAD; write_unlock_irq(&tasklist_lock); - spin_unlock(&leader->proc_lock); - spin_unlock(¤t->proc_lock); - proc_pid_flush(proc_dentry1); - proc_pid_flush(proc_dentry2); } /* @@ -865,7 +854,6 @@ int flush_old_exec(struct linux_binprm * bprm->mm = NULL; /* We're using it now */ /* This is the point of no return */ - steal_locks(files); put_files_struct(files); current->sas_ss_sp = current->sas_ss_size = 0; @@ -1085,6 +1073,11 @@ #endif /* kernel module loader fixup */ /* so we don't try to load run modprobe in kernel space. */ set_fs(USER_DS); + + retval = audit_bprm(bprm); + if (retval) + return retval; + retval = -ENOENT; for (try=0; try<2; try++) { read_lock(&binfmt_lock); @@ -1374,67 +1367,102 @@ static void format_corename(char *corena *out_ptr = 0; } -static void zap_threads (struct mm_struct *mm) +static void zap_process(struct task_struct *start) { - struct task_struct *g, *p; - struct task_struct *tsk = current; - struct completion *vfork_done = tsk->vfork_done; - int traced = 0; + struct task_struct *t; - /* - * Make sure nobody is waiting for us to release the VM, - * otherwise we can deadlock when we wait on each other - */ - if (vfork_done) { - tsk->vfork_done = NULL; - complete(vfork_done); - } + start->signal->flags = SIGNAL_GROUP_EXIT; + start->signal->group_stop_count = 0; - read_lock(&tasklist_lock); - do_each_thread(g,p) - if (mm == p->mm && p != tsk) { - force_sig_specific(SIGKILL, p); - mm->core_waiters++; - if (unlikely(p->ptrace) && - unlikely(p->parent->mm == mm)) - traced = 1; + t = start; + do { + if (t != current && t->mm) { + t->mm->core_waiters++; + sigaddset(&t->pending.signal, SIGKILL); + signal_wake_up(t, 1); } - while_each_thread(g,p); + } while ((t = next_thread(t)) != start); +} - read_unlock(&tasklist_lock); +static inline int zap_threads(struct task_struct *tsk, struct mm_struct *mm, + int exit_code) +{ + struct task_struct *g, *p; + unsigned long flags; + int err = -EAGAIN; + + spin_lock_irq(&tsk->sighand->siglock); + if (!(tsk->signal->flags & SIGNAL_GROUP_EXIT)) { + tsk->signal->group_exit_code = exit_code; + zap_process(tsk); + err = 0; + } + spin_unlock_irq(&tsk->sighand->siglock); + if (err) + return err; - if (unlikely(traced)) { - /* - * We are zapping a thread and the thread it ptraces. - * If the tracee went into a ptrace stop for exit tracing, - * we could deadlock since the tracer is waiting for this - * coredump to finish. Detach them so they can both die. - */ - write_lock_irq(&tasklist_lock); - do_each_thread(g,p) { - if (mm == p->mm && p != tsk && - p->ptrace && p->parent->mm == mm) { - __ptrace_detach(p, 0); + if (atomic_read(&mm->mm_users) == mm->core_waiters + 1) + goto done; + + rcu_read_lock(); + for_each_process(g) { + if (g == tsk->group_leader) + continue; + + p = g; + do { + if (p->mm) { + if (p->mm == mm) { + /* + * p->sighand can't disappear, but + * may be changed by de_thread() + */ + lock_task_sighand(p, &flags); + zap_process(p); + unlock_task_sighand(p, &flags); + } + break; } - } while_each_thread(g,p); - write_unlock_irq(&tasklist_lock); + } while ((p = next_thread(p)) != g); } + rcu_read_unlock(); +done: + return mm->core_waiters; } -static void coredump_wait(struct mm_struct *mm) +static int coredump_wait(int exit_code) { - DECLARE_COMPLETION(startup_done); + struct task_struct *tsk = current; + struct mm_struct *mm = tsk->mm; + struct completion startup_done; + struct completion *vfork_done; int core_waiters; + init_completion(&mm->core_done); + init_completion(&startup_done); mm->core_startup_done = &startup_done; - zap_threads(mm); - core_waiters = mm->core_waiters; + core_waiters = zap_threads(tsk, mm, exit_code); up_write(&mm->mmap_sem); + if (unlikely(core_waiters < 0)) + goto fail; + + /* + * Make sure nobody is waiting for us to release the VM, + * otherwise we can deadlock when we wait on each other + */ + vfork_done = tsk->vfork_done; + if (vfork_done) { + tsk->vfork_done = NULL; + complete(vfork_done); + } + if (core_waiters) wait_for_completion(&startup_done); +fail: BUG_ON(mm->core_waiters); + return core_waiters; } int do_coredump(long signr, int exit_code, struct pt_regs * regs) @@ -1468,22 +1496,9 @@ int do_coredump(long signr, int exit_cod } mm->dumpable = 0; - retval = -EAGAIN; - spin_lock_irq(¤t->sighand->siglock); - if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) { - current->signal->flags = SIGNAL_GROUP_EXIT; - current->signal->group_exit_code = exit_code; - current->signal->group_stop_count = 0; - retval = 0; - } - spin_unlock_irq(¤t->sighand->siglock); - if (retval) { - up_write(&mm->mmap_sem); + retval = coredump_wait(exit_code); + if (retval < 0) goto fail; - } - - init_completion(&mm->core_done); - coredump_wait(mm); /* * Clear any false indication of pending signals that might diff --git a/fs/ext2/Makefile b/fs/ext2/Makefile index c5d02da..e0b2b43 100644 --- a/fs/ext2/Makefile +++ b/fs/ext2/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_EXT2_FS) += ext2.o -ext2-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ +ext2-y := balloc.o dir.o file.o fsync.o ialloc.o inode.o \ ioctl.o namei.o super.o symlink.o ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 2c00953..d487043 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -11,7 +11,6 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include #include "ext2.h" #include #include @@ -521,6 +520,26 @@ io_error: goto out_release; } +#ifdef EXT2FS_DEBUG + +static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; + +unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars) +{ + unsigned int i; + unsigned long sum = 0; + + if (!map) + return (0); + for (i = 0; i < numchars; i++) + sum += nibblemap[map->b_data[i] & 0xf] + + nibblemap[(map->b_data[i] >> 4) & 0xf]; + return (sum); +} + +#endif /* EXT2FS_DEBUG */ + +/* Superblock must be locked */ unsigned long ext2_count_free_blocks (struct super_block * sb) { struct ext2_group_desc * desc; @@ -530,7 +549,6 @@ #ifdef EXT2FS_DEBUG unsigned long bitmap_count, x; struct ext2_super_block *es; - lock_super (sb); es = EXT2_SB(sb)->s_es; desc_count = 0; bitmap_count = 0; @@ -554,7 +572,6 @@ #ifdef EXT2FS_DEBUG printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n", (long)le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count); - unlock_super (sb); return bitmap_count; #else for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { diff --git a/fs/ext2/bitmap.c b/fs/ext2/bitmap.c deleted file mode 100644 index e9983a0..0000000 --- a/fs/ext2/bitmap.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * linux/fs/ext2/bitmap.c - * - * Copyright (C) 1992, 1993, 1994, 1995 - * Remy Card (card@masi.ibp.fr) - * Laboratoire MASI - Institut Blaise Pascal - * Universite Pierre et Marie Curie (Paris VI) - */ - -#ifdef EXT2FS_DEBUG - -#include - -#include "ext2.h" - -static int nibblemap[] = {4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0}; - -unsigned long ext2_count_free (struct buffer_head * map, unsigned int numchars) -{ - unsigned int i; - unsigned long sum = 0; - - if (!map) - return (0); - for (i = 0; i < numchars; i++) - sum += nibblemap[map->b_data[i] & 0xf] + - nibblemap[(map->b_data[i] >> 4) & 0xf]; - return (sum); -} - -#endif /* EXT2FS_DEBUG */ - diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index d672aa9..92ea826 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -159,8 +159,7 @@ fail: static struct page * ext2_get_page(struct inode *dir, unsigned long n) { struct address_space *mapping = dir->i_mapping; - struct page *page = read_cache_page(mapping, n, - (filler_t*)mapping->a_ops->readpage, NULL); + struct page *page = read_mapping_page(mapping, n, NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); kmap(page); @@ -400,8 +399,7 @@ ino_t ext2_inode_by_name(struct inode * de = ext2_find_entry (dir, dentry, &page); if (de) { res = le32_to_cpu(de->inode); - kunmap(page); - page_cache_release(page); + ext2_put_page(page); } return res; } diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 9f74a62..e65a019 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -162,9 +162,9 @@ extern const struct file_operations ext2 extern const struct file_operations ext2_xip_file_operations; /* inode.c */ -extern struct address_space_operations ext2_aops; -extern struct address_space_operations ext2_aops_xip; -extern struct address_space_operations ext2_nobh_aops; +extern const struct address_space_operations ext2_aops; +extern const struct address_space_operations ext2_aops_xip; +extern const struct address_space_operations ext2_nobh_aops; /* namei.c */ extern struct inode_operations ext2_dir_inode_operations; diff --git a/fs/ext2/fsync.c b/fs/ext2/fsync.c index c9c2e5f..7806b9e 100644 --- a/fs/ext2/fsync.c +++ b/fs/ext2/fsync.c @@ -24,7 +24,7 @@ #include "ext2.h" #include -#include /* for fsync_inode_buffers() */ +#include /* for sync_mapping_buffers() */ /* diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index e527652..de85c61 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -12,7 +12,6 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include #include #include #include @@ -638,6 +637,7 @@ fail: return ERR_PTR(err); } +/* Superblock must be locked */ unsigned long ext2_count_free_inodes (struct super_block * sb) { struct ext2_group_desc *desc; @@ -649,7 +649,6 @@ #ifdef EXT2FS_DEBUG unsigned long bitmap_count = 0; struct buffer_head *bitmap_bh = NULL; - lock_super (sb); es = EXT2_SB(sb)->s_es; for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { unsigned x; @@ -672,7 +671,6 @@ #ifdef EXT2FS_DEBUG printk("ext2_count_free_inodes: stored = %lu, computed = %lu, %lu\n", percpu_counter_read(&EXT2_SB(sb)->s_freeinodes_counter), desc_count, bitmap_count); - unlock_super(sb); return desc_count; #else for (i = 0; i < EXT2_SB(sb)->s_groups_count; i++) { diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 04af9c4..fb4d322 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -684,7 +684,7 @@ ext2_writepages(struct address_space *ma return mpage_writepages(mapping, wbc, ext2_get_block); } -struct address_space_operations ext2_aops = { +const struct address_space_operations ext2_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_writepage, @@ -697,12 +697,12 @@ struct address_space_operations ext2_aop .migratepage = buffer_migrate_page, }; -struct address_space_operations ext2_aops_xip = { +const struct address_space_operations ext2_aops_xip = { .bmap = ext2_bmap, .get_xip_page = ext2_get_xip_page, }; -struct address_space_operations ext2_nobh_aops = { +const struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages, .writepage = ext2_nobh_writepage, diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 7e30bae..f2702cd 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -16,7 +16,6 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include #include #include #include @@ -39,7 +38,7 @@ #include "xip.h" static void ext2_sync_super(struct super_block *sb, struct ext2_super_block *es); static int ext2_remount (struct super_block * sb, int * flags, char * data); -static int ext2_statfs (struct super_block * sb, struct kstatfs * buf); +static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf); void ext2_error (struct super_block * sb, const char * function, const char * fmt, ...) @@ -834,9 +833,6 @@ static int ext2_fill_super(struct super_ printk ("EXT2-fs: not enough memory\n"); goto failed_mount; } - percpu_counter_init(&sbi->s_freeblocks_counter); - percpu_counter_init(&sbi->s_freeinodes_counter); - percpu_counter_init(&sbi->s_dirs_counter); bgl_lock_init(&sbi->s_blockgroup_lock); sbi->s_debts = kmalloc(sbi->s_groups_count * sizeof(*sbi->s_debts), GFP_KERNEL); @@ -857,12 +853,18 @@ static int ext2_fill_super(struct super_ } if (!ext2_check_descriptors (sb)) { printk ("EXT2-fs: group descriptors corrupted!\n"); - db_count = i; goto failed_mount2; } sbi->s_gdb_count = db_count; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); spin_lock_init(&sbi->s_next_gen_lock); + + percpu_counter_init(&sbi->s_freeblocks_counter, + ext2_count_free_blocks(sb)); + percpu_counter_init(&sbi->s_freeinodes_counter, + ext2_count_free_inodes(sb)); + percpu_counter_init(&sbi->s_dirs_counter, + ext2_count_dirs(sb)); /* * set up enough so that it can read an inode */ @@ -874,24 +876,18 @@ static int ext2_fill_super(struct super_ if (!sb->s_root) { iput(root); printk(KERN_ERR "EXT2-fs: get root inode failed\n"); - goto failed_mount2; + goto failed_mount3; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { dput(sb->s_root); sb->s_root = NULL; printk(KERN_ERR "EXT2-fs: corrupt root inode, run e2fsck\n"); - goto failed_mount2; + goto failed_mount3; } if (EXT2_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) ext2_warning(sb, __FUNCTION__, "mounting ext3 filesystem as ext2"); ext2_setup_super (sb, es, sb->s_flags & MS_RDONLY); - percpu_counter_mod(&sbi->s_freeblocks_counter, - ext2_count_free_blocks(sb)); - percpu_counter_mod(&sbi->s_freeinodes_counter, - ext2_count_free_inodes(sb)); - percpu_counter_mod(&sbi->s_dirs_counter, - ext2_count_dirs(sb)); return 0; cantfind_ext2: @@ -899,7 +895,10 @@ cantfind_ext2: printk("VFS: Can't find an ext2 filesystem on dev %s.\n", sb->s_id); goto failed_mount; - +failed_mount3: + percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeinodes_counter); + percpu_counter_destroy(&sbi->s_dirs_counter); failed_mount2: for (i = 0; i < db_count; i++) brelse(sbi->s_group_desc[i]); @@ -1038,12 +1037,14 @@ restore_opts: return err; } -static int ext2_statfs (struct super_block * sb, struct kstatfs * buf) +static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf) { + struct super_block *sb = dentry->d_sb; struct ext2_sb_info *sbi = EXT2_SB(sb); unsigned long overhead; int i; + lock_super(sb); if (test_opt (sb, MINIX_DF)) overhead = 0; else { @@ -1084,13 +1085,14 @@ static int ext2_statfs (struct super_blo buf->f_files = le32_to_cpu(sbi->s_es->s_inodes_count); buf->f_ffree = ext2_count_free_inodes (sb); buf->f_namelen = EXT2_NAME_LEN; + unlock_super(sb); return 0; } -static struct super_block *ext2_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ext2_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, ext2_fill_super, mnt); } #ifdef CONFIG_QUOTA @@ -1155,7 +1157,7 @@ static ssize_t ext2_quota_write(struct s struct buffer_head tmp_bh; struct buffer_head *bh; - mutex_lock(&inode->i_mutex); + mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); while (towrite > 0) { tocopy = sb->s_blocksize - offset < towrite ? sb->s_blocksize - offset : towrite; diff --git a/fs/ext2/xattr.h b/fs/ext2/xattr.h index 67cfeb6..bf8175b 100644 --- a/fs/ext2/xattr.h +++ b/fs/ext2/xattr.h @@ -6,7 +6,6 @@ (C) 2001 Andreas Gruenbacher, */ -#include #include #include diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 77927d6..a504a40 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -11,7 +11,6 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include #include #include #include @@ -163,20 +162,19 @@ #define rsv_window_dump(root, verbose) d #endif static int -goal_in_my_reservation(struct ext3_reserve_window *rsv, int goal, +goal_in_my_reservation(struct ext3_reserve_window *rsv, ext3_grpblk_t grp_goal, unsigned int group, struct super_block * sb) { - unsigned long group_first_block, group_last_block; + ext3_fsblk_t group_first_block, group_last_block; - group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + - group * EXT3_BLOCKS_PER_GROUP(sb); + group_first_block = ext3_group_first_block_no(sb, group); group_last_block = group_first_block + EXT3_BLOCKS_PER_GROUP(sb) - 1; if ((rsv->_rsv_start > group_last_block) || (rsv->_rsv_end < group_first_block)) return 0; - if ((goal >= 0) && ((goal + group_first_block < rsv->_rsv_start) - || (goal + group_first_block > rsv->_rsv_end))) + if ((grp_goal >= 0) && ((grp_goal + group_first_block < rsv->_rsv_start) + || (grp_goal + group_first_block > rsv->_rsv_end))) return 0; return 1; } @@ -187,7 +185,7 @@ goal_in_my_reservation(struct ext3_reser * Returns NULL if there are no windows or if all windows start after the goal. */ static struct ext3_reserve_window_node * -search_reserve_window(struct rb_root *root, unsigned long goal) +search_reserve_window(struct rb_root *root, ext3_fsblk_t goal) { struct rb_node *n = root->rb_node; struct ext3_reserve_window_node *rsv; @@ -223,7 +221,7 @@ void ext3_rsv_window_add(struct super_bl { struct rb_root *root = &EXT3_SB(sb)->s_rsv_window_root; struct rb_node *node = &rsv->rsv_node; - unsigned int start = rsv->rsv_start; + ext3_fsblk_t start = rsv->rsv_start; struct rb_node ** p = &root->rb_node; struct rb_node * parent = NULL; @@ -310,20 +308,20 @@ void ext3_discard_reservation(struct ino /* Free given blocks, update quota and i_blocks field */ void ext3_free_blocks_sb(handle_t *handle, struct super_block *sb, - unsigned long block, unsigned long count, - int *pdquot_freed_blocks) + ext3_fsblk_t block, unsigned long count, + unsigned long *pdquot_freed_blocks) { struct buffer_head *bitmap_bh = NULL; struct buffer_head *gd_bh; unsigned long block_group; - unsigned long bit; + ext3_grpblk_t bit; unsigned long i; unsigned long overflow; struct ext3_group_desc * desc; struct ext3_super_block * es; struct ext3_sb_info *sbi; int err = 0, ret; - unsigned group_freed; + ext3_grpblk_t group_freed; *pdquot_freed_blocks = 0; sbi = EXT3_SB(sb); @@ -333,7 +331,7 @@ void ext3_free_blocks_sb(handle_t *handl block + count > le32_to_cpu(es->s_blocks_count)) { ext3_error (sb, "ext3_free_blocks", "Freeing blocks not in datazone - " - "block = %lu, count = %lu", block, count); + "block = "E3FSBLK", count = %lu", block, count); goto error_return; } @@ -369,7 +367,7 @@ do_more: sbi->s_itb_per_group)) ext3_error (sb, "ext3_free_blocks", "Freeing blocks in system zones - " - "Block = %lu, count = %lu", + "Block = "E3FSBLK", count = %lu", block, count); /* @@ -453,7 +451,8 @@ #endif bit + i, bitmap_bh->b_data)) { jbd_unlock_bh_state(bitmap_bh); ext3_error(sb, __FUNCTION__, - "bit already cleared for block %lu", block + i); + "bit already cleared for block "E3FSBLK, + block + i); jbd_lock_bh_state(bitmap_bh); BUFFER_TRACE(bitmap_bh, "bit already cleared"); } else { @@ -493,10 +492,10 @@ error_return: /* Free given blocks, update quota and i_blocks field */ void ext3_free_blocks(handle_t *handle, struct inode *inode, - unsigned long block, unsigned long count) + ext3_fsblk_t block, unsigned long count) { struct super_block * sb; - int dquot_freed_blocks; + unsigned long dquot_freed_blocks; sb = inode->i_sb; if (!sb) { @@ -525,7 +524,7 @@ void ext3_free_blocks(handle_t *handle, * data-writes at some point, and disable it for metadata allocations or * sync-data inodes. */ -static int ext3_test_allocatable(int nr, struct buffer_head *bh) +static int ext3_test_allocatable(ext3_grpblk_t nr, struct buffer_head *bh) { int ret; struct journal_head *jh = bh2jh(bh); @@ -542,11 +541,11 @@ static int ext3_test_allocatable(int nr, return ret; } -static int -bitmap_search_next_usable_block(int start, struct buffer_head *bh, - int maxblocks) +static ext3_grpblk_t +bitmap_search_next_usable_block(ext3_grpblk_t start, struct buffer_head *bh, + ext3_grpblk_t maxblocks) { - int next; + ext3_grpblk_t next; struct journal_head *jh = bh2jh(bh); /* @@ -576,10 +575,11 @@ bitmap_search_next_usable_block(int star * the initial goal; then for a free byte somewhere in the bitmap; then * for any free bit in the bitmap. */ -static int -find_next_usable_block(int start, struct buffer_head *bh, int maxblocks) +static ext3_grpblk_t +find_next_usable_block(ext3_grpblk_t start, struct buffer_head *bh, + ext3_grpblk_t maxblocks) { - int here, next; + ext3_grpblk_t here, next; char *p, *r; if (start > 0) { @@ -591,7 +591,7 @@ find_next_usable_block(int start, struct * less than EXT3_BLOCKS_PER_GROUP. Aligning up to the * next 64-bit boundary is simple.. */ - int end_goal = (start + 63) & ~63; + ext3_grpblk_t end_goal = (start + 63) & ~63; if (end_goal > maxblocks) end_goal = maxblocks; here = ext3_find_next_zero_bit(bh->b_data, end_goal, start); @@ -628,7 +628,7 @@ find_next_usable_block(int start, struct * zero (failure). */ static inline int -claim_block(spinlock_t *lock, int block, struct buffer_head *bh) +claim_block(spinlock_t *lock, ext3_grpblk_t block, struct buffer_head *bh) { struct journal_head *jh = bh2jh(bh); int ret; @@ -651,19 +651,18 @@ claim_block(spinlock_t *lock, int block, * new bitmap. In that case we must release write access to the old one via * ext3_journal_release_buffer(), else we'll run out of credits. */ -static int +static ext3_grpblk_t ext3_try_to_allocate(struct super_block *sb, handle_t *handle, int group, - struct buffer_head *bitmap_bh, int goal, + struct buffer_head *bitmap_bh, ext3_grpblk_t grp_goal, unsigned long *count, struct ext3_reserve_window *my_rsv) { - int group_first_block, start, end; + ext3_fsblk_t group_first_block; + ext3_grpblk_t start, end; unsigned long num = 0; /* we do allocation within the reservation window if we have a window */ if (my_rsv) { - group_first_block = - le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + - group * EXT3_BLOCKS_PER_GROUP(sb); + group_first_block = ext3_group_first_block_no(sb, group); if (my_rsv->_rsv_start >= group_first_block) start = my_rsv->_rsv_start - group_first_block; else @@ -673,13 +672,13 @@ ext3_try_to_allocate(struct super_block if (end > EXT3_BLOCKS_PER_GROUP(sb)) /* reservation window crosses group boundary */ end = EXT3_BLOCKS_PER_GROUP(sb); - if ((start <= goal) && (goal < end)) - start = goal; + if ((start <= grp_goal) && (grp_goal < end)) + start = grp_goal; else - goal = -1; + grp_goal = -1; } else { - if (goal > 0) - start = goal; + if (grp_goal > 0) + start = grp_goal; else start = 0; end = EXT3_BLOCKS_PER_GROUP(sb); @@ -688,43 +687,43 @@ ext3_try_to_allocate(struct super_block BUG_ON(start > EXT3_BLOCKS_PER_GROUP(sb)); repeat: - if (goal < 0 || !ext3_test_allocatable(goal, bitmap_bh)) { - goal = find_next_usable_block(start, bitmap_bh, end); - if (goal < 0) + if (grp_goal < 0 || !ext3_test_allocatable(grp_goal, bitmap_bh)) { + grp_goal = find_next_usable_block(start, bitmap_bh, end); + if (grp_goal < 0) goto fail_access; if (!my_rsv) { int i; - for (i = 0; i < 7 && goal > start && - ext3_test_allocatable(goal - 1, + for (i = 0; i < 7 && grp_goal > start && + ext3_test_allocatable(grp_goal - 1, bitmap_bh); - i++, goal--) + i++, grp_goal--) ; } } - start = goal; + start = grp_goal; - if (!claim_block(sb_bgl_lock(EXT3_SB(sb), group), goal, bitmap_bh)) { + if (!claim_block(sb_bgl_lock(EXT3_SB(sb), group), grp_goal, bitmap_bh)) { /* * The block was allocated by another thread, or it was * allocated and then freed by another thread */ start++; - goal++; + grp_goal++; if (start >= end) goto fail_access; goto repeat; } num++; - goal++; - while (num < *count && goal < end - && ext3_test_allocatable(goal, bitmap_bh) - && claim_block(sb_bgl_lock(EXT3_SB(sb), group), goal, bitmap_bh)) { + grp_goal++; + while (num < *count && grp_goal < end + && ext3_test_allocatable(grp_goal, bitmap_bh) + && claim_block(sb_bgl_lock(EXT3_SB(sb), group), grp_goal, bitmap_bh)) { num++; - goal++; + grp_goal++; } *count = num; - return goal - num; + return grp_goal - num; fail_access: *count = num; return -1; @@ -766,12 +765,13 @@ fail_access: static int find_next_reservable_window( struct ext3_reserve_window_node *search_head, struct ext3_reserve_window_node *my_rsv, - struct super_block * sb, int start_block, - int last_block) + struct super_block * sb, + ext3_fsblk_t start_block, + ext3_fsblk_t last_block) { struct rb_node *next; struct ext3_reserve_window_node *rsv, *prev; - int cur; + ext3_fsblk_t cur; int size = my_rsv->rsv_goal_size; /* TODO: make the start of the reservation window byte-aligned */ @@ -873,10 +873,10 @@ static int find_next_reservable_window( * * @rsv: the reservation * - * @goal: The goal (group-relative). It is where the search for a + * @grp_goal: The goal (group-relative). It is where the search for a * free reservable space should start from. - * if we have a goal(goal >0 ), then start from there, - * no goal(goal = -1), we start from the first block + * if we have a grp_goal(grp_goal >0 ), then start from there, + * no grp_goal(grp_goal = -1), we start from the first block * of the group. * * @sb: the super block @@ -885,25 +885,24 @@ static int find_next_reservable_window( * */ static int alloc_new_reservation(struct ext3_reserve_window_node *my_rsv, - int goal, struct super_block *sb, + ext3_grpblk_t grp_goal, struct super_block *sb, unsigned int group, struct buffer_head *bitmap_bh) { struct ext3_reserve_window_node *search_head; - int group_first_block, group_end_block, start_block; - int first_free_block; + ext3_fsblk_t group_first_block, group_end_block, start_block; + ext3_grpblk_t first_free_block; struct rb_root *fs_rsv_root = &EXT3_SB(sb)->s_rsv_window_root; unsigned long size; int ret; spinlock_t *rsv_lock = &EXT3_SB(sb)->s_rsv_window_lock; - group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + - group * EXT3_BLOCKS_PER_GROUP(sb); + group_first_block = ext3_group_first_block_no(sb, group); group_end_block = group_first_block + EXT3_BLOCKS_PER_GROUP(sb) - 1; - if (goal < 0) + if (grp_goal < 0) start_block = group_first_block; else - start_block = goal + group_first_block; + start_block = grp_goal + group_first_block; size = my_rsv->rsv_goal_size; @@ -1057,14 +1056,15 @@ static void try_to_extend_reservation(st * sorted double linked list should be fast. * */ -static int +static ext3_grpblk_t ext3_try_to_allocate_with_rsv(struct super_block *sb, handle_t *handle, unsigned int group, struct buffer_head *bitmap_bh, - int goal, struct ext3_reserve_window_node * my_rsv, + ext3_grpblk_t grp_goal, + struct ext3_reserve_window_node * my_rsv, unsigned long *count, int *errp) { - unsigned long group_first_block; - int ret = 0; + ext3_fsblk_t group_first_block; + ext3_grpblk_t ret = 0; int fatal; unsigned long num = *count; @@ -1090,17 +1090,16 @@ ext3_try_to_allocate_with_rsv(struct sup */ if (my_rsv == NULL ) { ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, - goal, count, NULL); + grp_goal, count, NULL); goto out; } /* - * goal is a group relative block number (if there is a goal) - * 0 < goal < EXT3_BLOCKS_PER_GROUP(sb) + * grp_goal is a group relative block number (if there is a goal) + * 0 < grp_goal < EXT3_BLOCKS_PER_GROUP(sb) * first block is a filesystem wide block number * first block is the block number of the first block in this group */ - group_first_block = le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block) + - group * EXT3_BLOCKS_PER_GROUP(sb); + group_first_block = ext3_group_first_block_no(sb, group); /* * Basically we will allocate a new block from inode's reservation @@ -1119,24 +1118,24 @@ ext3_try_to_allocate_with_rsv(struct sup */ while (1) { if (rsv_is_empty(&my_rsv->rsv_window) || (ret < 0) || - !goal_in_my_reservation(&my_rsv->rsv_window, goal, group, sb)) { + !goal_in_my_reservation(&my_rsv->rsv_window, grp_goal, group, sb)) { if (my_rsv->rsv_goal_size < *count) my_rsv->rsv_goal_size = *count; - ret = alloc_new_reservation(my_rsv, goal, sb, + ret = alloc_new_reservation(my_rsv, grp_goal, sb, group, bitmap_bh); if (ret < 0) break; /* failed */ - if (!goal_in_my_reservation(&my_rsv->rsv_window, goal, group, sb)) - goal = -1; - } else if (goal > 0 && (my_rsv->rsv_end-goal+1) < *count) + if (!goal_in_my_reservation(&my_rsv->rsv_window, grp_goal, group, sb)) + grp_goal = -1; + } else if (grp_goal > 0 && (my_rsv->rsv_end-grp_goal+1) < *count) try_to_extend_reservation(my_rsv, sb, - *count-my_rsv->rsv_end + goal - 1); + *count-my_rsv->rsv_end + grp_goal - 1); if ((my_rsv->rsv_start >= group_first_block + EXT3_BLOCKS_PER_GROUP(sb)) || (my_rsv->rsv_end < group_first_block)) BUG(); - ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, goal, + ret = ext3_try_to_allocate(sb, handle, group, bitmap_bh, grp_goal, &num, &my_rsv->rsv_window); if (ret >= 0) { my_rsv->rsv_alloc_hit += num; @@ -1164,7 +1163,7 @@ out: static int ext3_has_free_blocks(struct ext3_sb_info *sbi) { - int free_blocks, root_blocks; + ext3_fsblk_t free_blocks, root_blocks; free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count); @@ -1200,19 +1199,20 @@ int ext3_should_retry_alloc(struct super * bitmap, and then for any free bit if that fails. * This function also updates quota and i_blocks field. */ -int ext3_new_blocks(handle_t *handle, struct inode *inode, - unsigned long goal, unsigned long *count, int *errp) +ext3_fsblk_t ext3_new_blocks(handle_t *handle, struct inode *inode, + ext3_fsblk_t goal, unsigned long *count, int *errp) { struct buffer_head *bitmap_bh = NULL; struct buffer_head *gdp_bh; int group_no; int goal_group; - int ret_block; + ext3_grpblk_t grp_target_blk; /* blockgroup relative goal block */ + ext3_grpblk_t grp_alloc_blk; /* blockgroup-relative allocated block*/ + ext3_fsblk_t ret_block; /* filesyetem-wide allocated block */ int bgi; /* blockgroup iteration index */ - int target_block; int fatal = 0, err; int performed_allocation = 0; - int free_blocks; + ext3_grpblk_t free_blocks; /* number of free blocks in a group */ struct super_block *sb; struct ext3_group_desc *gdp; struct ext3_super_block *es; @@ -1285,16 +1285,17 @@ retry: my_rsv = NULL; if (free_blocks > 0) { - ret_block = ((goal - le32_to_cpu(es->s_first_data_block)) % + grp_target_blk = ((goal - le32_to_cpu(es->s_first_data_block)) % EXT3_BLOCKS_PER_GROUP(sb)); bitmap_bh = read_block_bitmap(sb, group_no); if (!bitmap_bh) goto io_error; - ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no, - bitmap_bh, ret_block, my_rsv, &num, &fatal); + grp_alloc_blk = ext3_try_to_allocate_with_rsv(sb, handle, + group_no, bitmap_bh, grp_target_blk, + my_rsv, &num, &fatal); if (fatal) goto out; - if (ret_block >= 0) + if (grp_alloc_blk >= 0) goto allocated; } @@ -1327,11 +1328,15 @@ retry: bitmap_bh = read_block_bitmap(sb, group_no); if (!bitmap_bh) goto io_error; - ret_block = ext3_try_to_allocate_with_rsv(sb, handle, group_no, - bitmap_bh, -1, my_rsv, &num, &fatal); + /* + * try to allocate block(s) from this group, without a goal(-1). + */ + grp_alloc_blk = ext3_try_to_allocate_with_rsv(sb, handle, + group_no, bitmap_bh, -1, my_rsv, + &num, &fatal); if (fatal) goto out; - if (ret_block >= 0) + if (grp_alloc_blk >= 0) goto allocated; } /* @@ -1360,18 +1365,18 @@ allocated: if (fatal) goto out; - target_block = ret_block + group_no * EXT3_BLOCKS_PER_GROUP(sb) - + le32_to_cpu(es->s_first_data_block); + ret_block = grp_alloc_blk + ext3_group_first_block_no(sb, group_no); - if (in_range(le32_to_cpu(gdp->bg_block_bitmap), target_block, num) || - in_range(le32_to_cpu(gdp->bg_inode_bitmap), target_block, num) || - in_range(target_block, le32_to_cpu(gdp->bg_inode_table), + if (in_range(le32_to_cpu(gdp->bg_block_bitmap), ret_block, num) || + in_range(le32_to_cpu(gdp->bg_inode_bitmap), ret_block, num) || + in_range(ret_block, le32_to_cpu(gdp->bg_inode_table), EXT3_SB(sb)->s_itb_per_group) || - in_range(target_block + num - 1, le32_to_cpu(gdp->bg_inode_table), + in_range(ret_block + num - 1, le32_to_cpu(gdp->bg_inode_table), EXT3_SB(sb)->s_itb_per_group)) ext3_error(sb, "ext3_new_block", "Allocating block in system zone - " - "blocks from %u, length %lu", target_block, num); + "blocks from "E3FSBLK", length %lu", + ret_block, num); performed_allocation = 1; @@ -1380,7 +1385,7 @@ #ifdef CONFIG_JBD_DEBUG struct buffer_head *debug_bh; /* Record bitmap buffer state in the newly allocated block */ - debug_bh = sb_find_get_block(sb, target_block); + debug_bh = sb_find_get_block(sb, ret_block); if (debug_bh) { BUFFER_TRACE(debug_bh, "state when allocated"); BUFFER_TRACE2(debug_bh, bitmap_bh, "bitmap state"); @@ -1393,24 +1398,21 @@ #ifdef CONFIG_JBD_DEBUG int i; for (i = 0; i < num; i++) { - if (ext3_test_bit(ret_block, + if (ext3_test_bit(grp_alloc_blk+i, bh2jh(bitmap_bh)->b_committed_data)) { printk("%s: block was unexpectedly set in " "b_committed_data\n", __FUNCTION__); } } } - ext3_debug("found bit %d\n", ret_block); + ext3_debug("found bit %d\n", grp_alloc_blk); spin_unlock(sb_bgl_lock(sbi, group_no)); jbd_unlock_bh_state(bitmap_bh); #endif - /* ret_block was blockgroup-relative. Now it becomes fs-relative */ - ret_block = target_block; - if (ret_block + num - 1 >= le32_to_cpu(es->s_blocks_count)) { ext3_error(sb, "ext3_new_block", - "block(%d) >= blocks count(%d) - " + "block("E3FSBLK") >= blocks count(%d) - " "block_group = %d, es == %p ", ret_block, le32_to_cpu(es->s_blocks_count), group_no, es); goto out; @@ -1421,7 +1423,7 @@ #endif * list of some description. We don't know in advance whether * the caller wants to use it as metadata or data. */ - ext3_debug("allocating block %d. Goal hits %d of %d.\n", + ext3_debug("allocating block %lu. Goal hits %d of %d.\n", ret_block, goal_hits, goal_attempts); spin_lock(sb_bgl_lock(sbi, group_no)); @@ -1461,23 +1463,24 @@ out: return 0; } -int ext3_new_block(handle_t *handle, struct inode *inode, - unsigned long goal, int *errp) +ext3_fsblk_t ext3_new_block(handle_t *handle, struct inode *inode, + ext3_fsblk_t goal, int *errp) { unsigned long count = 1; return ext3_new_blocks(handle, inode, goal, &count, errp); } -unsigned long ext3_count_free_blocks(struct super_block *sb) +ext3_fsblk_t ext3_count_free_blocks(struct super_block *sb) { - unsigned long desc_count; + ext3_fsblk_t desc_count; struct ext3_group_desc *gdp; int i; unsigned long ngroups = EXT3_SB(sb)->s_groups_count; #ifdef EXT3FS_DEBUG struct ext3_super_block *es; - unsigned long bitmap_count, x; + ext3_fsblk_t bitmap_count; + unsigned long x; struct buffer_head *bitmap_bh = NULL; es = EXT3_SB(sb)->s_es; @@ -1502,8 +1505,10 @@ #ifdef EXT3FS_DEBUG bitmap_count += x; } brelse(bitmap_bh); - printk("ext3_count_free_blocks: stored = %u, computed = %lu, %lu\n", - le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count); + printk("ext3_count_free_blocks: stored = "E3FSBLK + ", computed = "E3FSBLK", "E3FSBLK"\n", + le32_to_cpu(es->s_free_blocks_count), + desc_count, bitmap_count); return bitmap_count; #else desc_count = 0; @@ -1520,7 +1525,7 @@ #endif } static inline int -block_in_use(unsigned long block, struct super_block *sb, unsigned char *map) +block_in_use(ext3_fsblk_t block, struct super_block *sb, unsigned char *map) { return ext3_test_bit ((block - le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block)) % diff --git a/fs/ext3/dir.c b/fs/ext3/dir.c index f37528e..fbb0d4e 100644 --- a/fs/ext3/dir.c +++ b/fs/ext3/dir.c @@ -284,7 +284,7 @@ static void free_rb_tree_fname(struct rb * beginning of the loop and try to free the parent * node. */ - parent = n->rb_parent; + parent = rb_parent(n); fname = rb_entry(n, struct fname, rb_hash); while (fname) { struct fname * old = fname; diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index dc82646..36546ed 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -262,9 +262,11 @@ static int find_group_orlov(struct super int ngroups = sbi->s_groups_count; int inodes_per_group = EXT3_INODES_PER_GROUP(sb); int freei, avefreei; - int freeb, avefreeb; - int blocks_per_dir, ndirs; - int max_debt, max_dirs, min_blocks, min_inodes; + ext3_fsblk_t freeb, avefreeb; + ext3_fsblk_t blocks_per_dir; + int ndirs; + int max_debt, max_dirs, min_inodes; + ext3_grpblk_t min_blocks; int group = -1, i; struct ext3_group_desc *desc; struct buffer_head *bh; @@ -307,7 +309,7 @@ static int find_group_orlov(struct super min_inodes = avefreei - inodes_per_group / 4; min_blocks = avefreeb - EXT3_BLOCKS_PER_GROUP(sb) / 4; - max_debt = EXT3_BLOCKS_PER_GROUP(sb) / max(blocks_per_dir, BLOCK_COST); + max_debt = EXT3_BLOCKS_PER_GROUP(sb) / max(blocks_per_dir, (ext3_fsblk_t)BLOCK_COST); if (max_debt * INODE_COST > inodes_per_group) max_debt = inodes_per_group / INODE_COST; if (max_debt > 255) diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2edd7ee..f804d5e 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -62,7 +62,7 @@ static int ext3_inode_is_fast_symlink(st * still needs to be revoked. */ int ext3_forget(handle_t *handle, int is_metadata, struct inode *inode, - struct buffer_head *bh, int blocknr) + struct buffer_head *bh, ext3_fsblk_t blocknr) { int err; @@ -407,13 +407,13 @@ no_block: * * Caller must make sure that @ind is valid and will stay that way. */ -static unsigned long ext3_find_near(struct inode *inode, Indirect *ind) +static ext3_fsblk_t ext3_find_near(struct inode *inode, Indirect *ind) { struct ext3_inode_info *ei = EXT3_I(inode); __le32 *start = ind->bh ? (__le32*) ind->bh->b_data : ei->i_data; __le32 *p; - unsigned long bg_start; - unsigned long colour; + ext3_fsblk_t bg_start; + ext3_grpblk_t colour; /* Try to find previous block */ for (p = ind->p - 1; p >= start; p--) { @@ -429,8 +429,7 @@ static unsigned long ext3_find_near(stru * It is going to be referred to from the inode itself? OK, just put it * into the same cylinder group then. */ - bg_start = (ei->i_block_group * EXT3_BLOCKS_PER_GROUP(inode->i_sb)) + - le32_to_cpu(EXT3_SB(inode->i_sb)->s_es->s_first_data_block); + bg_start = ext3_group_first_block_no(inode->i_sb, ei->i_block_group); colour = (current->pid % 16) * (EXT3_BLOCKS_PER_GROUP(inode->i_sb) / 16); return bg_start + colour; @@ -448,7 +447,7 @@ static unsigned long ext3_find_near(stru * stores it in *@goal and returns zero. */ -static unsigned long ext3_find_goal(struct inode *inode, long block, +static ext3_fsblk_t ext3_find_goal(struct inode *inode, long block, Indirect chain[4], Indirect *partial) { struct ext3_block_alloc_info *block_i; @@ -516,13 +515,13 @@ static int ext3_blks_to_allocate(Indirec * direct blocks */ static int ext3_alloc_blocks(handle_t *handle, struct inode *inode, - unsigned long goal, int indirect_blks, int blks, - unsigned long long new_blocks[4], int *err) + ext3_fsblk_t goal, int indirect_blks, int blks, + ext3_fsblk_t new_blocks[4], int *err) { int target, i; unsigned long count = 0; int index = 0; - unsigned long current_block = 0; + ext3_fsblk_t current_block = 0; int ret = 0; /* @@ -592,7 +591,7 @@ failed_out: * as described above and return 0. */ static int ext3_alloc_branch(handle_t *handle, struct inode *inode, - int indirect_blks, int *blks, unsigned long goal, + int indirect_blks, int *blks, ext3_fsblk_t goal, int *offsets, Indirect *branch) { int blocksize = inode->i_sb->s_blocksize; @@ -600,8 +599,8 @@ static int ext3_alloc_branch(handle_t *h int err = 0; struct buffer_head *bh; int num; - unsigned long long new_blocks[4]; - unsigned long long current_block; + ext3_fsblk_t new_blocks[4]; + ext3_fsblk_t current_block; num = ext3_alloc_blocks(handle, inode, goal, indirect_blks, *blks, new_blocks, &err); @@ -688,7 +687,7 @@ static int ext3_splice_branch(handle_t * int i; int err = 0; struct ext3_block_alloc_info *block_i; - unsigned long current_block; + ext3_fsblk_t current_block; block_i = EXT3_I(inode)->i_block_alloc_info; /* @@ -795,13 +794,13 @@ int ext3_get_blocks_handle(handle_t *han int offsets[4]; Indirect chain[4]; Indirect *partial; - unsigned long goal; + ext3_fsblk_t goal; int indirect_blks; int blocks_to_boundary = 0; int depth; struct ext3_inode_info *ei = EXT3_I(inode); int count = 0; - unsigned long first_block = 0; + ext3_fsblk_t first_block = 0; J_ASSERT(handle != NULL || create == 0); @@ -819,7 +818,7 @@ int ext3_get_blocks_handle(handle_t *han count++; /*map more blocks*/ while (count < maxblocks && count <= blocks_to_boundary) { - unsigned long blk; + ext3_fsblk_t blk; if (!verify_chain(chain, partial)) { /* @@ -1699,7 +1698,7 @@ static int ext3_journalled_set_page_dirt return __set_page_dirty_nobuffers(page); } -static struct address_space_operations ext3_ordered_aops = { +static const struct address_space_operations ext3_ordered_aops = { .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_ordered_writepage, @@ -1713,7 +1712,7 @@ static struct address_space_operations e .migratepage = buffer_migrate_page, }; -static struct address_space_operations ext3_writeback_aops = { +static const struct address_space_operations ext3_writeback_aops = { .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_writeback_writepage, @@ -1727,7 +1726,7 @@ static struct address_space_operations e .migratepage = buffer_migrate_page, }; -static struct address_space_operations ext3_journalled_aops = { +static const struct address_space_operations ext3_journalled_aops = { .readpage = ext3_readpage, .readpages = ext3_readpages, .writepage = ext3_journalled_writepage, @@ -1759,7 +1758,7 @@ void ext3_set_aops(struct inode *inode) static int ext3_block_truncate_page(handle_t *handle, struct page *page, struct address_space *mapping, loff_t from) { - unsigned long index = from >> PAGE_CACHE_SHIFT; + ext3_fsblk_t index = from >> PAGE_CACHE_SHIFT; unsigned offset = from & (PAGE_CACHE_SIZE-1); unsigned blocksize, iblock, length, pos; struct inode *inode = mapping->host; @@ -1960,7 +1959,7 @@ no_top: * than `count' because there can be holes in there. */ static void ext3_clear_blocks(handle_t *handle, struct inode *inode, - struct buffer_head *bh, unsigned long block_to_free, + struct buffer_head *bh, ext3_fsblk_t block_to_free, unsigned long count, __le32 *first, __le32 *last) { __le32 *p; @@ -2022,12 +2021,12 @@ static void ext3_free_data(handle_t *han struct buffer_head *this_bh, __le32 *first, __le32 *last) { - unsigned long block_to_free = 0; /* Starting block # of a run */ + ext3_fsblk_t block_to_free = 0; /* Starting block # of a run */ unsigned long count = 0; /* Number of blocks in the run */ __le32 *block_to_free_p = NULL; /* Pointer into inode/ind corresponding to block_to_free */ - unsigned long nr; /* Current block # */ + ext3_fsblk_t nr; /* Current block # */ __le32 *p; /* Pointer into inode/ind for current block */ int err; @@ -2089,7 +2088,7 @@ static void ext3_free_branches(handle_t struct buffer_head *parent_bh, __le32 *first, __le32 *last, int depth) { - unsigned long nr; + ext3_fsblk_t nr; __le32 *p; if (is_handle_aborted(handle)) @@ -2113,7 +2112,7 @@ static void ext3_free_branches(handle_t */ if (!bh) { ext3_error(inode->i_sb, "ext3_free_branches", - "Read failure, inode=%ld, block=%ld", + "Read failure, inode=%ld, block="E3FSBLK, inode->i_ino, nr); continue; } @@ -2394,11 +2393,12 @@ out_stop: ext3_journal_stop(handle); } -static unsigned long ext3_get_inode_block(struct super_block *sb, +static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb, unsigned long ino, struct ext3_iloc *iloc) { unsigned long desc, group_desc, block_group; - unsigned long offset, block; + unsigned long offset; + ext3_fsblk_t block; struct buffer_head *bh; struct ext3_group_desc * gdp; @@ -2448,7 +2448,7 @@ static unsigned long ext3_get_inode_bloc static int __ext3_get_inode_loc(struct inode *inode, struct ext3_iloc *iloc, int in_mem) { - unsigned long block; + ext3_fsblk_t block; struct buffer_head *bh; block = ext3_get_inode_block(inode->i_sb, inode->i_ino, iloc); @@ -2459,7 +2459,8 @@ static int __ext3_get_inode_loc(struct i if (!bh) { ext3_error (inode->i_sb, "ext3_get_inode_loc", "unable to read inode block - " - "inode=%lu, block=%lu", inode->i_ino, block); + "inode=%lu, block="E3FSBLK, + inode->i_ino, block); return -EIO; } if (!buffer_uptodate(bh)) { @@ -2540,7 +2541,7 @@ make_io: if (!buffer_uptodate(bh)) { ext3_error(inode->i_sb, "ext3_get_inode_loc", "unable to read inode block - " - "inode=%lu, block=%lu", + "inode=%lu, block="E3FSBLK, inode->i_ino, block); brelse(bh); return -EIO; diff --git a/fs/ext3/ioctl.c b/fs/ext3/ioctl.c index 8c22aa9..3a6b012 100644 --- a/fs/ext3/ioctl.c +++ b/fs/ext3/ioctl.c @@ -204,7 +204,7 @@ #endif return 0; } case EXT3_IOC_GROUP_EXTEND: { - unsigned long n_blocks_count; + ext3_fsblk_t n_blocks_count; struct super_block *sb = inode->i_sb; int err; diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index b8f5cd1..d9176db 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1379,7 +1379,6 @@ #ifdef CONFIG_EXT3_INDEX int dx_fallback=0; #endif unsigned blocksize; - unsigned nlen, rlen; u32 block, blocks; sb = dir->i_sb; @@ -1417,8 +1416,7 @@ #endif return retval; de = (struct ext3_dir_entry_2 *) bh->b_data; de->inode = 0; - de->rec_len = cpu_to_le16(rlen = blocksize); - nlen = 0; + de->rec_len = cpu_to_le16(blocksize); return add_dirent_to_buf(handle, dentry, inode, de, bh); } diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 34b39e9..5e1337f 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -8,7 +8,6 @@ * This could probably be made into a module, because it is not often in use. */ -#include #define EXT3FS_DEBUG @@ -28,16 +27,16 @@ static int verify_group_input(struct sup { struct ext3_sb_info *sbi = EXT3_SB(sb); struct ext3_super_block *es = sbi->s_es; - unsigned start = le32_to_cpu(es->s_blocks_count); - unsigned end = start + input->blocks_count; + ext3_fsblk_t start = le32_to_cpu(es->s_blocks_count); + ext3_fsblk_t end = start + input->blocks_count; unsigned group = input->group; - unsigned itend = input->inode_table + sbi->s_itb_per_group; + ext3_fsblk_t itend = input->inode_table + sbi->s_itb_per_group; unsigned overhead = ext3_bg_has_super(sb, group) ? (1 + ext3_bg_num_gdb(sb, group) + le16_to_cpu(es->s_reserved_gdt_blocks)) : 0; - unsigned metaend = start + overhead; + ext3_fsblk_t metaend = start + overhead; struct buffer_head *bh = NULL; - int free_blocks_count; + ext3_grpblk_t free_blocks_count; int err = -EINVAL; input->free_blocks_count = free_blocks_count = @@ -64,7 +63,8 @@ static int verify_group_input(struct sup ext3_warning(sb, __FUNCTION__, "Bad blocks count %u", input->blocks_count); else if (!(bh = sb_bread(sb, end - 1))) - ext3_warning(sb, __FUNCTION__, "Cannot read last block (%u)", + ext3_warning(sb, __FUNCTION__, + "Cannot read last block ("E3FSBLK")", end - 1); else if (outside(input->block_bitmap, start, end)) ext3_warning(sb, __FUNCTION__, @@ -77,7 +77,7 @@ static int verify_group_input(struct sup else if (outside(input->inode_table, start, end) || outside(itend - 1, start, end)) ext3_warning(sb, __FUNCTION__, - "Inode table not in group (blocks %u-%u)", + "Inode table not in group (blocks %u-"E3FSBLK")", input->inode_table, itend - 1); else if (input->inode_bitmap == input->block_bitmap) ext3_warning(sb, __FUNCTION__, @@ -85,24 +85,27 @@ static int verify_group_input(struct sup input->block_bitmap); else if (inside(input->block_bitmap, input->inode_table, itend)) ext3_warning(sb, __FUNCTION__, - "Block bitmap (%u) in inode table (%u-%u)", + "Block bitmap (%u) in inode table (%u-"E3FSBLK")", input->block_bitmap, input->inode_table, itend-1); else if (inside(input->inode_bitmap, input->inode_table, itend)) ext3_warning(sb, __FUNCTION__, - "Inode bitmap (%u) in inode table (%u-%u)", + "Inode bitmap (%u) in inode table (%u-"E3FSBLK")", input->inode_bitmap, input->inode_table, itend-1); else if (inside(input->block_bitmap, start, metaend)) ext3_warning(sb, __FUNCTION__, - "Block bitmap (%u) in GDT table (%u-%u)", + "Block bitmap (%u) in GDT table" + " ("E3FSBLK"-"E3FSBLK")", input->block_bitmap, start, metaend - 1); else if (inside(input->inode_bitmap, start, metaend)) ext3_warning(sb, __FUNCTION__, - "Inode bitmap (%u) in GDT table (%u-%u)", + "Inode bitmap (%u) in GDT table" + " ("E3FSBLK"-"E3FSBLK")", input->inode_bitmap, start, metaend - 1); else if (inside(input->inode_table, start, metaend) || inside(itend - 1, start, metaend)) ext3_warning(sb, __FUNCTION__, - "Inode table (%u-%u) overlaps GDT table (%u-%u)", + "Inode table (%u-"E3FSBLK") overlaps" + "GDT table ("E3FSBLK"-"E3FSBLK")", input->inode_table, itend - 1, start, metaend - 1); else err = 0; @@ -112,7 +115,7 @@ static int verify_group_input(struct sup } static struct buffer_head *bclean(handle_t *handle, struct super_block *sb, - unsigned long blk) + ext3_fsblk_t blk) { struct buffer_head *bh; int err; @@ -163,15 +166,14 @@ static int setup_new_group_blocks(struct struct ext3_new_group_data *input) { struct ext3_sb_info *sbi = EXT3_SB(sb); - unsigned long start = input->group * sbi->s_blocks_per_group + - le32_to_cpu(sbi->s_es->s_first_data_block); + ext3_fsblk_t start = ext3_group_first_block_no(sb, input->group); int reserved_gdb = ext3_bg_has_super(sb, input->group) ? le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) : 0; unsigned long gdblocks = ext3_bg_num_gdb(sb, input->group); struct buffer_head *bh; handle_t *handle; - unsigned long block; - int bit; + ext3_fsblk_t block; + ext3_grpblk_t bit; int i; int err = 0, err2; @@ -328,7 +330,7 @@ static unsigned ext3_list_backups(struct static int verify_reserved_gdb(struct super_block *sb, struct buffer_head *primary) { - const unsigned long blk = primary->b_blocknr; + const ext3_fsblk_t blk = primary->b_blocknr; const unsigned long end = EXT3_SB(sb)->s_groups_count; unsigned three = 1; unsigned five = 5; @@ -340,7 +342,8 @@ static int verify_reserved_gdb(struct su while ((grp = ext3_list_backups(sb, &three, &five, &seven)) < end) { if (le32_to_cpu(*p++) != grp * EXT3_BLOCKS_PER_GROUP(sb) + blk){ ext3_warning(sb, __FUNCTION__, - "reserved GDT %ld missing grp %d (%ld)", + "reserved GDT "E3FSBLK + " missing grp %d ("E3FSBLK")", blk, grp, grp * EXT3_BLOCKS_PER_GROUP(sb) + blk); return -EINVAL; @@ -372,7 +375,7 @@ static int add_new_gdb(handle_t *handle, struct super_block *sb = inode->i_sb; struct ext3_super_block *es = EXT3_SB(sb)->s_es; unsigned long gdb_num = input->group / EXT3_DESC_PER_BLOCK(sb); - unsigned long gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num; + ext3_fsblk_t gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num; struct buffer_head **o_group_desc, **n_group_desc; struct buffer_head *dind; int gdbackups; @@ -417,7 +420,7 @@ static int add_new_gdb(handle_t *handle, data = (__u32 *)dind->b_data; if (le32_to_cpu(data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)]) != gdblock) { ext3_warning(sb, __FUNCTION__, - "new group %u GDT block %lu not reserved", + "new group %u GDT block "E3FSBLK" not reserved", input->group, gdblock); err = -EINVAL; goto exit_dind; @@ -515,7 +518,7 @@ static int reserve_backup_gdb(handle_t * struct buffer_head **primary; struct buffer_head *dind; struct ext3_iloc iloc; - unsigned long blk; + ext3_fsblk_t blk; __u32 *data, *end; int gdbackups = 0; int res, i; @@ -540,7 +543,8 @@ static int reserve_backup_gdb(handle_t * for (res = 0; res < reserved_gdb; res++, blk++) { if (le32_to_cpu(*data) != blk) { ext3_warning(sb, __FUNCTION__, - "reserved block %lu not at offset %ld", + "reserved block "E3FSBLK + " not at offset %ld", blk, (long)(data - (__u32 *)dind->b_data)); err = -EINVAL; goto exit_bh; @@ -902,15 +906,16 @@ exit_put: * GDT blocks are reserved to grow to the desired size. */ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, - unsigned long n_blocks_count) + ext3_fsblk_t n_blocks_count) { - unsigned long o_blocks_count; + ext3_fsblk_t o_blocks_count; unsigned long o_groups_count; - unsigned long last; - int add; + ext3_grpblk_t last; + ext3_grpblk_t add; struct buffer_head * bh; handle_t *handle; - int err, freed_blocks; + int err; + unsigned long freed_blocks; /* We don't need to worry about locking wrt other resizers just * yet: we're going to revalidate es->s_blocks_count after @@ -919,12 +924,22 @@ int ext3_group_extend(struct super_block o_groups_count = EXT3_SB(sb)->s_groups_count; if (test_opt(sb, DEBUG)) - printk(KERN_DEBUG "EXT3-fs: extending last group from %lu to %lu blocks\n", + printk(KERN_DEBUG "EXT3-fs: extending last group from "E3FSBLK" uto "E3FSBLK" blocks\n", o_blocks_count, n_blocks_count); if (n_blocks_count == 0 || n_blocks_count == o_blocks_count) return 0; + if (n_blocks_count > (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) { + printk(KERN_ERR "EXT3-fs: filesystem on %s:" + " too large to resize to %lu blocks safely\n", + sb->s_id, n_blocks_count); + if (sizeof(sector_t) < 8) + ext3_warning(sb, __FUNCTION__, + "CONFIG_LBD not enabled\n"); + return -EINVAL; + } + if (n_blocks_count < o_blocks_count) { ext3_warning(sb, __FUNCTION__, "can't shrink FS - resize aborted"); @@ -948,7 +963,8 @@ int ext3_group_extend(struct super_block if (o_blocks_count + add < n_blocks_count) ext3_warning(sb, __FUNCTION__, - "will only finish group (%lu blocks, %u new)", + "will only finish group ("E3FSBLK + " blocks, %u new)", o_blocks_count + add, add); /* See if the device is actually as big as what was requested */ @@ -991,10 +1007,10 @@ int ext3_group_extend(struct super_block ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh); sb->s_dirt = 1; unlock_super(sb); - ext3_debug("freeing blocks %ld through %ld\n", o_blocks_count, + ext3_debug("freeing blocks %lu through "E3FSBLK"\n", o_blocks_count, o_blocks_count + add); ext3_free_blocks_sb(handle, sb, o_blocks_count, add, &freed_blocks); - ext3_debug("freed blocks %ld through %ld\n", o_blocks_count, + ext3_debug("freed blocks "E3FSBLK" through "E3FSBLK"\n", o_blocks_count, o_blocks_count + add); if ((err = ext3_journal_stop(handle))) goto exit_put; diff --git a/fs/ext3/super.c b/fs/ext3/super.c index f8a5266..813d589 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -16,7 +16,6 @@ * David S. Miller (davem@caip.rutgers.edu), 1995 */ -#include #include #include #include @@ -58,7 +57,7 @@ static int ext3_sync_fs(struct super_blo static const char *ext3_decode_error(struct super_block * sb, int errno, char nbuf[16]); static int ext3_remount (struct super_block * sb, int * flags, char * data); -static int ext3_statfs (struct super_block * sb, struct kstatfs * buf); +static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf); static void ext3_unlockfs(struct super_block *sb); static void ext3_write_super (struct super_block * sb); static void ext3_write_super_lockfs(struct super_block *sb); @@ -499,20 +498,21 @@ static void ext3_clear_inode(struct inod { struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info; #ifdef CONFIG_EXT3_FS_POSIX_ACL - if (EXT3_I(inode)->i_acl && - EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) { - posix_acl_release(EXT3_I(inode)->i_acl); - EXT3_I(inode)->i_acl = EXT3_ACL_NOT_CACHED; - } - if (EXT3_I(inode)->i_default_acl && - EXT3_I(inode)->i_default_acl != EXT3_ACL_NOT_CACHED) { - posix_acl_release(EXT3_I(inode)->i_default_acl); - EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED; - } + if (EXT3_I(inode)->i_acl && + EXT3_I(inode)->i_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(EXT3_I(inode)->i_acl); + EXT3_I(inode)->i_acl = EXT3_ACL_NOT_CACHED; + } + if (EXT3_I(inode)->i_default_acl && + EXT3_I(inode)->i_default_acl != EXT3_ACL_NOT_CACHED) { + posix_acl_release(EXT3_I(inode)->i_default_acl); + EXT3_I(inode)->i_default_acl = EXT3_ACL_NOT_CACHED; + } #endif ext3_discard_reservation(inode); EXT3_I(inode)->i_block_alloc_info = NULL; - kfree(rsv); + if (unlikely(rsv)) + kfree(rsv); } static inline void ext3_show_quota_options(struct seq_file *seq, struct super_block *sb) @@ -629,7 +629,7 @@ enum { Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl, - Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, + Opt_reservation, Opt_noreservation, Opt_noload, Opt_nobh, Opt_bh, Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, @@ -665,6 +665,7 @@ static match_table_t tokens = { {Opt_noreservation, "noreservation"}, {Opt_noload, "noload"}, {Opt_nobh, "nobh"}, + {Opt_bh, "bh"}, {Opt_commit, "commit=%u"}, {Opt_journal_update, "journal=update"}, {Opt_journal_inum, "journal=%u"}, @@ -688,14 +689,15 @@ static match_table_t tokens = { {Opt_resize, "resize"}, }; -static unsigned long get_sb_block(void **data) +static ext3_fsblk_t get_sb_block(void **data) { - unsigned long sb_block; + ext3_fsblk_t sb_block; char *options = (char *) *data; if (!options || strncmp(options, "sb=", 3) != 0) return 1; /* Default location */ options += 3; + /*todo: use simple_strtoll with >32bit ext3 */ sb_block = simple_strtoul(options, &options, 0); if (*options && *options != ',') { printk("EXT3-fs: Invalid sb specification: %s\n", @@ -710,7 +712,7 @@ static unsigned long get_sb_block(void * static int parse_options (char *options, struct super_block *sb, unsigned long *inum, unsigned long *journal_devnum, - unsigned long *n_blocks_count, int is_remount) + ext3_fsblk_t *n_blocks_count, int is_remount) { struct ext3_sb_info *sbi = EXT3_SB(sb); char * p; @@ -1012,6 +1014,9 @@ #endif case Opt_nobh: set_opt(sbi->s_mount_opt, NOBH); break; + case Opt_bh: + clear_opt(sbi->s_mount_opt, NOBH); + break; default: printk (KERN_ERR "EXT3-fs: Unrecognized mount option \"%s\" " @@ -1127,7 +1132,7 @@ #endif static int ext3_check_descriptors (struct super_block * sb) { struct ext3_sb_info *sbi = EXT3_SB(sb); - unsigned long block = le32_to_cpu(sbi->s_es->s_first_data_block); + ext3_fsblk_t block = le32_to_cpu(sbi->s_es->s_first_data_block); struct ext3_group_desc * gdp = NULL; int desc_block = 0; int i; @@ -1314,15 +1319,14 @@ static loff_t ext3_max_size(int bits) return res; } -static unsigned long descriptor_loc(struct super_block *sb, - unsigned long logic_sb_block, +static ext3_fsblk_t descriptor_loc(struct super_block *sb, + ext3_fsblk_t logic_sb_block, int nr) { struct ext3_sb_info *sbi = EXT3_SB(sb); - unsigned long bg, first_data_block, first_meta_bg; + unsigned long bg, first_meta_bg; int has_super = 0; - first_data_block = le32_to_cpu(sbi->s_es->s_first_data_block); first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_META_BG) || @@ -1331,7 +1335,7 @@ static unsigned long descriptor_loc(stru bg = sbi->s_desc_per_block * nr; if (ext3_bg_has_super(sb, bg)) has_super = 1; - return (first_data_block + has_super + (bg * sbi->s_blocks_per_group)); + return (has_super + ext3_group_first_block_no(sb, bg)); } @@ -1340,9 +1344,9 @@ static int ext3_fill_super (struct super struct buffer_head * bh; struct ext3_super_block *es = NULL; struct ext3_sb_info *sbi; - unsigned long block; - unsigned long sb_block = get_sb_block(&data); - unsigned long logic_sb_block; + ext3_fsblk_t block; + ext3_fsblk_t sb_block = get_sb_block(&data); + ext3_fsblk_t logic_sb_block; unsigned long offset = 0; unsigned long journal_inum = 0; unsigned long journal_devnum = 0; @@ -1564,6 +1568,16 @@ static int ext3_fill_super (struct super goto failed_mount; } + if (le32_to_cpu(es->s_blocks_count) > + (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) { + printk(KERN_ERR "EXT3-fs: filesystem on %s:" + " too large to mount safely\n", sb->s_id); + if (sizeof(sector_t) < 8) + printk(KERN_WARNING "EXT3-fs: CONFIG_LBD not " + "enabled\n"); + goto failed_mount; + } + if (EXT3_BLOCKS_PER_GROUP(sb) == 0) goto cantfind_ext3; sbi->s_groups_count = (le32_to_cpu(es->s_blocks_count) - @@ -1579,9 +1593,6 @@ static int ext3_fill_super (struct super goto failed_mount; } - percpu_counter_init(&sbi->s_freeblocks_counter); - percpu_counter_init(&sbi->s_freeinodes_counter); - percpu_counter_init(&sbi->s_dirs_counter); bgl_lock_init(&sbi->s_blockgroup_lock); for (i = 0; i < db_count; i++) { @@ -1595,12 +1606,20 @@ static int ext3_fill_super (struct super } } if (!ext3_check_descriptors (sb)) { - printk (KERN_ERR "EXT3-fs: group descriptors corrupted !\n"); + printk(KERN_ERR "EXT3-fs: group descriptors corrupted!\n"); goto failed_mount2; } sbi->s_gdb_count = db_count; get_random_bytes(&sbi->s_next_generation, sizeof(u32)); spin_lock_init(&sbi->s_next_gen_lock); + + percpu_counter_init(&sbi->s_freeblocks_counter, + ext3_count_free_blocks(sb)); + percpu_counter_init(&sbi->s_freeinodes_counter, + ext3_count_free_inodes(sb)); + percpu_counter_init(&sbi->s_dirs_counter, + ext3_count_dirs(sb)); + /* per fileystem reservation list head & lock */ spin_lock_init(&sbi->s_rsv_window_lock); sbi->s_rsv_window_root = RB_ROOT; @@ -1639,16 +1658,16 @@ #endif if (!test_opt(sb, NOLOAD) && EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { if (ext3_load_journal(sb, es, journal_devnum)) - goto failed_mount2; + goto failed_mount3; } else if (journal_inum) { if (ext3_create_journal(sb, es, journal_inum)) - goto failed_mount2; + goto failed_mount3; } else { if (!silent) printk (KERN_ERR "ext3: No journal on filesystem on %s\n", sb->s_id); - goto failed_mount2; + goto failed_mount3; } /* We have now updated the journal if required, so we can @@ -1671,7 +1690,7 @@ #endif (sbi->s_journal, 0, 0, JFS_FEATURE_INCOMPAT_REVOKE)) { printk(KERN_ERR "EXT3-fs: Journal does not support " "requested data journaling mode\n"); - goto failed_mount3; + goto failed_mount4; } default: break; @@ -1694,13 +1713,13 @@ #endif if (!sb->s_root) { printk(KERN_ERR "EXT3-fs: get root inode failed\n"); iput(root); - goto failed_mount3; + goto failed_mount4; } if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { dput(sb->s_root); sb->s_root = NULL; printk(KERN_ERR "EXT3-fs: corrupt root inode, run e2fsck\n"); - goto failed_mount3; + goto failed_mount4; } ext3_setup_super (sb, es, sb->s_flags & MS_RDONLY); @@ -1723,13 +1742,6 @@ #endif test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA ? "ordered": "writeback"); - percpu_counter_mod(&sbi->s_freeblocks_counter, - ext3_count_free_blocks(sb)); - percpu_counter_mod(&sbi->s_freeinodes_counter, - ext3_count_free_inodes(sb)); - percpu_counter_mod(&sbi->s_dirs_counter, - ext3_count_dirs(sb)); - lock_kernel(); return 0; @@ -1739,8 +1751,12 @@ cantfind_ext3: sb->s_id); goto failed_mount; -failed_mount3: +failed_mount4: journal_destroy(sbi->s_journal); +failed_mount3: + percpu_counter_destroy(&sbi->s_freeblocks_counter); + percpu_counter_destroy(&sbi->s_freeinodes_counter); + percpu_counter_destroy(&sbi->s_dirs_counter); failed_mount2: for (i = 0; i < db_count; i++) brelse(sbi->s_group_desc[i]); @@ -1827,10 +1843,10 @@ static journal_t *ext3_get_dev_journal(s { struct buffer_head * bh; journal_t *journal; - int start; - int len; + ext3_fsblk_t start; + ext3_fsblk_t len; int hblock, blocksize; - unsigned long sb_block; + ext3_fsblk_t sb_block; unsigned long offset; struct ext3_super_block * es; struct block_device *bdev; @@ -2203,7 +2219,7 @@ static int ext3_remount (struct super_bl { struct ext3_super_block * es; struct ext3_sb_info *sbi = EXT3_SB(sb); - unsigned long n_blocks_count = 0; + ext3_fsblk_t n_blocks_count = 0; unsigned long old_sb_flags; struct ext3_mount_options old_opts; int err; @@ -2318,11 +2334,12 @@ #endif return err; } -static int ext3_statfs (struct super_block * sb, struct kstatfs * buf) +static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf) { + struct super_block *sb = dentry->d_sb; struct ext3_sb_info *sbi = EXT3_SB(sb); struct ext3_super_block *es = sbi->s_es; - unsigned long overhead; + ext3_fsblk_t overhead; int i; if (test_opt (sb, MINIX_DF)) @@ -2597,7 +2614,7 @@ static ssize_t ext3_quota_write(struct s struct buffer_head *bh; handle_t *handle = journal_current_handle(); - mutex_lock(&inode->i_mutex); + mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); while (towrite > 0) { tocopy = sb->s_blocksize - offset < towrite ? sb->s_blocksize - offset : towrite; @@ -2646,10 +2663,10 @@ out: #endif -static struct super_block *ext3_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ext3_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, ext3_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, ext3_fill_super, mnt); } static struct file_system_type ext3_fs_type = { diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index e8d60bf..a44a056 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -225,7 +225,7 @@ ext3_xattr_block_get(struct inode *inode error = -ENODATA; if (!EXT3_I(inode)->i_file_acl) goto cleanup; - ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl); + ea_idebug(inode, "reading block %u", EXT3_I(inode)->i_file_acl); bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); if (!bh) goto cleanup; @@ -233,7 +233,7 @@ ext3_xattr_block_get(struct inode *inode atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); if (ext3_xattr_check_block(bh)) { bad_block: ext3_error(inode->i_sb, __FUNCTION__, - "inode %ld: bad block %d", inode->i_ino, + "inode %ld: bad block "E3FSBLK, inode->i_ino, EXT3_I(inode)->i_file_acl); error = -EIO; goto cleanup; @@ -366,7 +366,7 @@ ext3_xattr_block_list(struct inode *inod error = 0; if (!EXT3_I(inode)->i_file_acl) goto cleanup; - ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl); + ea_idebug(inode, "reading block %u", EXT3_I(inode)->i_file_acl); bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); error = -EIO; if (!bh) @@ -375,7 +375,7 @@ ext3_xattr_block_list(struct inode *inod atomic_read(&(bh->b_count)), le32_to_cpu(BHDR(bh)->h_refcount)); if (ext3_xattr_check_block(bh)) { ext3_error(inode->i_sb, __FUNCTION__, - "inode %ld: bad block %d", inode->i_ino, + "inode %ld: bad block "E3FSBLK, inode->i_ino, EXT3_I(inode)->i_file_acl); error = -EIO; goto cleanup; @@ -647,7 +647,7 @@ ext3_xattr_block_find(struct inode *inod le32_to_cpu(BHDR(bs->bh)->h_refcount)); if (ext3_xattr_check_block(bs->bh)) { ext3_error(sb, __FUNCTION__, - "inode %ld: bad block %d", inode->i_ino, + "inode %ld: bad block "E3FSBLK, inode->i_ino, EXT3_I(inode)->i_file_acl); error = -EIO; goto cleanup; @@ -792,11 +792,12 @@ inserted: get_bh(new_bh); } else { /* We need to allocate a new block */ - int goal = le32_to_cpu( + ext3_fsblk_t goal = le32_to_cpu( EXT3_SB(sb)->s_es->s_first_data_block) + - EXT3_I(inode)->i_block_group * + (ext3_fsblk_t)EXT3_I(inode)->i_block_group * EXT3_BLOCKS_PER_GROUP(sb); - int block = ext3_new_block(handle, inode, goal, &error); + ext3_fsblk_t block = ext3_new_block(handle, inode, + goal, &error); if (error) goto cleanup; ea_idebug(inode, "creating block %d", block); @@ -847,7 +848,7 @@ cleanup_dquot: bad_block: ext3_error(inode->i_sb, __FUNCTION__, - "inode %ld: bad block %d", inode->i_ino, + "inode %ld: bad block "E3FSBLK, inode->i_ino, EXT3_I(inode)->i_file_acl); goto cleanup; @@ -1076,14 +1077,14 @@ ext3_xattr_delete_inode(handle_t *handle bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); if (!bh) { ext3_error(inode->i_sb, __FUNCTION__, - "inode %ld: block %d read error", inode->i_ino, + "inode %ld: block "E3FSBLK" read error", inode->i_ino, EXT3_I(inode)->i_file_acl); goto cleanup; } if (BHDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || BHDR(bh)->h_blocks != cpu_to_le32(1)) { ext3_error(inode->i_sb, __FUNCTION__, - "inode %ld: bad block %d", inode->i_ino, + "inode %ld: bad block "E3FSBLK, inode->i_ino, EXT3_I(inode)->i_file_acl); goto cleanup; } @@ -1210,11 +1211,11 @@ again: bh = sb_bread(inode->i_sb, ce->e_block); if (!bh) { ext3_error(inode->i_sb, __FUNCTION__, - "inode %ld: block %ld read error", + "inode %ld: block %lu read error", inode->i_ino, (unsigned long) ce->e_block); } else if (le32_to_cpu(BHDR(bh)->h_refcount) >= EXT3_XATTR_REFCOUNT_MAX) { - ea_idebug(inode, "block %ld refcount %d>=%d", + ea_idebug(inode, "block %lu refcount %d>=%d", (unsigned long) ce->e_block, le32_to_cpu(BHDR(bh)->h_refcount), EXT3_XATTR_REFCOUNT_MAX); diff --git a/fs/ext3/xattr.h b/fs/ext3/xattr.h index 2ceae38..6b1ae1c 100644 --- a/fs/ext3/xattr.h +++ b/fs/ext3/xattr.h @@ -6,7 +6,6 @@ (C) 2001 Andreas Gruenbacher, */ -#include #include /* Magic value in attribute blocks */ diff --git a/fs/fat/inode.c b/fs/fat/inode.c index c1ce284..31b7174 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -196,7 +196,7 @@ static sector_t _fat_bmap(struct address return generic_block_bmap(mapping, block, fat_get_block); } -static struct address_space_operations fat_aops = { +static const struct address_space_operations fat_aops = { .readpage = fat_readpage, .readpages = fat_readpages, .writepage = fat_writepage, @@ -539,18 +539,18 @@ static int fat_remount(struct super_bloc return 0; } -static int fat_statfs(struct super_block *sb, struct kstatfs *buf) +static int fat_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct msdos_sb_info *sbi = MSDOS_SB(sb); + struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); /* If the count of free cluster is still unknown, counts it here. */ if (sbi->free_clusters == -1) { - int err = fat_count_free_clusters(sb); + int err = fat_count_free_clusters(dentry->d_sb); if (err) return err; } - buf->f_type = sb->s_magic; + buf->f_type = dentry->d_sb->s_magic; buf->f_bsize = sbi->cluster_size; buf->f_blocks = sbi->max_cluster - FAT_START_ENT; buf->f_bfree = sbi->free_clusters; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 944652e..308f2b6 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -210,4 +210,3 @@ int fat_sync_bhs(struct buffer_head **bh return err; } -EXPORT_SYMBOL_GPL(fat_sync_bhs); diff --git a/fs/file_table.c b/fs/file_table.c index bcea199..0131ba0 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -5,7 +5,6 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) */ -#include #include #include #include @@ -300,5 +299,5 @@ void __init files_init(unsigned long mem if (files_stat.max_files < NR_FILE) files_stat.max_files = NR_FILE; files_defer_init(); - percpu_counter_init(&nr_files); + percpu_counter_init(&nr_files, 0); } diff --git a/fs/freevxfs/vxfs.h b/fs/freevxfs/vxfs.h index 583bd78..d35979a 100644 --- a/fs/freevxfs/vxfs.h +++ b/fs/freevxfs/vxfs.h @@ -159,11 +159,11 @@ struct vxfs_sb { * In core superblock filesystem private data for VxFS. */ struct vxfs_sb_info { - struct vxfs_sb *vsi_raw; /* raw (on disk) supeblock */ + struct vxfs_sb *vsi_raw; /* raw (on disk) superblock */ struct buffer_head *vsi_bp; /* buffer for raw superblock*/ struct inode *vsi_fship; /* fileset header inode */ struct inode *vsi_ilist; /* inode list inode */ - struct inode *vsi_stilist; /* structual inode list inode */ + struct inode *vsi_stilist; /* structural inode list inode */ u_long vsi_iext; /* initial inode list */ ino_t vsi_fshino; /* fileset header inode */ daddr_t vsi_oltext; /* OLT extent */ diff --git a/fs/freevxfs/vxfs_fshead.c b/fs/freevxfs/vxfs_fshead.c index 6dee109..78948b4 100644 --- a/fs/freevxfs/vxfs_fshead.c +++ b/fs/freevxfs/vxfs_fshead.c @@ -112,7 +112,7 @@ vxfs_read_fshead(struct super_block *sbp vip = vxfs_blkiget(sbp, infp->vsi_iext, infp->vsi_fshino); if (!vip) { - printk(KERN_ERR "vxfs: unabled to read fsh inode\n"); + printk(KERN_ERR "vxfs: unable to read fsh inode\n"); return -EINVAL; } if (!VXFS_ISFSH(vip)) { @@ -129,13 +129,13 @@ #endif infp->vsi_fship = vxfs_get_fake_inode(sbp, vip); if (!infp->vsi_fship) { - printk(KERN_ERR "vxfs: unabled to get fsh inode\n"); + printk(KERN_ERR "vxfs: unable to get fsh inode\n"); goto out_free_fship; } sfp = vxfs_getfsh(infp->vsi_fship, 0); if (!sfp) { - printk(KERN_ERR "vxfs: unabled to get structural fsh\n"); + printk(KERN_ERR "vxfs: unable to get structural fsh\n"); goto out_iput_fship; } @@ -145,7 +145,7 @@ #endif pfp = vxfs_getfsh(infp->vsi_fship, 1); if (!pfp) { - printk(KERN_ERR "vxfs: unabled to get primary fsh\n"); + printk(KERN_ERR "vxfs: unable to get primary fsh\n"); goto out_free_sfp; } @@ -159,7 +159,7 @@ #endif infp->vsi_stilist = vxfs_get_fake_inode(sbp, tip); if (!infp->vsi_stilist) { - printk(KERN_ERR "vxfs: unabled to get structual list inode\n"); + printk(KERN_ERR "vxfs: unable to get structural list inode\n"); kfree(tip); goto out_free_pfp; } @@ -174,7 +174,7 @@ #endif goto out_iput_stilist; infp->vsi_ilist = vxfs_get_fake_inode(sbp, tip); if (!infp->vsi_ilist) { - printk(KERN_ERR "vxfs: unabled to get inode list inode\n"); + printk(KERN_ERR "vxfs: unable to get inode list inode\n"); kfree(tip); goto out_iput_stilist; } diff --git a/fs/freevxfs/vxfs_immed.c b/fs/freevxfs/vxfs_immed.c index 6f5df17..4e25f3f 100644 --- a/fs/freevxfs/vxfs_immed.c +++ b/fs/freevxfs/vxfs_immed.c @@ -56,7 +56,7 @@ struct inode_operations vxfs_immed_symli /* * Adress space operations for immed files and directories. */ -struct address_space_operations vxfs_immed_aops = { +const struct address_space_operations vxfs_immed_aops = { .readpage = vxfs_immed_readpage, }; diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index f544aae..ca6a397 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -41,8 +41,8 @@ #include "vxfs_inode.h" #include "vxfs_extern.h" -extern struct address_space_operations vxfs_aops; -extern struct address_space_operations vxfs_immed_aops; +extern const struct address_space_operations vxfs_aops; +extern const struct address_space_operations vxfs_immed_aops; extern struct inode_operations vxfs_immed_symlink_iops; @@ -295,7 +295,7 @@ vxfs_read_inode(struct inode *ip) { struct super_block *sbp = ip->i_sb; struct vxfs_inode_info *vip; - struct address_space_operations *aops; + const struct address_space_operations *aops; ino_t ino = ip->i_ino; if (!(vip = __vxfs_iget(ino, VXFS_SBI(sbp)->vsi_ilist))) diff --git a/fs/freevxfs/vxfs_subr.c b/fs/freevxfs/vxfs_subr.c index 50aae77..decac62 100644 --- a/fs/freevxfs/vxfs_subr.c +++ b/fs/freevxfs/vxfs_subr.c @@ -42,7 +42,7 @@ #include "vxfs_extern.h" static int vxfs_readpage(struct file *, struct page *); static sector_t vxfs_bmap(struct address_space *, sector_t); -struct address_space_operations vxfs_aops = { +const struct address_space_operations vxfs_aops = { .readpage = vxfs_readpage, .bmap = vxfs_bmap, .sync_page = block_sync_page, @@ -71,8 +71,7 @@ vxfs_get_page(struct address_space *mapp { struct page * pp; - pp = read_cache_page(mapping, n, - (filler_t*)mapping->a_ops->readpage, NULL); + pp = read_mapping_page(mapping, n, NULL); if (!IS_ERR(pp)) { wait_on_page_locked(pp); diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index b44c916..b74b791 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -40,6 +40,7 @@ #include #include #include #include +#include #include "vxfs.h" #include "vxfs_extern.h" @@ -55,7 +56,7 @@ MODULE_ALIAS("vxfs"); /* makes mount -t static void vxfs_put_super(struct super_block *); -static int vxfs_statfs(struct super_block *, struct kstatfs *); +static int vxfs_statfs(struct dentry *, struct kstatfs *); static int vxfs_remount(struct super_block *, int *, char *); static struct super_operations vxfs_super_ops = { @@ -90,12 +91,12 @@ vxfs_put_super(struct super_block *sbp) /** * vxfs_statfs - get filesystem information - * @sbp: VFS superblock + * @dentry: VFS dentry to locate superblock * @bufp: output buffer * * Description: * vxfs_statfs fills the statfs buffer @bufp with information - * about the filesystem described by @sbp. + * about the filesystem described by @dentry. * * Returns: * Zero. @@ -107,12 +108,12 @@ vxfs_put_super(struct super_block *sbp) * This is everything but complete... */ static int -vxfs_statfs(struct super_block *sbp, struct kstatfs *bufp) +vxfs_statfs(struct dentry *dentry, struct kstatfs *bufp) { - struct vxfs_sb_info *infp = VXFS_SBI(sbp); + struct vxfs_sb_info *infp = VXFS_SBI(dentry->d_sb); bufp->f_type = VXFS_SUPER_MAGIC; - bufp->f_bsize = sbp->s_blocksize; + bufp->f_bsize = dentry->d_sb->s_blocksize; bufp->f_blocks = infp->vsi_raw->vs_dsize; bufp->f_bfree = infp->vsi_raw->vs_free; bufp->f_bavail = 0; @@ -241,10 +242,11 @@ out: /* * The usual module blurb. */ -static struct super_block *vxfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int vxfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, vxfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, vxfs_fill_super, + mnt); } static struct file_system_type vxfs_fs_type = { diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index f3fbe2d..892643d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -461,9 +461,11 @@ void sync_inodes_sb(struct super_block * { struct writeback_control wbc = { .sync_mode = wait ? WB_SYNC_ALL : WB_SYNC_HOLD, + .range_start = 0, + .range_end = LLONG_MAX, }; - unsigned long nr_dirty = read_page_state(nr_dirty); - unsigned long nr_unstable = read_page_state(nr_unstable); + unsigned long nr_dirty = global_page_state(NR_FILE_DIRTY); + unsigned long nr_unstable = global_page_state(NR_UNSTABLE_NFS); wbc.nr_to_write = nr_dirty + nr_unstable + (inodes_stat.nr_inodes - inodes_stat.nr_unused) + @@ -559,6 +561,8 @@ int write_inode_now(struct inode *inode, struct writeback_control wbc = { .nr_to_write = LONG_MAX, .sync_mode = WB_SYNC_ALL, + .range_start = 0, + .range_end = LLONG_MAX, }; if (!mapping_cap_writeback_dirty(inode->i_mapping)) @@ -619,7 +623,6 @@ int generic_osync_inode(struct inode *in int need_write_inode_now = 0; int err2; - current->flags |= PF_SYNCWRITE; if (what & OSYNC_DATA) err = filemap_fdatawrite(mapping); if (what & (OSYNC_METADATA|OSYNC_DATA)) { @@ -632,7 +635,6 @@ int generic_osync_inode(struct inode *in if (!err) err = err2; } - current->flags &= ~PF_SYNCWRITE; spin_lock(&inode_lock); if ((inode->i_state & I_DIRTY) && diff --git a/fs/fuse/Makefile b/fs/fuse/Makefile index c3e1f76..7243706 100644 --- a/fs/fuse/Makefile +++ b/fs/fuse/Makefile @@ -4,4 +4,4 @@ # obj-$(CONFIG_FUSE_FS) += fuse.o -fuse-objs := dev.o dir.o file.o inode.o +fuse-objs := dev.o dir.o file.o inode.o control.o diff --git a/fs/fuse/control.c b/fs/fuse/control.c new file mode 100644 index 0000000..a3bce3a --- /dev/null +++ b/fs/fuse/control.c @@ -0,0 +1,218 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2006 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +#include "fuse_i.h" + +#include +#include + +#define FUSE_CTL_SUPER_MAGIC 0x65735543 + +/* + * This is non-NULL when the single instance of the control filesystem + * exists. Protected by fuse_mutex + */ +static struct super_block *fuse_control_sb; + +static struct fuse_conn *fuse_ctl_file_conn_get(struct file *file) +{ + struct fuse_conn *fc; + mutex_lock(&fuse_mutex); + fc = file->f_dentry->d_inode->u.generic_ip; + if (fc) + fc = fuse_conn_get(fc); + mutex_unlock(&fuse_mutex); + return fc; +} + +static ssize_t fuse_conn_abort_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct fuse_conn *fc = fuse_ctl_file_conn_get(file); + if (fc) { + fuse_abort_conn(fc); + fuse_conn_put(fc); + } + return count; +} + +static ssize_t fuse_conn_waiting_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + char tmp[32]; + size_t size; + + if (!*ppos) { + struct fuse_conn *fc = fuse_ctl_file_conn_get(file); + if (!fc) + return 0; + + file->private_data=(void *)(long)atomic_read(&fc->num_waiting); + fuse_conn_put(fc); + } + size = sprintf(tmp, "%ld\n", (long)file->private_data); + return simple_read_from_buffer(buf, len, ppos, tmp, size); +} + +static const struct file_operations fuse_ctl_abort_ops = { + .open = nonseekable_open, + .write = fuse_conn_abort_write, +}; + +static const struct file_operations fuse_ctl_waiting_ops = { + .open = nonseekable_open, + .read = fuse_conn_waiting_read, +}; + +static struct dentry *fuse_ctl_add_dentry(struct dentry *parent, + struct fuse_conn *fc, + const char *name, + int mode, int nlink, + struct inode_operations *iop, + const struct file_operations *fop) +{ + struct dentry *dentry; + struct inode *inode; + + BUG_ON(fc->ctl_ndents >= FUSE_CTL_NUM_DENTRIES); + dentry = d_alloc_name(parent, name); + if (!dentry) + return NULL; + + fc->ctl_dentry[fc->ctl_ndents++] = dentry; + inode = new_inode(fuse_control_sb); + if (!inode) + return NULL; + + inode->i_mode = mode; + inode->i_uid = fc->user_id; + inode->i_gid = fc->group_id; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + /* setting ->i_op to NULL is not allowed */ + if (iop) + inode->i_op = iop; + inode->i_fop = fop; + inode->i_nlink = nlink; + inode->u.generic_ip = fc; + d_add(dentry, inode); + return dentry; +} + +/* + * Add a connection to the control filesystem (if it exists). Caller + * must host fuse_mutex + */ +int fuse_ctl_add_conn(struct fuse_conn *fc) +{ + struct dentry *parent; + char name[32]; + + if (!fuse_control_sb) + return 0; + + parent = fuse_control_sb->s_root; + parent->d_inode->i_nlink++; + sprintf(name, "%llu", (unsigned long long) fc->id); + parent = fuse_ctl_add_dentry(parent, fc, name, S_IFDIR | 0500, 2, + &simple_dir_inode_operations, + &simple_dir_operations); + if (!parent) + goto err; + + if (!fuse_ctl_add_dentry(parent, fc, "waiting", S_IFREG | 0400, 1, + NULL, &fuse_ctl_waiting_ops) || + !fuse_ctl_add_dentry(parent, fc, "abort", S_IFREG | 0200, 1, + NULL, &fuse_ctl_abort_ops)) + goto err; + + return 0; + + err: + fuse_ctl_remove_conn(fc); + return -ENOMEM; +} + +/* + * Remove a connection from the control filesystem (if it exists). + * Caller must host fuse_mutex + */ +void fuse_ctl_remove_conn(struct fuse_conn *fc) +{ + int i; + + if (!fuse_control_sb) + return; + + for (i = fc->ctl_ndents - 1; i >= 0; i--) { + struct dentry *dentry = fc->ctl_dentry[i]; + dentry->d_inode->u.generic_ip = NULL; + d_drop(dentry); + dput(dentry); + } + fuse_control_sb->s_root->d_inode->i_nlink--; +} + +static int fuse_ctl_fill_super(struct super_block *sb, void *data, int silent) +{ + struct tree_descr empty_descr = {""}; + struct fuse_conn *fc; + int err; + + err = simple_fill_super(sb, FUSE_CTL_SUPER_MAGIC, &empty_descr); + if (err) + return err; + + mutex_lock(&fuse_mutex); + BUG_ON(fuse_control_sb); + fuse_control_sb = sb; + list_for_each_entry(fc, &fuse_conn_list, entry) { + err = fuse_ctl_add_conn(fc); + if (err) { + fuse_control_sb = NULL; + mutex_unlock(&fuse_mutex); + return err; + } + } + mutex_unlock(&fuse_mutex); + + return 0; +} + +static int fuse_ctl_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data, + struct vfsmount *mnt) +{ + return get_sb_single(fs_type, flags, raw_data, + fuse_ctl_fill_super, mnt); +} + +static void fuse_ctl_kill_sb(struct super_block *sb) +{ + mutex_lock(&fuse_mutex); + fuse_control_sb = NULL; + mutex_unlock(&fuse_mutex); + + kill_litter_super(sb); +} + +static struct file_system_type fuse_ctl_fs_type = { + .owner = THIS_MODULE, + .name = "fusectl", + .get_sb = fuse_ctl_get_sb, + .kill_sb = fuse_ctl_kill_sb, +}; + +int __init fuse_ctl_init(void) +{ + return register_filesystem(&fuse_ctl_fs_type); +} + +void fuse_ctl_cleanup(void) +{ + unregister_filesystem(&fuse_ctl_fs_type); +} diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 104a62d..1e2006c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -34,6 +34,7 @@ static void fuse_request_init(struct fus { memset(req, 0, sizeof(*req)); INIT_LIST_HEAD(&req->list); + INIT_LIST_HEAD(&req->intr_entry); init_waitqueue_head(&req->waitq); atomic_set(&req->count, 1); } @@ -64,18 +65,6 @@ static void restore_sigs(sigset_t *oldse sigprocmask(SIG_SETMASK, oldset, NULL); } -/* - * Reset request, so that it can be reused - * - * The caller must be _very_ careful to make sure, that it is holding - * the only reference to req - */ -void fuse_reset_request(struct fuse_req *req) -{ - BUG_ON(atomic_read(&req->count) != 1); - fuse_request_init(req); -} - static void __fuse_get_request(struct fuse_req *req) { atomic_inc(&req->count); @@ -88,6 +77,13 @@ static void __fuse_put_request(struct fu atomic_dec(&req->count); } +static void fuse_req_init_context(struct fuse_req *req) +{ + req->in.h.uid = current->fsuid; + req->in.h.gid = current->fsgid; + req->in.h.pid = current->pid; +} + struct fuse_req *fuse_get_req(struct fuse_conn *fc) { struct fuse_req *req; @@ -103,14 +99,16 @@ struct fuse_req *fuse_get_req(struct fus if (intr) goto out; + err = -ENOTCONN; + if (!fc->connected) + goto out; + req = fuse_request_alloc(); err = -ENOMEM; if (!req) goto out; - req->in.h.uid = current->fsuid; - req->in.h.gid = current->fsgid; - req->in.h.pid = current->pid; + fuse_req_init_context(req); req->waiting = 1; return req; @@ -119,142 +117,183 @@ struct fuse_req *fuse_get_req(struct fus return ERR_PTR(err); } -void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) +/* + * Return request in fuse_file->reserved_req. However that may + * currently be in use. If that is the case, wait for it to become + * available. + */ +static struct fuse_req *get_reserved_req(struct fuse_conn *fc, + struct file *file) { - if (atomic_dec_and_test(&req->count)) { - if (req->waiting) - atomic_dec(&fc->num_waiting); - fuse_request_free(req); - } + struct fuse_req *req = NULL; + struct fuse_file *ff = file->private_data; + + do { + wait_event(fc->blocked_waitq, ff->reserved_req); + spin_lock(&fc->lock); + if (ff->reserved_req) { + req = ff->reserved_req; + ff->reserved_req = NULL; + get_file(file); + req->stolen_file = file; + } + spin_unlock(&fc->lock); + } while (!req); + + return req; } /* - * Called with sbput_sem held for read (request_end) or write - * (fuse_put_super). By the time fuse_put_super() is finished, all - * inodes belonging to background requests must be released, so the - * iputs have to be done within the locked region. + * Put stolen request back into fuse_file->reserved_req */ -void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req) +static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req) { - iput(req->inode); - iput(req->inode2); + struct file *file = req->stolen_file; + struct fuse_file *ff = file->private_data; + spin_lock(&fc->lock); - list_del(&req->bg_entry); - if (fc->num_background == FUSE_MAX_BACKGROUND) { - fc->blocked = 0; - wake_up_all(&fc->blocked_waitq); - } - fc->num_background--; + fuse_request_init(req); + BUG_ON(ff->reserved_req); + ff->reserved_req = req; + wake_up(&fc->blocked_waitq); spin_unlock(&fc->lock); + fput(file); } /* - * This function is called when a request is finished. Either a reply - * has arrived or it was interrupted (and not yet sent) or some error - * occurred during communication with userspace, or the device file - * was closed. In case of a background request the reference to the - * stored objects are released. The requester thread is woken up (if - * still waiting), the 'end' callback is called if given, else the - * reference to the request is released + * Gets a requests for a file operation, always succeeds * - * Releasing extra reference for foreground requests must be done - * within the same locked region as setting state to finished. This - * is because fuse_reset_request() may be called after request is - * finished and it must be the sole possessor. If request is - * interrupted and put in the background, it will return with an error - * and hence never be reset and reused. + * This is used for sending the FLUSH request, which must get to + * userspace, due to POSIX locks which may need to be unlocked. * - * Called with fc->lock, unlocks it + * If allocation fails due to OOM, use the reserved request in + * fuse_file. + * + * This is very unlikely to deadlock accidentally, since the + * filesystem should not have it's own file open. If deadlock is + * intentional, it can still be broken by "aborting" the filesystem. */ -static void request_end(struct fuse_conn *fc, struct fuse_req *req) +struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file) { - list_del(&req->list); - req->state = FUSE_REQ_FINISHED; - if (!req->background) { - spin_unlock(&fc->lock); - wake_up(&req->waitq); - fuse_put_request(fc, req); - } else { - void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; - req->end = NULL; - spin_unlock(&fc->lock); - down_read(&fc->sbput_sem); - if (fc->mounted) - fuse_release_background(fc, req); - up_read(&fc->sbput_sem); + struct fuse_req *req; - /* fput must go outside sbput_sem, otherwise it can deadlock */ - if (req->file) - fput(req->file); + atomic_inc(&fc->num_waiting); + wait_event(fc->blocked_waitq, !fc->blocked); + req = fuse_request_alloc(); + if (!req) + req = get_reserved_req(fc, file); - if (end) - end(fc, req); + fuse_req_init_context(req); + req->waiting = 1; + return req; +} + +void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) +{ + if (atomic_dec_and_test(&req->count)) { + if (req->waiting) + atomic_dec(&fc->num_waiting); + + if (req->stolen_file) + put_reserved_req(fc, req); else - fuse_put_request(fc, req); + fuse_request_free(req); } } /* - * Unfortunately request interruption not just solves the deadlock - * problem, it causes problems too. These stem from the fact, that an - * interrupted request is continued to be processed in userspace, - * while all the locks and object references (inode and file) held - * during the operation are released. - * - * To release the locks is exactly why there's a need to interrupt the - * request, so there's not a lot that can be done about this, except - * introduce additional locking in userspace. - * - * More important is to keep inode and file references until userspace - * has replied, otherwise FORGET and RELEASE could be sent while the - * inode/file is still used by the filesystem. - * - * For this reason the concept of "background" request is introduced. - * An interrupted request is backgrounded if it has been already sent - * to userspace. Backgrounding involves getting an extra reference to - * inode(s) or file used in the request, and adding the request to - * fc->background list. When a reply is received for a background - * request, the object references are released, and the request is - * removed from the list. If the filesystem is unmounted while there - * are still background requests, the list is walked and references - * are released as if a reply was received. + * This function is called when a request is finished. Either a reply + * has arrived or it was aborted (and not yet sent) or some error + * occurred during communication with userspace, or the device file + * was closed. The requester thread is woken up (if still waiting), + * the 'end' callback is called if given, else the reference to the + * request is released * - * There's one more use for a background request. The RELEASE message is - * always sent as background, since it doesn't return an error or - * data. + * Called with fc->lock, unlocks it */ -static void background_request(struct fuse_conn *fc, struct fuse_req *req) -{ - req->background = 1; - list_add(&req->bg_entry, &fc->background); - fc->num_background++; - if (fc->num_background == FUSE_MAX_BACKGROUND) - fc->blocked = 1; - if (req->inode) - req->inode = igrab(req->inode); - if (req->inode2) - req->inode2 = igrab(req->inode2); +static void request_end(struct fuse_conn *fc, struct fuse_req *req) +{ + void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; + req->end = NULL; + list_del(&req->list); + list_del(&req->intr_entry); + req->state = FUSE_REQ_FINISHED; + if (req->background) { + if (fc->num_background == FUSE_MAX_BACKGROUND) { + fc->blocked = 0; + wake_up_all(&fc->blocked_waitq); + } + fc->num_background--; + } + spin_unlock(&fc->lock); + dput(req->dentry); + mntput(req->vfsmount); if (req->file) - get_file(req->file); + fput(req->file); + wake_up(&req->waitq); + if (end) + end(fc, req); + else + fuse_put_request(fc, req); } -/* Called with fc->lock held. Releases, and then reacquires it. */ -static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) +static void wait_answer_interruptible(struct fuse_conn *fc, + struct fuse_req *req) { - sigset_t oldset; + if (signal_pending(current)) + return; spin_unlock(&fc->lock); - block_sigs(&oldset); wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED); - restore_sigs(&oldset); spin_lock(&fc->lock); - if (req->state == FUSE_REQ_FINISHED && !req->interrupted) - return; +} + +static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req) +{ + list_add_tail(&req->intr_entry, &fc->interrupts); + wake_up(&fc->waitq); + kill_fasync(&fc->fasync, SIGIO, POLL_IN); +} + +/* Called with fc->lock held. Releases, and then reacquires it. */ +static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req) +{ + if (!fc->no_interrupt) { + /* Any signal may interrupt this */ + wait_answer_interruptible(fc, req); + + if (req->aborted) + goto aborted; + if (req->state == FUSE_REQ_FINISHED) + return; - if (!req->interrupted) { - req->out.h.error = -EINTR; req->interrupted = 1; + if (req->state == FUSE_REQ_SENT) + queue_interrupt(fc, req); + } + + if (req->force) { + spin_unlock(&fc->lock); + wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); + spin_lock(&fc->lock); + } else { + sigset_t oldset; + + /* Only fatal signals may interrupt this */ + block_sigs(&oldset); + wait_answer_interruptible(fc, req); + restore_sigs(&oldset); } + + if (req->aborted) + goto aborted; + if (req->state == FUSE_REQ_FINISHED) + return; + + req->out.h.error = -EINTR; + req->aborted = 1; + + aborted: if (req->locked) { /* This is uninterruptible sleep, because data is being copied to/from the buffers of req. During @@ -268,8 +307,11 @@ static void request_wait_answer(struct f if (req->state == FUSE_REQ_PENDING) { list_del(&req->list); __fuse_put_request(req); - } else if (req->state == FUSE_REQ_SENT) - background_request(fc, req); + } else if (req->state == FUSE_REQ_SENT) { + spin_unlock(&fc->lock); + wait_event(req->waitq, req->state == FUSE_REQ_FINISHED); + spin_lock(&fc->lock); + } } static unsigned len_args(unsigned numargs, struct fuse_arg *args) @@ -283,13 +325,19 @@ static unsigned len_args(unsigned numarg return nbytes; } +static u64 fuse_get_unique(struct fuse_conn *fc) + { + fc->reqctr++; + /* zero is special */ + if (fc->reqctr == 0) + fc->reqctr = 1; + + return fc->reqctr; +} + static void queue_request(struct fuse_conn *fc, struct fuse_req *req) { - fc->reqctr++; - /* zero is special */ - if (fc->reqctr == 0) - fc->reqctr = 1; - req->in.h.unique = fc->reqctr; + req->in.h.unique = fuse_get_unique(fc); req->in.h.len = sizeof(struct fuse_in_header) + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); list_add_tail(&req->list, &fc->pending); @@ -302,9 +350,6 @@ static void queue_request(struct fuse_co kill_fasync(&fc->fasync, SIGIO, POLL_IN); } -/* - * This can only be interrupted by a SIGKILL - */ void request_send(struct fuse_conn *fc, struct fuse_req *req) { req->isreply = 1; @@ -327,8 +372,12 @@ void request_send(struct fuse_conn *fc, static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) { spin_lock(&fc->lock); - background_request(fc, req); if (fc->connected) { + req->background = 1; + fc->num_background++; + if (fc->num_background == FUSE_MAX_BACKGROUND) + fc->blocked = 1; + queue_request(fc, req); spin_unlock(&fc->lock); } else { @@ -352,14 +401,14 @@ void request_send_background(struct fuse /* * Lock the request. Up to the next unlock_request() there mustn't be * anything that could cause a page-fault. If the request was already - * interrupted bail out. + * aborted bail out. */ static int lock_request(struct fuse_conn *fc, struct fuse_req *req) { int err = 0; if (req) { spin_lock(&fc->lock); - if (req->interrupted) + if (req->aborted) err = -ENOENT; else req->locked = 1; @@ -369,7 +418,7 @@ static int lock_request(struct fuse_conn } /* - * Unlock request. If it was interrupted during being locked, the + * Unlock request. If it was aborted during being locked, the * requester thread is currently waiting for it to be unlocked, so * wake it up. */ @@ -378,7 +427,7 @@ static void unlock_request(struct fuse_c if (req) { spin_lock(&fc->lock); req->locked = 0; - if (req->interrupted) + if (req->aborted) wake_up(&req->waitq); spin_unlock(&fc->lock); } @@ -557,13 +606,18 @@ static int fuse_copy_args(struct fuse_co return err; } +static int request_pending(struct fuse_conn *fc) +{ + return !list_empty(&fc->pending) || !list_empty(&fc->interrupts); +} + /* Wait until a request is available on the pending list */ static void request_wait(struct fuse_conn *fc) { DECLARE_WAITQUEUE(wait, current); add_wait_queue_exclusive(&fc->waitq, &wait); - while (fc->connected && list_empty(&fc->pending)) { + while (fc->connected && !request_pending(fc)) { set_current_state(TASK_INTERRUPTIBLE); if (signal_pending(current)) break; @@ -577,11 +631,50 @@ static void request_wait(struct fuse_con } /* + * Transfer an interrupt request to userspace + * + * Unlike other requests this is assembled on demand, without a need + * to allocate a separate fuse_req structure. + * + * Called with fc->lock held, releases it + */ +static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_req *req, + const struct iovec *iov, unsigned long nr_segs) +{ + struct fuse_copy_state cs; + struct fuse_in_header ih; + struct fuse_interrupt_in arg; + unsigned reqsize = sizeof(ih) + sizeof(arg); + int err; + + list_del_init(&req->intr_entry); + req->intr_unique = fuse_get_unique(fc); + memset(&ih, 0, sizeof(ih)); + memset(&arg, 0, sizeof(arg)); + ih.len = reqsize; + ih.opcode = FUSE_INTERRUPT; + ih.unique = req->intr_unique; + arg.unique = req->in.h.unique; + + spin_unlock(&fc->lock); + if (iov_length(iov, nr_segs) < reqsize) + return -EINVAL; + + fuse_copy_init(&cs, fc, 1, NULL, iov, nr_segs); + err = fuse_copy_one(&cs, &ih, sizeof(ih)); + if (!err) + err = fuse_copy_one(&cs, &arg, sizeof(arg)); + fuse_copy_finish(&cs); + + return err ? err : reqsize; +} + +/* * Read a single request into the userspace filesystem's buffer. This * function waits until a request is available, then removes it from * the pending list and copies request data to userspace buffer. If - * no reply is needed (FORGET) or request has been interrupted or - * there was an error during the copying then it's finished by calling + * no reply is needed (FORGET) or request has been aborted or there + * was an error during the copying then it's finished by calling * request_end(). Otherwise add it to the processing list, and set * the 'sent' flag. */ @@ -601,7 +694,7 @@ static ssize_t fuse_dev_readv(struct fil spin_lock(&fc->lock); err = -EAGAIN; if ((file->f_flags & O_NONBLOCK) && fc->connected && - list_empty(&fc->pending)) + !request_pending(fc)) goto err_unlock; request_wait(fc); @@ -609,9 +702,15 @@ static ssize_t fuse_dev_readv(struct fil if (!fc->connected) goto err_unlock; err = -ERESTARTSYS; - if (list_empty(&fc->pending)) + if (!request_pending(fc)) goto err_unlock; + if (!list_empty(&fc->interrupts)) { + req = list_entry(fc->interrupts.next, struct fuse_req, + intr_entry); + return fuse_read_interrupt(fc, req, iov, nr_segs); + } + req = list_entry(fc->pending.next, struct fuse_req, list); req->state = FUSE_REQ_READING; list_move(&req->list, &fc->io); @@ -636,10 +735,10 @@ static ssize_t fuse_dev_readv(struct fil fuse_copy_finish(&cs); spin_lock(&fc->lock); req->locked = 0; - if (!err && req->interrupted) + if (!err && req->aborted) err = -ENOENT; if (err) { - if (!req->interrupted) + if (!req->aborted) req->out.h.error = -EIO; request_end(fc, req); return err; @@ -649,6 +748,8 @@ static ssize_t fuse_dev_readv(struct fil else { req->state = FUSE_REQ_SENT; list_move_tail(&req->list, &fc->processing); + if (req->interrupted) + queue_interrupt(fc, req); spin_unlock(&fc->lock); } return reqsize; @@ -675,7 +776,7 @@ static struct fuse_req *request_find(str list_for_each(entry, &fc->processing) { struct fuse_req *req; req = list_entry(entry, struct fuse_req, list); - if (req->in.h.unique == unique) + if (req->in.h.unique == unique || req->intr_unique == unique) return req; } return NULL; @@ -741,17 +842,33 @@ static ssize_t fuse_dev_writev(struct fi goto err_unlock; req = request_find(fc, oh.unique); - err = -EINVAL; if (!req) goto err_unlock; - if (req->interrupted) { + if (req->aborted) { spin_unlock(&fc->lock); fuse_copy_finish(&cs); spin_lock(&fc->lock); request_end(fc, req); return -ENOENT; } + /* Is it an interrupt reply? */ + if (req->intr_unique == oh.unique) { + err = -EINVAL; + if (nbytes != sizeof(struct fuse_out_header)) + goto err_unlock; + + if (oh.error == -ENOSYS) + fc->no_interrupt = 1; + else if (oh.error == -EAGAIN) + queue_interrupt(fc, req); + + spin_unlock(&fc->lock); + fuse_copy_finish(&cs); + return nbytes; + } + + req->state = FUSE_REQ_WRITING; list_move(&req->list, &fc->io); req->out.h = oh; req->locked = 1; @@ -764,9 +881,9 @@ static ssize_t fuse_dev_writev(struct fi spin_lock(&fc->lock); req->locked = 0; if (!err) { - if (req->interrupted) + if (req->aborted) err = -ENOENT; - } else if (!req->interrupted) + } else if (!req->aborted) req->out.h.error = -EIO; request_end(fc, req); @@ -800,7 +917,7 @@ static unsigned fuse_dev_poll(struct fil spin_lock(&fc->lock); if (!fc->connected) mask = POLLERR; - else if (!list_empty(&fc->pending)) + else if (request_pending(fc)) mask |= POLLIN | POLLRDNORM; spin_unlock(&fc->lock); @@ -826,7 +943,7 @@ static void end_requests(struct fuse_con /* * Abort requests under I/O * - * The requests are set to interrupted and finished, and the request + * The requests are set to aborted and finished, and the request * waiter is woken up. This will make request_wait_answer() wait * until the request is unlocked and then return. * @@ -841,7 +958,7 @@ static void end_io_requests(struct fuse_ list_entry(fc->io.next, struct fuse_req, list); void (*end) (struct fuse_conn *, struct fuse_req *) = req->end; - req->interrupted = 1; + req->aborted = 1; req->out.h.error = -ECONNABORTED; req->state = FUSE_REQ_FINISHED; list_del_init(&req->list); @@ -874,19 +991,20 @@ static void end_io_requests(struct fuse_ * onto the pending list is prevented by req->connected being false. * * Progression of requests under I/O to the processing list is - * prevented by the req->interrupted flag being true for these - * requests. For this reason requests on the io list must be aborted - * first. + * prevented by the req->aborted flag being true for these requests. + * For this reason requests on the io list must be aborted first. */ void fuse_abort_conn(struct fuse_conn *fc) { spin_lock(&fc->lock); if (fc->connected) { fc->connected = 0; + fc->blocked = 0; end_io_requests(fc); end_requests(fc, &fc->pending); end_requests(fc, &fc->processing); wake_up_all(&fc->waitq); + wake_up_all(&fc->blocked_waitq); kill_fasync(&fc->fasync, SIGIO, POLL_IN); } spin_unlock(&fc->lock); @@ -902,7 +1020,7 @@ static int fuse_dev_release(struct inode end_requests(fc, &fc->processing); spin_unlock(&fc->lock); fasync_helper(-1, file, 0, &fc->fasync); - kobject_put(&fc->kobj); + fuse_conn_put(fc); } return 0; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 8d7546e..72a74cd 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1,6 +1,6 @@ /* FUSE: Filesystem in Userspace - Copyright (C) 2001-2005 Miklos Szeredi + Copyright (C) 2001-2006 Miklos Szeredi This program can be distributed under the terms of the GNU GPL. See the file COPYING. @@ -79,7 +79,6 @@ static void fuse_lookup_init(struct fuse { req->in.h.opcode = FUSE_LOOKUP; req->in.h.nodeid = get_node_id(dir); - req->inode = dir; req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -225,6 +224,20 @@ static struct dentry *fuse_lookup(struct } /* + * Synchronous release for the case when something goes wrong in CREATE_OPEN + */ +static void fuse_sync_release(struct fuse_conn *fc, struct fuse_file *ff, + u64 nodeid, int flags) +{ + struct fuse_req *req; + + req = fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE); + req->force = 1; + request_send(fc, req); + fuse_put_request(fc, req); +} + +/* * Atomic create+open operation * * If the filesystem doesn't support this, then fall back to separate @@ -237,6 +250,7 @@ static int fuse_create_open(struct inode struct inode *inode; struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req; + struct fuse_req *forget_req; struct fuse_open_in inarg; struct fuse_open_out outopen; struct fuse_entry_out outentry; @@ -247,9 +261,14 @@ static int fuse_create_open(struct inode if (fc->no_create) return -ENOSYS; + forget_req = fuse_get_req(fc); + if (IS_ERR(forget_req)) + return PTR_ERR(forget_req); + req = fuse_get_req(fc); + err = PTR_ERR(req); if (IS_ERR(req)) - return PTR_ERR(req); + goto out_put_forget_req; err = -ENOMEM; ff = fuse_file_alloc(); @@ -262,7 +281,6 @@ static int fuse_create_open(struct inode inarg.mode = mode; req->in.h.opcode = FUSE_CREATE; req->in.h.nodeid = get_node_id(dir); - req->inode = dir; req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -285,25 +303,23 @@ static int fuse_create_open(struct inode if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid)) goto out_free_ff; + fuse_put_request(fc, req); inode = fuse_iget(dir->i_sb, outentry.nodeid, outentry.generation, &outentry.attr); - err = -ENOMEM; if (!inode) { flags &= ~(O_CREAT | O_EXCL | O_TRUNC); ff->fh = outopen.fh; - /* Special release, with inode = NULL, this will - trigger a 'forget' request when the release is - complete */ - fuse_send_release(fc, ff, outentry.nodeid, NULL, flags, 0); - goto out_put_request; + fuse_sync_release(fc, ff, outentry.nodeid, flags); + fuse_send_forget(fc, forget_req, outentry.nodeid, 1); + return -ENOMEM; } - fuse_put_request(fc, req); + fuse_put_request(fc, forget_req); d_instantiate(entry, inode); fuse_change_timeout(entry, &outentry); file = lookup_instantiate_filp(nd, entry, generic_file_open); if (IS_ERR(file)) { ff->fh = outopen.fh; - fuse_send_release(fc, ff, outentry.nodeid, inode, flags, 0); + fuse_sync_release(fc, ff, outentry.nodeid, flags); return PTR_ERR(file); } fuse_finish_open(inode, file, ff, &outopen); @@ -313,6 +329,8 @@ static int fuse_create_open(struct inode fuse_file_free(ff); out_put_request: fuse_put_request(fc, req); + out_put_forget_req: + fuse_put_request(fc, forget_req); return err; } @@ -328,7 +346,6 @@ static int create_new_entry(struct fuse_ int err; req->in.h.nodeid = get_node_id(dir); - req->inode = dir; req->out.numargs = 1; req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; @@ -448,7 +465,6 @@ static int fuse_unlink(struct inode *dir req->in.h.opcode = FUSE_UNLINK; req->in.h.nodeid = get_node_id(dir); - req->inode = dir; req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -480,7 +496,6 @@ static int fuse_rmdir(struct inode *dir, req->in.h.opcode = FUSE_RMDIR; req->in.h.nodeid = get_node_id(dir); - req->inode = dir; req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -510,8 +525,6 @@ static int fuse_rename(struct inode *old inarg.newdir = get_node_id(newdir); req->in.h.opcode = FUSE_RENAME; req->in.h.nodeid = get_node_id(olddir); - req->inode = olddir; - req->inode2 = newdir; req->in.numargs = 3; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -558,7 +571,6 @@ static int fuse_link(struct dentry *entr memset(&inarg, 0, sizeof(inarg)); inarg.oldnodeid = get_node_id(inode); req->in.h.opcode = FUSE_LINK; - req->inode2 = inode; req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -587,7 +599,6 @@ int fuse_do_getattr(struct inode *inode) req->in.h.opcode = FUSE_GETATTR; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->out.numargs = 1; req->out.args[0].size = sizeof(arg); req->out.args[0].value = &arg; @@ -679,7 +690,6 @@ static int fuse_access(struct inode *ino inarg.mask = mask; req->in.h.opcode = FUSE_ACCESS; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -820,7 +830,6 @@ static char *read_link(struct dentry *de } req->in.h.opcode = FUSE_READLINK; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->out.argvar = 1; req->out.numargs = 1; req->out.args[0].size = PAGE_SIZE - 1; @@ -939,7 +948,6 @@ static int fuse_setattr(struct dentry *e iattr_to_fattr(attr, &inarg); req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1002,7 +1010,6 @@ static int fuse_setxattr(struct dentry * inarg.flags = flags; req->in.h.opcode = FUSE_SETXATTR; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 3; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1041,7 +1048,6 @@ static ssize_t fuse_getxattr(struct dent inarg.size = size; req->in.h.opcode = FUSE_GETXATTR; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1091,7 +1097,6 @@ static ssize_t fuse_listxattr(struct den inarg.size = size; req->in.h.opcode = FUSE_LISTXATTR; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1135,7 +1140,6 @@ static int fuse_removexattr(struct dentr req->in.h.opcode = FUSE_REMOVEXATTR; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 1; req->in.args[0].size = strlen(name) + 1; req->in.args[0].value = name; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index fc342cf..63614ed 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -30,7 +30,6 @@ static int fuse_send_open(struct inode * inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -49,8 +48,8 @@ struct fuse_file *fuse_file_alloc(void) struct fuse_file *ff; ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL); if (ff) { - ff->release_req = fuse_request_alloc(); - if (!ff->release_req) { + ff->reserved_req = fuse_request_alloc(); + if (!ff->reserved_req) { kfree(ff); ff = NULL; } @@ -60,7 +59,7 @@ struct fuse_file *fuse_file_alloc(void) void fuse_file_free(struct fuse_file *ff) { - fuse_request_free(ff->release_req); + fuse_request_free(ff->reserved_req); kfree(ff); } @@ -113,37 +112,22 @@ int fuse_open_common(struct inode *inode return err; } -/* Special case for failed iget in CREATE */ -static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) +struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, + int opcode) { - /* If called from end_io_requests(), req has more than one - reference and fuse_reset_request() cannot work */ - if (fc->connected) { - u64 nodeid = req->in.h.nodeid; - fuse_reset_request(req); - fuse_send_forget(fc, req, nodeid, 1); - } else - fuse_put_request(fc, req); -} - -void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, - u64 nodeid, struct inode *inode, int flags, int isdir) -{ - struct fuse_req * req = ff->release_req; + struct fuse_req *req = ff->reserved_req; struct fuse_release_in *inarg = &req->misc.release_in; inarg->fh = ff->fh; inarg->flags = flags; - req->in.h.opcode = isdir ? FUSE_RELEASEDIR : FUSE_RELEASE; + req->in.h.opcode = opcode; req->in.h.nodeid = nodeid; - req->inode = inode; req->in.numargs = 1; req->in.args[0].size = sizeof(struct fuse_release_in); req->in.args[0].value = inarg; - request_send_background(fc, req); - if (!inode) - req->end = fuse_release_end; kfree(ff); + + return req; } int fuse_release_common(struct inode *inode, struct file *file, int isdir) @@ -151,8 +135,15 @@ int fuse_release_common(struct inode *in struct fuse_file *ff = file->private_data; if (ff) { struct fuse_conn *fc = get_fuse_conn(inode); - u64 nodeid = get_node_id(inode); - fuse_send_release(fc, ff, nodeid, inode, file->f_flags, isdir); + struct fuse_req *req; + + req = fuse_release_fill(ff, get_node_id(inode), file->f_flags, + isdir ? FUSE_RELEASEDIR : FUSE_RELEASE); + + /* Hold vfsmount and dentry until release is finished */ + req->vfsmount = mntget(file->f_vfsmnt); + req->dentry = dget(file->f_dentry); + request_send_background(fc, req); } /* Return value is ignored by VFS */ @@ -169,7 +160,29 @@ static int fuse_release(struct inode *in return fuse_release_common(inode, file, 0); } -static int fuse_flush(struct file *file) +/* + * Scramble the ID space with XTEA, so that the value of the files_struct + * pointer is not exposed to userspace. + */ +static u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id) +{ + u32 *k = fc->scramble_key; + u64 v = (unsigned long) id; + u32 v0 = v; + u32 v1 = v >> 32; + u32 sum = 0; + int i; + + for (i = 0; i < 32; i++) { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + sum += 0x9E3779B9; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[sum>>11 & 3]); + } + + return (u64) v0 + ((u64) v1 << 32); +} + +static int fuse_flush(struct file *file, fl_owner_t id) { struct inode *inode = file->f_dentry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); @@ -184,19 +197,16 @@ static int fuse_flush(struct file *file) if (fc->no_flush) return 0; - req = fuse_get_req(fc); - if (IS_ERR(req)) - return PTR_ERR(req); - + req = fuse_get_req_nofail(fc, file); memset(&inarg, 0, sizeof(inarg)); inarg.fh = ff->fh; + inarg.lock_owner = fuse_lock_owner_id(fc, id); req->in.h.opcode = FUSE_FLUSH; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; - req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; + req->force = 1; request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); @@ -232,8 +242,6 @@ int fuse_fsync_common(struct file *file, inarg.fsync_flags = datasync ? 1 : 0; req->in.h.opcode = isdir ? FUSE_FSYNCDIR : FUSE_FSYNC; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; - req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -266,8 +274,6 @@ void fuse_read_fill(struct fuse_req *req inarg->size = count; req->in.h.opcode = opcode; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; - req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(struct fuse_read_in); req->in.args[0].value = inarg; @@ -342,6 +348,8 @@ static void fuse_send_readpages(struct f req->out.page_zeroing = 1; fuse_read_fill(req, file, inode, pos, count, FUSE_READ); if (fc->async_read) { + get_file(file); + req->file = file; req->end = fuse_readpages_end; request_send_background(fc, req); } else { @@ -420,8 +428,6 @@ static size_t fuse_send_write(struct fus inarg.size = count; req->in.h.opcode = FUSE_WRITE; req->in.h.nodeid = get_node_id(inode); - req->inode = inode; - req->file = file; req->in.argpages = 1; req->in.numargs = 2; req->in.args[0].size = sizeof(struct fuse_write_in); @@ -619,6 +625,126 @@ static int fuse_set_page_dirty(struct pa return 0; } +static int convert_fuse_file_lock(const struct fuse_file_lock *ffl, + struct file_lock *fl) +{ + switch (ffl->type) { + case F_UNLCK: + break; + + case F_RDLCK: + case F_WRLCK: + if (ffl->start > OFFSET_MAX || ffl->end > OFFSET_MAX || + ffl->end < ffl->start) + return -EIO; + + fl->fl_start = ffl->start; + fl->fl_end = ffl->end; + fl->fl_pid = ffl->pid; + break; + + default: + return -EIO; + } + fl->fl_type = ffl->type; + return 0; +} + +static void fuse_lk_fill(struct fuse_req *req, struct file *file, + const struct file_lock *fl, int opcode, pid_t pid) +{ + struct inode *inode = file->f_dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_file *ff = file->private_data; + struct fuse_lk_in *arg = &req->misc.lk_in; + + arg->fh = ff->fh; + arg->owner = fuse_lock_owner_id(fc, fl->fl_owner); + arg->lk.start = fl->fl_start; + arg->lk.end = fl->fl_end; + arg->lk.type = fl->fl_type; + arg->lk.pid = pid; + req->in.h.opcode = opcode; + req->in.h.nodeid = get_node_id(inode); + req->in.numargs = 1; + req->in.args[0].size = sizeof(*arg); + req->in.args[0].value = arg; +} + +static int fuse_getlk(struct file *file, struct file_lock *fl) +{ + struct inode *inode = file->f_dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + struct fuse_lk_out outarg; + int err; + + req = fuse_get_req(fc); + if (IS_ERR(req)) + return PTR_ERR(req); + + fuse_lk_fill(req, file, fl, FUSE_GETLK, 0); + req->out.numargs = 1; + req->out.args[0].size = sizeof(outarg); + req->out.args[0].value = &outarg; + request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + if (!err) + err = convert_fuse_file_lock(&outarg.lk, fl); + + return err; +} + +static int fuse_setlk(struct file *file, struct file_lock *fl) +{ + struct inode *inode = file->f_dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + int opcode = (fl->fl_flags & FL_SLEEP) ? FUSE_SETLKW : FUSE_SETLK; + pid_t pid = fl->fl_type != F_UNLCK ? current->tgid : 0; + int err; + + /* Unlock on close is handled by the flush method */ + if (fl->fl_flags & FL_CLOSE) + return 0; + + req = fuse_get_req(fc); + if (IS_ERR(req)) + return PTR_ERR(req); + + fuse_lk_fill(req, file, fl, opcode, pid); + request_send(fc, req); + err = req->out.h.error; + /* locking is restartable */ + if (err == -EINTR) + err = -ERESTARTSYS; + fuse_put_request(fc, req); + return err; +} + +static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) +{ + struct inode *inode = file->f_dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + int err; + + if (cmd == F_GETLK) { + if (fc->no_lock) { + if (!posix_test_lock(file, fl, fl)) + fl->fl_type = F_UNLCK; + err = 0; + } else + err = fuse_getlk(file, fl); + } else { + if (fc->no_lock) + err = posix_lock_file_wait(file, fl); + else + err = fuse_setlk(file, fl); + } + return err; +} + static const struct file_operations fuse_file_operations = { .llseek = generic_file_llseek, .read = generic_file_read, @@ -628,6 +754,7 @@ static const struct file_operations fuse .flush = fuse_flush, .release = fuse_release, .fsync = fuse_fsync, + .lock = fuse_file_lock, .sendfile = generic_file_sendfile, }; @@ -639,10 +766,11 @@ static const struct file_operations fuse .flush = fuse_flush, .release = fuse_release, .fsync = fuse_fsync, + .lock = fuse_file_lock, /* no mmap and sendfile */ }; -static struct address_space_operations fuse_file_aops = { +static const struct address_space_operations fuse_file_aops = { .readpage = fuse_readpage, .prepare_write = fuse_prepare_write, .commit_write = fuse_commit_write, diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 0474202..0dbf966 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -8,12 +8,13 @@ #include #include +#include #include #include #include #include #include -#include +#include /** Max number of pages that can be used in a single read request */ #define FUSE_MAX_PAGES_PER_REQ 32 @@ -24,6 +25,9 @@ #define FUSE_MAX_BACKGROUND 10 /** It could be as large as PATH_MAX, but would that have any uses? */ #define FUSE_NAME_MAX 1024 +/** Number of dentries for each connection in the control filesystem */ +#define FUSE_CTL_NUM_DENTRIES 3 + /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem module will check permissions based on the file mode. Otherwise no permission checking is done in the kernel */ @@ -33,6 +37,11 @@ #define FUSE_DEFAULT_PERMISSIONS (1 << 0 doing the mount will be allowed to access the filesystem */ #define FUSE_ALLOW_OTHER (1 << 1) +/** List of active connections */ +extern struct list_head fuse_conn_list; + +/** Global mutex protecting fuse_conn_list and the control filesystem */ +extern struct mutex fuse_mutex; /** FUSE inode */ struct fuse_inode { @@ -56,7 +65,7 @@ struct fuse_inode { /** FUSE specific file data */ struct fuse_file { /** Request reserved for flush and release */ - struct fuse_req *release_req; + struct fuse_req *reserved_req; /** File handle used by userspace */ u64 fh; @@ -122,6 +131,7 @@ enum fuse_req_state { FUSE_REQ_PENDING, FUSE_REQ_READING, FUSE_REQ_SENT, + FUSE_REQ_WRITING, FUSE_REQ_FINISHED }; @@ -135,12 +145,15 @@ struct fuse_req { fuse_conn */ struct list_head list; - /** Entry on the background list */ - struct list_head bg_entry; + /** Entry on the interrupts list */ + struct list_head intr_entry; /** refcount */ atomic_t count; + /** Unique ID for the interrupt request */ + u64 intr_unique; + /* * The following bitfields are either set once before the * request is queued or setting/clearing them is protected by @@ -150,12 +163,18 @@ struct fuse_req { /** True if the request has reply */ unsigned isreply:1; - /** The request was interrupted */ - unsigned interrupted:1; + /** Force sending of the request even if interrupted */ + unsigned force:1; + + /** The request was aborted */ + unsigned aborted:1; /** Request is sent in the background */ unsigned background:1; + /** The request has been interrupted */ + unsigned interrupted:1; + /** Data is being copied to/from the request */ unsigned locked:1; @@ -181,6 +200,7 @@ struct fuse_req { struct fuse_init_in init_in; struct fuse_init_out init_out; struct fuse_read_in read_in; + struct fuse_lk_in lk_in; } misc; /** page vector */ @@ -192,17 +212,20 @@ struct fuse_req { /** offset of data on first page */ unsigned page_offset; - /** Inode used in the request */ - struct inode *inode; - - /** Second inode used in the request (or NULL) */ - struct inode *inode2; - /** File used in the request (or NULL) */ struct file *file; + /** vfsmount used in release */ + struct vfsmount *vfsmount; + + /** dentry used in release */ + struct dentry *dentry; + /** Request completion callback */ void (*end)(struct fuse_conn *, struct fuse_req *); + + /** Request is stolen from fuse_file->reserved_req */ + struct file *stolen_file; }; /** @@ -216,6 +239,9 @@ struct fuse_conn { /** Lock protecting accessess to members of this structure */ spinlock_t lock; + /** Refcount */ + atomic_t count; + /** The user id for this mount */ uid_t user_id; @@ -243,13 +269,12 @@ struct fuse_conn { /** The list of requests under I/O */ struct list_head io; - /** Requests put in the background (RELEASE or any other - interrupted request) */ - struct list_head background; - /** Number of requests currently in the background */ unsigned num_background; + /** Pending interrupts */ + struct list_head interrupts; + /** Flag indicating if connection is blocked. This will be the case before the INIT reply is received, and if there are too many outstading backgrounds requests */ @@ -258,15 +283,9 @@ struct fuse_conn { /** waitq for blocked connection */ wait_queue_head_t blocked_waitq; - /** RW semaphore for exclusion with fuse_put_super() */ - struct rw_semaphore sbput_sem; - /** The next unique request id */ u64 reqctr; - /** Mount is active */ - unsigned mounted; - /** Connection established, cleared on umount, connection abort and device release */ unsigned connected; @@ -305,12 +324,18 @@ struct fuse_conn { /** Is removexattr not implemented by fs? */ unsigned no_removexattr : 1; + /** Are file locking primitives not implemented by fs? */ + unsigned no_lock : 1; + /** Is access not implemented by fs? */ unsigned no_access : 1; /** Is create not implemented by fs? */ unsigned no_create : 1; + /** Is interrupt not implemented by fs? */ + unsigned no_interrupt : 1; + /** The number of requests waiting for completion */ atomic_t num_waiting; @@ -320,11 +345,23 @@ struct fuse_conn { /** Backing dev info */ struct backing_dev_info bdi; - /** kobject */ - struct kobject kobj; + /** Entry on the fuse_conn_list */ + struct list_head entry; + + /** Unique ID */ + u64 id; + + /** Dentries in the control filesystem */ + struct dentry *ctl_dentry[FUSE_CTL_NUM_DENTRIES]; + + /** number of dentries used in the above array */ + int ctl_ndents; /** O_ASYNC requests */ struct fasync_struct *fasync; + + /** Key for lock owner ID scrambling */ + u32 scramble_key[4]; }; static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) @@ -337,11 +374,6 @@ static inline struct fuse_conn *get_fuse return get_fuse_conn_super(inode->i_sb); } -static inline struct fuse_conn *get_fuse_conn_kobj(struct kobject *obj) -{ - return container_of(obj, struct fuse_conn, kobj); -} - static inline struct fuse_inode *get_fuse_inode(struct inode *inode) { return container_of(inode, struct fuse_inode, inode); @@ -383,12 +415,9 @@ void fuse_file_free(struct fuse_file *ff void fuse_finish_open(struct inode *inode, struct file *file, struct fuse_file *ff, struct fuse_open_out *outarg); -/** - * Send a RELEASE request - */ -void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, - u64 nodeid, struct inode *inode, int flags, int isdir); - +/** */ +struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, + int opcode); /** * Send RELEASE or RELEASEDIR request */ @@ -435,6 +464,9 @@ int fuse_dev_init(void); */ void fuse_dev_cleanup(void); +int fuse_ctl_init(void); +void fuse_ctl_cleanup(void); + /** * Allocate a request */ @@ -446,14 +478,14 @@ struct fuse_req *fuse_request_alloc(void void fuse_request_free(struct fuse_req *req); /** - * Reinitialize a request, the preallocated flag is left unmodified + * Get a request, may fail with -ENOMEM */ -void fuse_reset_request(struct fuse_req *req); +struct fuse_req *fuse_get_req(struct fuse_conn *fc); /** - * Reserve a preallocated request + * Gets a requests for a file operation, always succeeds */ -struct fuse_req *fuse_get_req(struct fuse_conn *fc); +struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file); /** * Decrement reference count of a request. If count goes to zero free @@ -476,11 +508,6 @@ void request_send_noreply(struct fuse_co */ void request_send_background(struct fuse_conn *fc, struct fuse_req *req); -/** - * Release inodes and file associated with background request - */ -void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req); - /* Abort all requests */ void fuse_abort_conn(struct fuse_conn *fc); @@ -493,3 +520,23 @@ int fuse_do_getattr(struct inode *inode) * Invalidate inode attributes */ void fuse_invalidate_attr(struct inode *inode); + +/** + * Acquire reference to fuse_conn + */ +struct fuse_conn *fuse_conn_get(struct fuse_conn *fc); + +/** + * Release reference to fuse_conn + */ +void fuse_conn_put(struct fuse_conn *fc); + +/** + * Add connection to control filesystem + */ +int fuse_ctl_add_conn(struct fuse_conn *fc); + +/** + * Remove connection from control filesystem + */ +void fuse_ctl_remove_conn(struct fuse_conn *fc); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 7627022..dcaaabd 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -11,25 +11,20 @@ #include "fuse_i.h" #include #include #include -#include #include #include #include #include #include +#include MODULE_AUTHOR("Miklos Szeredi "); MODULE_DESCRIPTION("Filesystem in Userspace"); MODULE_LICENSE("GPL"); static kmem_cache_t *fuse_inode_cachep; -static struct subsystem connections_subsys; - -struct fuse_conn_attr { - struct attribute attr; - ssize_t (*show)(struct fuse_conn *, char *); - ssize_t (*store)(struct fuse_conn *, const char *, size_t); -}; +struct list_head fuse_conn_list; +DEFINE_MUTEX(fuse_mutex); #define FUSE_SUPER_MAGIC 0x65735546 @@ -104,6 +99,14 @@ static void fuse_clear_inode(struct inod } } +static int fuse_remount_fs(struct super_block *sb, int *flags, char *data) +{ + if (*flags & MS_MANDLOCK) + return -EINVAL; + + return 0; +} + void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr) { if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size) @@ -195,31 +198,29 @@ struct inode *fuse_iget(struct super_blo return inode; } -static void fuse_umount_begin(struct super_block *sb) +static void fuse_umount_begin(struct vfsmount *vfsmnt, int flags) { - fuse_abort_conn(get_fuse_conn_super(sb)); + if (flags & MNT_FORCE) + fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb)); } static void fuse_put_super(struct super_block *sb) { struct fuse_conn *fc = get_fuse_conn_super(sb); - down_write(&fc->sbput_sem); - while (!list_empty(&fc->background)) - fuse_release_background(fc, - list_entry(fc->background.next, - struct fuse_req, bg_entry)); - spin_lock(&fc->lock); - fc->mounted = 0; fc->connected = 0; + fc->blocked = 0; spin_unlock(&fc->lock); - up_write(&fc->sbput_sem); /* Flush all readers on this fs */ kill_fasync(&fc->fasync, SIGIO, POLL_IN); wake_up_all(&fc->waitq); - kobject_del(&fc->kobj); - kobject_put(&fc->kobj); + wake_up_all(&fc->blocked_waitq); + mutex_lock(&fuse_mutex); + list_del(&fc->entry); + fuse_ctl_remove_conn(fc); + mutex_unlock(&fuse_mutex); + fuse_conn_put(fc); } static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr) @@ -236,8 +237,9 @@ static void convert_fuse_statfs(struct k /* fsid is left zero */ } -static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) +static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; struct fuse_conn *fc = get_fuse_conn_super(sb); struct fuse_req *req; struct fuse_statfs_out outarg; @@ -368,11 +370,6 @@ static int fuse_show_options(struct seq_ return 0; } -static void fuse_conn_release(struct kobject *kobj) -{ - kfree(get_fuse_conn_kobj(kobj)); -} - static struct fuse_conn *new_conn(void) { struct fuse_conn *fc; @@ -380,24 +377,35 @@ static struct fuse_conn *new_conn(void) fc = kzalloc(sizeof(*fc), GFP_KERNEL); if (fc) { spin_lock_init(&fc->lock); + atomic_set(&fc->count, 1); init_waitqueue_head(&fc->waitq); init_waitqueue_head(&fc->blocked_waitq); INIT_LIST_HEAD(&fc->pending); INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->io); - INIT_LIST_HEAD(&fc->background); - init_rwsem(&fc->sbput_sem); - kobj_set_kset_s(fc, connections_subsys); - kobject_init(&fc->kobj); + INIT_LIST_HEAD(&fc->interrupts); atomic_set(&fc->num_waiting, 0); fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; fc->bdi.unplug_io_fn = default_unplug_io_fn; fc->reqctr = 0; fc->blocked = 1; + get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key)); } return fc; } +void fuse_conn_put(struct fuse_conn *fc) +{ + if (atomic_dec_and_test(&fc->count)) + kfree(fc); +} + +struct fuse_conn *fuse_conn_get(struct fuse_conn *fc) +{ + atomic_inc(&fc->count); + return fc; +} + static struct inode *get_root_inode(struct super_block *sb, unsigned mode) { struct fuse_attr attr; @@ -413,6 +421,7 @@ static struct super_operations fuse_supe .destroy_inode = fuse_destroy_inode, .read_inode = fuse_read_inode, .clear_inode = fuse_clear_inode, + .remount_fs = fuse_remount_fs, .put_super = fuse_put_super, .umount_begin = fuse_umount_begin, .statfs = fuse_statfs, @@ -432,8 +441,12 @@ static void process_init_reply(struct fu ra_pages = arg->max_readahead / PAGE_CACHE_SIZE; if (arg->flags & FUSE_ASYNC_READ) fc->async_read = 1; - } else + if (!(arg->flags & FUSE_POSIX_LOCKS)) + fc->no_lock = 1; + } else { ra_pages = fc->max_read / PAGE_CACHE_SIZE; + fc->no_lock = 1; + } fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages); fc->minor = arg->minor; @@ -451,7 +464,7 @@ static void fuse_send_init(struct fuse_c arg->major = FUSE_KERNEL_VERSION; arg->minor = FUSE_KERNEL_MINOR_VERSION; arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE; - arg->flags |= FUSE_ASYNC_READ; + arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS; req->in.h.opcode = FUSE_INIT; req->in.numargs = 1; req->in.args[0].size = sizeof(*arg); @@ -467,10 +480,9 @@ static void fuse_send_init(struct fuse_c request_send_background(fc, req); } -static unsigned long long conn_id(void) +static u64 conn_id(void) { - /* BKL is held for ->get_sb() */ - static unsigned long long ctr = 1; + static u64 ctr = 1; return ctr++; } @@ -484,6 +496,9 @@ static int fuse_fill_super(struct super_ struct fuse_req *init_req; int err; + if (sb->s_flags & MS_MANDLOCK) + return -EINVAL; + if (!parse_fuse_opt((char *) data, &d)) return -EINVAL; @@ -527,25 +542,21 @@ static int fuse_fill_super(struct super_ if (!init_req) goto err_put_root; - err = kobject_set_name(&fc->kobj, "%llu", conn_id()); - if (err) - goto err_free_req; - - err = kobject_add(&fc->kobj); - if (err) - goto err_free_req; - - /* Setting file->private_data can't race with other mount() - instances, since BKL is held for ->get_sb() */ + mutex_lock(&fuse_mutex); err = -EINVAL; if (file->private_data) - goto err_kobject_del; + goto err_unlock; + fc->id = conn_id(); + err = fuse_ctl_add_conn(fc); + if (err) + goto err_unlock; + + list_add_tail(&fc->entry, &fuse_conn_list); sb->s_root = root_dentry; - fc->mounted = 1; fc->connected = 1; - kobject_get(&fc->kobj); - file->private_data = fc; + file->private_data = fuse_conn_get(fc); + mutex_unlock(&fuse_mutex); /* * atomic_dec_and_test() in fput() provides the necessary * memory barrier for file->private_data to be visible on all @@ -557,23 +568,22 @@ static int fuse_fill_super(struct super_ return 0; - err_kobject_del: - kobject_del(&fc->kobj); - err_free_req: + err_unlock: + mutex_unlock(&fuse_mutex); fuse_request_free(init_req); err_put_root: dput(root_dentry); err: fput(file); - kobject_put(&fc->kobj); + fuse_conn_put(fc); return err; } -static struct super_block *fuse_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *raw_data) +static int fuse_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *raw_data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super); + return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt); } static struct file_system_type fuse_fs_type = { @@ -583,68 +593,8 @@ static struct file_system_type fuse_fs_t .kill_sb = kill_anon_super, }; -static ssize_t fuse_conn_waiting_show(struct fuse_conn *fc, char *page) -{ - return sprintf(page, "%i\n", atomic_read(&fc->num_waiting)); -} - -static ssize_t fuse_conn_abort_store(struct fuse_conn *fc, const char *page, - size_t count) -{ - fuse_abort_conn(fc); - return count; -} - -static struct fuse_conn_attr fuse_conn_waiting = - __ATTR(waiting, 0400, fuse_conn_waiting_show, NULL); -static struct fuse_conn_attr fuse_conn_abort = - __ATTR(abort, 0600, NULL, fuse_conn_abort_store); - -static struct attribute *fuse_conn_attrs[] = { - &fuse_conn_waiting.attr, - &fuse_conn_abort.attr, - NULL, -}; - -static ssize_t fuse_conn_attr_show(struct kobject *kobj, - struct attribute *attr, - char *page) -{ - struct fuse_conn_attr *fca = - container_of(attr, struct fuse_conn_attr, attr); - - if (fca->show) - return fca->show(get_fuse_conn_kobj(kobj), page); - else - return -EACCES; -} - -static ssize_t fuse_conn_attr_store(struct kobject *kobj, - struct attribute *attr, - const char *page, size_t count) -{ - struct fuse_conn_attr *fca = - container_of(attr, struct fuse_conn_attr, attr); - - if (fca->store) - return fca->store(get_fuse_conn_kobj(kobj), page, count); - else - return -EACCES; -} - -static struct sysfs_ops fuse_conn_sysfs_ops = { - .show = &fuse_conn_attr_show, - .store = &fuse_conn_attr_store, -}; - -static struct kobj_type ktype_fuse_conn = { - .release = fuse_conn_release, - .sysfs_ops = &fuse_conn_sysfs_ops, - .default_attrs = fuse_conn_attrs, -}; - static decl_subsys(fuse, NULL, NULL); -static decl_subsys(connections, &ktype_fuse_conn, NULL); +static decl_subsys(connections, NULL, NULL); static void fuse_inode_init_once(void *foo, kmem_cache_t *cachep, unsigned long flags) @@ -718,6 +668,7 @@ static int __init fuse_init(void) printk("fuse init (API version %i.%i)\n", FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); + INIT_LIST_HEAD(&fuse_conn_list); res = fuse_fs_init(); if (res) goto err; @@ -730,8 +681,14 @@ static int __init fuse_init(void) if (res) goto err_dev_cleanup; + res = fuse_ctl_init(); + if (res) + goto err_sysfs_cleanup; + return 0; + err_sysfs_cleanup: + fuse_sysfs_cleanup(); err_dev_cleanup: fuse_dev_cleanup(); err_fs_cleanup: @@ -744,6 +701,7 @@ static void __exit fuse_exit(void) { printk(KERN_DEBUG "fuse exit\n"); + fuse_ctl_cleanup(); fuse_sysfs_cleanup(); fuse_fs_cleanup(); fuse_dev_cleanup(); diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c index 1e44dcf..13231dd 100644 --- a/fs/hfs/bnode.c +++ b/fs/hfs/bnode.c @@ -280,7 +280,7 @@ static struct hfs_bnode *__hfs_bnode_cre block = off >> PAGE_CACHE_SHIFT; node->page_offset = off & ~PAGE_CACHE_MASK; for (i = 0; i < tree->pages_per_bnode; i++) { - page = read_cache_page(mapping, block++, (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, block++, NULL); if (IS_ERR(page)) goto fail; if (PageError(page)) { diff --git a/fs/hfs/btree.c b/fs/hfs/btree.c index d20131c..4003579 100644 --- a/fs/hfs/btree.c +++ b/fs/hfs/btree.c @@ -59,7 +59,7 @@ struct hfs_btree *hfs_btree_open(struct unlock_new_inode(tree->inode); mapping = tree->inode->i_mapping; - page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, 0, NULL); if (IS_ERR(page)) goto free_tree; diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index 3ed8663..735332d 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -182,8 +182,8 @@ extern void hfs_file_truncate(struct ino extern int hfs_get_block(struct inode *, sector_t, struct buffer_head *, int); /* inode.c */ -extern struct address_space_operations hfs_aops; -extern struct address_space_operations hfs_btree_aops; +extern const struct address_space_operations hfs_aops; +extern const struct address_space_operations hfs_btree_aops; extern struct inode *hfs_new_inode(struct inode *, struct qstr *, int); extern void hfs_inode_write_fork(struct inode *, struct hfs_extent *, __be32 *, __be32 *); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 2d4ced2..315cf44 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -114,7 +114,7 @@ static int hfs_writepages(struct address return mpage_writepages(mapping, wbc, hfs_get_block); } -struct address_space_operations hfs_btree_aops = { +const struct address_space_operations hfs_btree_aops = { .readpage = hfs_readpage, .writepage = hfs_writepage, .sync_page = block_sync_page, @@ -124,7 +124,7 @@ struct address_space_operations hfs_btre .releasepage = hfs_releasepage, }; -struct address_space_operations hfs_aops = { +const struct address_space_operations hfs_aops = { .readpage = hfs_readpage, .writepage = hfs_writepage, .sync_page = block_sync_page, diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 1181d11..34937ee 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -12,7 +12,6 @@ * Based on the minix file system code, (C) 1991, 1992 by Linus Torvalds */ -#include #include #include #include @@ -80,8 +79,10 @@ static void hfs_put_super(struct super_b * * changed f_files/f_ffree to reflect the fs_ablock/free_ablocks. */ -static int hfs_statfs(struct super_block *sb, struct kstatfs *buf) +static int hfs_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; + buf->f_type = HFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = (u32)HFS_SB(sb)->fs_ablocks * HFS_SB(sb)->fs_div; @@ -413,10 +414,11 @@ bail: return res; } -static struct super_block *hfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int hfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, hfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, hfs_fill_super, mnt); } static struct file_system_type hfs_fs_type = { diff --git a/fs/hfsplus/bitmap.c b/fs/hfsplus/bitmap.c index 9fb5163..d128a25 100644 --- a/fs/hfsplus/bitmap.c +++ b/fs/hfsplus/bitmap.c @@ -31,8 +31,7 @@ int hfsplus_block_allocate(struct super_ dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len); mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex); mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; - page = read_cache_page(mapping, offset / PAGE_CACHE_BITS, - (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL); pptr = kmap(page); curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32; i = offset % 32; @@ -72,8 +71,8 @@ int hfsplus_block_allocate(struct super_ offset += PAGE_CACHE_BITS; if (offset >= size) break; - page = read_cache_page(mapping, offset / PAGE_CACHE_BITS, - (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, + NULL); curr = pptr = kmap(page); if ((size ^ offset) / PAGE_CACHE_BITS) end = pptr + PAGE_CACHE_BITS / 32; @@ -119,8 +118,8 @@ found: set_page_dirty(page); kunmap(page); offset += PAGE_CACHE_BITS; - page = read_cache_page(mapping, offset / PAGE_CACHE_BITS, - (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, + NULL); pptr = kmap(page); curr = pptr; end = pptr + PAGE_CACHE_BITS / 32; @@ -167,7 +166,7 @@ int hfsplus_block_free(struct super_bloc mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex); mapping = HFSPLUS_SB(sb).alloc_file->i_mapping; pnr = offset / PAGE_CACHE_BITS; - page = read_cache_page(mapping, pnr, (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, pnr, NULL); pptr = kmap(page); curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32; end = pptr + PAGE_CACHE_BITS / 32; @@ -199,7 +198,7 @@ int hfsplus_block_free(struct super_bloc break; set_page_dirty(page); kunmap(page); - page = read_cache_page(mapping, ++pnr, (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, ++pnr, NULL); pptr = kmap(page); curr = pptr; end = pptr + PAGE_CACHE_BITS / 32; diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c index 746abc9..77bf434 100644 --- a/fs/hfsplus/bnode.c +++ b/fs/hfsplus/bnode.c @@ -440,7 +440,7 @@ static struct hfs_bnode *__hfs_bnode_cre block = off >> PAGE_CACHE_SHIFT; node->page_offset = off & ~PAGE_CACHE_MASK; for (i = 0; i < tree->pages_per_bnode; block++, i++) { - page = read_cache_page(mapping, block, (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, block, NULL); if (IS_ERR(page)) goto fail; if (PageError(page)) { diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c index effa899..cfc852f 100644 --- a/fs/hfsplus/btree.c +++ b/fs/hfsplus/btree.c @@ -38,7 +38,7 @@ struct hfs_btree *hfs_btree_open(struct goto free_tree; mapping = tree->inode->i_mapping; - page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, 0, NULL); if (IS_ERR(page)) goto free_tree; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 7ae3936..8a1ca5e 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -323,8 +323,8 @@ int hfsplus_file_extend(struct inode *); void hfsplus_file_truncate(struct inode *); /* inode.c */ -extern struct address_space_operations hfsplus_aops; -extern struct address_space_operations hfsplus_btree_aops; +extern const struct address_space_operations hfsplus_aops; +extern const struct address_space_operations hfsplus_btree_aops; void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *); void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *); diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index acf66db..924ecde 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -109,7 +109,7 @@ static int hfsplus_writepages(struct add return mpage_writepages(mapping, wbc, hfsplus_get_block); } -struct address_space_operations hfsplus_btree_aops = { +const struct address_space_operations hfsplus_btree_aops = { .readpage = hfsplus_readpage, .writepage = hfsplus_writepage, .sync_page = block_sync_page, @@ -119,7 +119,7 @@ struct address_space_operations hfsplus_ .releasepage = hfsplus_releasepage, }; -struct address_space_operations hfsplus_aops = { +const struct address_space_operations hfsplus_aops = { .readpage = hfsplus_readpage, .writepage = hfsplus_writepage, .sync_page = block_sync_page, diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 7843f79..d279d59 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -7,7 +7,6 @@ * */ -#include #include #include #include @@ -212,8 +211,10 @@ static void hfsplus_put_super(struct sup sb->s_fs_info = NULL; } -static int hfsplus_statfs(struct super_block *sb, struct kstatfs *buf) +static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; + buf->f_type = HFSPLUS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = HFSPLUS_SB(sb).total_blocks << HFSPLUS_SB(sb).fs_shift; @@ -450,10 +451,12 @@ static void hfsplus_destroy_inode(struct #define HFSPLUS_INODE_SIZE sizeof(struct hfsplus_inode_info) -static struct super_block *hfsplus_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int hfsplus_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super, + mnt); } static struct file_system_type hfsplus_fs_type = { diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index bf0f8e1..b82e3d9 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -54,7 +54,7 @@ #define HOSTFS_SUPER_MAGIC 0x00c0ffee static struct inode_operations hostfs_iops; static struct inode_operations hostfs_dir_iops; -static struct address_space_operations hostfs_link_aops; +static const struct address_space_operations hostfs_link_aops; #ifndef MODULE static int __init hostfs_args(char *options, int *add) @@ -239,7 +239,7 @@ static int read_inode(struct inode *ino) return(err); } -int hostfs_statfs(struct super_block *sb, struct kstatfs *sf) +int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) { /* do_statfs uses struct statfs64 internally, but the linux kernel * struct statfs still has 32-bit versions for most of these fields, @@ -252,7 +252,7 @@ int hostfs_statfs(struct super_block *sb long long f_files; long long f_ffree; - err = do_statfs(HOSTFS_I(sb->s_root->d_inode)->host_filename, + err = do_statfs(HOSTFS_I(dentry->d_sb->s_root->d_inode)->host_filename, &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), &sf->f_namelen, sf->f_spare); @@ -518,7 +518,7 @@ int hostfs_commit_write(struct file *fil return(err); } -static struct address_space_operations hostfs_aops = { +static const struct address_space_operations hostfs_aops = { .writepage = hostfs_writepage, .readpage = hostfs_readpage, .set_page_dirty = __set_page_dirty_nobuffers, @@ -935,7 +935,7 @@ int hostfs_link_readpage(struct file *fi return(err); } -static struct address_space_operations hostfs_link_aops = { +static const struct address_space_operations hostfs_link_aops = { .readpage = hostfs_link_readpage, }; @@ -993,11 +993,11 @@ static int hostfs_fill_sb_common(struct return(err); } -static struct super_block *hostfs_read_sb(struct file_system_type *type, - int flags, const char *dev_name, - void *data) +static int hostfs_read_sb(struct file_system_type *type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return(get_sb_nodev(type, flags, data, hostfs_fill_sb_common)); + return get_sb_nodev(type, flags, data, hostfs_fill_sb_common, mnt); } static struct file_system_type hostfs_type = { diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index d3b9fff..d9eb19b 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -99,7 +99,7 @@ static sector_t _hpfs_bmap(struct addres { return generic_block_bmap(mapping,block,hpfs_get_block); } -struct address_space_operations hpfs_aops = { +const struct address_space_operations hpfs_aops = { .readpage = hpfs_readpage, .writepage = hpfs_writepage, .sync_page = block_sync_page, diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 29b7a3e..f687d54 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -268,7 +268,7 @@ void hpfs_set_ea(struct inode *, struct int hpfs_file_fsync(struct file *, struct dentry *, int); extern const struct file_operations hpfs_file_ops; extern struct inode_operations hpfs_file_iops; -extern struct address_space_operations hpfs_aops; +extern const struct address_space_operations hpfs_aops; /* inode.c */ @@ -304,7 +304,7 @@ void hpfs_decide_conv(struct inode *, un /* namei.c */ extern struct inode_operations hpfs_dir_iops; -extern struct address_space_operations hpfs_symlink_aops; +extern const struct address_space_operations hpfs_symlink_aops; static inline struct hpfs_inode_info *hpfs_i(struct inode *inode) { diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c index a03abb1..59e7dc1 100644 --- a/fs/hpfs/namei.c +++ b/fs/hpfs/namei.c @@ -538,7 +538,7 @@ fail: return err; } -struct address_space_operations hpfs_symlink_aops = { +const struct address_space_operations hpfs_symlink_aops = { .readpage = hpfs_symlink_readpage }; diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index d72d8c8..f798480 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -135,8 +135,9 @@ static unsigned count_bitmaps(struct sup return count; } -static int hpfs_statfs(struct super_block *s, struct kstatfs *buf) +static int hpfs_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *s = dentry->d_sb; struct hpfs_sb_info *sbi = hpfs_sb(s); lock_kernel(); @@ -662,10 +663,11 @@ bail0: return -EINVAL; } -static struct super_block *hpfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int hpfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, hpfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, hpfs_fill_super, + mnt); } static struct file_system_type hpfs_fs_type = { diff --git a/fs/hppfs/hppfs_kern.c b/fs/hppfs/hppfs_kern.c index 5e6363b..3a9bdf5 100644 --- a/fs/hppfs/hppfs_kern.c +++ b/fs/hppfs/hppfs_kern.c @@ -616,7 +616,7 @@ static const struct file_operations hppf .fsync = hppfs_fsync, }; -static int hppfs_statfs(struct super_block *sb, struct kstatfs *sf) +static int hppfs_statfs(struct dentry *dentry, struct kstatfs *sf) { sf->f_blocks = 0; sf->f_bfree = 0; @@ -769,11 +769,11 @@ static int hppfs_fill_super(struct super return(err); } -static struct super_block *hppfs_read_super(struct file_system_type *type, - int flags, const char *dev_name, - void *data) +static int hppfs_read_super(struct file_system_type *type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return(get_sb_nodev(type, flags, data, hppfs_fill_super)); + return get_sb_nodev(type, flags, data, hppfs_fill_super, mnt); } static struct file_system_type hppfs_type = { diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 3a5b4e9..6449cb6 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -34,7 +34,7 @@ #include #define HUGETLBFS_MAGIC 0x958458f6 static struct super_operations hugetlbfs_ops; -static struct address_space_operations hugetlbfs_aops; +static const struct address_space_operations hugetlbfs_aops; const struct file_operations hugetlbfs_file_operations; static struct inode_operations hugetlbfs_dir_inode_operations; static struct inode_operations hugetlbfs_inode_operations; @@ -59,7 +59,6 @@ static void huge_pagevec_release(struct static int hugetlbfs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file->f_dentry->d_inode; - struct hugetlbfs_inode_info *info = HUGETLBFS_I(inode); loff_t len, vma_len; int ret; @@ -87,9 +86,10 @@ static int hugetlbfs_file_mmap(struct fi if (!(vma->vm_flags & VM_WRITE) && len > inode->i_size) goto out; - if (vma->vm_flags & VM_MAYSHARE) - if (hugetlb_extend_reservation(info, len >> HPAGE_SHIFT) != 0) - goto out; + if (vma->vm_flags & VM_MAYSHARE && + hugetlb_reserve_pages(inode, vma->vm_pgoff >> (HPAGE_SHIFT-PAGE_SHIFT), + len >> HPAGE_SHIFT)) + goto out; ret = 0; hugetlb_prefault_arch_hook(vma->vm_mm); @@ -195,12 +195,8 @@ static void truncate_hugepages(struct in const pgoff_t start = lstart >> HPAGE_SHIFT; struct pagevec pvec; pgoff_t next; - int i; + int i, freed = 0; - hugetlb_truncate_reservation(HUGETLBFS_I(inode), - lstart >> HPAGE_SHIFT); - if (!mapping->nrpages) - return; pagevec_init(&pvec, 0); next = start; while (1) { @@ -221,10 +217,12 @@ static void truncate_hugepages(struct in truncate_huge_page(page); unlock_page(page); hugetlb_put_quota(mapping); + freed++; } huge_pagevec_release(&pvec); } BUG_ON(!lstart && mapping->nrpages); + hugetlb_unreserve_pages(inode, start, freed); } static void hugetlbfs_delete_inode(struct inode *inode) @@ -366,6 +364,7 @@ static struct inode *hugetlbfs_get_inode inode->i_mapping->a_ops = &hugetlbfs_aops; inode->i_mapping->backing_dev_info =&hugetlbfs_backing_dev_info; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + INIT_LIST_HEAD(&inode->i_mapping->private_list); info = HUGETLBFS_I(inode); mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, NULL); switch (mode & S_IFMT) { @@ -467,9 +466,9 @@ static int hugetlbfs_set_page_dirty(stru return 0; } -static int hugetlbfs_statfs(struct super_block *sb, struct kstatfs *buf) +static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(sb); + struct hugetlbfs_sb_info *sbinfo = HUGETLBFS_SB(dentry->d_sb); buf->f_type = HUGETLBFS_MAGIC; buf->f_bsize = HPAGE_SIZE; @@ -538,7 +537,6 @@ static struct inode *hugetlbfs_alloc_ino hugetlbfs_inc_free_inodes(sbinfo); return NULL; } - p->prereserved_hpages = 0; return &p->vfs_inode; } @@ -549,7 +547,7 @@ static void hugetlbfs_destroy_inode(stru kmem_cache_free(hugetlbfs_inode_cachep, HUGETLBFS_I(inode)); } -static struct address_space_operations hugetlbfs_aops = { +static const struct address_space_operations hugetlbfs_aops = { .readpage = hugetlbfs_readpage, .prepare_write = hugetlbfs_prepare_write, .commit_write = hugetlbfs_commit_write, @@ -723,10 +721,10 @@ void hugetlb_put_quota(struct address_sp } } -static struct super_block *hugetlbfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int hugetlbfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super); + return get_sb_nodev(fs_type, flags, data, hugetlbfs_fill_super, mnt); } static struct file_system_type hugetlbfs_fs_type = { @@ -781,8 +779,7 @@ struct file *hugetlb_zero_setup(size_t s goto out_file; error = -ENOMEM; - if (hugetlb_extend_reservation(HUGETLBFS_I(inode), - size >> HPAGE_SHIFT) != 0) + if (hugetlb_reserve_pages(inode, 0, size >> HPAGE_SHIFT)) goto out_inode; d_instantiate(dentry, inode); diff --git a/fs/inode.c b/fs/inode.c index 3a2446a..0bf9f04 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -4,7 +4,6 @@ * (C) 1997 Linus Torvalds */ -#include #include #include #include @@ -102,7 +101,7 @@ static kmem_cache_t * inode_cachep __rea static struct inode *alloc_inode(struct super_block *sb) { - static struct address_space_operations empty_aops; + static const struct address_space_operations empty_aops; static struct inode_operations empty_iops; static const struct file_operations empty_fops; struct inode *inode; @@ -452,15 +451,14 @@ static void prune_icache(int nr_to_scan) nr_pruned++; } inodes_stat.nr_unused -= nr_pruned; + if (current_is_kswapd()) + __count_vm_events(KSWAPD_INODESTEAL, reap); + else + __count_vm_events(PGINODESTEAL, reap); spin_unlock(&inode_lock); dispose_list(&freeable); mutex_unlock(&iprune_mutex); - - if (current_is_kswapd()) - mod_page_state(kswapd_inodesteal, reap); - else - mod_page_state(pginodesteal, reap); } /* diff --git a/fs/inotify.c b/fs/inotify.c index 732ec4b..723836a 100644 --- a/fs/inotify.c +++ b/fs/inotify.c @@ -5,7 +5,10 @@ * John McCutchan * Robert Love * + * Kernel API added by: Amy Griffis + * * Copyright (C) 2005 John McCutchan + * Copyright 2006 Hewlett-Packard Development Company, L.P. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -20,35 +23,17 @@ #include #include -#include #include #include #include #include -#include -#include -#include -#include #include #include #include #include -#include - -#include static atomic_t inotify_cookie; -static kmem_cache_t *watch_cachep __read_mostly; -static kmem_cache_t *event_cachep __read_mostly; - -static struct vfsmount *inotify_mnt __read_mostly; - -/* these are configurable via /proc/sys/fs/inotify/ */ -int inotify_max_user_instances __read_mostly; -int inotify_max_user_watches __read_mostly; -int inotify_max_queued_events __read_mostly; - /* * Lock ordering: * @@ -56,327 +41,108 @@ int inotify_max_queued_events __read_mos * iprune_mutex (synchronize shrink_icache_memory()) * inode_lock (protects the super_block->s_inodes list) * inode->inotify_mutex (protects inode->inotify_watches and watches->i_list) - * inotify_dev->mutex (protects inotify_device and watches->d_list) + * inotify_handle->mutex (protects inotify_handle and watches->h_list) + * + * The inode->inotify_mutex and inotify_handle->mutex and held during execution + * of a caller's event handler. Thus, the caller must not hold any locks + * taken in their event handler while calling any of the published inotify + * interfaces. */ /* - * Lifetimes of the three main data structures--inotify_device, inode, and + * Lifetimes of the three main data structures--inotify_handle, inode, and * inotify_watch--are managed by reference count. * - * inotify_device: Lifetime is from inotify_init() until release. Additional - * references can bump the count via get_inotify_dev() and drop the count via - * put_inotify_dev(). + * inotify_handle: Lifetime is from inotify_init() to inotify_destroy(). + * Additional references can bump the count via get_inotify_handle() and drop + * the count via put_inotify_handle(). * - * inotify_watch: Lifetime is from create_watch() to destory_watch(). - * Additional references can bump the count via get_inotify_watch() and drop - * the count via put_inotify_watch(). + * inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch() + * to remove_watch_no_event(). Additional references can bump the count via + * get_inotify_watch() and drop the count via put_inotify_watch(). The caller + * is reponsible for the final put after receiving IN_IGNORED, or when using + * IN_ONESHOT after receiving the first event. Inotify does the final put if + * inotify_destroy() is called. * * inode: Pinned so long as the inode is associated with a watch, from - * create_watch() to put_inotify_watch(). + * inotify_add_watch() to the final put_inotify_watch(). */ /* - * struct inotify_device - represents an inotify instance + * struct inotify_handle - represents an inotify instance * * This structure is protected by the mutex 'mutex'. */ -struct inotify_device { - wait_queue_head_t wq; /* wait queue for i/o */ +struct inotify_handle { struct idr idr; /* idr mapping wd -> watch */ struct mutex mutex; /* protects this bad boy */ - struct list_head events; /* list of queued events */ struct list_head watches; /* list of watches */ atomic_t count; /* reference count */ - struct user_struct *user; /* user who opened this dev */ - unsigned int queue_size; /* size of the queue (bytes) */ - unsigned int event_count; /* number of pending events */ - unsigned int max_events; /* maximum number of events */ u32 last_wd; /* the last wd allocated */ + const struct inotify_operations *in_ops; /* inotify caller operations */ }; -/* - * struct inotify_kernel_event - An inotify event, originating from a watch and - * queued for user-space. A list of these is attached to each instance of the - * device. In read(), this list is walked and all events that can fit in the - * buffer are returned. - * - * Protected by dev->mutex of the device in which we are queued. - */ -struct inotify_kernel_event { - struct inotify_event event; /* the user-space event */ - struct list_head list; /* entry in inotify_device's list */ - char *name; /* filename, if any */ -}; - -/* - * struct inotify_watch - represents a watch request on a specific inode - * - * d_list is protected by dev->mutex of the associated watch->dev. - * i_list and mask are protected by inode->inotify_mutex of the associated inode. - * dev, inode, and wd are never written to once the watch is created. - */ -struct inotify_watch { - struct list_head d_list; /* entry in inotify_device's list */ - struct list_head i_list; /* entry in inode's list */ - atomic_t count; /* reference count */ - struct inotify_device *dev; /* associated device */ - struct inode *inode; /* associated inode */ - s32 wd; /* watch descriptor */ - u32 mask; /* event mask for this watch */ -}; - -#ifdef CONFIG_SYSCTL - -#include - -static int zero; - -ctl_table inotify_table[] = { - { - .ctl_name = INOTIFY_MAX_USER_INSTANCES, - .procname = "max_user_instances", - .data = &inotify_max_user_instances, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, - .extra1 = &zero, - }, - { - .ctl_name = INOTIFY_MAX_USER_WATCHES, - .procname = "max_user_watches", - .data = &inotify_max_user_watches, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, - .extra1 = &zero, - }, - { - .ctl_name = INOTIFY_MAX_QUEUED_EVENTS, - .procname = "max_queued_events", - .data = &inotify_max_queued_events, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .strategy = &sysctl_intvec, - .extra1 = &zero - }, - { .ctl_name = 0 } -}; -#endif /* CONFIG_SYSCTL */ - -static inline void get_inotify_dev(struct inotify_device *dev) +static inline void get_inotify_handle(struct inotify_handle *ih) { - atomic_inc(&dev->count); + atomic_inc(&ih->count); } -static inline void put_inotify_dev(struct inotify_device *dev) +static inline void put_inotify_handle(struct inotify_handle *ih) { - if (atomic_dec_and_test(&dev->count)) { - atomic_dec(&dev->user->inotify_devs); - free_uid(dev->user); - idr_destroy(&dev->idr); - kfree(dev); + if (atomic_dec_and_test(&ih->count)) { + idr_destroy(&ih->idr); + kfree(ih); } } -static inline void get_inotify_watch(struct inotify_watch *watch) +/** + * get_inotify_watch - grab a reference to an inotify_watch + * @watch: watch to grab + */ +void get_inotify_watch(struct inotify_watch *watch) { atomic_inc(&watch->count); } +EXPORT_SYMBOL_GPL(get_inotify_watch); -/* +/** * put_inotify_watch - decrements the ref count on a given watch. cleans up - * the watch and its references if the count reaches zero. + * watch references if the count reaches zero. inotify_watch is freed by + * inotify callers via the destroy_watch() op. + * @watch: watch to release */ -static inline void put_inotify_watch(struct inotify_watch *watch) +void put_inotify_watch(struct inotify_watch *watch) { if (atomic_dec_and_test(&watch->count)) { - put_inotify_dev(watch->dev); - iput(watch->inode); - kmem_cache_free(watch_cachep, watch); - } -} - -/* - * kernel_event - create a new kernel event with the given parameters - * - * This function can sleep. - */ -static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie, - const char *name) -{ - struct inotify_kernel_event *kevent; - - kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL); - if (unlikely(!kevent)) - return NULL; - - /* we hand this out to user-space, so zero it just in case */ - memset(&kevent->event, 0, sizeof(struct inotify_event)); - - kevent->event.wd = wd; - kevent->event.mask = mask; - kevent->event.cookie = cookie; - - INIT_LIST_HEAD(&kevent->list); - - if (name) { - size_t len, rem, event_size = sizeof(struct inotify_event); - - /* - * We need to pad the filename so as to properly align an - * array of inotify_event structures. Because the structure is - * small and the common case is a small filename, we just round - * up to the next multiple of the structure's sizeof. This is - * simple and safe for all architectures. - */ - len = strlen(name) + 1; - rem = event_size - len; - if (len > event_size) { - rem = event_size - (len % event_size); - if (len % event_size == 0) - rem = 0; - } - - kevent->name = kmalloc(len + rem, GFP_KERNEL); - if (unlikely(!kevent->name)) { - kmem_cache_free(event_cachep, kevent); - return NULL; - } - memcpy(kevent->name, name, len); - if (rem) - memset(kevent->name + len, 0, rem); - kevent->event.len = len + rem; - } else { - kevent->event.len = 0; - kevent->name = NULL; - } - - return kevent; -} - -/* - * inotify_dev_get_event - return the next event in the given dev's queue - * - * Caller must hold dev->mutex. - */ -static inline struct inotify_kernel_event * -inotify_dev_get_event(struct inotify_device *dev) -{ - return list_entry(dev->events.next, struct inotify_kernel_event, list); -} - -/* - * inotify_dev_queue_event - add a new event to the given device - * - * Caller must hold dev->mutex. Can sleep (calls kernel_event()). - */ -static void inotify_dev_queue_event(struct inotify_device *dev, - struct inotify_watch *watch, u32 mask, - u32 cookie, const char *name) -{ - struct inotify_kernel_event *kevent, *last; - - /* coalescing: drop this event if it is a dupe of the previous */ - last = inotify_dev_get_event(dev); - if (last && last->event.mask == mask && last->event.wd == watch->wd && - last->event.cookie == cookie) { - const char *lastname = last->name; - - if (!name && !lastname) - return; - if (name && lastname && !strcmp(lastname, name)) - return; - } - - /* the queue overflowed and we already sent the Q_OVERFLOW event */ - if (unlikely(dev->event_count > dev->max_events)) - return; - - /* if the queue overflows, we need to notify user space */ - if (unlikely(dev->event_count == dev->max_events)) - kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL); - else - kevent = kernel_event(watch->wd, mask, cookie, name); - - if (unlikely(!kevent)) - return; - - /* queue the event and wake up anyone waiting */ - dev->event_count++; - dev->queue_size += sizeof(struct inotify_event) + kevent->event.len; - list_add_tail(&kevent->list, &dev->events); - wake_up_interruptible(&dev->wq); -} - -/* - * remove_kevent - cleans up and ultimately frees the given kevent - * - * Caller must hold dev->mutex. - */ -static void remove_kevent(struct inotify_device *dev, - struct inotify_kernel_event *kevent) -{ - list_del(&kevent->list); - - dev->event_count--; - dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len; - - kfree(kevent->name); - kmem_cache_free(event_cachep, kevent); -} + struct inotify_handle *ih = watch->ih; -/* - * inotify_dev_event_dequeue - destroy an event on the given device - * - * Caller must hold dev->mutex. - */ -static void inotify_dev_event_dequeue(struct inotify_device *dev) -{ - if (!list_empty(&dev->events)) { - struct inotify_kernel_event *kevent; - kevent = inotify_dev_get_event(dev); - remove_kevent(dev, kevent); + iput(watch->inode); + ih->in_ops->destroy_watch(watch); + put_inotify_handle(ih); } } +EXPORT_SYMBOL_GPL(put_inotify_watch); /* - * inotify_dev_get_wd - returns the next WD for use by the given dev + * inotify_handle_get_wd - returns the next WD for use by the given handle * - * Callers must hold dev->mutex. This function can sleep. + * Callers must hold ih->mutex. This function can sleep. */ -static int inotify_dev_get_wd(struct inotify_device *dev, - struct inotify_watch *watch) +static int inotify_handle_get_wd(struct inotify_handle *ih, + struct inotify_watch *watch) { int ret; do { - if (unlikely(!idr_pre_get(&dev->idr, GFP_KERNEL))) + if (unlikely(!idr_pre_get(&ih->idr, GFP_KERNEL))) return -ENOSPC; - ret = idr_get_new_above(&dev->idr, watch, dev->last_wd+1, &watch->wd); + ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd); } while (ret == -EAGAIN); - return ret; -} + if (likely(!ret)) + ih->last_wd = watch->wd; -/* - * find_inode - resolve a user-given path to a specific inode and return a nd - */ -static int find_inode(const char __user *dirname, struct nameidata *nd, - unsigned flags) -{ - int error; - - error = __user_walk(dirname, flags, nd); - if (error) - return error; - /* you can only watch an inode if you have read permissions on it */ - error = vfs_permission(nd, MAY_READ); - if (error) - path_release(nd); - return error; + return ret; } /* @@ -422,67 +188,18 @@ static void set_dentry_child_flags(struc } /* - * create_watch - creates a watch on the given device. - * - * Callers must hold dev->mutex. Calls inotify_dev_get_wd() so may sleep. - * Both 'dev' and 'inode' (by way of nameidata) need to be pinned. - */ -static struct inotify_watch *create_watch(struct inotify_device *dev, - u32 mask, struct inode *inode) -{ - struct inotify_watch *watch; - int ret; - - if (atomic_read(&dev->user->inotify_watches) >= - inotify_max_user_watches) - return ERR_PTR(-ENOSPC); - - watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL); - if (unlikely(!watch)) - return ERR_PTR(-ENOMEM); - - ret = inotify_dev_get_wd(dev, watch); - if (unlikely(ret)) { - kmem_cache_free(watch_cachep, watch); - return ERR_PTR(ret); - } - - dev->last_wd = watch->wd; - watch->mask = mask; - atomic_set(&watch->count, 0); - INIT_LIST_HEAD(&watch->d_list); - INIT_LIST_HEAD(&watch->i_list); - - /* save a reference to device and bump the count to make it official */ - get_inotify_dev(dev); - watch->dev = dev; - - /* - * Save a reference to the inode and bump the ref count to make it - * official. We hold a reference to nameidata, which makes this safe. - */ - watch->inode = igrab(inode); - - /* bump our own count, corresponding to our entry in dev->watches */ - get_inotify_watch(watch); - - atomic_inc(&dev->user->inotify_watches); - - return watch; -} - -/* - * inotify_find_dev - find the watch associated with the given inode and dev + * inotify_find_handle - find the watch associated with the given inode and + * handle * * Callers must hold inode->inotify_mutex. */ -static struct inotify_watch *inode_find_dev(struct inode *inode, - struct inotify_device *dev) +static struct inotify_watch *inode_find_handle(struct inode *inode, + struct inotify_handle *ih) { struct inotify_watch *watch; list_for_each_entry(watch, &inode->inotify_watches, i_list) { - if (watch->dev == dev) + if (watch->ih == ih) return watch; } @@ -490,40 +207,40 @@ static struct inotify_watch *inode_find_ } /* - * remove_watch_no_event - remove_watch() without the IN_IGNORED event. + * remove_watch_no_event - remove watch without the IN_IGNORED event. + * + * Callers must hold both inode->inotify_mutex and ih->mutex. */ static void remove_watch_no_event(struct inotify_watch *watch, - struct inotify_device *dev) + struct inotify_handle *ih) { list_del(&watch->i_list); - list_del(&watch->d_list); + list_del(&watch->h_list); if (!inotify_inode_watched(watch->inode)) set_dentry_child_flags(watch->inode, 0); - atomic_dec(&dev->user->inotify_watches); - idr_remove(&dev->idr, watch->wd); - put_inotify_watch(watch); + idr_remove(&ih->idr, watch->wd); } -/* - * remove_watch - Remove a watch from both the device and the inode. Sends - * the IN_IGNORED event to the given device signifying that the inode is no - * longer watched. - * - * Callers must hold both inode->inotify_mutex and dev->mutex. We drop a - * reference to the inode before returning. +/** + * inotify_remove_watch_locked - Remove a watch from both the handle and the + * inode. Sends the IN_IGNORED event signifying that the inode is no longer + * watched. May be invoked from a caller's event handler. + * @ih: inotify handle associated with watch + * @watch: watch to remove * - * The inode is not iput() so as to remain atomic. If the inode needs to be - * iput(), the call returns one. Otherwise, it returns zero. + * Callers must hold both inode->inotify_mutex and ih->mutex. */ -static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev) +void inotify_remove_watch_locked(struct inotify_handle *ih, + struct inotify_watch *watch) { - inotify_dev_queue_event(dev, watch, IN_IGNORED, 0, NULL); - remove_watch_no_event(watch, dev); + remove_watch_no_event(watch, ih); + ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL); } +EXPORT_SYMBOL_GPL(inotify_remove_watch_locked); -/* Kernel API */ +/* Kernel API for producing events */ /* * inotify_d_instantiate - instantiate dcache entry for inode @@ -563,9 +280,10 @@ void inotify_d_move(struct dentry *entry * @mask: event mask describing this event * @cookie: cookie for synchronization, or zero * @name: filename, if any + * @n_inode: inode associated with name */ void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie, - const char *name) + const char *name, struct inode *n_inode) { struct inotify_watch *watch, *next; @@ -576,14 +294,13 @@ void inotify_inode_queue_event(struct in list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { u32 watch_mask = watch->mask; if (watch_mask & mask) { - struct inotify_device *dev = watch->dev; - get_inotify_watch(watch); - mutex_lock(&dev->mutex); - inotify_dev_queue_event(dev, watch, mask, cookie, name); + struct inotify_handle *ih= watch->ih; + mutex_lock(&ih->mutex); if (watch_mask & IN_ONESHOT) - remove_watch_no_event(watch, dev); - mutex_unlock(&dev->mutex); - put_inotify_watch(watch); + remove_watch_no_event(watch, ih); + ih->in_ops->handle_event(watch, watch->wd, mask, cookie, + name, n_inode); + mutex_unlock(&ih->mutex); } } mutex_unlock(&inode->inotify_mutex); @@ -613,7 +330,8 @@ void inotify_dentry_parent_queue_event(s if (inotify_inode_watched(inode)) { dget(parent); spin_unlock(&dentry->d_lock); - inotify_inode_queue_event(inode, mask, cookie, name); + inotify_inode_queue_event(inode, mask, cookie, name, + dentry->d_inode); dput(parent); } else spin_unlock(&dentry->d_lock); @@ -665,7 +383,7 @@ void inotify_unmount_inodes(struct list_ need_iput_tmp = need_iput; need_iput = NULL; - /* In case the remove_watch() drops a reference. */ + /* In case inotify_remove_watch_locked() drops a reference. */ if (inode != need_iput_tmp) __iget(inode); else @@ -694,11 +412,12 @@ void inotify_unmount_inodes(struct list_ mutex_lock(&inode->inotify_mutex); watches = &inode->inotify_watches; list_for_each_entry_safe(watch, next_w, watches, i_list) { - struct inotify_device *dev = watch->dev; - mutex_lock(&dev->mutex); - inotify_dev_queue_event(dev, watch, IN_UNMOUNT,0,NULL); - remove_watch(watch, dev); - mutex_unlock(&dev->mutex); + struct inotify_handle *ih= watch->ih; + mutex_lock(&ih->mutex); + ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0, + NULL, NULL); + inotify_remove_watch_locked(ih, watch); + mutex_unlock(&ih->mutex); } mutex_unlock(&inode->inotify_mutex); iput(inode); @@ -718,432 +437,292 @@ void inotify_inode_is_dead(struct inode mutex_lock(&inode->inotify_mutex); list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { - struct inotify_device *dev = watch->dev; - mutex_lock(&dev->mutex); - remove_watch(watch, dev); - mutex_unlock(&dev->mutex); + struct inotify_handle *ih = watch->ih; + mutex_lock(&ih->mutex); + inotify_remove_watch_locked(ih, watch); + mutex_unlock(&ih->mutex); } mutex_unlock(&inode->inotify_mutex); } EXPORT_SYMBOL_GPL(inotify_inode_is_dead); -/* Device Interface */ +/* Kernel Consumer API */ -static unsigned int inotify_poll(struct file *file, poll_table *wait) +/** + * inotify_init - allocate and initialize an inotify instance + * @ops: caller's inotify operations + */ +struct inotify_handle *inotify_init(const struct inotify_operations *ops) { - struct inotify_device *dev = file->private_data; - int ret = 0; + struct inotify_handle *ih; - poll_wait(file, &dev->wq, wait); - mutex_lock(&dev->mutex); - if (!list_empty(&dev->events)) - ret = POLLIN | POLLRDNORM; - mutex_unlock(&dev->mutex); + ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL); + if (unlikely(!ih)) + return ERR_PTR(-ENOMEM); - return ret; + idr_init(&ih->idr); + INIT_LIST_HEAD(&ih->watches); + mutex_init(&ih->mutex); + ih->last_wd = 0; + ih->in_ops = ops; + atomic_set(&ih->count, 0); + get_inotify_handle(ih); + + return ih; } +EXPORT_SYMBOL_GPL(inotify_init); -static ssize_t inotify_read(struct file *file, char __user *buf, - size_t count, loff_t *pos) +/** + * inotify_init_watch - initialize an inotify watch + * @watch: watch to initialize + */ +void inotify_init_watch(struct inotify_watch *watch) { - size_t event_size = sizeof (struct inotify_event); - struct inotify_device *dev; - char __user *start; - int ret; - DEFINE_WAIT(wait); - - start = buf; - dev = file->private_data; - - while (1) { - int events; - - prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE); - - mutex_lock(&dev->mutex); - events = !list_empty(&dev->events); - mutex_unlock(&dev->mutex); - if (events) { - ret = 0; - break; - } - - if (file->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } - - if (signal_pending(current)) { - ret = -EINTR; - break; - } - - schedule(); - } - - finish_wait(&dev->wq, &wait); - if (ret) - return ret; - - mutex_lock(&dev->mutex); - while (1) { - struct inotify_kernel_event *kevent; - - ret = buf - start; - if (list_empty(&dev->events)) - break; - - kevent = inotify_dev_get_event(dev); - if (event_size + kevent->event.len > count) - break; - - if (copy_to_user(buf, &kevent->event, event_size)) { - ret = -EFAULT; - break; - } - buf += event_size; - count -= event_size; - - if (kevent->name) { - if (copy_to_user(buf, kevent->name, kevent->event.len)){ - ret = -EFAULT; - break; - } - buf += kevent->event.len; - count -= kevent->event.len; - } - - remove_kevent(dev, kevent); - } - mutex_unlock(&dev->mutex); - - return ret; + INIT_LIST_HEAD(&watch->h_list); + INIT_LIST_HEAD(&watch->i_list); + atomic_set(&watch->count, 0); + get_inotify_watch(watch); /* initial get */ } +EXPORT_SYMBOL_GPL(inotify_init_watch); -static int inotify_release(struct inode *ignored, struct file *file) +/** + * inotify_destroy - clean up and destroy an inotify instance + * @ih: inotify handle + */ +void inotify_destroy(struct inotify_handle *ih) { - struct inotify_device *dev = file->private_data; - /* - * Destroy all of the watches on this device. Unfortunately, not very + * Destroy all of the watches for this handle. Unfortunately, not very * pretty. We cannot do a simple iteration over the list, because we * do not know the inode until we iterate to the watch. But we need to - * hold inode->inotify_mutex before dev->mutex. The following works. + * hold inode->inotify_mutex before ih->mutex. The following works. */ while (1) { struct inotify_watch *watch; struct list_head *watches; struct inode *inode; - mutex_lock(&dev->mutex); - watches = &dev->watches; + mutex_lock(&ih->mutex); + watches = &ih->watches; if (list_empty(watches)) { - mutex_unlock(&dev->mutex); + mutex_unlock(&ih->mutex); break; } - watch = list_entry(watches->next, struct inotify_watch, d_list); + watch = list_entry(watches->next, struct inotify_watch, h_list); get_inotify_watch(watch); - mutex_unlock(&dev->mutex); + mutex_unlock(&ih->mutex); inode = watch->inode; mutex_lock(&inode->inotify_mutex); - mutex_lock(&dev->mutex); + mutex_lock(&ih->mutex); /* make sure we didn't race with another list removal */ - if (likely(idr_find(&dev->idr, watch->wd))) - remove_watch_no_event(watch, dev); + if (likely(idr_find(&ih->idr, watch->wd))) { + remove_watch_no_event(watch, ih); + put_inotify_watch(watch); + } - mutex_unlock(&dev->mutex); + mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex); put_inotify_watch(watch); } - /* destroy all of the events on this device */ - mutex_lock(&dev->mutex); - while (!list_empty(&dev->events)) - inotify_dev_event_dequeue(dev); - mutex_unlock(&dev->mutex); - - /* free this device: the put matching the get in inotify_init() */ - put_inotify_dev(dev); - - return 0; + /* free this handle: the put matching the get in inotify_init() */ + put_inotify_handle(ih); } +EXPORT_SYMBOL_GPL(inotify_destroy); -/* - * inotify_ignore - remove a given wd from this inotify instance. +/** + * inotify_find_watch - find an existing watch for an (ih,inode) pair + * @ih: inotify handle + * @inode: inode to watch + * @watchp: pointer to existing inotify_watch * - * Can sleep. + * Caller must pin given inode (via nameidata). */ -static int inotify_ignore(struct inotify_device *dev, s32 wd) +s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, + struct inotify_watch **watchp) { - struct inotify_watch *watch; - struct inode *inode; - - mutex_lock(&dev->mutex); - watch = idr_find(&dev->idr, wd); - if (unlikely(!watch)) { - mutex_unlock(&dev->mutex); - return -EINVAL; - } - get_inotify_watch(watch); - inode = watch->inode; - mutex_unlock(&dev->mutex); + struct inotify_watch *old; + int ret = -ENOENT; mutex_lock(&inode->inotify_mutex); - mutex_lock(&dev->mutex); + mutex_lock(&ih->mutex); - /* make sure that we did not race */ - if (likely(idr_find(&dev->idr, wd) == watch)) - remove_watch(watch, dev); + old = inode_find_handle(inode, ih); + if (unlikely(old)) { + get_inotify_watch(old); /* caller must put watch */ + *watchp = old; + ret = old->wd; + } - mutex_unlock(&dev->mutex); + mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex); - put_inotify_watch(watch); - return 0; + return ret; } +EXPORT_SYMBOL_GPL(inotify_find_watch); -static long inotify_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +/** + * inotify_find_update_watch - find and update the mask of an existing watch + * @ih: inotify handle + * @inode: inode's watch to update + * @mask: mask of events to watch + * + * Caller must pin given inode (via nameidata). + */ +s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode, + u32 mask) { - struct inotify_device *dev; - void __user *p; - int ret = -ENOTTY; - - dev = file->private_data; - p = (void __user *) arg; - - switch (cmd) { - case FIONREAD: - ret = put_user(dev->queue_size, (int __user *) p); - break; - } - - return ret; -} + struct inotify_watch *old; + int mask_add = 0; + int ret; -static const struct file_operations inotify_fops = { - .poll = inotify_poll, - .read = inotify_read, - .release = inotify_release, - .unlocked_ioctl = inotify_ioctl, - .compat_ioctl = inotify_ioctl, -}; + if (mask & IN_MASK_ADD) + mask_add = 1; -asmlinkage long sys_inotify_init(void) -{ - struct inotify_device *dev; - struct user_struct *user; - struct file *filp; - int fd, ret; - - fd = get_unused_fd(); - if (fd < 0) - return fd; - - filp = get_empty_filp(); - if (!filp) { - ret = -ENFILE; - goto out_put_fd; - } + /* don't allow invalid bits: we don't want flags set */ + mask &= IN_ALL_EVENTS | IN_ONESHOT; + if (unlikely(!mask)) + return -EINVAL; - user = get_uid(current->user); - if (unlikely(atomic_read(&user->inotify_devs) >= - inotify_max_user_instances)) { - ret = -EMFILE; - goto out_free_uid; - } + mutex_lock(&inode->inotify_mutex); + mutex_lock(&ih->mutex); - dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL); - if (unlikely(!dev)) { - ret = -ENOMEM; - goto out_free_uid; + /* + * Handle the case of re-adding a watch on an (inode,ih) pair that we + * are already watching. We just update the mask and return its wd. + */ + old = inode_find_handle(inode, ih); + if (unlikely(!old)) { + ret = -ENOENT; + goto out; } - filp->f_op = &inotify_fops; - filp->f_vfsmnt = mntget(inotify_mnt); - filp->f_dentry = dget(inotify_mnt->mnt_root); - filp->f_mapping = filp->f_dentry->d_inode->i_mapping; - filp->f_mode = FMODE_READ; - filp->f_flags = O_RDONLY; - filp->private_data = dev; - - idr_init(&dev->idr); - INIT_LIST_HEAD(&dev->events); - INIT_LIST_HEAD(&dev->watches); - init_waitqueue_head(&dev->wq); - mutex_init(&dev->mutex); - dev->event_count = 0; - dev->queue_size = 0; - dev->max_events = inotify_max_queued_events; - dev->user = user; - dev->last_wd = 0; - atomic_set(&dev->count, 0); - - get_inotify_dev(dev); - atomic_inc(&user->inotify_devs); - fd_install(fd, filp); - - return fd; -out_free_uid: - free_uid(user); - put_filp(filp); -out_put_fd: - put_unused_fd(fd); + if (mask_add) + old->mask |= mask; + else + old->mask = mask; + ret = old->wd; +out: + mutex_unlock(&ih->mutex); + mutex_unlock(&inode->inotify_mutex); return ret; } +EXPORT_SYMBOL_GPL(inotify_find_update_watch); -asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) +/** + * inotify_add_watch - add a watch to an inotify instance + * @ih: inotify handle + * @watch: caller allocated watch structure + * @inode: inode to watch + * @mask: mask of events to watch + * + * Caller must pin given inode (via nameidata). + * Caller must ensure it only calls inotify_add_watch() once per watch. + * Calls inotify_handle_get_wd() so may sleep. + */ +s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, + struct inode *inode, u32 mask) { - struct inotify_watch *watch, *old; - struct inode *inode; - struct inotify_device *dev; - struct nameidata nd; - struct file *filp; - int ret, fput_needed; - int mask_add = 0; - unsigned flags = 0; - - filp = fget_light(fd, &fput_needed); - if (unlikely(!filp)) - return -EBADF; - - /* verify that this is indeed an inotify instance */ - if (unlikely(filp->f_op != &inotify_fops)) { - ret = -EINVAL; - goto fput_and_out; - } - - if (!(mask & IN_DONT_FOLLOW)) - flags |= LOOKUP_FOLLOW; - if (mask & IN_ONLYDIR) - flags |= LOOKUP_DIRECTORY; - - ret = find_inode(path, &nd, flags); - if (unlikely(ret)) - goto fput_and_out; + int ret = 0; - /* inode held in place by reference to nd; dev by fget on fd */ - inode = nd.dentry->d_inode; - dev = filp->private_data; + /* don't allow invalid bits: we don't want flags set */ + mask &= IN_ALL_EVENTS | IN_ONESHOT; + if (unlikely(!mask)) + return -EINVAL; + watch->mask = mask; mutex_lock(&inode->inotify_mutex); - mutex_lock(&dev->mutex); - - if (mask & IN_MASK_ADD) - mask_add = 1; + mutex_lock(&ih->mutex); - /* don't let user-space set invalid bits: we don't want flags set */ - mask &= IN_ALL_EVENTS | IN_ONESHOT; - if (unlikely(!mask)) { - ret = -EINVAL; + /* Initialize a new watch */ + ret = inotify_handle_get_wd(ih, watch); + if (unlikely(ret)) goto out; - } + ret = watch->wd; + + /* save a reference to handle and bump the count to make it official */ + get_inotify_handle(ih); + watch->ih = ih; /* - * Handle the case of re-adding a watch on an (inode,dev) pair that we - * are already watching. We just update the mask and return its wd. + * Save a reference to the inode and bump the ref count to make it + * official. We hold a reference to nameidata, which makes this safe. */ - old = inode_find_dev(inode, dev); - if (unlikely(old)) { - if (mask_add) - old->mask |= mask; - else - old->mask = mask; - ret = old->wd; - goto out; - } - - watch = create_watch(dev, mask, inode); - if (unlikely(IS_ERR(watch))) { - ret = PTR_ERR(watch); - goto out; - } + watch->inode = igrab(inode); if (!inotify_inode_watched(inode)) set_dentry_child_flags(inode, 1); - /* Add the watch to the device's and the inode's list */ - list_add(&watch->d_list, &dev->watches); + /* Add the watch to the handle's and the inode's list */ + list_add(&watch->h_list, &ih->watches); list_add(&watch->i_list, &inode->inotify_watches); - ret = watch->wd; out: - mutex_unlock(&dev->mutex); + mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex); - path_release(&nd); -fput_and_out: - fput_light(filp, fput_needed); return ret; } +EXPORT_SYMBOL_GPL(inotify_add_watch); -asmlinkage long sys_inotify_rm_watch(int fd, u32 wd) +/** + * inotify_rm_wd - remove a watch from an inotify instance + * @ih: inotify handle + * @wd: watch descriptor to remove + * + * Can sleep. + */ +int inotify_rm_wd(struct inotify_handle *ih, u32 wd) { - struct file *filp; - struct inotify_device *dev; - int ret, fput_needed; - - filp = fget_light(fd, &fput_needed); - if (unlikely(!filp)) - return -EBADF; + struct inotify_watch *watch; + struct inode *inode; - /* verify that this is indeed an inotify instance */ - if (unlikely(filp->f_op != &inotify_fops)) { - ret = -EINVAL; - goto out; + mutex_lock(&ih->mutex); + watch = idr_find(&ih->idr, wd); + if (unlikely(!watch)) { + mutex_unlock(&ih->mutex); + return -EINVAL; } + get_inotify_watch(watch); + inode = watch->inode; + mutex_unlock(&ih->mutex); - dev = filp->private_data; - ret = inotify_ignore(dev, wd); + mutex_lock(&inode->inotify_mutex); + mutex_lock(&ih->mutex); -out: - fput_light(filp, fput_needed); - return ret; + /* make sure that we did not race */ + if (likely(idr_find(&ih->idr, wd) == watch)) + inotify_remove_watch_locked(ih, watch); + + mutex_unlock(&ih->mutex); + mutex_unlock(&inode->inotify_mutex); + put_inotify_watch(watch); + + return 0; } +EXPORT_SYMBOL_GPL(inotify_rm_wd); -static struct super_block * -inotify_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) +/** + * inotify_rm_watch - remove a watch from an inotify instance + * @ih: inotify handle + * @watch: watch to remove + * + * Can sleep. + */ +int inotify_rm_watch(struct inotify_handle *ih, + struct inotify_watch *watch) { - return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA); + return inotify_rm_wd(ih, watch->wd); } - -static struct file_system_type inotify_fs_type = { - .name = "inotifyfs", - .get_sb = inotify_get_sb, - .kill_sb = kill_anon_super, -}; +EXPORT_SYMBOL_GPL(inotify_rm_watch); /* - * inotify_setup - Our initialization function. Note that we cannnot return - * error because we have compiled-in VFS hooks. So an (unlikely) failure here - * must result in panic(). + * inotify_setup - core initialization function */ static int __init inotify_setup(void) { - int ret; - - ret = register_filesystem(&inotify_fs_type); - if (unlikely(ret)) - panic("inotify: register_filesystem returned %d!\n", ret); - - inotify_mnt = kern_mount(&inotify_fs_type); - if (IS_ERR(inotify_mnt)) - panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt)); - - inotify_max_queued_events = 16384; - inotify_max_user_instances = 128; - inotify_max_user_watches = 8192; - atomic_set(&inotify_cookie, 0); - watch_cachep = kmem_cache_create("inotify_watch_cache", - sizeof(struct inotify_watch), - 0, SLAB_PANIC, NULL, NULL); - event_cachep = kmem_cache_create("inotify_event_cache", - sizeof(struct inotify_kernel_event), - 0, SLAB_PANIC, NULL, NULL); - return 0; } diff --git a/fs/inotify_user.c b/fs/inotify_user.c new file mode 100644 index 0000000..f238644 --- /dev/null +++ b/fs/inotify_user.c @@ -0,0 +1,719 @@ +/* + * fs/inotify_user.c - inotify support for userspace + * + * Authors: + * John McCutchan + * Robert Love + * + * Copyright (C) 2005 John McCutchan + * Copyright 2006 Hewlett-Packard Development Company, L.P. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static kmem_cache_t *watch_cachep __read_mostly; +static kmem_cache_t *event_cachep __read_mostly; + +static struct vfsmount *inotify_mnt __read_mostly; + +/* these are configurable via /proc/sys/fs/inotify/ */ +int inotify_max_user_instances __read_mostly; +int inotify_max_user_watches __read_mostly; +int inotify_max_queued_events __read_mostly; + +/* + * Lock ordering: + * + * inotify_dev->up_mutex (ensures we don't re-add the same watch) + * inode->inotify_mutex (protects inode's watch list) + * inotify_handle->mutex (protects inotify_handle's watch list) + * inotify_dev->ev_mutex (protects device's event queue) + */ + +/* + * Lifetimes of the main data structures: + * + * inotify_device: Lifetime is managed by reference count, from + * sys_inotify_init() until release. Additional references can bump the count + * via get_inotify_dev() and drop the count via put_inotify_dev(). + * + * inotify_user_watch: Lifetime is from create_watch() to the receipt of an + * IN_IGNORED event from inotify, or when using IN_ONESHOT, to receipt of the + * first event, or to inotify_destroy(). + */ + +/* + * struct inotify_device - represents an inotify instance + * + * This structure is protected by the mutex 'mutex'. + */ +struct inotify_device { + wait_queue_head_t wq; /* wait queue for i/o */ + struct mutex ev_mutex; /* protects event queue */ + struct mutex up_mutex; /* synchronizes watch updates */ + struct list_head events; /* list of queued events */ + atomic_t count; /* reference count */ + struct user_struct *user; /* user who opened this dev */ + struct inotify_handle *ih; /* inotify handle */ + unsigned int queue_size; /* size of the queue (bytes) */ + unsigned int event_count; /* number of pending events */ + unsigned int max_events; /* maximum number of events */ +}; + +/* + * struct inotify_kernel_event - An inotify event, originating from a watch and + * queued for user-space. A list of these is attached to each instance of the + * device. In read(), this list is walked and all events that can fit in the + * buffer are returned. + * + * Protected by dev->ev_mutex of the device in which we are queued. + */ +struct inotify_kernel_event { + struct inotify_event event; /* the user-space event */ + struct list_head list; /* entry in inotify_device's list */ + char *name; /* filename, if any */ +}; + +/* + * struct inotify_user_watch - our version of an inotify_watch, we add + * a reference to the associated inotify_device. + */ +struct inotify_user_watch { + struct inotify_device *dev; /* associated device */ + struct inotify_watch wdata; /* inotify watch data */ +}; + +#ifdef CONFIG_SYSCTL + +#include + +static int zero; + +ctl_table inotify_table[] = { + { + .ctl_name = INOTIFY_MAX_USER_INSTANCES, + .procname = "max_user_instances", + .data = &inotify_max_user_instances, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + }, + { + .ctl_name = INOTIFY_MAX_USER_WATCHES, + .procname = "max_user_watches", + .data = &inotify_max_user_watches, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero, + }, + { + .ctl_name = INOTIFY_MAX_QUEUED_EVENTS, + .procname = "max_queued_events", + .data = &inotify_max_queued_events, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + .strategy = &sysctl_intvec, + .extra1 = &zero + }, + { .ctl_name = 0 } +}; +#endif /* CONFIG_SYSCTL */ + +static inline void get_inotify_dev(struct inotify_device *dev) +{ + atomic_inc(&dev->count); +} + +static inline void put_inotify_dev(struct inotify_device *dev) +{ + if (atomic_dec_and_test(&dev->count)) { + atomic_dec(&dev->user->inotify_devs); + free_uid(dev->user); + kfree(dev); + } +} + +/* + * free_inotify_user_watch - cleans up the watch and its references + */ +static void free_inotify_user_watch(struct inotify_watch *w) +{ + struct inotify_user_watch *watch; + struct inotify_device *dev; + + watch = container_of(w, struct inotify_user_watch, wdata); + dev = watch->dev; + + atomic_dec(&dev->user->inotify_watches); + put_inotify_dev(dev); + kmem_cache_free(watch_cachep, watch); +} + +/* + * kernel_event - create a new kernel event with the given parameters + * + * This function can sleep. + */ +static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie, + const char *name) +{ + struct inotify_kernel_event *kevent; + + kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL); + if (unlikely(!kevent)) + return NULL; + + /* we hand this out to user-space, so zero it just in case */ + memset(&kevent->event, 0, sizeof(struct inotify_event)); + + kevent->event.wd = wd; + kevent->event.mask = mask; + kevent->event.cookie = cookie; + + INIT_LIST_HEAD(&kevent->list); + + if (name) { + size_t len, rem, event_size = sizeof(struct inotify_event); + + /* + * We need to pad the filename so as to properly align an + * array of inotify_event structures. Because the structure is + * small and the common case is a small filename, we just round + * up to the next multiple of the structure's sizeof. This is + * simple and safe for all architectures. + */ + len = strlen(name) + 1; + rem = event_size - len; + if (len > event_size) { + rem = event_size - (len % event_size); + if (len % event_size == 0) + rem = 0; + } + + kevent->name = kmalloc(len + rem, GFP_KERNEL); + if (unlikely(!kevent->name)) { + kmem_cache_free(event_cachep, kevent); + return NULL; + } + memcpy(kevent->name, name, len); + if (rem) + memset(kevent->name + len, 0, rem); + kevent->event.len = len + rem; + } else { + kevent->event.len = 0; + kevent->name = NULL; + } + + return kevent; +} + +/* + * inotify_dev_get_event - return the next event in the given dev's queue + * + * Caller must hold dev->ev_mutex. + */ +static inline struct inotify_kernel_event * +inotify_dev_get_event(struct inotify_device *dev) +{ + return list_entry(dev->events.next, struct inotify_kernel_event, list); +} + +/* + * inotify_dev_queue_event - event handler registered with core inotify, adds + * a new event to the given device + * + * Can sleep (calls kernel_event()). + */ +static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask, + u32 cookie, const char *name, + struct inode *ignored) +{ + struct inotify_user_watch *watch; + struct inotify_device *dev; + struct inotify_kernel_event *kevent, *last; + + watch = container_of(w, struct inotify_user_watch, wdata); + dev = watch->dev; + + mutex_lock(&dev->ev_mutex); + + /* we can safely put the watch as we don't reference it while + * generating the event + */ + if (mask & IN_IGNORED || mask & IN_ONESHOT) + put_inotify_watch(w); /* final put */ + + /* coalescing: drop this event if it is a dupe of the previous */ + last = inotify_dev_get_event(dev); + if (last && last->event.mask == mask && last->event.wd == wd && + last->event.cookie == cookie) { + const char *lastname = last->name; + + if (!name && !lastname) + goto out; + if (name && lastname && !strcmp(lastname, name)) + goto out; + } + + /* the queue overflowed and we already sent the Q_OVERFLOW event */ + if (unlikely(dev->event_count > dev->max_events)) + goto out; + + /* if the queue overflows, we need to notify user space */ + if (unlikely(dev->event_count == dev->max_events)) + kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL); + else + kevent = kernel_event(wd, mask, cookie, name); + + if (unlikely(!kevent)) + goto out; + + /* queue the event and wake up anyone waiting */ + dev->event_count++; + dev->queue_size += sizeof(struct inotify_event) + kevent->event.len; + list_add_tail(&kevent->list, &dev->events); + wake_up_interruptible(&dev->wq); + +out: + mutex_unlock(&dev->ev_mutex); +} + +/* + * remove_kevent - cleans up and ultimately frees the given kevent + * + * Caller must hold dev->ev_mutex. + */ +static void remove_kevent(struct inotify_device *dev, + struct inotify_kernel_event *kevent) +{ + list_del(&kevent->list); + + dev->event_count--; + dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len; + + kfree(kevent->name); + kmem_cache_free(event_cachep, kevent); +} + +/* + * inotify_dev_event_dequeue - destroy an event on the given device + * + * Caller must hold dev->ev_mutex. + */ +static void inotify_dev_event_dequeue(struct inotify_device *dev) +{ + if (!list_empty(&dev->events)) { + struct inotify_kernel_event *kevent; + kevent = inotify_dev_get_event(dev); + remove_kevent(dev, kevent); + } +} + +/* + * find_inode - resolve a user-given path to a specific inode and return a nd + */ +static int find_inode(const char __user *dirname, struct nameidata *nd, + unsigned flags) +{ + int error; + + error = __user_walk(dirname, flags, nd); + if (error) + return error; + /* you can only watch an inode if you have read permissions on it */ + error = vfs_permission(nd, MAY_READ); + if (error) + path_release(nd); + return error; +} + +/* + * create_watch - creates a watch on the given device. + * + * Callers must hold dev->up_mutex. + */ +static int create_watch(struct inotify_device *dev, struct inode *inode, + u32 mask) +{ + struct inotify_user_watch *watch; + int ret; + + if (atomic_read(&dev->user->inotify_watches) >= + inotify_max_user_watches) + return -ENOSPC; + + watch = kmem_cache_alloc(watch_cachep, GFP_KERNEL); + if (unlikely(!watch)) + return -ENOMEM; + + /* save a reference to device and bump the count to make it official */ + get_inotify_dev(dev); + watch->dev = dev; + + atomic_inc(&dev->user->inotify_watches); + + inotify_init_watch(&watch->wdata); + ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask); + if (ret < 0) + free_inotify_user_watch(&watch->wdata); + + return ret; +} + +/* Device Interface */ + +static unsigned int inotify_poll(struct file *file, poll_table *wait) +{ + struct inotify_device *dev = file->private_data; + int ret = 0; + + poll_wait(file, &dev->wq, wait); + mutex_lock(&dev->ev_mutex); + if (!list_empty(&dev->events)) + ret = POLLIN | POLLRDNORM; + mutex_unlock(&dev->ev_mutex); + + return ret; +} + +static ssize_t inotify_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + size_t event_size = sizeof (struct inotify_event); + struct inotify_device *dev; + char __user *start; + int ret; + DEFINE_WAIT(wait); + + start = buf; + dev = file->private_data; + + while (1) { + int events; + + prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE); + + mutex_lock(&dev->ev_mutex); + events = !list_empty(&dev->events); + mutex_unlock(&dev->ev_mutex); + if (events) { + ret = 0; + break; + } + + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + if (signal_pending(current)) { + ret = -EINTR; + break; + } + + schedule(); + } + + finish_wait(&dev->wq, &wait); + if (ret) + return ret; + + mutex_lock(&dev->ev_mutex); + while (1) { + struct inotify_kernel_event *kevent; + + ret = buf - start; + if (list_empty(&dev->events)) + break; + + kevent = inotify_dev_get_event(dev); + if (event_size + kevent->event.len > count) + break; + + if (copy_to_user(buf, &kevent->event, event_size)) { + ret = -EFAULT; + break; + } + buf += event_size; + count -= event_size; + + if (kevent->name) { + if (copy_to_user(buf, kevent->name, kevent->event.len)){ + ret = -EFAULT; + break; + } + buf += kevent->event.len; + count -= kevent->event.len; + } + + remove_kevent(dev, kevent); + } + mutex_unlock(&dev->ev_mutex); + + return ret; +} + +static int inotify_release(struct inode *ignored, struct file *file) +{ + struct inotify_device *dev = file->private_data; + + inotify_destroy(dev->ih); + + /* destroy all of the events on this device */ + mutex_lock(&dev->ev_mutex); + while (!list_empty(&dev->events)) + inotify_dev_event_dequeue(dev); + mutex_unlock(&dev->ev_mutex); + + /* free this device: the put matching the get in inotify_init() */ + put_inotify_dev(dev); + + return 0; +} + +static long inotify_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct inotify_device *dev; + void __user *p; + int ret = -ENOTTY; + + dev = file->private_data; + p = (void __user *) arg; + + switch (cmd) { + case FIONREAD: + ret = put_user(dev->queue_size, (int __user *) p); + break; + } + + return ret; +} + +static const struct file_operations inotify_fops = { + .poll = inotify_poll, + .read = inotify_read, + .release = inotify_release, + .unlocked_ioctl = inotify_ioctl, + .compat_ioctl = inotify_ioctl, +}; + +static const struct inotify_operations inotify_user_ops = { + .handle_event = inotify_dev_queue_event, + .destroy_watch = free_inotify_user_watch, +}; + +asmlinkage long sys_inotify_init(void) +{ + struct inotify_device *dev; + struct inotify_handle *ih; + struct user_struct *user; + struct file *filp; + int fd, ret; + + fd = get_unused_fd(); + if (fd < 0) + return fd; + + filp = get_empty_filp(); + if (!filp) { + ret = -ENFILE; + goto out_put_fd; + } + + user = get_uid(current->user); + if (unlikely(atomic_read(&user->inotify_devs) >= + inotify_max_user_instances)) { + ret = -EMFILE; + goto out_free_uid; + } + + dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL); + if (unlikely(!dev)) { + ret = -ENOMEM; + goto out_free_uid; + } + + ih = inotify_init(&inotify_user_ops); + if (unlikely(IS_ERR(ih))) { + ret = PTR_ERR(ih); + goto out_free_dev; + } + dev->ih = ih; + + filp->f_op = &inotify_fops; + filp->f_vfsmnt = mntget(inotify_mnt); + filp->f_dentry = dget(inotify_mnt->mnt_root); + filp->f_mapping = filp->f_dentry->d_inode->i_mapping; + filp->f_mode = FMODE_READ; + filp->f_flags = O_RDONLY; + filp->private_data = dev; + + INIT_LIST_HEAD(&dev->events); + init_waitqueue_head(&dev->wq); + mutex_init(&dev->ev_mutex); + mutex_init(&dev->up_mutex); + dev->event_count = 0; + dev->queue_size = 0; + dev->max_events = inotify_max_queued_events; + dev->user = user; + atomic_set(&dev->count, 0); + + get_inotify_dev(dev); + atomic_inc(&user->inotify_devs); + fd_install(fd, filp); + + return fd; +out_free_dev: + kfree(dev); +out_free_uid: + free_uid(user); + put_filp(filp); +out_put_fd: + put_unused_fd(fd); + return ret; +} + +asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask) +{ + struct inode *inode; + struct inotify_device *dev; + struct nameidata nd; + struct file *filp; + int ret, fput_needed; + unsigned flags = 0; + + filp = fget_light(fd, &fput_needed); + if (unlikely(!filp)) + return -EBADF; + + /* verify that this is indeed an inotify instance */ + if (unlikely(filp->f_op != &inotify_fops)) { + ret = -EINVAL; + goto fput_and_out; + } + + if (!(mask & IN_DONT_FOLLOW)) + flags |= LOOKUP_FOLLOW; + if (mask & IN_ONLYDIR) + flags |= LOOKUP_DIRECTORY; + + ret = find_inode(path, &nd, flags); + if (unlikely(ret)) + goto fput_and_out; + + /* inode held in place by reference to nd; dev by fget on fd */ + inode = nd.dentry->d_inode; + dev = filp->private_data; + + mutex_lock(&dev->up_mutex); + ret = inotify_find_update_watch(dev->ih, inode, mask); + if (ret == -ENOENT) + ret = create_watch(dev, inode, mask); + mutex_unlock(&dev->up_mutex); + + path_release(&nd); +fput_and_out: + fput_light(filp, fput_needed); + return ret; +} + +asmlinkage long sys_inotify_rm_watch(int fd, u32 wd) +{ + struct file *filp; + struct inotify_device *dev; + int ret, fput_needed; + + filp = fget_light(fd, &fput_needed); + if (unlikely(!filp)) + return -EBADF; + + /* verify that this is indeed an inotify instance */ + if (unlikely(filp->f_op != &inotify_fops)) { + ret = -EINVAL; + goto out; + } + + dev = filp->private_data; + + /* we free our watch data when we get IN_IGNORED */ + ret = inotify_rm_wd(dev->ih, wd); + +out: + fput_light(filp, fput_needed); + return ret; +} + +static int +inotify_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA, mnt); +} + +static struct file_system_type inotify_fs_type = { + .name = "inotifyfs", + .get_sb = inotify_get_sb, + .kill_sb = kill_anon_super, +}; + +/* + * inotify_user_setup - Our initialization function. Note that we cannnot return + * error because we have compiled-in VFS hooks. So an (unlikely) failure here + * must result in panic(). + */ +static int __init inotify_user_setup(void) +{ + int ret; + + ret = register_filesystem(&inotify_fs_type); + if (unlikely(ret)) + panic("inotify: register_filesystem returned %d!\n", ret); + + inotify_mnt = kern_mount(&inotify_fs_type); + if (IS_ERR(inotify_mnt)) + panic("inotify: kern_mount ret %ld!\n", PTR_ERR(inotify_mnt)); + + inotify_max_queued_events = 16384; + inotify_max_user_instances = 128; + inotify_max_user_watches = 8192; + + watch_cachep = kmem_cache_create("inotify_watch_cache", + sizeof(struct inotify_user_watch), + 0, SLAB_PANIC, NULL, NULL); + event_cachep = kmem_cache_create("inotify_event_cache", + sizeof(struct inotify_kernel_event), + 0, SLAB_PANIC, NULL, NULL); + + return 0; +} + +module_init(inotify_user_setup); diff --git a/fs/ioctl.c b/fs/ioctl.c index f8aeec3..4b7660b 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include #include #include #include diff --git a/fs/ioprio.c b/fs/ioprio.c index ca77008..93aa571 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -24,15 +24,21 @@ #include #include #include #include +#include static int set_task_ioprio(struct task_struct *task, int ioprio) { + int err; struct io_context *ioc; if (task->uid != current->euid && task->uid != current->uid && !capable(CAP_SYS_NICE)) return -EPERM; + err = security_task_setioprio(task, ioprio); + if (err) + return err; + task_lock(task); task->ioprio = ioprio; @@ -119,11 +125,24 @@ asmlinkage long sys_ioprio_set(int which return ret; } +static int get_task_ioprio(struct task_struct *p) +{ + int ret; + + ret = security_task_getioprio(p); + if (ret) + goto out; + ret = p->ioprio; +out: + return ret; +} + asmlinkage long sys_ioprio_get(int which, int who) { struct task_struct *g, *p; struct user_struct *user; int ret = -ESRCH; + int tmpio; read_lock_irq(&tasklist_lock); switch (which) { @@ -133,16 +152,19 @@ asmlinkage long sys_ioprio_get(int which else p = find_task_by_pid(who); if (p) - ret = p->ioprio; + ret = get_task_ioprio(p); break; case IOPRIO_WHO_PGRP: if (!who) who = process_group(current); do_each_task_pid(who, PIDTYPE_PGID, p) { + tmpio = get_task_ioprio(p); + if (tmpio < 0) + continue; if (ret == -ESRCH) - ret = p->ioprio; + ret = tmpio; else - ret = ioprio_best(ret, p->ioprio); + ret = ioprio_best(ret, tmpio); } while_each_task_pid(who, PIDTYPE_PGID, p); break; case IOPRIO_WHO_USER: @@ -157,10 +179,13 @@ asmlinkage long sys_ioprio_get(int which do_each_thread(g, p) { if (p->uid != user->uid) continue; + tmpio = get_task_ioprio(p); + if (tmpio < 0) + continue; if (ret == -ESRCH) - ret = p->ioprio; + ret = tmpio; else - ret = ioprio_best(ret, p->ioprio); + ret = ioprio_best(ret, tmpio); } while_each_thread(g, p); if (who) diff --git a/fs/isofs/compress.c b/fs/isofs/compress.c index 4917315..7318163 100644 --- a/fs/isofs/compress.c +++ b/fs/isofs/compress.c @@ -16,7 +16,6 @@ * Transparent decompression of files on an iso9660 filesystem */ -#include #include #include @@ -312,7 +311,7 @@ eio: return err; } -struct address_space_operations zisofs_aops = { +const struct address_space_operations zisofs_aops = { .readpage = zisofs_readpage, /* No sync_page operation supported? */ /* No bmap operation supported */ diff --git a/fs/isofs/dir.c b/fs/isofs/dir.c index 5440ea2..27e2769 100644 --- a/fs/isofs/dir.c +++ b/fs/isofs/dir.c @@ -10,7 +10,6 @@ * * isofs directory handling functions */ -#include #include #include "isofs.h" diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 70adbb9..1439136 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -11,7 +11,6 @@ * 2004 Paul Serice - NFS Export Operations */ -#include #include #include @@ -56,7 +55,7 @@ #endif } static void isofs_read_inode(struct inode *); -static int isofs_statfs (struct super_block *, struct kstatfs *); +static int isofs_statfs (struct dentry *, struct kstatfs *); static kmem_cache_t *isofs_inode_cachep; @@ -901,8 +900,10 @@ out_freesbi: return -EINVAL; } -static int isofs_statfs (struct super_block *sb, struct kstatfs *buf) +static int isofs_statfs (struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; + buf->f_type = ISOFS_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = (ISOFS_SB(sb)->s_nzones @@ -1052,7 +1053,7 @@ static sector_t _isofs_bmap(struct addre return generic_block_bmap(mapping,block,isofs_get_block); } -static struct address_space_operations isofs_aops = { +static const struct address_space_operations isofs_aops = { .readpage = isofs_readpage, .sync_page = block_sync_page, .bmap = _isofs_bmap @@ -1399,10 +1400,11 @@ struct inode *isofs_iget(struct super_bl return inode; } -static struct super_block *isofs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int isofs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, isofs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, isofs_fill_super, + mnt); } static struct file_system_type iso9660_fs_type = { diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index b87ba06..e6308c8 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -176,5 +176,5 @@ isofs_normalize_block_and_offset(struct extern struct inode_operations isofs_dir_inode_operations; extern const struct file_operations isofs_dir_operations; -extern struct address_space_operations isofs_symlink_aops; +extern const struct address_space_operations isofs_symlink_aops; extern struct export_operations isofs_export_ops; diff --git a/fs/isofs/rock.c b/fs/isofs/rock.c index 4326cb4..f3a1db3 100644 --- a/fs/isofs/rock.c +++ b/fs/isofs/rock.c @@ -754,6 +754,6 @@ error: return -EIO; } -struct address_space_operations isofs_symlink_aops = { +const struct address_space_operations isofs_symlink_aops = { .readpage = rock_ridge_symlink_readpage }; diff --git a/fs/isofs/zisofs.h b/fs/isofs/zisofs.h index d78485d..2737957 100644 --- a/fs/isofs/zisofs.h +++ b/fs/isofs/zisofs.h @@ -15,7 +15,7 @@ */ #ifdef CONFIG_ZISOFS -extern struct address_space_operations zisofs_aops; +extern const struct address_space_operations zisofs_aops; extern int __init zisofs_init(void); extern void zisofs_cleanup(void); #endif diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c index 3f5102b..47678a2 100644 --- a/fs/jbd/checkpoint.c +++ b/fs/jbd/checkpoint.c @@ -24,29 +24,67 @@ #include #include /* - * Unlink a buffer from a transaction. + * Unlink a buffer from a transaction checkpoint list. * * Called with j_list_lock held. */ - -static inline void __buffer_unlink(struct journal_head *jh) +static inline void __buffer_unlink_first(struct journal_head *jh) { - transaction_t *transaction; - - transaction = jh->b_cp_transaction; - jh->b_cp_transaction = NULL; + transaction_t *transaction = jh->b_cp_transaction; jh->b_cpnext->b_cpprev = jh->b_cpprev; jh->b_cpprev->b_cpnext = jh->b_cpnext; - if (transaction->t_checkpoint_list == jh) + if (transaction->t_checkpoint_list == jh) { transaction->t_checkpoint_list = jh->b_cpnext; - if (transaction->t_checkpoint_list == jh) - transaction->t_checkpoint_list = NULL; + if (transaction->t_checkpoint_list == jh) + transaction->t_checkpoint_list = NULL; + } +} + +/* + * Unlink a buffer from a transaction checkpoint(io) list. + * + * Called with j_list_lock held. + */ +static inline void __buffer_unlink(struct journal_head *jh) +{ + transaction_t *transaction = jh->b_cp_transaction; + + __buffer_unlink_first(jh); + if (transaction->t_checkpoint_io_list == jh) { + transaction->t_checkpoint_io_list = jh->b_cpnext; + if (transaction->t_checkpoint_io_list == jh) + transaction->t_checkpoint_io_list = NULL; + } +} + +/* + * Move a buffer from the checkpoint list to the checkpoint io list + * + * Called with j_list_lock held + */ +static inline void __buffer_relink_io(struct journal_head *jh) +{ + transaction_t *transaction = jh->b_cp_transaction; + + __buffer_unlink_first(jh); + + if (!transaction->t_checkpoint_io_list) { + jh->b_cpnext = jh->b_cpprev = jh; + } else { + jh->b_cpnext = transaction->t_checkpoint_io_list; + jh->b_cpprev = transaction->t_checkpoint_io_list->b_cpprev; + jh->b_cpprev->b_cpnext = jh; + jh->b_cpnext->b_cpprev = jh; + } + transaction->t_checkpoint_io_list = jh; } /* * Try to release a checkpointed buffer from its transaction. - * Returns 1 if we released it. + * Returns 1 if we released it and 2 if we also released the + * whole transaction. + * * Requires j_list_lock * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it */ @@ -57,12 +95,11 @@ static int __try_to_free_cp_buf(struct j if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) { JBUFFER_TRACE(jh, "remove from checkpoint list"); - __journal_remove_checkpoint(jh); + ret = __journal_remove_checkpoint(jh) + 1; jbd_unlock_bh_state(bh); journal_remove_journal_head(bh); BUFFER_TRACE(bh, "release"); __brelse(bh); - ret = 1; } else { jbd_unlock_bh_state(bh); } @@ -117,83 +154,54 @@ static void jbd_sync_bh(journal_t *journ } /* - * Clean up a transaction's checkpoint list. - * - * We wait for any pending IO to complete and make sure any clean - * buffers are removed from the transaction. - * - * Return 1 if we performed any actions which might have destroyed the - * checkpoint. (journal_remove_checkpoint() deletes the transaction when - * the last checkpoint buffer is cleansed) + * Clean up transaction's list of buffers submitted for io. + * We wait for any pending IO to complete and remove any clean + * buffers. Note that we take the buffers in the opposite ordering + * from the one in which they were submitted for IO. * * Called with j_list_lock held. */ -static int __cleanup_transaction(journal_t *journal, transaction_t *transaction) +static void __wait_cp_io(journal_t *journal, transaction_t *transaction) { - struct journal_head *jh, *next_jh, *last_jh; + struct journal_head *jh; struct buffer_head *bh; - int ret = 0; - - assert_spin_locked(&journal->j_list_lock); - jh = transaction->t_checkpoint_list; - if (!jh) - return 0; - - last_jh = jh->b_cpprev; - next_jh = jh; - do { - jh = next_jh; + tid_t this_tid; + int released = 0; + + this_tid = transaction->t_tid; +restart: + /* Did somebody clean up the transaction in the meanwhile? */ + if (journal->j_checkpoint_transactions != transaction || + transaction->t_tid != this_tid) + return; + while (!released && transaction->t_checkpoint_io_list) { + jh = transaction->t_checkpoint_io_list; bh = jh2bh(jh); + if (!jbd_trylock_bh_state(bh)) { + jbd_sync_bh(journal, bh); + spin_lock(&journal->j_list_lock); + goto restart; + } if (buffer_locked(bh)) { atomic_inc(&bh->b_count); spin_unlock(&journal->j_list_lock); + jbd_unlock_bh_state(bh); wait_on_buffer(bh); /* the journal_head may have gone by now */ BUFFER_TRACE(bh, "brelse"); __brelse(bh); - goto out_return_1; + spin_lock(&journal->j_list_lock); + goto restart; } - /* - * This is foul + * Now in whatever state the buffer currently is, we know that + * it has been written out and so we can drop it from the list */ - if (!jbd_trylock_bh_state(bh)) { - jbd_sync_bh(journal, bh); - goto out_return_1; - } - - if (jh->b_transaction != NULL) { - transaction_t *t = jh->b_transaction; - tid_t tid = t->t_tid; - - spin_unlock(&journal->j_list_lock); - jbd_unlock_bh_state(bh); - log_start_commit(journal, tid); - log_wait_commit(journal, tid); - goto out_return_1; - } - - /* - * AKPM: I think the buffer_jbddirty test is redundant - it - * shouldn't have NULL b_transaction? - */ - next_jh = jh->b_cpnext; - if (!buffer_dirty(bh) && !buffer_jbddirty(bh)) { - BUFFER_TRACE(bh, "remove from checkpoint"); - __journal_remove_checkpoint(jh); - jbd_unlock_bh_state(bh); - journal_remove_journal_head(bh); - __brelse(bh); - ret = 1; - } else { - jbd_unlock_bh_state(bh); - } - } while (jh != last_jh); - - return ret; -out_return_1: - spin_lock(&journal->j_list_lock); - return 1; + released = __journal_remove_checkpoint(jh); + jbd_unlock_bh_state(bh); + journal_remove_journal_head(bh); + __brelse(bh); + } } #define NR_BATCH 64 @@ -203,9 +211,7 @@ __flush_batch(journal_t *journal, struct { int i; - spin_unlock(&journal->j_list_lock); ll_rw_block(SWRITE, *batch_count, bhs); - spin_lock(&journal->j_list_lock); for (i = 0; i < *batch_count; i++) { struct buffer_head *bh = bhs[i]; clear_buffer_jwrite(bh); @@ -221,19 +227,43 @@ __flush_batch(journal_t *journal, struct * Return 1 if something happened which requires us to abort the current * scan of the checkpoint list. * - * Called with j_list_lock held. + * Called with j_list_lock held and drops it if 1 is returned * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it */ -static int __flush_buffer(journal_t *journal, struct journal_head *jh, - struct buffer_head **bhs, int *batch_count, - int *drop_count) +static int __process_buffer(journal_t *journal, struct journal_head *jh, + struct buffer_head **bhs, int *batch_count) { struct buffer_head *bh = jh2bh(jh); int ret = 0; - if (buffer_dirty(bh) && !buffer_locked(bh) && jh->b_jlist == BJ_None) { - J_ASSERT_JH(jh, jh->b_transaction == NULL); + if (buffer_locked(bh)) { + atomic_inc(&bh->b_count); + spin_unlock(&journal->j_list_lock); + jbd_unlock_bh_state(bh); + wait_on_buffer(bh); + /* the journal_head may have gone by now */ + BUFFER_TRACE(bh, "brelse"); + __brelse(bh); + ret = 1; + } else if (jh->b_transaction != NULL) { + transaction_t *t = jh->b_transaction; + tid_t tid = t->t_tid; + spin_unlock(&journal->j_list_lock); + jbd_unlock_bh_state(bh); + log_start_commit(journal, tid); + log_wait_commit(journal, tid); + ret = 1; + } else if (!buffer_dirty(bh)) { + J_ASSERT_JH(jh, !buffer_jbddirty(bh)); + BUFFER_TRACE(bh, "remove from checkpoint"); + __journal_remove_checkpoint(jh); + spin_unlock(&journal->j_list_lock); + jbd_unlock_bh_state(bh); + journal_remove_journal_head(bh); + __brelse(bh); + ret = 1; + } else { /* * Important: we are about to write the buffer, and * possibly block, while still holding the journal lock. @@ -246,45 +276,30 @@ static int __flush_buffer(journal_t *jou J_ASSERT_BH(bh, !buffer_jwrite(bh)); set_buffer_jwrite(bh); bhs[*batch_count] = bh; + __buffer_relink_io(jh); jbd_unlock_bh_state(bh); (*batch_count)++; if (*batch_count == NR_BATCH) { + spin_unlock(&journal->j_list_lock); __flush_batch(journal, bhs, batch_count); ret = 1; } - } else { - int last_buffer = 0; - if (jh->b_cpnext == jh) { - /* We may be about to drop the transaction. Tell the - * caller that the lists have changed. - */ - last_buffer = 1; - } - if (__try_to_free_cp_buf(jh)) { - (*drop_count)++; - ret = last_buffer; - } } return ret; } /* - * Perform an actual checkpoint. We don't write out only enough to - * satisfy the current blocked requests: rather we submit a reasonably - * sized chunk of the outstanding data to disk at once for - * efficiency. __log_wait_for_space() will retry if we didn't free enough. + * Perform an actual checkpoint. We take the first transaction on the + * list of transactions to be checkpointed and send all its buffers + * to disk. We submit larger chunks of data at once. * - * However, we _do_ take into account the amount requested so that once - * the IO has been queued, we can return as soon as enough of it has - * completed to disk. - * * The journal should be locked before calling this function. */ int log_do_checkpoint(journal_t *journal) { + transaction_t *transaction; + tid_t this_tid; int result; - int batch_count = 0; - struct buffer_head *bhs[NR_BATCH]; jbd_debug(1, "Start checkpoint\n"); @@ -299,79 +314,68 @@ int log_do_checkpoint(journal_t *journal return result; /* - * OK, we need to start writing disk blocks. Try to free up a - * quarter of the log in a single checkpoint if we can. + * OK, we need to start writing disk blocks. Take one transaction + * and write it. */ + spin_lock(&journal->j_list_lock); + if (!journal->j_checkpoint_transactions) + goto out; + transaction = journal->j_checkpoint_transactions; + this_tid = transaction->t_tid; +restart: /* - * AKPM: check this code. I had a feeling a while back that it - * degenerates into a busy loop at unmount time. + * If someone cleaned up this transaction while we slept, we're + * done (maybe it's a new transaction, but it fell at the same + * address). */ - spin_lock(&journal->j_list_lock); - while (journal->j_checkpoint_transactions) { - transaction_t *transaction; - struct journal_head *jh, *last_jh, *next_jh; - int drop_count = 0; - int cleanup_ret, retry = 0; - tid_t this_tid; - - transaction = journal->j_checkpoint_transactions; - this_tid = transaction->t_tid; - jh = transaction->t_checkpoint_list; - last_jh = jh->b_cpprev; - next_jh = jh; - do { + if (journal->j_checkpoint_transactions == transaction && + transaction->t_tid == this_tid) { + int batch_count = 0; + struct buffer_head *bhs[NR_BATCH]; + struct journal_head *jh; + int retry = 0; + + while (!retry && transaction->t_checkpoint_list) { struct buffer_head *bh; - jh = next_jh; - next_jh = jh->b_cpnext; + jh = transaction->t_checkpoint_list; bh = jh2bh(jh); if (!jbd_trylock_bh_state(bh)) { jbd_sync_bh(journal, bh); - spin_lock(&journal->j_list_lock); retry = 1; break; } - retry = __flush_buffer(journal, jh, bhs, &batch_count, &drop_count); - if (cond_resched_lock(&journal->j_list_lock)) { + retry = __process_buffer(journal, jh, bhs,&batch_count); + if (!retry && lock_need_resched(&journal->j_list_lock)){ + spin_unlock(&journal->j_list_lock); retry = 1; break; } - } while (jh != last_jh && !retry); + } if (batch_count) { + if (!retry) { + spin_unlock(&journal->j_list_lock); + retry = 1; + } __flush_batch(journal, bhs, &batch_count); - retry = 1; } + if (retry) { + spin_lock(&journal->j_list_lock); + goto restart; + } /* - * If someone cleaned up this transaction while we slept, we're - * done - */ - if (journal->j_checkpoint_transactions != transaction) - break; - if (retry) - continue; - /* - * Maybe it's a new transaction, but it fell at the same - * address - */ - if (transaction->t_tid != this_tid) - continue; - /* - * We have walked the whole transaction list without - * finding anything to write to disk. We had better be - * able to make some progress or we are in trouble. + * Now we have cleaned up the first transaction's checkpoint + * list. Let's clean up the second one */ - cleanup_ret = __cleanup_transaction(journal, transaction); - J_ASSERT(drop_count != 0 || cleanup_ret != 0); - if (journal->j_checkpoint_transactions != transaction) - break; + __wait_cp_io(journal, transaction); } +out: spin_unlock(&journal->j_list_lock); result = cleanup_journal_tail(journal); if (result < 0) return result; - return 0; } @@ -456,52 +460,98 @@ int cleanup_journal_tail(journal_t *jour /* Checkpoint list management */ /* + * journal_clean_one_cp_list + * + * Find all the written-back checkpoint buffers in the given list and release them. + * + * Called with the journal locked. + * Called with j_list_lock held. + * Returns number of bufers reaped (for debug) + */ + +static int journal_clean_one_cp_list(struct journal_head *jh, int *released) +{ + struct journal_head *last_jh; + struct journal_head *next_jh = jh; + int ret, freed = 0; + + *released = 0; + if (!jh) + return 0; + + last_jh = jh->b_cpprev; + do { + jh = next_jh; + next_jh = jh->b_cpnext; + /* Use trylock because of the ranking */ + if (jbd_trylock_bh_state(jh2bh(jh))) { + ret = __try_to_free_cp_buf(jh); + if (ret) { + freed++; + if (ret == 2) { + *released = 1; + return freed; + } + } + } + /* + * This function only frees up some memory + * if possible so we dont have an obligation + * to finish processing. Bail out if preemption + * requested: + */ + if (need_resched()) + return freed; + } while (jh != last_jh); + + return freed; +} + +/* * journal_clean_checkpoint_list * * Find all the written-back checkpoint buffers in the journal and release them. * * Called with the journal locked. * Called with j_list_lock held. - * Returns number of bufers reaped (for debug) + * Returns number of buffers reaped (for debug) */ int __journal_clean_checkpoint_list(journal_t *journal) { transaction_t *transaction, *last_transaction, *next_transaction; int ret = 0; + int released; transaction = journal->j_checkpoint_transactions; - if (transaction == 0) + if (!transaction) goto out; last_transaction = transaction->t_cpprev; next_transaction = transaction; do { - struct journal_head *jh; - transaction = next_transaction; next_transaction = transaction->t_cpnext; - jh = transaction->t_checkpoint_list; - if (jh) { - struct journal_head *last_jh = jh->b_cpprev; - struct journal_head *next_jh = jh; - - do { - jh = next_jh; - next_jh = jh->b_cpnext; - /* Use trylock because of the ranknig */ - if (jbd_trylock_bh_state(jh2bh(jh))) - ret += __try_to_free_cp_buf(jh); - /* - * This function only frees up some memory - * if possible so we dont have an obligation - * to finish processing. Bail out if preemption - * requested: - */ - if (need_resched()) - goto out; - } while (jh != last_jh); - } + ret += journal_clean_one_cp_list(transaction-> + t_checkpoint_list, &released); + /* + * This function only frees up some memory if possible so we + * dont have an obligation to finish processing. Bail out if + * preemption requested: + */ + if (need_resched()) + goto out; + if (released) + continue; + /* + * It is essential that we are as careful as in the case of + * t_checkpoint_list with removing the buffer from the list as + * we can possibly see not yet submitted buffers on io_list + */ + ret += journal_clean_one_cp_list(transaction-> + t_checkpoint_io_list, &released); + if (need_resched()) + goto out; } while (transaction != last_transaction); out: return ret; @@ -516,18 +566,22 @@ out: * buffer updates committed in that transaction have safely been stored * elsewhere on disk. To achieve this, all of the buffers in a * transaction need to be maintained on the transaction's checkpoint - * list until they have been rewritten, at which point this function is + * lists until they have been rewritten, at which point this function is * called to remove the buffer from the existing transaction's - * checkpoint list. + * checkpoint lists. + * + * The function returns 1 if it frees the transaction, 0 otherwise. * * This function is called with the journal locked. * This function is called with j_list_lock held. + * This function is called with jbd_lock_bh_state(jh2bh(jh)) */ -void __journal_remove_checkpoint(struct journal_head *jh) +int __journal_remove_checkpoint(struct journal_head *jh) { transaction_t *transaction; journal_t *journal; + int ret = 0; JBUFFER_TRACE(jh, "entry"); @@ -538,8 +592,10 @@ void __journal_remove_checkpoint(struct journal = transaction->t_journal; __buffer_unlink(jh); + jh->b_cp_transaction = NULL; - if (transaction->t_checkpoint_list != NULL) + if (transaction->t_checkpoint_list != NULL || + transaction->t_checkpoint_io_list != NULL) goto out; JBUFFER_TRACE(jh, "transaction has no more buffers"); @@ -565,8 +621,10 @@ void __journal_remove_checkpoint(struct /* Just in case anybody was waiting for more transactions to be checkpointed... */ wake_up(&journal->j_wait_logspace); + ret = 1; out: JBUFFER_TRACE(jh, "exit"); + return ret; } /* @@ -628,6 +686,7 @@ void __journal_drop_transaction(journal_ J_ASSERT(transaction->t_shadow_list == NULL); J_ASSERT(transaction->t_log_list == NULL); J_ASSERT(transaction->t_checkpoint_list == NULL); + J_ASSERT(transaction->t_checkpoint_io_list == NULL); J_ASSERT(transaction->t_updates == 0); J_ASSERT(journal->j_committing_transaction != transaction); J_ASSERT(journal->j_running_transaction != transaction); diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 002ad2b..0971814 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c @@ -790,11 +790,22 @@ restart_loop: jbd_unlock_bh_state(bh); } else { J_ASSERT_BH(bh, !buffer_dirty(bh)); - J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - __journal_unfile_buffer(jh); - jbd_unlock_bh_state(bh); - journal_remove_journal_head(bh); /* needs a brelse */ - release_buffer_page(bh); + /* The buffer on BJ_Forget list and not jbddirty means + * it has been freed by this transaction and hence it + * could not have been reallocated until this + * transaction has committed. *BUT* it could be + * reallocated once we have written all the data to + * disk and before we process the buffer on BJ_Forget + * list. */ + JBUFFER_TRACE(jh, "refile or unfile freed buffer"); + __journal_refile_buffer(jh); + if (!jh->b_transaction) { + jbd_unlock_bh_state(bh); + /* needs a brelse */ + journal_remove_journal_head(bh); + release_buffer_page(bh); + } else + jbd_unlock_bh_state(bh); } cond_resched_lock(&journal->j_list_lock); } diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 7f96b5c..8c9b28d 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -34,6 +34,7 @@ #include #include #include #include +#include #include #include @@ -1675,7 +1676,7 @@ static void journal_free_journal_head(st { #ifdef CONFIG_JBD_DEBUG atomic_dec(&nr_journal_heads); - memset(jh, 0x5b, sizeof(*jh)); + memset(jh, JBD_POISON_FREE, sizeof(*jh)); #endif kmem_cache_free(journal_head_cache, jh); } diff --git a/fs/jbd/recovery.c b/fs/jbd/recovery.c index 80d7f53..de5bafb 100644 --- a/fs/jbd/recovery.c +++ b/fs/jbd/recovery.c @@ -531,6 +531,7 @@ static int do_one_pass(journal_t *journa default: jbd_debug(3, "Unrecognised magic %d, end of scan.\n", blocktype); + brelse(bh); goto done; } } diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index c609f50..508b2ea 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -227,7 +227,8 @@ repeat_locked: spin_unlock(&transaction->t_handle_lock); spin_unlock(&journal->j_state_lock); out: - kfree(new_transaction); + if (unlikely(new_transaction)) /* It's usually NULL */ + kfree(new_transaction); return ret; } @@ -724,7 +725,8 @@ done: journal_cancel_revoke(handle, jh); out: - kfree(frozen_buffer); + if (unlikely(frozen_buffer)) /* It's usually NULL */ + kfree(frozen_buffer); JBUFFER_TRACE(jh, "exit"); return error; @@ -903,7 +905,8 @@ repeat: jbd_unlock_bh_state(bh); out: journal_put_journal_head(jh); - kfree(committed_data); + if (unlikely(committed_data)) + kfree(committed_data); return err; } @@ -2038,7 +2041,8 @@ void __journal_refile_buffer(struct jour __journal_temp_unlink_buffer(jh); jh->b_transaction = jh->b_next_transaction; jh->b_next_transaction = NULL; - __journal_file_buffer(jh, jh->b_transaction, BJ_Metadata); + __journal_file_buffer(jh, jh->b_transaction, + was_dirty ? BJ_Metadata : BJ_Reserved); J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING); if (was_dirty) diff --git a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c index 020cc09..9306869 100644 --- a/fs/jffs/inode-v23.c +++ b/fs/jffs/inode-v23.c @@ -59,7 +59,7 @@ static const struct file_operations jffs static struct inode_operations jffs_file_inode_operations; static const struct file_operations jffs_dir_operations; static struct inode_operations jffs_dir_inode_operations; -static struct address_space_operations jffs_address_operations; +static const struct address_space_operations jffs_address_operations; kmem_cache_t *node_cache = NULL; kmem_cache_t *fm_cache = NULL; @@ -377,9 +377,9 @@ jffs_new_inode(const struct inode * dir, /* Get statistics of the file system. */ static int -jffs_statfs(struct super_block *sb, struct kstatfs *buf) +jffs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct jffs_control *c = (struct jffs_control *) sb->s_fs_info; + struct jffs_control *c = (struct jffs_control *) dentry->d_sb->s_fs_info; struct jffs_fmcontrol *fmc; lock_kernel(); @@ -1614,7 +1614,7 @@ jffs_ioctl(struct inode *inode, struct f } /* jffs_ioctl() */ -static struct address_space_operations jffs_address_operations = { +static const struct address_space_operations jffs_address_operations = { .readpage = jffs_readpage, .prepare_write = jffs_prepare_write, .commit_write = jffs_commit_write, @@ -1785,10 +1785,11 @@ static struct super_operations jffs_ops .remount_fs = jffs_remount, }; -static struct super_block *jffs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int jffs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, jffs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, jffs_fill_super, + mnt); } static struct file_system_type jffs_fs_type = { diff --git a/fs/jffs/intrep.c b/fs/jffs/intrep.c index 0ef207d..9000f1e 100644 --- a/fs/jffs/intrep.c +++ b/fs/jffs/intrep.c @@ -55,7 +55,6 @@ * */ -#include #include #include #include @@ -247,7 +246,7 @@ flash_safe_read(struct mtd_info *mtd, lo D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n", mtd, (unsigned int) from, buf, count)); - res = MTD_READ(mtd, from, count, &retlen, buf); + res = mtd->read(mtd, from, count, &retlen, buf); if (retlen != count) { panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res); } @@ -262,7 +261,7 @@ flash_read_u32(struct mtd_info *mtd, lof __u32 ret; int res; - res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret); + res = mtd->read(mtd, from, 4, &retlen, (unsigned char *)&ret); if (retlen != 4) { printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res); return 0; @@ -282,7 +281,7 @@ flash_safe_write(struct mtd_info *mtd, l D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n", mtd, (unsigned int) to, buf, count)); - res = MTD_WRITE(mtd, to, count, &retlen, buf); + res = mtd->write(mtd, to, count, &retlen, buf); if (retlen != count) { printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res); } @@ -300,9 +299,9 @@ flash_safe_writev(struct mtd_info *mtd, D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n", mtd, (unsigned int) to, vecs)); - + if (mtd->writev) { - res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen); + res = mtd->writev(mtd, vecs, iovec_cnt, to, &retlen); return res ? res : retlen; } /* Not implemented writev. Repeatedly use write - on the not so @@ -312,7 +311,8 @@ flash_safe_writev(struct mtd_info *mtd, retlen=0; for (i=0; !res && iwrite(mtd, to, vecs[i].iov_len, &retlen_a, + vecs[i].iov_base); if (retlen_a != vecs[i].iov_len) { printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res); if (i != iovec_cnt-1) @@ -393,7 +393,7 @@ flash_erase_region(struct mtd_info *mtd, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&wait_q, &wait); - if (MTD_ERASE(mtd, erase) < 0) { + if (mtd->erase(mtd, erase) < 0) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); kfree(erase); diff --git a/fs/jffs/jffs_fm.h b/fs/jffs/jffs_fm.h index c794d92..9ee6ad2 100644 --- a/fs/jffs/jffs_fm.h +++ b/fs/jffs/jffs_fm.h @@ -20,7 +20,6 @@ #ifndef __LINUX_JFFS_FM_H__ #define __LINUX_JFFS_FM_H__ -#include #include #include #include diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index 77dc556..7f28ee0 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o bac jffs2-y += super.o debug.o jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o xattr_trusted.o xattr_user.o +jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o +jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking index b794343..c8f0bd6 100644 --- a/fs/jffs2/README.Locking +++ b/fs/jffs2/README.Locking @@ -150,3 +150,24 @@ the buffer. Ordering constraints: Lock wbuf_sem last, after the alloc_sem or and f->sem. + + + c->xattr_sem + ------------ + +This read/write semaphore protects against concurrent access to the +xattr related objects which include stuff in superblock and ic->xref. +In read-only path, write-semaphore is too much exclusion. It's enough +by read-semaphore. But you must hold write-semaphore when updating, +creating or deleting any xattr related object. + +Once xattr_sem released, there would be no assurance for the existence +of those objects. Thus, a series of processes is often required to retry, +when updating such a object is necessary under holding read semaphore. +For example, do_jffs2_getxattr() holds read-semaphore to scan xref and +xdatum at first. But it retries this process with holding write-semaphore +after release read-semaphore, if it's necessary to load name/value pair +from medium. + +Ordering constraints: + Lock xattr_sem last, after the alloc_sem. diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c new file mode 100644 index 0000000..0ae3cd1 --- /dev/null +++ b/fs/jffs2/acl.c @@ -0,0 +1,485 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +static size_t jffs2_acl_size(int count) +{ + if (count <= 4) { + return sizeof(struct jffs2_acl_header) + + count * sizeof(struct jffs2_acl_entry_short); + } else { + return sizeof(struct jffs2_acl_header) + + 4 * sizeof(struct jffs2_acl_entry_short) + + (count - 4) * sizeof(struct jffs2_acl_entry); + } +} + +static int jffs2_acl_count(size_t size) +{ + size_t s; + + size -= sizeof(struct jffs2_acl_header); + s = size - 4 * sizeof(struct jffs2_acl_entry_short); + if (s < 0) { + if (size % sizeof(struct jffs2_acl_entry_short)) + return -1; + return size / sizeof(struct jffs2_acl_entry_short); + } else { + if (s % sizeof(struct jffs2_acl_entry)) + return -1; + return s / sizeof(struct jffs2_acl_entry) + 4; + } +} + +static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size) +{ + void *end = value + size; + struct jffs2_acl_header *header = value; + struct jffs2_acl_entry *entry; + struct posix_acl *acl; + uint32_t ver; + int i, count; + + if (!value) + return NULL; + if (size < sizeof(struct jffs2_acl_header)) + return ERR_PTR(-EINVAL); + ver = je32_to_cpu(header->a_version); + if (ver != JFFS2_ACL_VERSION) { + JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver); + return ERR_PTR(-EINVAL); + } + + value += sizeof(struct jffs2_acl_header); + count = jffs2_acl_count(size); + if (count < 0) + return ERR_PTR(-EINVAL); + if (count == 0) + return NULL; + + acl = posix_acl_alloc(count, GFP_KERNEL); + if (!acl) + return ERR_PTR(-ENOMEM); + + for (i=0; i < count; i++) { + entry = value; + if (value + sizeof(struct jffs2_acl_entry_short) > end) + goto fail; + acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag); + acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm); + switch (acl->a_entries[i].e_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + value += sizeof(struct jffs2_acl_entry_short); + acl->a_entries[i].e_id = ACL_UNDEFINED_ID; + break; + + case ACL_USER: + case ACL_GROUP: + value += sizeof(struct jffs2_acl_entry); + if (value > end) + goto fail; + acl->a_entries[i].e_id = je32_to_cpu(entry->e_id); + break; + + default: + goto fail; + } + } + if (value != end) + goto fail; + return acl; + fail: + posix_acl_release(acl); + return ERR_PTR(-EINVAL); +} + +static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size) +{ + struct jffs2_acl_header *header; + struct jffs2_acl_entry *entry; + void *e; + size_t i; + + *size = jffs2_acl_size(acl->a_count); + header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL); + if (!header) + return ERR_PTR(-ENOMEM); + header->a_version = cpu_to_je32(JFFS2_ACL_VERSION); + e = header + 1; + for (i=0; i < acl->a_count; i++) { + entry = e; + entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag); + entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm); + switch(acl->a_entries[i].e_tag) { + case ACL_USER: + case ACL_GROUP: + entry->e_id = cpu_to_je32(acl->a_entries[i].e_id); + e += sizeof(struct jffs2_acl_entry); + break; + + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + e += sizeof(struct jffs2_acl_entry_short); + break; + + default: + goto fail; + } + } + return header; + fail: + kfree(header); + return ERR_PTR(-EINVAL); +} + +static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl) +{ + struct posix_acl *acl = JFFS2_ACL_NOT_CACHED; + + spin_lock(&inode->i_lock); + if (*i_acl != JFFS2_ACL_NOT_CACHED) + acl = posix_acl_dup(*i_acl); + spin_unlock(&inode->i_lock); + return acl; +} + +static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl) +{ + spin_lock(&inode->i_lock); + if (*i_acl != JFFS2_ACL_NOT_CACHED) + posix_acl_release(*i_acl); + *i_acl = posix_acl_dup(acl); + spin_unlock(&inode->i_lock); +} + +static struct posix_acl *jffs2_get_acl(struct inode *inode, int type) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct posix_acl *acl; + char *value = NULL; + int rc, xprefix; + + switch (type) { + case ACL_TYPE_ACCESS: + acl = jffs2_iget_acl(inode, &f->i_acl_access); + if (acl != JFFS2_ACL_NOT_CACHED) + return acl; + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + break; + case ACL_TYPE_DEFAULT: + acl = jffs2_iget_acl(inode, &f->i_acl_default); + if (acl != JFFS2_ACL_NOT_CACHED) + return acl; + xprefix = JFFS2_XPREFIX_ACL_DEFAULT; + break; + default: + return ERR_PTR(-EINVAL); + } + rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0); + if (rc > 0) { + value = kmalloc(rc, GFP_KERNEL); + if (!value) + return ERR_PTR(-ENOMEM); + rc = do_jffs2_getxattr(inode, xprefix, "", value, rc); + } + if (rc > 0) { + acl = jffs2_acl_from_medium(value, rc); + } else if (rc == -ENODATA || rc == -ENOSYS) { + acl = NULL; + } else { + acl = ERR_PTR(rc); + } + if (value) + kfree(value); + if (!IS_ERR(acl)) { + switch (type) { + case ACL_TYPE_ACCESS: + jffs2_iset_acl(inode, &f->i_acl_access, acl); + break; + case ACL_TYPE_DEFAULT: + jffs2_iset_acl(inode, &f->i_acl_default, acl); + break; + } + } + return acl; +} + +static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + size_t size = 0; + char *value = NULL; + int rc, xprefix; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + + switch (type) { + case ACL_TYPE_ACCESS: + xprefix = JFFS2_XPREFIX_ACL_ACCESS; + if (acl) { + mode_t mode = inode->i_mode; + rc = posix_acl_equiv_mode(acl, &mode); + if (rc < 0) + return rc; + if (inode->i_mode != mode) { + inode->i_mode = mode; + jffs2_dirty_inode(inode); + } + if (rc == 0) + acl = NULL; + } + break; + case ACL_TYPE_DEFAULT: + xprefix = JFFS2_XPREFIX_ACL_DEFAULT; + if (!S_ISDIR(inode->i_mode)) + return acl ? -EACCES : 0; + break; + default: + return -EINVAL; + } + if (acl) { + value = jffs2_acl_to_medium(acl, &size); + if (IS_ERR(value)) + return PTR_ERR(value); + } + + rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); + if (!value && rc == -ENODATA) + rc = 0; + if (value) + kfree(value); + if (!rc) { + switch(type) { + case ACL_TYPE_ACCESS: + jffs2_iset_acl(inode, &f->i_acl_access, acl); + break; + case ACL_TYPE_DEFAULT: + jffs2_iset_acl(inode, &f->i_acl_default, acl); + break; + } + } + return rc; +} + +static int jffs2_check_acl(struct inode *inode, int mask) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_permission(inode, acl, mask); + posix_acl_release(acl); + return rc; + } + return -EAGAIN; +} + +int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + return generic_permission(inode, mask, jffs2_check_acl); +} + +int jffs2_init_acl(struct inode *inode, struct inode *dir) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct posix_acl *acl = NULL, *clone; + mode_t mode; + int rc = 0; + + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + if (!S_ISLNK(inode->i_mode)) { + acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + inode->i_mode &= ~current->fs->umask; + } + if (acl) { + if (S_ISDIR(inode->i_mode)) { + rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); + if (rc) + goto cleanup; + } + clone = posix_acl_clone(acl, GFP_KERNEL); + rc = -ENOMEM; + if (!clone) + goto cleanup; + mode = inode->i_mode; + rc = posix_acl_create_masq(clone, &mode); + if (rc >= 0) { + inode->i_mode = mode; + if (rc > 0) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + } + posix_acl_release(clone); + } + cleanup: + posix_acl_release(acl); + return rc; +} + +void jffs2_clear_acl(struct jffs2_inode_info *f) +{ + if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_access); + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + } + if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) { + posix_acl_release(f->i_acl_default); + f->i_acl_default = JFFS2_ACL_NOT_CACHED; + } +} + +int jffs2_acl_chmod(struct inode *inode) +{ + struct posix_acl *acl, *clone; + int rc; + + if (S_ISLNK(inode->i_mode)) + return -EOPNOTSUPP; + acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl) || !acl) + return PTR_ERR(acl); + clone = posix_acl_clone(acl, GFP_KERNEL); + posix_acl_release(acl); + if (!clone) + return -ENOMEM; + rc = posix_acl_chmod_masq(clone, inode->i_mode); + if (!rc) + rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone); + posix_acl_release(clone); + return rc; +} + +static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_ACCESS); + return retlen; +} + +static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT); + + if (list && retlen <= list_size) + strcpy(list, POSIX_ACL_XATTR_DEFAULT); + return retlen; +} + +static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size) +{ + struct posix_acl *acl; + int rc; + + acl = jffs2_get_acl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (!acl) + return -ENODATA; + rc = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + + return rc; +} + +static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size) +{ + struct posix_acl *acl; + int rc; + + if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) + return -EPERM; + + if (value) { + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + rc = posix_acl_valid(acl); + if (rc) + goto out; + } + } else { + acl = NULL; + } + rc = jffs2_set_acl(inode, type, acl); + out: + posix_acl_release(acl); + return rc; +} + +static int jffs2_acl_access_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size); +} + +static int jffs2_acl_default_setxattr(struct inode *inode, const char *name, + const void *buffer, size_t size, int flags) +{ + if (name[0] != '\0') + return -EINVAL; + return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size); +} + +struct xattr_handler jffs2_acl_access_xattr_handler = { + .prefix = POSIX_ACL_XATTR_ACCESS, + .list = jffs2_acl_access_listxattr, + .get = jffs2_acl_access_getxattr, + .set = jffs2_acl_access_setxattr, +}; + +struct xattr_handler jffs2_acl_default_xattr_handler = { + .prefix = POSIX_ACL_XATTR_DEFAULT, + .list = jffs2_acl_default_listxattr, + .get = jffs2_acl_default_getxattr, + .set = jffs2_acl_default_setxattr, +}; diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h new file mode 100644 index 0000000..fa327db --- /dev/null +++ b/fs/jffs2/acl.h @@ -0,0 +1,45 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +struct jffs2_acl_entry { + jint16_t e_tag; + jint16_t e_perm; + jint32_t e_id; +}; + +struct jffs2_acl_entry_short { + jint16_t e_tag; + jint16_t e_perm; +}; + +struct jffs2_acl_header { + jint32_t a_version; +}; + +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + +#define JFFS2_ACL_NOT_CACHED ((void *)-1) + +extern int jffs2_permission(struct inode *, int, struct nameidata *); +extern int jffs2_acl_chmod(struct inode *); +extern int jffs2_init_acl(struct inode *, struct inode *); +extern void jffs2_clear_acl(struct jffs2_inode_info *); + +extern struct xattr_handler jffs2_acl_access_xattr_handler; +extern struct xattr_handler jffs2_acl_default_xattr_handler; + +#else + +#define jffs2_permission NULL +#define jffs2_acl_chmod(inode) (0) +#define jffs2_init_acl(inode,dir) (0) +#define jffs2_clear_acl(f) + +#endif /* CONFIG_JFFS2_FS_POSIX_ACL */ diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 70f7a89..0282696 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct ic->scan_dents = NULL; cond_resched(); } + jffs2_build_xattr_subsystem(c); c->flags &= ~JFFS2_SB_FLAG_BUILDING; dbg_fsbuild("FS build complete\n"); @@ -178,6 +179,7 @@ exit: jffs2_free_full_dirent(fd); } } + jffs2_clear_xattr_subsystem(c); } return ret; diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index e7944e6..7001ba2 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -412,7 +412,7 @@ void jffs2_free_comprbuf(unsigned char * kfree(comprbuf); } -int jffs2_compressors_init(void) +int __init jffs2_compressors_init(void) { /* Registering compressors */ #ifdef CONFIG_JFFS2_ZLIB diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h index a77e830..509b8b1 100644 --- a/fs/jffs2/compr.h +++ b/fs/jffs2/compr.h @@ -23,8 +23,8 @@ #include #include #include #include -#include -#include +#include "jffs2_fs_i.h" +#include "jffs2_fs_sb.h" #include "nodelist.h" #define JFFS2_RUBINMIPS_PRIORITY 10 diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index 5c63e0c..3681d07 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -15,7 +15,6 @@ #if !defined(__KERNEL__) && !defined(__E #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" #endif -#include #include #include #include diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index 1fe17de..72b4fc1 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -192,13 +192,13 @@ __jffs2_dbg_acct_paranoia_check_nolock(s else my_dirty_size += totlen; - if ((!ref2->next_phys) != (ref2 == jeb->last_node)) { - JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), last_node is at %#08x (mem %p).\n", - ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys, - ref_offset(jeb->last_node), jeb->last_node); + if ((!ref_next(ref2)) != (ref2 == jeb->last_node)) { + JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next at %#08x (mem %p), last_node is at %#08x (mem %p).\n", + ref_offset(ref2), ref2, ref_offset(ref_next(ref2)), ref_next(ref2), + ref_offset(jeb->last_node), jeb->last_node); goto error; } - ref2 = ref2->next_phys; + ref2 = ref_next(ref2); } if (my_used_size != jeb->used_size) { @@ -268,9 +268,9 @@ __jffs2_dbg_dump_node_refs_nolock(struct } printk(JFFS2_DBG); - for (ref = jeb->first_node; ; ref = ref->next_phys) { + for (ref = jeb->first_node; ; ref = ref_next(ref)) { printk("%#08x(%#x)", ref_offset(ref), ref->__totlen); - if (ref->next_phys) + if (ref_next(ref)) printk("->"); else break; diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index 162af6d..3daf3bc 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -13,7 +13,6 @@ #ifndef _JFFS2_DEBUG_H_ #define _JFFS2_DEBUG_H_ -#include #ifndef CONFIG_JFFS2_FS_DEBUG #define CONFIG_JFFS2_FS_DEBUG 0 @@ -171,6 +170,12 @@ #else #define dbg_memalloc(fmt, ...) #endif +/* Watch the XATTR subsystem */ +#ifdef JFFS2_DBG_XATTR_MESSAGES +#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__) +#else +#define dbg_xattr(fmt, ...) +#endif /* "Sanity" checks */ void diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 8bc7a50..edd8371 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -17,8 +17,8 @@ #include #include #include #include -#include -#include +#include "jffs2_fs_i.h" +#include "jffs2_fs_sb.h" #include #include "nodelist.h" @@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_ .rmdir = jffs2_rmdir, .mknod = jffs2_mknod, .rename = jffs2_rename, + .permission = jffs2_permission, .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; /***********************************************************************/ @@ -78,6 +83,9 @@ static struct dentry *jffs2_lookup(struc D1(printk(KERN_DEBUG "jffs2_lookup()\n")); + if (target->d_name.len > JFFS2_MAX_NAME_LEN) + return ERR_PTR(-ENAMETOOLONG); + dir_f = JFFS2_INODE_INFO(dir_i); c = JFFS2_SB_INFO(dir_i->i_sb); @@ -206,12 +214,15 @@ static int jffs2_create(struct inode *di ret = jffs2_do_create(c, dir_f, f, ri, dentry->d_name.name, dentry->d_name.len); - if (ret) { - make_bad_inode(inode); - iput(inode); - jffs2_free_raw_inode(ri); - return ret; - } + if (ret) + goto fail; + + ret = jffs2_init_security(inode, dir_i); + if (ret) + goto fail; + ret = jffs2_init_acl(inode, dir_i); + if (ret) + goto fail; dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); @@ -221,6 +232,12 @@ static int jffs2_create(struct inode *di D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); return 0; + + fail: + make_bad_inode(inode); + iput(inode); + jffs2_free_raw_inode(ri); + return ret; } /***********************************************************************/ @@ -291,7 +308,7 @@ static int jffs2_symlink (struct inode * struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret, targetlen = strlen(target); /* FIXME: If you care. We'd need to use frags for the target @@ -310,8 +327,8 @@ static int jffs2_symlink (struct inode * * Just the node will do for now, though */ namelen = dentry->d_name.len; - ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); @@ -339,7 +356,7 @@ static int jffs2_symlink (struct inode * ri->data_crc = cpu_to_je32(crc32(0, target, targetlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, ri, target, targetlen, ALLOC_NORMAL); jffs2_free_raw_inode(ri); @@ -371,8 +388,20 @@ static int jffs2_symlink (struct inode * up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + + ret = jffs2_init_security(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_init_acl(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); @@ -404,7 +433,7 @@ static int jffs2_symlink (struct inode * rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally @@ -442,7 +471,7 @@ static int jffs2_mkdir (struct inode *di struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret; mode |= S_IFDIR; @@ -457,8 +486,8 @@ static int jffs2_mkdir (struct inode *di * Just the node will do for now, though */ namelen = dentry->d_name.len; - ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, - JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL, + JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); @@ -483,7 +512,7 @@ static int jffs2_mkdir (struct inode *di ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL); jffs2_free_raw_inode(ri); @@ -501,8 +530,20 @@ static int jffs2_mkdir (struct inode *di up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + + ret = jffs2_init_security(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_init_acl(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); @@ -534,7 +575,7 @@ static int jffs2_mkdir (struct inode *di rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally @@ -588,12 +629,12 @@ static int jffs2_mknod (struct inode *di struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; - jint16_t dev; + union jffs2_device_node dev; int devlen = 0; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret; - if (!old_valid_dev(rdev)) + if (!new_valid_dev(rdev)) return -EINVAL; ri = jffs2_alloc_raw_inode(); @@ -602,17 +643,15 @@ static int jffs2_mknod (struct inode *di c = JFFS2_SB_INFO(dir_i->i_sb); - if (S_ISBLK(mode) || S_ISCHR(mode)) { - dev = cpu_to_je16(old_encode_dev(rdev)); - devlen = sizeof(dev); - } + if (S_ISBLK(mode) || S_ISCHR(mode)) + devlen = jffs2_encode_dev(&dev, rdev); /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ namelen = dentry->d_name.len; - ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); @@ -639,7 +678,7 @@ static int jffs2_mknod (struct inode *di ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, ALLOC_NORMAL); jffs2_free_raw_inode(ri); @@ -657,8 +696,20 @@ static int jffs2_mknod (struct inode *di up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + + ret = jffs2_init_security(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + ret = jffs2_init_acl(inode, dir_i); + if (ret) { + jffs2_clear_inode(inode); + return ret; + } + + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { /* Eep. */ jffs2_clear_inode(inode); @@ -693,7 +744,7 @@ static int jffs2_mknod (struct inode *di rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL); if (IS_ERR(fd)) { /* dirent failed to write. Delete the inode normally diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index dad68fd..ad01210 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -30,7 +30,6 @@ static void jffs2_erase_callback(struct #endif static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); -static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); static void jffs2_erase_block(struct jffs2_sb_info *c, @@ -54,8 +53,7 @@ #else /* Linux */ if (!instr) { printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); spin_lock(&c->erase_completion_lock); - list_del(&jeb->list); - list_add(&jeb->list, &c->erase_pending_list); + list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; @@ -87,8 +85,7 @@ #endif /* __ECOS */ /* Erase failed immediately. Refile it on the list */ D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); spin_lock(&c->erase_completion_lock); - list_del(&jeb->list); - list_add(&jeb->list, &c->erase_pending_list); + list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; @@ -136,7 +133,7 @@ void jffs2_erase_pending_blocks(struct j c->used_size -= jeb->used_size; c->dirty_size -= jeb->dirty_size; jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; - jffs2_free_all_node_refs(c, jeb); + jffs2_free_jeb_node_refs(c, jeb); list_add(&jeb->list, &c->erasing_list); spin_unlock(&c->erase_completion_lock); @@ -162,8 +159,7 @@ static void jffs2_erase_succeeded(struct { D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); spin_lock(&c->erase_completion_lock); - list_del(&jeb->list); - list_add_tail(&jeb->list, &c->erase_complete_list); + list_move_tail(&jeb->list, &c->erase_complete_list); spin_unlock(&c->erase_completion_lock); /* Ensure that kupdated calls us again to mark them clean */ jffs2_erase_pending_trigger(c); @@ -179,8 +175,7 @@ static void jffs2_erase_failed(struct jf if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { /* We'd like to give this block another try. */ spin_lock(&c->erase_completion_lock); - list_del(&jeb->list); - list_add(&jeb->list, &c->erase_pending_list); + list_move(&jeb->list, &c->erase_pending_list); c->erasing_size -= c->sector_size; c->dirty_size += c->sector_size; jeb->dirty_size = c->sector_size; @@ -192,8 +187,7 @@ static void jffs2_erase_failed(struct jf spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; - list_del(&jeb->list); - list_add(&jeb->list, &c->bad_list); + list_move(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); @@ -254,7 +248,8 @@ static inline void jffs2_remove_node_ref /* PARANOIA */ if (!ic) { - printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n"); + JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref" + " not found in remove_node_refs()!!\n"); return; } @@ -279,26 +274,42 @@ static inline void jffs2_remove_node_ref printk("\n"); }); - if (ic->nodes == (void *)ic && ic->nlink == 0) - jffs2_del_ino_cache(c, ic); + switch (ic->class) { +#ifdef CONFIG_JFFS2_FS_XATTR + case RAWNODE_CLASS_XATTR_DATUM: + jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic); + break; + case RAWNODE_CLASS_XATTR_REF: + jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic); + break; +#endif + default: + if (ic->nodes == (void *)ic && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + } } -static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_raw_node_ref *ref; + struct jffs2_raw_node_ref *block, *ref; D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset)); - while(jeb->first_node) { - ref = jeb->first_node; - jeb->first_node = ref->next_phys; - /* Remove from the inode-list */ - if (ref->next_in_ino) + block = ref = jeb->first_node; + + while (ref) { + if (ref->flash_offset == REF_LINK_NODE) { + ref = ref->next_in_ino; + jffs2_free_refblock(block); + block = ref; + continue; + } + if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino) jffs2_remove_node_refs_from_ino_list(c, ref, jeb); /* else it was a non-inode node or already removed, so don't bother */ - jffs2_free_raw_node_ref(ref); + ref++; } - jeb->last_node = NULL; + jeb->first_node = jeb->last_node = NULL; } static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset) @@ -351,7 +362,6 @@ fail: static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_raw_node_ref *marker_ref = NULL; size_t retlen; int ret; uint32_t bad_offset; @@ -373,12 +383,8 @@ static void jffs2_mark_erased_block(stru goto filebad; } - jeb->first_node = jeb->last_node = NULL; + /* Everything else got zeroed before the erase */ jeb->free_size = c->sector_size; - jeb->used_size = 0; - jeb->dirty_size = 0; - jeb->wasted_size = 0; - } else { struct kvec vecs[1]; @@ -388,11 +394,7 @@ static void jffs2_mark_erased_block(stru .totlen = cpu_to_je32(c->cleanmarker_size) }; - marker_ref = jffs2_alloc_raw_node_ref(); - if (!marker_ref) { - printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n"); - goto refile; - } + jffs2_prealloc_raw_node_refs(c, jeb, 1); marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); @@ -408,21 +410,13 @@ static void jffs2_mark_erased_block(stru printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", jeb->offset, sizeof(marker), retlen); - jffs2_free_raw_node_ref(marker_ref); goto filebad; } - marker_ref->next_in_ino = NULL; - marker_ref->next_phys = NULL; - marker_ref->flash_offset = jeb->offset | REF_NORMAL; - marker_ref->__totlen = c->cleanmarker_size; - - jeb->first_node = jeb->last_node = marker_ref; - - jeb->free_size = c->sector_size - c->cleanmarker_size; - jeb->used_size = c->cleanmarker_size; - jeb->dirty_size = 0; - jeb->wasted_size = 0; + /* Everything else got zeroed before the erase */ + jeb->free_size = c->sector_size; + /* FIXME Special case for cleanmarker in empty block */ + jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); } spin_lock(&c->erase_completion_lock); diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 9f41712..3ed6e3e 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -54,10 +54,15 @@ const struct file_operations jffs2_file_ struct inode_operations jffs2_file_inode_operations = { - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; -struct address_space_operations jffs2_file_address_operations = +const struct address_space_operations jffs2_file_address_operations = { .readpage = jffs2_readpage, .prepare_write =jffs2_prepare_write, @@ -129,13 +134,13 @@ static int jffs2_prepare_write (struct f struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode ri; struct jffs2_full_dnode *fn; - uint32_t phys_ofs, alloc_len; + uint32_t alloc_len; D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", (unsigned int)inode->i_size, pageofs)); - ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len, - ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) return ret; @@ -161,7 +166,7 @@ static int jffs2_prepare_write (struct f ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(0); - fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL); if (IS_ERR(fn)) { ret = PTR_ERR(fn); @@ -215,12 +220,20 @@ static int jffs2_commit_write (struct fi D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); - if (!start && end == PAGE_CACHE_SIZE) { - /* We need to avoid deadlock with page_cache_read() in - jffs2_garbage_collect_pass(). So we have to mark the - page up to date, to prevent page_cache_read() from - trying to re-lock it. */ - SetPageUptodate(pg); + if (end == PAGE_CACHE_SIZE) { + if (!start) { + /* We need to avoid deadlock with page_cache_read() in + jffs2_garbage_collect_pass(). So we have to mark the + page up to date, to prevent page_cache_read() from + trying to re-lock it. */ + SetPageUptodate(pg); + } else { + /* When writing out the end of a page, write out the + _whole_ page. This helps to reduce the number of + nodes in files which have many short writes, like + syslog files. */ + start = aligned_start = 0; + } } ri = jffs2_alloc_raw_inode(); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 09e5d10..4780f82 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include @@ -33,11 +32,11 @@ static int jffs2_do_setattr (struct inod struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_raw_inode *ri; - unsigned short dev; + union jffs2_device_node dev; unsigned char *mdata = NULL; int mdatalen = 0; unsigned int ivalid; - uint32_t phys_ofs, alloclen; + uint32_t alloclen; int ret; D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); ret = inode_change_ok(inode, iattr); @@ -51,20 +50,24 @@ static int jffs2_do_setattr (struct inod it out again with the appropriate data attached */ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { /* For these, we don't actually need to read the old node */ - dev = old_encode_dev(inode->i_rdev); + mdatalen = jffs2_encode_dev(&dev, inode->i_rdev); mdata = (char *)&dev; - mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(inode->i_mode)) { + down(&f->sem); mdatalen = f->metadata->size; mdata = kmalloc(f->metadata->size, GFP_USER); - if (!mdata) + if (!mdata) { + up(&f->sem); return -ENOMEM; + } ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen); if (ret) { + up(&f->sem); kfree(mdata); return ret; } + up(&f->sem); D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); } @@ -75,8 +78,8 @@ static int jffs2_do_setattr (struct inod return -ENOMEM; } - ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, - ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &alloclen, + ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { jffs2_free_raw_inode(ri); if (S_ISLNK(inode->i_mode & S_IFMT)) @@ -127,7 +130,7 @@ static int jffs2_do_setattr (struct inod else ri->data_crc = cpu_to_je32(0); - new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL); + new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, ALLOC_NORMAL); if (S_ISLNK(inode->i_mode)) kfree(mdata); @@ -180,12 +183,17 @@ static int jffs2_do_setattr (struct inod int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) { - return jffs2_do_setattr(dentry->d_inode, iattr); + int rc; + + rc = jffs2_do_setattr(dentry->d_inode, iattr); + if (!rc && (iattr->ia_valid & ATTR_MODE)) + rc = jffs2_acl_chmod(dentry->d_inode); + return rc; } -int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) +int jffs2_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + struct jffs2_sb_info *c = JFFS2_SB_INFO(dentry->d_sb); unsigned long avail; buf->f_type = JFFS2_SUPER_MAGIC; @@ -218,7 +226,6 @@ void jffs2_clear_inode (struct inode *in struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); - jffs2_do_clear_inode(c, f); } @@ -227,6 +234,8 @@ void jffs2_read_inode (struct inode *ino struct jffs2_inode_info *f; struct jffs2_sb_info *c; struct jffs2_raw_inode latest_node; + union jffs2_device_node jdev; + dev_t rdev = 0; int ret; D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); @@ -258,7 +267,6 @@ void jffs2_read_inode (struct inode *ino inode->i_blocks = (inode->i_size + 511) >> 9; switch (inode->i_mode & S_IFMT) { - jint16_t rdev; case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; @@ -292,8 +300,16 @@ void jffs2_read_inode (struct inode *ino case S_IFBLK: case S_IFCHR: /* Read the device numbers from the media */ + if (f->metadata->size != sizeof(jdev.old) && + f->metadata->size != sizeof(jdev.new)) { + printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size); + up(&f->sem); + jffs2_do_clear_inode(c, f); + make_bad_inode(inode); + return; + } D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); - if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { + if (jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size) < 0) { /* Eep */ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); up(&f->sem); @@ -301,12 +317,15 @@ void jffs2_read_inode (struct inode *ino make_bad_inode(inode); return; } + if (f->metadata->size == sizeof(jdev.old)) + rdev = old_decode_dev(je16_to_cpu(jdev.old)); + else + rdev = new_decode_dev(je32_to_cpu(jdev.new)); case S_IFSOCK: case S_IFIFO: inode->i_op = &jffs2_file_inode_operations; - init_special_inode(inode, inode->i_mode, - old_decode_dev((je16_to_cpu(rdev)))); + init_special_inode(inode, inode->i_mode, rdev); break; default: @@ -492,6 +511,8 @@ #endif } memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); + jffs2_init_xattr_subsystem(c); + if ((ret = jffs2_do_mount_fs(c))) goto out_inohash; @@ -526,6 +547,7 @@ #endif else kfree(c->blocks); out_inohash: + jffs2_clear_xattr_subsystem(c); kfree(c->inocache_list); out_wbuf: jffs2_flash_cleanup(c); @@ -639,13 +661,6 @@ static int jffs2_flash_setup(struct jffs return ret; } - /* add setups for other bizarre flashes here... */ - if (jffs2_nor_ecc(c)) { - ret = jffs2_nor_ecc_flash_setup(c); - if (ret) - return ret; - } - /* and Dataflash */ if (jffs2_dataflash(c)) { ret = jffs2_dataflash_setup(c); @@ -669,11 +684,6 @@ void jffs2_flash_cleanup(struct jffs2_sb jffs2_nand_flash_cleanup(c); } - /* add cleanups for other bizarre flashes here... */ - if (jffs2_nor_ecc(c)) { - jffs2_nor_ecc_flash_cleanup(c); - } - /* and DataFlash */ if (jffs2_dataflash(c)) { jffs2_dataflash_cleanup(c); diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index f9ffece..daff334 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jf struct jffs2_eraseblock *jeb; struct jffs2_raw_node_ref *raw; int ret = 0, inum, nlink; + int xattr = 0; if (down_interruptible(&c->alloc_sem)) return -EINTR; @@ -138,7 +139,7 @@ int jffs2_garbage_collect_pass(struct jf the node CRCs etc. Do it now. */ /* checked_ino is protected by the alloc_sem */ - if (c->checked_ino > c->highest_ino) { + if (c->checked_ino > c->highest_ino && xattr) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); jffs2_dbg_dump_block_lists_nolock(c); @@ -148,6 +149,9 @@ int jffs2_garbage_collect_pass(struct jf spin_unlock(&c->erase_completion_lock); + if (!xattr) + xattr = jffs2_verify_xattr(c); + spin_lock(&c->inocache_lock); ic = jffs2_get_ino_cache(c, c->checked_ino++); @@ -161,6 +165,7 @@ int jffs2_garbage_collect_pass(struct jf D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ic->ino)); spin_unlock(&c->inocache_lock); + jffs2_xattr_delete_inode(c, ic); continue; } switch(ic->state) { @@ -181,6 +186,10 @@ int jffs2_garbage_collect_pass(struct jf and trigger the BUG() above while we haven't yet finished checking all its nodes */ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); + /* We need to come back again for the _same_ inode. We've + made no progress in this case, but that should be OK */ + c->checked_ino--; + up(&c->alloc_sem); sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); return 0; @@ -231,7 +240,7 @@ int jffs2_garbage_collect_pass(struct jf while(ref_obsolete(raw)) { D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); - raw = raw->next_phys; + raw = ref_next(raw); if (unlikely(!raw)) { printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", @@ -248,16 +257,36 @@ int jffs2_garbage_collect_pass(struct jf if (!raw->next_in_ino) { /* Inode-less node. Clean marker, snapshot or something like that */ - /* FIXME: If it's something that needs to be copied, including something - we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ spin_unlock(&c->erase_completion_lock); - jffs2_mark_node_obsolete(c, raw); + if (ref_flags(raw) == REF_PRISTINE) { + /* It's an unknown node with JFFS2_FEATURE_RWCOMPAT_COPY */ + jffs2_garbage_collect_pristine(c, NULL, raw); + } else { + /* Just mark it obsolete */ + jffs2_mark_node_obsolete(c, raw); + } up(&c->alloc_sem); goto eraseit_lock; } ic = jffs2_raw_ref_to_ic(raw); +#ifdef CONFIG_JFFS2_FS_XATTR + /* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr. + * We can decide whether this node is inode or xattr by ic->class. */ + if (ic->class == RAWNODE_CLASS_XATTR_DATUM + || ic->class == RAWNODE_CLASS_XATTR_REF) { + spin_unlock(&c->erase_completion_lock); + + if (ic->class == RAWNODE_CLASS_XATTR_DATUM) { + ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw); + } else { + ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw); + } + goto release_sem; + } +#endif + /* We need to hold the inocache. Either the erase_completion_lock or the inocache_lock are sufficient; we trade down since the inocache_lock causes less contention. */ @@ -499,7 +528,6 @@ static int jffs2_garbage_collect_pristin struct jffs2_raw_node_ref *raw) { union jffs2_node_union *node; - struct jffs2_raw_node_ref *nraw; size_t retlen; int ret; uint32_t phys_ofs, alloclen; @@ -508,15 +536,16 @@ static int jffs2_garbage_collect_pristin D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); - rawlen = ref_totlen(c, c->gcblock, raw); + alloclen = rawlen = ref_totlen(c, c->gcblock, raw); /* Ask for a small amount of space (or the totlen if smaller) because we don't want to force wastage of the end of a block if splitting would work. */ - ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + - JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen); - /* this is not the exact summary size of it, - it is only an upper estimation */ + if (ic && alloclen > sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) + alloclen = sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN; + + ret = jffs2_reserve_space_gc(c, alloclen, &alloclen, rawlen); + /* 'rawlen' is not the exact summary size; it is only an upper estimation */ if (ret) return ret; @@ -580,22 +609,17 @@ static int jffs2_garbage_collect_pristin } break; default: - printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", - ref_offset(raw), je16_to_cpu(node->u.nodetype)); - goto bail; - } - - nraw = jffs2_alloc_raw_node_ref(); - if (!nraw) { - ret = -ENOMEM; - goto out_node; + /* If it's inode-less, we don't _know_ what it is. Just copy it intact */ + if (ic) { + printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", + ref_offset(raw), je16_to_cpu(node->u.nodetype)); + goto bail; + } } /* OK, all the CRCs are good; this node can just be copied as-is. */ retry: - nraw->flash_offset = phys_ofs; - nraw->__totlen = rawlen; - nraw->next_phys = NULL; + phys_ofs = write_ofs(c); ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); @@ -603,17 +627,11 @@ static int jffs2_garbage_collect_pristin printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", rawlen, phys_ofs, ret, retlen); if (retlen) { - /* Doesn't belong to any inode */ - nraw->next_in_ino = NULL; - - nraw->flash_offset |= REF_OBSOLETE; - jffs2_add_physical_node_ref(c, nraw); - jffs2_mark_node_obsolete(c, nraw); + jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL); } else { - printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); - jffs2_free_raw_node_ref(nraw); + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", phys_ofs); } - if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { + if (!retried) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; @@ -625,7 +643,7 @@ static int jffs2_garbage_collect_pristin jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); - ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen); + ret = jffs2_reserve_space_gc(c, rawlen, &dummy, rawlen); /* this is not the exact summary size of it, it is only an upper estimation */ @@ -638,25 +656,13 @@ static int jffs2_garbage_collect_pristin goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); - jffs2_free_raw_node_ref(nraw); } - jffs2_free_raw_node_ref(nraw); if (!ret) ret = -EIO; goto out_node; } - nraw->flash_offset |= REF_PRISTINE; - jffs2_add_physical_node_ref(c, nraw); - - /* Link into per-inode list. This is safe because of the ic - state being INO_STATE_GC. Note that if we're doing this - for an inode which is in-core, the 'nraw' pointer is then - going to be fetched from ic->nodes by our caller. */ - spin_lock(&c->erase_completion_lock); - nraw->next_in_ino = ic->nodes; - ic->nodes = nraw; - spin_unlock(&c->erase_completion_lock); + jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, rawlen, ic); jffs2_mark_node_obsolete(c, raw); D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); @@ -675,19 +681,16 @@ static int jffs2_garbage_collect_metadat struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; struct jffs2_node_frag *last_frag; - jint16_t dev; + union jffs2_device_node dev; char *mdata = NULL, mdatalen = 0; - uint32_t alloclen, phys_ofs, ilen; + uint32_t alloclen, ilen; int ret; if (S_ISBLK(JFFS2_F_I_MODE(f)) || S_ISCHR(JFFS2_F_I_MODE(f)) ) { /* For these, we don't actually need to read the old node */ - /* FIXME: for minor or major > 255. */ - dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | - JFFS2_F_I_RDEV_MIN(f))); + mdatalen = jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f)); mdata = (char *)&dev; - mdatalen = sizeof(dev); D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { mdatalen = fn->size; @@ -706,7 +709,7 @@ static int jffs2_garbage_collect_metadat } - ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen, + ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", @@ -744,7 +747,7 @@ static int jffs2_garbage_collect_metadat ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); - new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC); + new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); @@ -765,7 +768,7 @@ static int jffs2_garbage_collect_dirent( { struct jffs2_full_dirent *new_fd; struct jffs2_raw_dirent rd; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret; rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -787,14 +790,14 @@ static int jffs2_garbage_collect_dirent( rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8)); rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize)); - ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen, + ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &alloclen, JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize)); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", sizeof(rd)+rd.nsize, ret); return ret; } - new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC); + new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, ALLOC_GC); if (IS_ERR(new_fd)) { printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); @@ -922,7 +925,7 @@ static int jffs2_garbage_collect_hole(st struct jffs2_raw_inode ri; struct jffs2_node_frag *frag; struct jffs2_full_dnode *new_fn; - uint32_t alloclen, phys_ofs, ilen; + uint32_t alloclen, ilen; int ret; D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", @@ -1001,14 +1004,14 @@ static int jffs2_garbage_collect_hole(st ri.data_crc = cpu_to_je32(0); ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); - ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen, - JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space_gc(c, sizeof(ri), &alloclen, + JFFS2_SUMMARY_INODE_SIZE); if (ret) { printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", sizeof(ri), ret); return ret; } - new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC); + new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_GC); if (IS_ERR(new_fn)) { printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); @@ -1070,7 +1073,7 @@ static int jffs2_garbage_collect_dnode(s { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; - uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; + uint32_t alloclen, offset, orig_end, orig_start; int ret = 0; unsigned char *comprbuf = NULL, *writebuf; unsigned long pg; @@ -1227,7 +1230,7 @@ static int jffs2_garbage_collect_dnode(s uint32_t cdatalen; uint16_t comprtype = JFFS2_COMPR_NONE; - ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, + ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &alloclen, JFFS2_SUMMARY_INODE_SIZE); if (ret) { @@ -1264,7 +1267,7 @@ static int jffs2_garbage_collect_dnode(s ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); - new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); + new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, ALLOC_GC); jffs2_free_comprbuf(comprbuf, writebuf); diff --git a/fs/jffs2/histo.h b/fs/jffs2/histo.h deleted file mode 100644 index 22a93a0..0000000 --- a/fs/jffs2/histo.h +++ /dev/null @@ -1,3 +0,0 @@ -/* This file provides the bit-probabilities for the input file */ -#define BIT_DIVIDER 629 -static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */ diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h new file mode 100644 index 0000000..2e0cc8e --- /dev/null +++ b/fs/jffs2/jffs2_fs_i.h @@ -0,0 +1,55 @@ +/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */ + +#ifndef _JFFS2_FS_I +#define _JFFS2_FS_I + +#include +#include +#include +#include + +struct jffs2_inode_info { + /* We need an internal mutex similar to inode->i_mutex. + Unfortunately, we can't used the existing one, because + either the GC would deadlock, or we'd have to release it + before letting GC proceed. Or we'd have to put ugliness + into the GC code so it didn't attempt to obtain the i_mutex + for the inode(s) which are already locked */ + struct semaphore sem; + + /* The highest (datanode) version number used for this ino */ + uint32_t highest_version; + + /* List of data fragments which make up the file */ + struct rb_root fragtree; + + /* There may be one datanode which isn't referenced by any of the + above fragments, if it contains a metadata update but no actual + data - or if this is a directory inode */ + /* This also holds the _only_ dnode for symlinks/device nodes, + etc. */ + struct jffs2_full_dnode *metadata; + + /* Directory entries */ + struct jffs2_full_dirent *dents; + + /* The target path if this is the inode of a symlink */ + unsigned char *target; + + /* Some stuff we just have to keep in-core at all times, for each inode. */ + struct jffs2_inode_cache *inocache; + + uint16_t flags; + uint8_t usercompr; +#if !defined (__ECOS) +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) + struct inode vfs_inode; +#endif +#endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + struct posix_acl *i_acl_access; + struct posix_acl *i_acl_default; +#endif +}; + +#endif /* _JFFS2_FS_I */ diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h new file mode 100644 index 0000000..b985949 --- /dev/null +++ b/fs/jffs2/jffs2_fs_sb.h @@ -0,0 +1,136 @@ +/* $Id: jffs2_fs_sb.h,v 1.54 2005/09/21 13:37:34 dedekind Exp $ */ + +#ifndef _JFFS2_FS_SB +#define _JFFS2_FS_SB + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JFFS2_SB_FLAG_RO 1 +#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ +#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ + +struct jffs2_inodirty; + +/* A struct for the overall file system control. Pointers to + jffs2_sb_info structs are named `c' in the source code. + Nee jffs_control +*/ +struct jffs2_sb_info { + struct mtd_info *mtd; + + uint32_t highest_ino; + uint32_t checked_ino; + + unsigned int flags; + + struct task_struct *gc_task; /* GC task struct */ + struct completion gc_thread_start; /* GC thread start completion */ + struct completion gc_thread_exit; /* GC thread exit completion port */ + + struct semaphore alloc_sem; /* Used to protect all the following + fields, and also to protect against + out-of-order writing of nodes. And GC. */ + uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER + (i.e. zero for OOB CLEANMARKER */ + + uint32_t flash_size; + uint32_t used_size; + uint32_t dirty_size; + uint32_t wasted_size; + uint32_t free_size; + uint32_t erasing_size; + uint32_t bad_size; + uint32_t sector_size; + uint32_t unchecked_size; + + uint32_t nr_free_blocks; + uint32_t nr_erasing_blocks; + + /* Number of free blocks there must be before we... */ + uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ + uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ + uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ + uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ + uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ + + uint32_t nospc_dirty_size; + + uint32_t nr_blocks; + struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks + * from the offset (blocks[ofs / sector_size]) */ + struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ + + struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ + + struct list_head clean_list; /* Blocks 100% full of clean data */ + struct list_head very_dirty_list; /* Blocks with lots of dirty space */ + struct list_head dirty_list; /* Blocks with some dirty space */ + struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ + struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ + struct list_head erasing_list; /* Blocks which are currently erasing */ + struct list_head erase_pending_list; /* Blocks which need erasing now */ + struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ + struct list_head free_list; /* Blocks which are free and ready to be used */ + struct list_head bad_list; /* Bad blocks. */ + struct list_head bad_used_list; /* Bad blocks with valid data in. */ + + spinlock_t erase_completion_lock; /* Protect free_list and erasing_list + against erase completion handler */ + wait_queue_head_t erase_wait; /* For waiting for erases to complete */ + + wait_queue_head_t inocache_wq; + struct jffs2_inode_cache **inocache_list; + spinlock_t inocache_lock; + + /* Sem to allow jffs2_garbage_collect_deletion_dirent to + drop the erase_completion_lock while it's holding a pointer + to an obsoleted node. I don't like this. Alternatives welcomed. */ + struct semaphore erase_free_sem; + + uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ + +#ifdef CONFIG_JFFS2_FS_WRITEBUFFER + /* Write-behind buffer for NAND flash */ + unsigned char *wbuf; + unsigned char *oobbuf; + uint32_t wbuf_ofs; + uint32_t wbuf_len; + struct jffs2_inodirty *wbuf_inodes; + + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + + /* Information about out-of-band area usage... */ + struct nand_ecclayout *ecclayout; + uint32_t badblock_pos; + uint32_t fsdata_pos; + uint32_t fsdata_len; +#endif + + struct jffs2_summary *summary; /* Summary information */ + +#ifdef CONFIG_JFFS2_FS_XATTR +#define XATTRINDEX_HASHSIZE (57) + uint32_t highest_xid; + uint32_t highest_xseqno; + struct list_head xattrindex[XATTRINDEX_HASHSIZE]; + struct list_head xattr_unchecked; + struct list_head xattr_dead_list; + struct jffs2_xattr_ref *xref_dead_list; + struct jffs2_xattr_ref *xref_temp; + struct rw_semaphore xattr_sem; + uint32_t xdatum_mem_usage; + uint32_t xdatum_mem_threshold; +#endif + /* OS-private pointer for getting back to master superblock info */ + void *os_priv; +}; + +#endif /* _JFFS2_FB_SB */ diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 036cbd1..33f2910 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -26,6 +26,10 @@ static kmem_cache_t *tmp_dnode_info_slab static kmem_cache_t *raw_node_ref_slab; static kmem_cache_t *node_frag_slab; static kmem_cache_t *inode_cache_slab; +#ifdef CONFIG_JFFS2_FS_XATTR +static kmem_cache_t *xattr_datum_cache; +static kmem_cache_t *xattr_ref_cache; +#endif int __init jffs2_create_slab_caches(void) { @@ -53,8 +57,8 @@ int __init jffs2_create_slab_caches(void if (!tmp_dnode_info_slab) goto err; - raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", - sizeof(struct jffs2_raw_node_ref), + raw_node_ref_slab = kmem_cache_create("jffs2_refblock", + sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1), 0, 0, NULL, NULL); if (!raw_node_ref_slab) goto err; @@ -68,8 +72,24 @@ int __init jffs2_create_slab_caches(void inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, 0, NULL, NULL); - if (inode_cache_slab) - return 0; + if (!inode_cache_slab) + goto err; + +#ifdef CONFIG_JFFS2_FS_XATTR + xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum", + sizeof(struct jffs2_xattr_datum), + 0, 0, NULL, NULL); + if (!xattr_datum_cache) + goto err; + + xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref", + sizeof(struct jffs2_xattr_ref), + 0, 0, NULL, NULL); + if (!xattr_ref_cache) + goto err; +#endif + + return 0; err: jffs2_destroy_slab_caches(); return -ENOMEM; @@ -91,6 +111,12 @@ void jffs2_destroy_slab_caches(void) kmem_cache_destroy(node_frag_slab); if(inode_cache_slab) kmem_cache_destroy(inode_cache_slab); +#ifdef CONFIG_JFFS2_FS_XATTR + if (xattr_datum_cache) + kmem_cache_destroy(xattr_datum_cache); + if (xattr_ref_cache) + kmem_cache_destroy(xattr_ref_cache); +#endif } struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) @@ -164,15 +190,65 @@ void jffs2_free_tmp_dnode_info(struct jf kmem_cache_free(tmp_dnode_info_slab, x); } -struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) +static struct jffs2_raw_node_ref *jffs2_alloc_refblock(void) { struct jffs2_raw_node_ref *ret; + ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); - dbg_memalloc("%p\n", ret); + if (ret) { + int i = 0; + for (i=0; i < REFS_PER_BLOCK; i++) { + ret[i].flash_offset = REF_EMPTY_NODE; + ret[i].next_in_ino = NULL; + } + ret[i].flash_offset = REF_LINK_NODE; + ret[i].next_in_ino = NULL; + } return ret; } -void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x) +int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int nr) +{ + struct jffs2_raw_node_ref **p, *ref; + int i = nr; + + dbg_memalloc("%d\n", nr); + + p = &jeb->last_node; + ref = *p; + + dbg_memalloc("Reserving %d refs for block @0x%08x\n", nr, jeb->offset); + + /* If jeb->last_node is really a valid node then skip over it */ + if (ref && ref->flash_offset != REF_EMPTY_NODE) + ref++; + + while (i) { + if (!ref) { + dbg_memalloc("Allocating new refblock linked from %p\n", p); + ref = *p = jffs2_alloc_refblock(); + if (!ref) + return -ENOMEM; + } + if (ref->flash_offset == REF_LINK_NODE) { + p = &ref->next_in_ino; + ref = *p; + continue; + } + i--; + ref++; + } + jeb->allocated_refs = nr; + + dbg_memalloc("Reserved %d refs for block @0x%08x, last_node is %p (%08x,%p)\n", + nr, jeb->offset, jeb->last_node, jeb->last_node->flash_offset, + jeb->last_node->next_in_ino); + + return 0; +} + +void jffs2_free_refblock(struct jffs2_raw_node_ref *x) { dbg_memalloc("%p\n", x); kmem_cache_free(raw_node_ref_slab, x); @@ -205,3 +281,42 @@ void jffs2_free_inode_cache(struct jffs2 dbg_memalloc("%p\n", x); kmem_cache_free(inode_cache_slab, x); } + +#ifdef CONFIG_JFFS2_FS_XATTR +struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void) +{ + struct jffs2_xattr_datum *xd; + xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL); + dbg_memalloc("%p\n", xd); + + memset(xd, 0, sizeof(struct jffs2_xattr_datum)); + xd->class = RAWNODE_CLASS_XATTR_DATUM; + xd->node = (void *)xd; + INIT_LIST_HEAD(&xd->xindex); + return xd; +} + +void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd) +{ + dbg_memalloc("%p\n", xd); + kmem_cache_free(xattr_datum_cache, xd); +} + +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) +{ + struct jffs2_xattr_ref *ref; + ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL); + dbg_memalloc("%p\n", ref); + + memset(ref, 0, sizeof(struct jffs2_xattr_ref)); + ref->class = RAWNODE_CLASS_XATTR_REF; + ref->node = (void *)ref; + return ref; +} + +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref) +{ + dbg_memalloc("%p\n", ref); + kmem_cache_free(xattr_ref_cache, ref); +} +#endif diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 1d46677..7675b33 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -438,8 +438,7 @@ #ifndef __ECOS if (c->mtd->point) { err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); if (!err && retlen < tn->csize) { - JFFS2_WARNING("MTD point returned len too short: %zu " - "instead of %u.\n", retlen, tn->csize); + JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); c->mtd->unpoint(c->mtd, buffer, ofs, len); } else if (err) JFFS2_WARNING("MTD point failed: error code %d.\n", err); @@ -462,8 +461,7 @@ #endif } if (retlen != len) { - JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", - ofs, retlen, len); + JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len); err = -EIO; goto free_out; } @@ -908,6 +906,9 @@ void jffs2_del_ino_cache(struct jffs2_sb { struct jffs2_inode_cache **prev; +#ifdef CONFIG_JFFS2_FS_XATTR + BUG_ON(old->xref); +#endif dbg_inocache("del %p (ino #%u)\n", old, old->ino); spin_lock(&c->inocache_lock); @@ -940,6 +941,7 @@ void jffs2_free_ino_caches(struct jffs2_ this = c->inocache_list[i]; while (this) { next = this->next; + jffs2_xattr_free_inode(c, this); jffs2_free_inode_cache(this); this = next; } @@ -954,9 +956,13 @@ void jffs2_free_raw_node_refs(struct jff for (i=0; inr_blocks; i++) { this = c->blocks[i].first_node; - while(this) { - next = this->next_phys; - jffs2_free_raw_node_ref(this); + while (this) { + if (this[REFS_PER_BLOCK].flash_offset == REF_LINK_NODE) + next = this[REFS_PER_BLOCK].next_in_ino; + else + next = NULL; + + jffs2_free_refblock(this); this = next; } c->blocks[i].first_node = c->blocks[i].last_node = NULL; @@ -1047,3 +1053,169 @@ void jffs2_kill_fragtree(struct rb_root cond_resched(); } } + +struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + uint32_t ofs, uint32_t len, + struct jffs2_inode_cache *ic) +{ + struct jffs2_raw_node_ref *ref; + + BUG_ON(!jeb->allocated_refs); + jeb->allocated_refs--; + + ref = jeb->last_node; + + dbg_noderef("Last node at %p is (%08x,%p)\n", ref, ref->flash_offset, + ref->next_in_ino); + + while (ref->flash_offset != REF_EMPTY_NODE) { + if (ref->flash_offset == REF_LINK_NODE) + ref = ref->next_in_ino; + else + ref++; + } + + dbg_noderef("New ref is %p (%08x becomes %08x,%p) len 0x%x\n", ref, + ref->flash_offset, ofs, ref->next_in_ino, len); + + ref->flash_offset = ofs; + + if (!jeb->first_node) { + jeb->first_node = ref; + BUG_ON(ref_offset(ref) != jeb->offset); + } else if (unlikely(ref_offset(ref) != jeb->offset + c->sector_size - jeb->free_size)) { + uint32_t last_len = ref_totlen(c, jeb, jeb->last_node); + + JFFS2_ERROR("Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n", + ref, ref_offset(ref), ref_offset(ref)+len, + ref_offset(jeb->last_node), + ref_offset(jeb->last_node)+last_len); + BUG(); + } + jeb->last_node = ref; + + if (ic) { + ref->next_in_ino = ic->nodes; + ic->nodes = ref; + } else { + ref->next_in_ino = NULL; + } + + switch(ref_flags(ref)) { + case REF_UNCHECKED: + c->unchecked_size += len; + jeb->unchecked_size += len; + break; + + case REF_NORMAL: + case REF_PRISTINE: + c->used_size += len; + jeb->used_size += len; + break; + + case REF_OBSOLETE: + c->dirty_size += len; + jeb->dirty_size += len; + break; + } + c->free_size -= len; + jeb->free_size -= len; + +#ifdef TEST_TOTLEN + /* Set (and test) __totlen field... for now */ + ref->__totlen = len; + ref_totlen(c, jeb, ref); +#endif + return ref; +} + +/* No locking, no reservation of 'ref'. Do not use on a live file system */ +int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + uint32_t size) +{ + if (!size) + return 0; + if (unlikely(size > jeb->free_size)) { + printk(KERN_CRIT "Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n", + size, jeb->free_size, jeb->wasted_size); + BUG(); + } + /* REF_EMPTY_NODE is !obsolete, so that works OK */ + if (jeb->last_node && ref_obsolete(jeb->last_node)) { +#ifdef TEST_TOTLEN + jeb->last_node->__totlen += size; +#endif + c->dirty_size += size; + c->free_size -= size; + jeb->dirty_size += size; + jeb->free_size -= size; + } else { + uint32_t ofs = jeb->offset + c->sector_size - jeb->free_size; + ofs |= REF_OBSOLETE; + + jffs2_link_node_ref(c, jeb, ofs, size, NULL); + } + + return 0; +} + +/* Calculate totlen from surrounding nodes or eraseblock */ +static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ref_end; + struct jffs2_raw_node_ref *next_ref = ref_next(ref); + + if (next_ref) + ref_end = ref_offset(next_ref); + else { + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + + /* Last node in block. Use free_space */ + if (unlikely(ref != jeb->last_node)) { + printk(KERN_CRIT "ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n", + ref, ref_offset(ref), jeb->last_node, jeb->last_node?ref_offset(jeb->last_node):0); + BUG(); + } + ref_end = jeb->offset + c->sector_size - jeb->free_size; + } + return ref_end - ref_offset(ref); +} + +uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref) +{ + uint32_t ret; + + ret = __ref_totlen(c, jeb, ref); + +#ifdef TEST_TOTLEN + if (unlikely(ret != ref->__totlen)) { + if (!jeb) + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + + printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", + ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, + ret, ref->__totlen); + if (ref_next(ref)) { + printk(KERN_CRIT "next %p (0x%08x-0x%08x)\n", ref_next(ref), ref_offset(ref_next(ref)), + ref_offset(ref_next(ref))+ref->__totlen); + } else + printk(KERN_CRIT "No next ref. jeb->last_node is %p\n", jeb->last_node); + + printk(KERN_CRIT "jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n", jeb->wasted_size, jeb->dirty_size, jeb->used_size, jeb->free_size); + +#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS) + __jffs2_dbg_dump_node_refs_nolock(c, jeb); +#endif + + WARN_ON(1); + + ret = ref->__totlen; + } +#endif /* TEST_TOTLEN */ + return ret; +} diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 23a67bb..cae92c1 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -14,12 +14,13 @@ #ifndef __JFFS2_NODELIST_H__ #define __JFFS2_NODELIST_H__ -#include #include #include #include -#include -#include +#include "jffs2_fs_sb.h" +#include "jffs2_fs_i.h" +#include "xattr.h" +#include "acl.h" #include "summary.h" #ifdef __ECOS @@ -75,14 +76,50 @@ #define JFFS2_MIN_NODE_HEADER sizeof(str struct jffs2_raw_node_ref { struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref - for this inode. If this is the last, it points to the inode_cache - for this inode instead. The inode_cache will have NULL in the first - word so you know when you've got there :) */ - struct jffs2_raw_node_ref *next_phys; + for this object. If this _is_ the last, it points to the inode_cache, + xattr_ref or xattr_datum instead. The common part of those structures + has NULL in the first word. See jffs2_raw_ref_to_ic() below */ uint32_t flash_offset; +#define TEST_TOTLEN +#ifdef TEST_TOTLEN uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ +#endif }; +#define REF_LINK_NODE ((int32_t)-1) +#define REF_EMPTY_NODE ((int32_t)-2) + +/* Use blocks of about 256 bytes */ +#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1) + +static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref) +{ + ref++; + + /* Link to another block of refs */ + if (ref->flash_offset == REF_LINK_NODE) { + ref = ref->next_in_ino; + if (!ref) + return ref; + } + + /* End of chain */ + if (ref->flash_offset == REF_EMPTY_NODE) + return NULL; + + return ref; +} + +static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) +{ + while(raw->next_in_ino) + raw = raw->next_in_ino; + + /* NB. This can be a jffs2_xattr_datum or jffs2_xattr_ref and + not actually a jffs2_inode_cache. Check ->class */ + return ((struct jffs2_inode_cache *)raw); +} + /* flash_offset & 3 always has to be zero, because nodes are always aligned at 4 bytes. So we have a couple of extra bits to play with, which indicate the node's status; see below: */ @@ -95,6 +132,11 @@ #define ref_offset(ref) ((ref)->flash_o #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) +/* NB: REF_PRISTINE for an inode-less node (ref->next_in_ino == NULL) indicates + it is an unknown node of type JFFS2_NODETYPE_RWCOMPAT_COPY, so it'll get + copied. If you need to do anything different to GC inode-less nodes, then + you need to modify gc.c accordingly. */ + /* For each inode in the filesystem, we need to keep a record of nlink, because it would be a PITA to scan the whole directory tree at read_inode() time to calculate it, and to keep sufficient information @@ -103,15 +145,27 @@ #define mark_ref_normal(ref) do { (re a pointer to the first physical node which is part of this inode, too. */ struct jffs2_inode_cache { + /* First part of structure is shared with other objects which + can terminate the raw node refs' next_in_ino list -- which + currently struct jffs2_xattr_datum and struct jffs2_xattr_ref. */ + struct jffs2_full_dirent *scan_dents; /* Used during scan to hold temporary lists of dirents, and later must be set to NULL to mark the end of the raw_node_ref->next_in_ino chain. */ - struct jffs2_inode_cache *next; struct jffs2_raw_node_ref *nodes; + uint8_t class; /* It's used for identification */ + + /* end of shared structure */ + + uint8_t flags; + uint16_t state; uint32_t ino; + struct jffs2_inode_cache *next; +#ifdef CONFIG_JFFS2_FS_XATTR + struct jffs2_xattr_ref *xref; +#endif int nlink; - int state; }; /* Inode states for 'state' above. We need the 'GC' state to prevent @@ -125,8 +179,16 @@ #define INO_STATE_GC 4 /* GCing a 'pris #define INO_STATE_READING 5 /* In read_inode() */ #define INO_STATE_CLEARING 6 /* In clear_inode() */ +#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */ + +#define RAWNODE_CLASS_INODE_CACHE 0 +#define RAWNODE_CLASS_XATTR_DATUM 1 +#define RAWNODE_CLASS_XATTR_REF 2 + #define INOCACHE_HASHSIZE 128 +#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size) + /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. @@ -192,6 +254,7 @@ struct jffs2_eraseblock uint32_t wasted_size; uint32_t free_size; /* Note that sector_size - free_size is the address of the first free space */ + uint32_t allocated_refs; struct jffs2_raw_node_ref *first_node; struct jffs2_raw_node_ref *last_node; @@ -203,57 +266,7 @@ static inline int jffs2_blocks_use_vmall return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024); } -/* Calculate totlen from surrounding nodes or eraseblock */ -static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, - struct jffs2_eraseblock *jeb, - struct jffs2_raw_node_ref *ref) -{ - uint32_t ref_end; - - if (ref->next_phys) - ref_end = ref_offset(ref->next_phys); - else { - if (!jeb) - jeb = &c->blocks[ref->flash_offset / c->sector_size]; - - /* Last node in block. Use free_space */ - BUG_ON(ref != jeb->last_node); - ref_end = jeb->offset + c->sector_size - jeb->free_size; - } - return ref_end - ref_offset(ref); -} - -static inline uint32_t ref_totlen(struct jffs2_sb_info *c, - struct jffs2_eraseblock *jeb, - struct jffs2_raw_node_ref *ref) -{ - uint32_t ret; - -#if CONFIG_JFFS2_FS_DEBUG > 0 - if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { - printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", - jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); - BUG(); - } -#endif - -#if 1 - ret = ref->__totlen; -#else - /* This doesn't actually work yet */ - ret = __ref_totlen(c, jeb, ref); - if (ret != ref->__totlen) { - printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", - ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, - ret, ref->__totlen); - if (!jeb) - jeb = &c->blocks[ref->flash_offset / c->sector_size]; - jffs2_dbg_dump_node_refs_nolock(c, jeb); - BUG(); - } -#endif - return ret; -} +#define ref_totlen(a, b, c) __jffs2_ref_totlen((a), (b), (c)) #define ALLOC_NORMAL 0 /* Normal allocation */ #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ @@ -268,13 +281,15 @@ #define ISDIRTY(size) ((size) > sizeof #define PAD(x) (((x)+3)&~3) -static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw) +static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev) { - while(raw->next_in_ino) { - raw = raw->next_in_ino; + if (old_valid_dev(rdev)) { + jdev->old = cpu_to_je16(old_encode_dev(rdev)); + return sizeof(jdev->old); + } else { + jdev->new = cpu_to_je32(new_encode_dev(rdev)); + return sizeof(jdev->new); } - - return ((struct jffs2_inode_cache *)raw); } static inline struct jffs2_node_frag *frag_first(struct rb_root *root) @@ -299,7 +314,6 @@ static inline struct jffs2_node_frag *fr return rb_entry(node, struct jffs2_node_frag, rb); } -#define rb_parent(rb) ((rb)->rb_parent) #define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) #define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) @@ -324,28 +338,44 @@ void jffs2_obsolete_node_frag(struct jff int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn); +struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + uint32_t ofs, uint32_t len, + struct jffs2_inode_cache *ic); +extern uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *ref); /* nodemgmt.c */ int jffs2_thread_should_wake(struct jffs2_sb_info *c); -int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, +int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, int prio, uint32_t sumsize); -int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, +int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, uint32_t sumsize); -int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); +struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, + uint32_t ofs, uint32_t len, + struct jffs2_inode_cache *ic); void jffs2_complete_reservation(struct jffs2_sb_info *c); void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); /* write.c */ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); -struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode); -struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode); +struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_raw_inode *ri, const unsigned char *data, + uint32_t datalen, int alloc_mode); +struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_raw_dirent *rd, const unsigned char *name, + uint32_t namelen, int alloc_mode); int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, unsigned char *buf, uint32_t offset, uint32_t writelen, uint32_t *retlen); -int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen); -int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time); -int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time); +int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, + struct jffs2_raw_inode *ri, const char *name, int namelen); +int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, + int namelen, struct jffs2_inode_info *dead_f, uint32_t time); +int jffs2_do_link(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, + uint8_t type, const char *name, int namelen, uint32_t time); /* readinode.c */ @@ -368,12 +398,19 @@ struct jffs2_raw_inode *jffs2_alloc_raw_ void jffs2_free_raw_inode(struct jffs2_raw_inode *); struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void); void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *); -struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void); -void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *); +int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int nr); +void jffs2_free_refblock(struct jffs2_raw_node_ref *); struct jffs2_node_frag *jffs2_alloc_node_frag(void); void jffs2_free_node_frag(struct jffs2_node_frag *); struct jffs2_inode_cache *jffs2_alloc_inode_cache(void); void jffs2_free_inode_cache(struct jffs2_inode_cache *); +#ifdef CONFIG_JFFS2_FS_XATTR +struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void); +void jffs2_free_xattr_datum(struct jffs2_xattr_datum *); +struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void); +void jffs2_free_xattr_ref(struct jffs2_xattr_ref *); +#endif /* gc.c */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); @@ -389,16 +426,16 @@ char *jffs2_getlink(struct jffs2_sb_info /* scan.c */ int jffs2_scan_medium(struct jffs2_sb_info *c); void jffs2_rotate_lists(struct jffs2_sb_info *c); -int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf, - uint32_t ofs, uint32_t len); struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino); int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t size); /* build.c */ int jffs2_do_mount_fs(struct jffs2_sb_info *c); /* erase.c */ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); +void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); #ifdef CONFIG_JFFS2_FS_WRITEBUFFER /* wbuf.c */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 49127a1..d883769 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -23,13 +23,12 @@ #include "debug.h" * jffs2_reserve_space - request physical space to write nodes to flash * @c: superblock info * @minsize: Minimum acceptable size of allocation - * @ofs: Returned value of node offset * @len: Returned value of allocation length * @prio: Allocation type - ALLOC_{NORMAL,DELETION} * * Requests a block of physical space on the flash. Returns zero for success - * and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC - * or other error if appropriate. + * and puts 'len' into the appropriate place, or returns -ENOSPC or other + * error if appropriate. Doesn't return len since that's * * If it returns zero, jffs2_reserve_space() also downs the per-filesystem * allocation semaphore, to prevent more than one allocation from being @@ -40,9 +39,9 @@ #include "debug.h" */ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, - uint32_t *ofs, uint32_t *len, uint32_t sumsize); + uint32_t *len, uint32_t sumsize); -int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, +int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, int prio, uint32_t sumsize) { int ret = -EAGAIN; @@ -132,19 +131,21 @@ int jffs2_reserve_space(struct jffs2_sb_ spin_lock(&c->erase_completion_lock); } - ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); + ret = jffs2_do_reserve_space(c, minsize, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); + if (!ret) + ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); if (ret) up(&c->alloc_sem); return ret; } -int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, - uint32_t *len, uint32_t sumsize) +int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, + uint32_t *len, uint32_t sumsize) { int ret = -EAGAIN; minsize = PAD(minsize); @@ -153,12 +154,15 @@ int jffs2_reserve_space_gc(struct jffs2_ spin_lock(&c->erase_completion_lock); while(ret == -EAGAIN) { - ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize); + ret = jffs2_do_reserve_space(c, minsize, len, sumsize); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); } } spin_unlock(&c->erase_completion_lock); + if (!ret) + ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); + return ret; } @@ -207,8 +211,7 @@ static int jffs2_find_nextblock(struct j struct jffs2_eraseblock *ejeb; ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); - list_del(&ejeb->list); - list_add_tail(&ejeb->list, &c->erase_pending_list); + list_move_tail(&ejeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); D1(printk(KERN_DEBUG "jffs2_find_nextblock: Triggering erase of erasable block at 0x%08x\n", @@ -259,10 +262,11 @@ static int jffs2_find_nextblock(struct j } /* Called with alloc sem _and_ erase_completion_lock */ -static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize) +static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, + uint32_t *len, uint32_t sumsize) { struct jffs2_eraseblock *jeb = c->nextblock; - uint32_t reserved_size; /* for summary information at the end of the jeb */ + uint32_t reserved_size; /* for summary information at the end of the jeb */ int ret; restart: @@ -312,6 +316,8 @@ static int jffs2_do_reserve_space(struct } } else { if (jeb && minsize > jeb->free_size) { + uint32_t waste; + /* Skip the end of this block and file it as having some dirty space */ /* If there's a pending write to it, flush now */ @@ -324,10 +330,26 @@ static int jffs2_do_reserve_space(struct goto restart; } - c->wasted_size += jeb->free_size; - c->free_size -= jeb->free_size; - jeb->wasted_size += jeb->free_size; - jeb->free_size = 0; + spin_unlock(&c->erase_completion_lock); + + ret = jffs2_prealloc_raw_node_refs(c, jeb, 1); + if (ret) + return ret; + /* Just lock it again and continue. Nothing much can change because + we hold c->alloc_sem anyway. In fact, it's not entirely clear why + we hold c->erase_completion_lock in the majority of this function... + but that's a question for another (more caffeine-rich) day. */ + spin_lock(&c->erase_completion_lock); + + waste = jeb->free_size; + jffs2_link_node_ref(c, jeb, + (jeb->offset + c->sector_size - waste) | REF_OBSOLETE, + waste, NULL); + /* FIXME: that made it count as dirty. Convert to wasted */ + jeb->dirty_size -= waste; + c->dirty_size -= waste; + jeb->wasted_size += waste; + c->wasted_size += waste; jffs2_close_nextblock(c, jeb); jeb = NULL; @@ -349,7 +371,6 @@ static int jffs2_do_reserve_space(struct } /* OK, jeb (==c->nextblock) is now pointing at a block which definitely has enough space */ - *ofs = jeb->offset + (c->sector_size - jeb->free_size); *len = jeb->free_size - reserved_size; if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && @@ -365,7 +386,8 @@ static int jffs2_do_reserve_space(struct spin_lock(&c->erase_completion_lock); } - D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); + D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", + *len, jeb->offset + (c->sector_size - jeb->free_size))); return 0; } @@ -374,7 +396,6 @@ static int jffs2_do_reserve_space(struct * @c: superblock info * @new: new node reference to add * @len: length of this physical node - * @dirty: dirty flag for new node * * Should only be used to report nodes for which space has been allocated * by jffs2_reserve_space. @@ -382,42 +403,30 @@ static int jffs2_do_reserve_space(struct * Must be called with the alloc_sem held. */ -int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) +struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, + uint32_t ofs, uint32_t len, + struct jffs2_inode_cache *ic) { struct jffs2_eraseblock *jeb; - uint32_t len; + struct jffs2_raw_node_ref *new; - jeb = &c->blocks[new->flash_offset / c->sector_size]; - len = ref_totlen(c, jeb, new); + jeb = &c->blocks[ofs / c->sector_size]; - D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); + D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", + ofs & ~3, ofs & 3, len)); #if 1 - /* we could get some obsolete nodes after nextblock was refiled - in wbuf.c */ - if ((c->nextblock || !ref_obsolete(new)) - &&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) { + /* Allow non-obsolete nodes only to be added at the end of c->nextblock, + if c->nextblock is set. Note that wbuf.c will file obsolete nodes + even after refiling c->nextblock */ + if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE)) + && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) { printk(KERN_WARNING "argh. node added in wrong place\n"); - jffs2_free_raw_node_ref(new); - return -EINVAL; + return ERR_PTR(-EINVAL); } #endif spin_lock(&c->erase_completion_lock); - if (!jeb->first_node) - jeb->first_node = new; - if (jeb->last_node) - jeb->last_node->next_phys = new; - jeb->last_node = new; - - jeb->free_size -= len; - c->free_size -= len; - if (ref_obsolete(new)) { - jeb->dirty_size += len; - c->dirty_size += len; - } else { - jeb->used_size += len; - c->used_size += len; - } + new = jffs2_link_node_ref(c, jeb, ofs, len, ic); if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) { /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ @@ -438,7 +447,7 @@ #endif spin_unlock(&c->erase_completion_lock); - return 0; + return new; } @@ -470,8 +479,9 @@ void jffs2_mark_node_obsolete(struct jff struct jffs2_unknown_node n; int ret, addedsize; size_t retlen; + uint32_t freed_len; - if(!ref) { + if(unlikely(!ref)) { printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n"); return; } @@ -499,32 +509,34 @@ void jffs2_mark_node_obsolete(struct jff spin_lock(&c->erase_completion_lock); + freed_len = ref_totlen(c, jeb, ref); + if (ref_flags(ref) == REF_UNCHECKED) { - D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { + D1(if (unlikely(jeb->unchecked_size < freed_len)) { printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", - ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); + freed_len, blocknr, ref->flash_offset, jeb->used_size); BUG(); }) - D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); - jeb->unchecked_size -= ref_totlen(c, jeb, ref); - c->unchecked_size -= ref_totlen(c, jeb, ref); + D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), freed_len)); + jeb->unchecked_size -= freed_len; + c->unchecked_size -= freed_len; } else { - D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { + D1(if (unlikely(jeb->used_size < freed_len)) { printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", - ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); + freed_len, blocknr, ref->flash_offset, jeb->used_size); BUG(); }) - D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); - jeb->used_size -= ref_totlen(c, jeb, ref); - c->used_size -= ref_totlen(c, jeb, ref); + D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), freed_len)); + jeb->used_size -= freed_len; + c->used_size -= freed_len; } // Take care, that wasted size is taken into concern - if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { - D1(printk(KERN_DEBUG "Dirtying\n")); - addedsize = ref_totlen(c, jeb, ref); - jeb->dirty_size += ref_totlen(c, jeb, ref); - c->dirty_size += ref_totlen(c, jeb, ref); + if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) { + D1(printk("Dirtying\n")); + addedsize = freed_len; + jeb->dirty_size += freed_len; + c->dirty_size += freed_len; /* Convert wasted space to dirty, if not a bad block */ if (jeb->wasted_size) { @@ -543,10 +555,10 @@ void jffs2_mark_node_obsolete(struct jff } } } else { - D1(printk(KERN_DEBUG "Wasting\n")); + D1(printk("Wasting\n")); addedsize = 0; - jeb->wasted_size += ref_totlen(c, jeb, ref); - c->wasted_size += ref_totlen(c, jeb, ref); + jeb->wasted_size += freed_len; + c->wasted_size += freed_len; } ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; @@ -622,7 +634,7 @@ void jffs2_mark_node_obsolete(struct jff /* The erase_free_sem is locked, and has been since before we marked the node obsolete and potentially put its eraseblock onto the erase_pending_list. Thus, we know that the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet - by jffs2_free_all_node_refs() in erase.c. Which is nice. */ + by jffs2_free_jeb_node_refs() in erase.c. Which is nice. */ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); @@ -634,8 +646,8 @@ void jffs2_mark_node_obsolete(struct jff printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); goto out_erase_sem; } - if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { - printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); + if (PAD(je32_to_cpu(n.totlen)) != PAD(freed_len)) { + printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), freed_len); goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { @@ -677,57 +689,23 @@ void jffs2_mark_node_obsolete(struct jff *p = ref->next_in_ino; ref->next_in_ino = NULL; - if (ic->nodes == (void *)ic && ic->nlink == 0) - jffs2_del_ino_cache(c, ic); - - spin_unlock(&c->erase_completion_lock); - } - - - /* Merge with the next node in the physical list, if there is one - and if it's also obsolete and if it doesn't belong to any inode */ - if (ref->next_phys && ref_obsolete(ref->next_phys) && - !ref->next_phys->next_in_ino) { - struct jffs2_raw_node_ref *n = ref->next_phys; - - spin_lock(&c->erase_completion_lock); - - ref->__totlen += n->__totlen; - ref->next_phys = n->next_phys; - if (jeb->last_node == n) jeb->last_node = ref; - if (jeb->gc_node == n) { - /* gc will be happy continuing gc on this node */ - jeb->gc_node=ref; + switch (ic->class) { +#ifdef CONFIG_JFFS2_FS_XATTR + case RAWNODE_CLASS_XATTR_DATUM: + jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic); + break; + case RAWNODE_CLASS_XATTR_REF: + jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic); + break; +#endif + default: + if (ic->nodes == (void *)ic && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + break; } spin_unlock(&c->erase_completion_lock); - - jffs2_free_raw_node_ref(n); } - /* Also merge with the previous node in the list, if there is one - and that one is obsolete */ - if (ref != jeb->first_node ) { - struct jffs2_raw_node_ref *p = jeb->first_node; - - spin_lock(&c->erase_completion_lock); - - while (p->next_phys != ref) - p = p->next_phys; - - if (ref_obsolete(p) && !ref->next_in_ino) { - p->__totlen += ref->__totlen; - if (jeb->last_node == ref) { - jeb->last_node = p; - } - if (jeb->gc_node == ref) { - /* gc will be happy continuing gc on this node */ - jeb->gc_node=p; - } - p->next_phys = ref->next_phys; - jffs2_free_raw_node_ref(ref); - } - spin_unlock(&c->erase_completion_lock); - } out_erase_sem: up(&c->erase_free_sem); } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index d307cf5..9f41fc0 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -31,9 +31,7 @@ #define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2S #define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) #define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) #define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) - -#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) -#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) +#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev) #define ITIME(sec) ((struct timespec){sec, 0}) #define I_SEC(tv) ((tv).tv_sec) @@ -60,6 +58,10 @@ static inline void jffs2_init_inode_info f->target = NULL; f->flags = 0; f->usercompr = 0; +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + f->i_acl_access = JFFS2_ACL_NOT_CACHED; + f->i_acl_default = JFFS2_ACL_NOT_CACHED; +#endif } @@ -90,13 +92,10 @@ #define jffs2_wbuf_dirty(c) (0) #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL -#define jffs2_nor_ecc(c) (0) #define jffs2_dataflash(c) (0) -#define jffs2_nor_wbuf_flash(c) (0) -#define jffs2_nor_ecc_flash_setup(c) (0) -#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) #define jffs2_dataflash_setup(c) (0) #define jffs2_dataflash_cleanup(c) do {} while (0) +#define jffs2_nor_wbuf_flash(c) (0) #define jffs2_nor_wbuf_flash_setup(c) (0) #define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0) @@ -107,9 +106,7 @@ #define jffs2_is_writebuffered(c) (c->wb #ifdef CONFIG_JFFS2_SUMMARY #define jffs2_can_mark_obsolete(c) (0) #else -#define jffs2_can_mark_obsolete(c) \ - ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \ - c->mtd->type == MTD_RAM) +#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE)) #endif #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) @@ -133,15 +130,11 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); -#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC)) -int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c); -void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c); - #define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH) int jffs2_dataflash_setup(struct jffs2_sb_info *c); void jffs2_dataflash_cleanup(struct jffs2_sb_info *c); -#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS)) +#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE)) int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c); void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c); @@ -165,7 +158,7 @@ extern struct inode_operations jffs2_dir /* file.c */ extern const struct file_operations jffs2_file_operations; extern struct inode_operations jffs2_file_inode_operations; -extern struct address_space_operations jffs2_file_address_operations; +extern const struct address_space_operations jffs2_file_address_operations; int jffs2_fsync(struct file *, struct dentry *, int); int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); @@ -182,7 +175,7 @@ void jffs2_clear_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); -int jffs2_statfs (struct super_block *, struct kstatfs *); +int jffs2_statfs (struct dentry *, struct kstatfs *); void jffs2_write_super (struct super_block *); int jffs2_remount_fs (struct super_block *, int *, char *); int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index f169564..266423b 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -66,7 +66,7 @@ static void jffs2_free_tmp_dnode_info_li jffs2_free_full_dnode(tn->fn); jffs2_free_tmp_dnode_info(tn); - this = this->rb_parent; + this = rb_parent(this); if (!this) break; @@ -116,19 +116,42 @@ static inline int read_direntry(struct j uint32_t *latest_mctime, uint32_t *mctime_ver) { struct jffs2_full_dirent *fd; + uint32_t crc; - /* The direntry nodes are checked during the flash scanning */ - BUG_ON(ref_flags(ref) == REF_UNCHECKED); /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); - /* Sanity check */ - if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { - JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", - ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); + crc = crc32(0, rd, sizeof(*rd) - 8); + if (unlikely(crc != je32_to_cpu(rd->node_crc))) { + JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); return 1; } + /* If we've never checked the CRCs on this node, check them now */ + if (ref_flags(ref) == REF_UNCHECKED) { + struct jffs2_eraseblock *jeb; + int len; + + /* Sanity check */ + if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) { + JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n", + ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen)); + return 1; + } + + jeb = &c->blocks[ref->flash_offset / c->sector_size]; + len = ref_totlen(c, jeb, ref); + + spin_lock(&c->erase_completion_lock); + jeb->used_size += len; + jeb->unchecked_size -= len; + c->used_size += len; + c->unchecked_size -= len; + ref->flash_offset = ref_offset(ref) | REF_PRISTINE; + spin_unlock(&c->erase_completion_lock); + } + fd = jffs2_alloc_full_dirent(rd->nsize + 1); if (unlikely(!fd)) return -ENOMEM; @@ -198,13 +221,21 @@ static inline int read_dnode(struct jffs struct jffs2_tmp_dnode_info *tn; uint32_t len, csize; int ret = 1; + uint32_t crc; /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ BUG_ON(ref_obsolete(ref)); + crc = crc32(0, rd, sizeof(*rd) - 8); + if (unlikely(crc != je32_to_cpu(rd->node_crc))) { + JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n", + ref_offset(ref), je32_to_cpu(rd->node_crc), crc); + return 1; + } + tn = jffs2_alloc_tmp_dnode_info(); if (!tn) { - JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn)); + JFFS2_ERROR("failed to allocate tn (%zu bytes).\n", sizeof(*tn)); return -ENOMEM; } @@ -213,14 +244,6 @@ static inline int read_dnode(struct jffs /* If we've never checked the CRCs on this node, check them now */ if (ref_flags(ref) == REF_UNCHECKED) { - uint32_t crc; - - crc = crc32(0, rd, sizeof(*rd) - 8); - if (unlikely(crc != je32_to_cpu(rd->node_crc))) { - JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n", - ref_offset(ref), je32_to_cpu(rd->node_crc), crc); - goto free_out; - } /* Sanity checks */ if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) || @@ -343,7 +366,7 @@ free_out: * Helper function for jffs2_get_inode_nodes(). * It is called every time an unknown node is found. * - * Returns: 0 on succes; + * Returns: 0 on success; * 1 if the node should be marked obsolete; * negative error code on failure. */ @@ -354,37 +377,30 @@ static inline int read_unknown(struct jf un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype)); - if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) { - /* Hmmm. This should have been caught at scan time. */ - JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref)); - jffs2_dbg_dump_node(c, ref_offset(ref)); - return 1; - } else { - switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { + switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) { - case JFFS2_FEATURE_INCOMPAT: - JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", - je16_to_cpu(un->nodetype), ref_offset(ref)); - /* EEP */ - BUG(); - break; + case JFFS2_FEATURE_INCOMPAT: + JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + /* EEP */ + BUG(); + break; - case JFFS2_FEATURE_ROCOMPAT: - JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", - je16_to_cpu(un->nodetype), ref_offset(ref)); - BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); - break; + case JFFS2_FEATURE_ROCOMPAT: + JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO)); + break; - case JFFS2_FEATURE_RWCOMPAT_COPY: - JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", - je16_to_cpu(un->nodetype), ref_offset(ref)); - break; + case JFFS2_FEATURE_RWCOMPAT_COPY: + JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + break; - case JFFS2_FEATURE_RWCOMPAT_DELETE: - JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", - je16_to_cpu(un->nodetype), ref_offset(ref)); - return 1; - } + case JFFS2_FEATURE_RWCOMPAT_DELETE: + JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n", + je16_to_cpu(un->nodetype), ref_offset(ref)); + return 1; } return 0; @@ -434,7 +450,7 @@ static int read_more(struct jffs2_sb_inf } if (retlen < len) { - JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", + JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", offs, retlen, len); return -EIO; } @@ -542,13 +558,25 @@ static int jffs2_get_inode_nodes(struct } if (retlen < len) { - JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len); + JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", ref_offset(ref), retlen, len); err = -EIO; goto free_out; } node = (union jffs2_node_union *)bufstart; + /* No need to mask in the valid bit; it shouldn't be invalid */ + if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) { + JFFS2_NOTICE("Node header CRC failed at %#08x. {%04x,%04x,%08x,%08x}\n", + ref_offset(ref), je16_to_cpu(node->u.magic), + je16_to_cpu(node->u.nodetype), + je32_to_cpu(node->u.totlen), + je32_to_cpu(node->u.hdr_crc)); + jffs2_dbg_dump_node(c, ref_offset(ref)); + jffs2_mark_node_obsolete(c, ref); + goto cont; + } + switch (je16_to_cpu(node->u.nodetype)) { case JFFS2_NODETYPE_DIRENT: @@ -606,6 +634,7 @@ static int jffs2_get_inode_nodes(struct goto free_out; } + cont: spin_lock(&c->erase_completion_lock); } @@ -679,12 +708,12 @@ static int jffs2_do_read_inode_internal( jffs2_mark_node_obsolete(c, fn->raw); BUG_ON(rb->rb_left); - if (rb->rb_parent && rb->rb_parent->rb_left == rb) { + if (rb_parent(rb) && rb_parent(rb)->rb_left == rb) { /* We were then left-hand child of our parent. We need * to move our own right-hand child into our place. */ repl_rb = rb->rb_right; if (repl_rb) - repl_rb->rb_parent = rb->rb_parent; + rb_set_parent(repl_rb, rb_parent(rb)); } else repl_rb = NULL; @@ -692,14 +721,14 @@ static int jffs2_do_read_inode_internal( /* Remove the spent tn from the tree; don't bother rebalancing * but put our right-hand child in our own place. */ - if (tn->rb.rb_parent) { - if (tn->rb.rb_parent->rb_left == &tn->rb) - tn->rb.rb_parent->rb_left = repl_rb; - else if (tn->rb.rb_parent->rb_right == &tn->rb) - tn->rb.rb_parent->rb_right = repl_rb; + if (rb_parent(&tn->rb)) { + if (rb_parent(&tn->rb)->rb_left == &tn->rb) + rb_parent(&tn->rb)->rb_left = repl_rb; + else if (rb_parent(&tn->rb)->rb_right == &tn->rb) + rb_parent(&tn->rb)->rb_right = repl_rb; else BUG(); } else if (tn->rb.rb_right) - tn->rb.rb_right->rb_parent = NULL; + rb_set_parent(tn->rb.rb_right, NULL); jffs2_free_tmp_dnode_info(tn); if (ret) { @@ -939,6 +968,8 @@ void jffs2_do_clear_inode(struct jffs2_s struct jffs2_full_dirent *fd, *fds; int deleted; + jffs2_clear_acl(f); + jffs2_xattr_delete_inode(c, f->inocache); down(&f->sem); deleted = f->inocache && !f->inocache->nlink; diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index cf55b22..e241346 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -65,6 +65,28 @@ static inline uint32_t EMPTY_SCAN_SIZE(u return DEFAULT_EMPTY_SCAN_SIZE; } +static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +{ + int ret; + + if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1))) + return ret; + if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size))) + return ret; + /* Turned wasted size into dirty, since we apparently + think it's recoverable now. */ + jeb->dirty_size += jeb->wasted_size; + c->dirty_size += jeb->wasted_size; + c->wasted_size -= jeb->wasted_size; + jeb->wasted_size = 0; + if (VERYDIRTY(c, jeb->dirty_size)) { + list_add(&jeb->list, &c->very_dirty_list); + } else { + list_add(&jeb->list, &c->dirty_list); + } + return 0; +} + int jffs2_scan_medium(struct jffs2_sb_info *c) { int i, ret; @@ -170,34 +192,20 @@ #endif (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ if (c->nextblock) { - c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; - c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; - c->free_size -= c->nextblock->free_size; - c->wasted_size -= c->nextblock->wasted_size; - c->nextblock->free_size = c->nextblock->wasted_size = 0; - if (VERYDIRTY(c, c->nextblock->dirty_size)) { - list_add(&c->nextblock->list, &c->very_dirty_list); - } else { - list_add(&c->nextblock->list, &c->dirty_list); - } + ret = file_dirty(c, c->nextblock); + if (ret) + return ret; /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } - /* update collected summary infromation for the current nextblock */ + /* update collected summary information for the current nextblock */ jffs2_sum_move_collected(c, s); D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset)); c->nextblock = jeb; } else { - jeb->dirty_size += jeb->free_size + jeb->wasted_size; - c->dirty_size += jeb->free_size + jeb->wasted_size; - c->free_size -= jeb->free_size; - c->wasted_size -= jeb->wasted_size; - jeb->free_size = jeb->wasted_size = 0; - if (VERYDIRTY(c, jeb->dirty_size)) { - list_add(&jeb->list, &c->very_dirty_list); - } else { - list_add(&jeb->list, &c->dirty_list); - } + ret = file_dirty(c, jeb); + if (ret) + return ret; } break; @@ -222,9 +230,6 @@ #endif } } - if (jffs2_sum_active() && s) - kfree(s); - /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ if (c->nextblock && (c->nextblock->dirty_size)) { c->nextblock->wasted_size += c->nextblock->dirty_size; @@ -242,11 +247,8 @@ #ifdef CONFIG_JFFS2_FS_WRITEBUFFER D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", skip)); - c->nextblock->wasted_size += skip; - c->wasted_size += skip; - - c->nextblock->free_size -= skip; - c->free_size -= skip; + jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); + jffs2_scan_dirty_space(c, c->nextblock, skip); } #endif if (c->nr_erasing_blocks) { @@ -266,11 +268,14 @@ #ifndef __ECOS else c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); #endif + if (s) + kfree(s); + return ret; } -int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf, - uint32_t ofs, uint32_t len) +static int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf, + uint32_t ofs, uint32_t len) { int ret; size_t retlen; @@ -290,7 +295,7 @@ int jffs2_fill_scan_buf (struct jffs2_sb int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size - && (!jeb->first_node || !jeb->first_node->next_phys) ) + && (!jeb->first_node || !ref_next(jeb->first_node)) ) return BLK_STATE_CLEANMARKER; /* move blocks with max 4 byte dirty space to cleanlist */ @@ -306,11 +311,126 @@ int jffs2_scan_classify_jeb(struct jffs2 return BLK_STATE_ALLDIRTY; } +#ifdef CONFIG_JFFS2_FS_XATTR +static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_xattr *rx, uint32_t ofs, + struct jffs2_summary *s) +{ + struct jffs2_xattr_datum *xd; + uint32_t xid, version, totlen, crc; + int err; + + crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4); + if (crc != je32_to_cpu(rx->node_crc)) { + JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ofs, je32_to_cpu(rx->node_crc), crc); + if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen)))) + return err; + return 0; + } + + xid = je32_to_cpu(rx->xid); + version = je32_to_cpu(rx->version); + + totlen = PAD(sizeof(struct jffs2_raw_xattr) + + rx->name_len + 1 + je16_to_cpu(rx->value_len)); + if (totlen != je32_to_cpu(rx->totlen)) { + JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n", + ofs, je32_to_cpu(rx->totlen), totlen); + if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen)))) + return err; + return 0; + } + + xd = jffs2_setup_xattr_datum(c, xid, version); + if (IS_ERR(xd)) + return PTR_ERR(xd); + + if (xd->version > version) { + struct jffs2_raw_node_ref *raw + = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL); + raw->next_in_ino = xd->node->next_in_ino; + xd->node->next_in_ino = raw; + } else { + xd->version = version; + xd->xprefix = rx->xprefix; + xd->name_len = rx->name_len; + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + + jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, (void *)xd); + } + + if (jffs2_sum_active()) + jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset); + dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n", + ofs, xd->xid, xd->version); + return 0; +} + +static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_xref *rr, uint32_t ofs, + struct jffs2_summary *s) +{ + struct jffs2_xattr_ref *ref; + uint32_t crc; + int err; + + crc = crc32(0, rr, sizeof(*rr) - 4); + if (crc != je32_to_cpu(rr->node_crc)) { + JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ofs, je32_to_cpu(rr->node_crc), crc); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen))))) + return err; + return 0; + } + + if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) { + JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%zd\n", + ofs, je32_to_cpu(rr->totlen), + PAD(sizeof(struct jffs2_raw_xref))); + if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rr->totlen)))) + return err; + return 0; + } + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return -ENOMEM; + + /* BEFORE jffs2_build_xattr_subsystem() called, + * and AFTER xattr_ref is marked as a dead xref, + * ref->xid is used to store 32bit xid, xd is not used + * ref->ino is used to store 32bit inode-number, ic is not used + * Thoes variables are declared as union, thus using those + * are exclusive. In a similar way, ref->next is temporarily + * used to chain all xattr_ref object. It's re-chained to + * jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly. + */ + ref->ino = je32_to_cpu(rr->ino); + ref->xid = je32_to_cpu(rr->xid); + ref->xseqno = je32_to_cpu(rr->xseqno); + if (ref->xseqno > c->highest_xseqno) + c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER); + ref->next = c->xref_temp; + c->xref_temp = ref; + + jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), (void *)ref); + + if (jffs2_sum_active()) + jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset); + dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n", + ofs, ref->xid, ref->ino); + return 0; +} +#endif + +/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into + the flash, XIP-style */ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { + unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { struct jffs2_unknown_node *node; struct jffs2_unknown_node crcnode; - struct jffs2_sum_marker *sm; uint32_t ofs, prevofs; uint32_t hdr_crc, buf_ofs, buf_len; int err; @@ -344,44 +464,75 @@ #ifdef CONFIG_JFFS2_FS_WRITEBUFFER #endif if (jffs2_sum_active()) { - sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL); - if (!sm) { - return -ENOMEM; - } - - err = jffs2_fill_scan_buf(c, (unsigned char *) sm, jeb->offset + c->sector_size - - sizeof(struct jffs2_sum_marker), sizeof(struct jffs2_sum_marker)); - if (err) { - kfree(sm); - return err; - } - - if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) { - err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random); - if (err) { - kfree(sm); + struct jffs2_sum_marker *sm; + void *sumptr = NULL; + uint32_t sumlen; + + if (!buf_size) { + /* XIP case. Just look, point at the summary if it's there */ + sm = (void *)buf + c->sector_size - sizeof(*sm); + if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) { + sumptr = buf + je32_to_cpu(sm->offset); + sumlen = c->sector_size - je32_to_cpu(sm->offset); + } + } else { + /* If NAND flash, read a whole page of it. Else just the end */ + if (c->wbuf_pagesize) + buf_len = c->wbuf_pagesize; + else + buf_len = sizeof(*sm); + + /* Read as much as we want into the _end_ of the preallocated buffer */ + err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len, + jeb->offset + c->sector_size - buf_len, + buf_len); + if (err) return err; + + sm = (void *)buf + buf_size - sizeof(*sm); + if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) { + sumlen = c->sector_size - je32_to_cpu(sm->offset); + sumptr = buf + buf_size - sumlen; + + /* Now, make sure the summary itself is available */ + if (sumlen > buf_size) { + /* Need to kmalloc for this. */ + sumptr = kmalloc(sumlen, GFP_KERNEL); + if (!sumptr) + return -ENOMEM; + memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len); + } + if (buf_len < sumlen) { + /* Need to read more so that the entire summary node is present */ + err = jffs2_fill_scan_buf(c, sumptr, + jeb->offset + c->sector_size - sumlen, + sumlen - buf_len); + if (err) + return err; + } } + } - kfree(sm); + if (sumptr) { + err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random); - ofs = jeb->offset; - prevofs = jeb->offset - 1; + if (buf_size && sumlen > buf_size) + kfree(sumptr); + /* If it returns with a real error, bail. + If it returns positive, that's a block classification + (i.e. BLK_STATE_xxx) so return that too. + If it returns zero, fall through to full scan. */ + if (err) + return err; + } } buf_ofs = jeb->offset; if (!buf_size) { + /* This is the XIP case -- we're reading _directly_ from the flash chip */ buf_len = c->sector_size; - - if (jffs2_sum_active()) { - /* must reread because of summary test */ - err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); - if (err) - return err; - } - } else { buf_len = EMPTY_SCAN_SIZE(c->sector_size); err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); @@ -418,7 +569,10 @@ #endif if (ofs) { D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, jeb->offset + ofs)); - DIRTY_SPACE(ofs); + if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1))) + return err; + if ((err = jffs2_scan_dirty_space(c, jeb, ofs))) + return err; } /* Now ofs is a complete physical flash offset as it always was... */ @@ -433,6 +587,11 @@ scan_more: jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + /* Make sure there are node refs available for use */ + err = jffs2_prealloc_raw_node_refs(c, jeb, 2); + if (err) + return err; + cond_resched(); if (ofs & 3) { @@ -442,7 +601,8 @@ scan_more: } if (ofs == prevofs) { printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } @@ -451,7 +611,8 @@ scan_more: if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), jeb->offset, c->sector_size, ofs, sizeof(*node))); - DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); + if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs))) + return err; break; } @@ -481,7 +642,8 @@ scan_more: if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) { printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); - DIRTY_SPACE(ofs-empty_start); + if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start))) + return err; goto scan_more; } @@ -494,7 +656,7 @@ scan_more: /* If we're only checking the beginning of a block with a cleanmarker, bail now */ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && - c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) { + c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) { D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size))); return BLK_STATE_CLEANMARKER; } @@ -518,20 +680,23 @@ scan_more: if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } @@ -540,7 +705,8 @@ scan_more: noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, je16_to_cpu(node->magic)); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } @@ -557,7 +723,8 @@ scan_more: je32_to_cpu(node->totlen), je32_to_cpu(node->hdr_crc), hdr_crc); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } @@ -568,7 +735,8 @@ scan_more: printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", ofs, je32_to_cpu(node->totlen)); printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); - DIRTY_SPACE(4); + if ((err = jffs2_scan_dirty_space(c, jeb, 4))) + return err; ofs += 4; continue; } @@ -576,7 +744,8 @@ scan_more: if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { /* Wheee. This is an obsoleted node */ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); - DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) + return err; ofs += PAD(je32_to_cpu(node->totlen)); continue; } @@ -614,30 +783,59 @@ scan_more: ofs += PAD(je32_to_cpu(node->totlen)); break; +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: + if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)" + " left to end of buf. Reading 0x%x at 0x%08x\n", + je32_to_cpu(node->totlen), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s); + if (err) + return err; + ofs += PAD(je32_to_cpu(node->totlen)); + break; + case JFFS2_NODETYPE_XREF: + if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { + buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); + D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)" + " left to end of buf. Reading 0x%x at 0x%08x\n", + je32_to_cpu(node->totlen), buf_len, ofs)); + err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); + if (err) + return err; + buf_ofs = ofs; + node = (void *)buf; + } + err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s); + if (err) + return err; + ofs += PAD(je32_to_cpu(node->totlen)); + break; +#endif /* CONFIG_JFFS2_FS_XATTR */ + case JFFS2_NODETYPE_CLEANMARKER: D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); - DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node))))) + return err; ofs += PAD(sizeof(struct jffs2_unknown_node)); } else if (jeb->first_node) { printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); - DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node))))) + return err; ofs += PAD(sizeof(struct jffs2_unknown_node)); } else { - struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); - if (!marker_ref) { - printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n"); - return -ENOMEM; - } - marker_ref->next_in_ino = NULL; - marker_ref->next_phys = NULL; - marker_ref->flash_offset = ofs | REF_NORMAL; - marker_ref->__totlen = c->cleanmarker_size; - jeb->first_node = jeb->last_node = marker_ref; + jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL); - USED_SPACE(PAD(c->cleanmarker_size)); ofs += PAD(c->cleanmarker_size); } break; @@ -645,7 +843,8 @@ scan_more: case JFFS2_NODETYPE_PADDING: if (jffs2_sum_active()) jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen)); - DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) + return err; ofs += PAD(je32_to_cpu(node->totlen)); break; @@ -656,7 +855,8 @@ scan_more: c->flags |= JFFS2_SB_FLAG_RO; if (!(jffs2_is_readonly(c))) return -EROFS; - DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) + return err; ofs += PAD(je32_to_cpu(node->totlen)); break; @@ -666,15 +866,21 @@ scan_more: case JFFS2_FEATURE_RWCOMPAT_DELETE: D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); - DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) + return err; ofs += PAD(je32_to_cpu(node->totlen)); break; - case JFFS2_FEATURE_RWCOMPAT_COPY: + case JFFS2_FEATURE_RWCOMPAT_COPY: { D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); - USED_SPACE(PAD(je32_to_cpu(node->totlen))); + + jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL); + + /* We can't summarise nodes we don't grok */ + jffs2_sum_disable_collecting(s); ofs += PAD(je32_to_cpu(node->totlen)); break; + } } } } @@ -687,9 +893,9 @@ scan_more: } } - D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, - jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size)); - + D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n", + jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size)); + /* mark_node_obsolete can add to wasted !! */ if (jeb->wasted_size) { jeb->dirty_size += jeb->wasted_size; @@ -730,9 +936,9 @@ struct jffs2_inode_cache *jffs2_scan_mak static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s) { - struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; uint32_t ino = je32_to_cpu(ri->ino); + int err; D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); @@ -745,12 +951,6 @@ static int jffs2_scan_inode_node(struct Which means that the _full_ amount of time to get to proper write mode with GC operational may actually be _longer_ than before. Sucks to be me. */ - raw = jffs2_alloc_raw_node_ref(); - if (!raw) { - printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n"); - return -ENOMEM; - } - ic = jffs2_get_ino_cache(c, ino); if (!ic) { /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the @@ -762,30 +962,17 @@ static int jffs2_scan_inode_node(struct printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(ri->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ - DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); - jffs2_free_raw_node_ref(raw); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(ri->totlen))))) + return err; return 0; } ic = jffs2_scan_make_ino_cache(c, ino); - if (!ic) { - jffs2_free_raw_node_ref(raw); + if (!ic) return -ENOMEM; - } } /* Wheee. It worked */ - - raw->flash_offset = ofs | REF_UNCHECKED; - raw->__totlen = PAD(je32_to_cpu(ri->totlen)); - raw->next_phys = NULL; - raw->next_in_ino = ic->nodes; - - ic->nodes = raw; - if (!jeb->first_node) - jeb->first_node = raw; - if (jeb->last_node) - jeb->last_node->next_phys = raw; - jeb->last_node = raw; + jffs2_link_node_ref(c, jeb, ofs | REF_UNCHECKED, PAD(je32_to_cpu(ri->totlen)), ic); D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", je32_to_cpu(ri->ino), je32_to_cpu(ri->version), @@ -794,8 +981,6 @@ static int jffs2_scan_inode_node(struct pseudo_random += je32_to_cpu(ri->version); - UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); - if (jffs2_sum_active()) { jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset); } @@ -806,10 +991,10 @@ static int jffs2_scan_inode_node(struct static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s) { - struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; struct jffs2_inode_cache *ic; uint32_t crc; + int err; D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); @@ -821,7 +1006,8 @@ static int jffs2_scan_dirent_node(struct printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ofs, je32_to_cpu(rd->node_crc), crc); /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ - DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen))))) + return err; return 0; } @@ -842,40 +1028,23 @@ static int jffs2_scan_dirent_node(struct jffs2_free_full_dirent(fd); /* FIXME: Why do we believe totlen? */ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ - DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen))))) + return err; return 0; } - raw = jffs2_alloc_raw_node_ref(); - if (!raw) { - jffs2_free_full_dirent(fd); - printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); - return -ENOMEM; - } ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); if (!ic) { jffs2_free_full_dirent(fd); - jffs2_free_raw_node_ref(raw); return -ENOMEM; } - raw->__totlen = PAD(je32_to_cpu(rd->totlen)); - raw->flash_offset = ofs | REF_PRISTINE; - raw->next_phys = NULL; - raw->next_in_ino = ic->nodes; - ic->nodes = raw; - if (!jeb->first_node) - jeb->first_node = raw; - if (jeb->last_node) - jeb->last_node->next_phys = raw; - jeb->last_node = raw; + fd->raw = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rd->totlen)), ic); - fd->raw = raw; fd->next = NULL; fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); fd->nhash = full_name_hash(fd->name, rd->nsize); fd->type = rd->type; - USED_SPACE(PAD(je32_to_cpu(rd->totlen))); jffs2_add_fd_to_list(c, fd, &ic->scan_dents); if (jffs2_sum_active()) { diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c new file mode 100644 index 0000000..52a9894 --- /dev/null +++ b/fs/jffs2/security.c @@ -0,0 +1,82 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + +/* ---- Initial Security Label Attachment -------------- */ +int jffs2_init_security(struct inode *inode, struct inode *dir) +{ + int rc; + size_t len; + void *value; + char *name; + + rc = security_inode_init_security(inode, dir, &name, &value, &len); + if (rc) { + if (rc == -EOPNOTSUPP) + return 0; + return rc; + } + rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); + + kfree(name); + kfree(value); + return rc; +} + +/* ---- XATTR Handler for "security.*" ----------------- */ +static int jffs2_security_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + + return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size); +} + +static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + if (!strcmp(name, "")) + return -EINVAL; + + return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags); +} + +static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_SECURITY_PREFIX); + strcpy(list + XATTR_SECURITY_PREFIX_LEN, name); + } + + return retlen; +} + +struct xattr_handler jffs2_security_xattr_handler = { + .prefix = XATTR_SECURITY_PREFIX, + .list = jffs2_security_listxattr, + .set = jffs2_security_setxattr, + .get = jffs2_security_getxattr +}; diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index fb9cec6..c19bd47 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -5,6 +5,7 @@ * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary + * 2006 KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * @@ -42,7 +43,7 @@ int jffs2_sum_init(struct jffs2_sb_info return -ENOMEM; } - dbg_summary("returned succesfully\n"); + dbg_summary("returned successfully\n"); return 0; } @@ -81,6 +82,19 @@ static int jffs2_sum_add_mem(struct jffs dbg_summary("dirent (%u) added to summary\n", je32_to_cpu(item->d.ino)); break; +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: + s->sum_size += JFFS2_SUMMARY_XATTR_SIZE; + s->sum_num++; + dbg_summary("xattr (xid=%u, version=%u) added to summary\n", + je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version)); + break; + case JFFS2_NODETYPE_XREF: + s->sum_size += JFFS2_SUMMARY_XREF_SIZE; + s->sum_num++; + dbg_summary("xref added to summary\n"); + break; +#endif default: JFFS2_WARNING("UNKNOWN node type %u\n", je16_to_cpu(item->u.nodetype)); @@ -141,6 +155,40 @@ int jffs2_sum_add_dirent_mem(struct jffs return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); } +#ifdef CONFIG_JFFS2_FS_XATTR +int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs) +{ + struct jffs2_sum_xattr_mem *temp; + + temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + temp->nodetype = rx->nodetype; + temp->xid = rx->xid; + temp->version = rx->version; + temp->offset = cpu_to_je32(ofs); + temp->totlen = rx->totlen; + temp->next = NULL; + + return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); +} + +int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs) +{ + struct jffs2_sum_xref_mem *temp; + + temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL); + if (!temp) + return -ENOMEM; + + temp->nodetype = rr->nodetype; + temp->offset = cpu_to_je32(ofs); + temp->next = NULL; + + return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp); +} +#endif /* Cleanup every collected summary information */ static void jffs2_sum_clean_collected(struct jffs2_summary *s) @@ -259,7 +307,34 @@ int jffs2_sum_add_kvec(struct jffs2_sb_i return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); } +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: { + struct jffs2_sum_xattr_mem *temp; + temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL); + if (!temp) + goto no_mem; + + temp->nodetype = node->x.nodetype; + temp->xid = node->x.xid; + temp->version = node->x.version; + temp->totlen = node->x.totlen; + temp->offset = cpu_to_je32(ofs); + temp->next = NULL; + return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); + } + case JFFS2_NODETYPE_XREF: { + struct jffs2_sum_xref_mem *temp; + temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL); + if (!temp) + goto no_mem; + temp->nodetype = node->r.nodetype; + temp->offset = cpu_to_je32(ofs); + temp->next = NULL; + + return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp); + } +#endif case JFFS2_NODETYPE_PADDING: dbg_summary("node PADDING\n"); c->summary->sum_padded += je32_to_cpu(node->u.totlen); @@ -288,23 +363,41 @@ no_mem: return -ENOMEM; } +static struct jffs2_raw_node_ref *sum_link_node_ref(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, + uint32_t ofs, uint32_t len, + struct jffs2_inode_cache *ic) +{ + /* If there was a gap, mark it dirty */ + if ((ofs & ~3) > c->sector_size - jeb->free_size) { + /* Ew. Summary doesn't actually tell us explicitly about dirty space */ + jffs2_scan_dirty_space(c, jeb, (ofs & ~3) - (c->sector_size - jeb->free_size)); + } + + return jffs2_link_node_ref(c, jeb, jeb->offset + ofs, len, ic); +} /* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_raw_summary *summary, uint32_t *pseudo_random) { - struct jffs2_raw_node_ref *raw; struct jffs2_inode_cache *ic; struct jffs2_full_dirent *fd; void *sp; int i, ino; + int err; sp = summary->sum; for (i=0; isum_num); i++) { dbg_summary("processing summary index %d\n", i); + /* Make sure there's a spare ref for dirty space */ + err = jffs2_prealloc_raw_node_refs(c, jeb, 2); + if (err) + return err; + switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *spi; @@ -312,38 +405,20 @@ static int jffs2_sum_process_sum_data(st ino = je32_to_cpu(spi->inode); - dbg_summary("Inode at 0x%08x\n", - jeb->offset + je32_to_cpu(spi->offset)); - - raw = jffs2_alloc_raw_node_ref(); - if (!raw) { - JFFS2_NOTICE("allocation of node reference failed\n"); - kfree(summary); - return -ENOMEM; - } + dbg_summary("Inode at 0x%08x-0x%08x\n", + jeb->offset + je32_to_cpu(spi->offset), + jeb->offset + je32_to_cpu(spi->offset) + je32_to_cpu(spi->totlen)); ic = jffs2_scan_make_ino_cache(c, ino); if (!ic) { JFFS2_NOTICE("scan_make_ino_cache failed\n"); - jffs2_free_raw_node_ref(raw); - kfree(summary); return -ENOMEM; } - raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED; - raw->__totlen = PAD(je32_to_cpu(spi->totlen)); - raw->next_phys = NULL; - raw->next_in_ino = ic->nodes; - - ic->nodes = raw; - if (!jeb->first_node) - jeb->first_node = raw; - if (jeb->last_node) - jeb->last_node->next_phys = raw; - jeb->last_node = raw; - *pseudo_random += je32_to_cpu(spi->version); + sum_link_node_ref(c, jeb, je32_to_cpu(spi->offset) | REF_UNCHECKED, + PAD(je32_to_cpu(spi->totlen)), ic); - UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen))); + *pseudo_random += je32_to_cpu(spi->version); sp += JFFS2_SUMMARY_INODE_SIZE; @@ -354,52 +429,33 @@ static int jffs2_sum_process_sum_data(st struct jffs2_sum_dirent_flash *spd; spd = sp; - dbg_summary("Dirent at 0x%08x\n", - jeb->offset + je32_to_cpu(spd->offset)); + dbg_summary("Dirent at 0x%08x-0x%08x\n", + jeb->offset + je32_to_cpu(spd->offset), + jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen)); + fd = jffs2_alloc_full_dirent(spd->nsize+1); - if (!fd) { - kfree(summary); + if (!fd) return -ENOMEM; - } memcpy(&fd->name, spd->name, spd->nsize); fd->name[spd->nsize] = 0; - raw = jffs2_alloc_raw_node_ref(); - if (!raw) { - jffs2_free_full_dirent(fd); - JFFS2_NOTICE("allocation of node reference failed\n"); - kfree(summary); - return -ENOMEM; - } - ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); if (!ic) { jffs2_free_full_dirent(fd); - jffs2_free_raw_node_ref(raw); - kfree(summary); return -ENOMEM; } - raw->__totlen = PAD(je32_to_cpu(spd->totlen)); - raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE; - raw->next_phys = NULL; - raw->next_in_ino = ic->nodes; - ic->nodes = raw; - if (!jeb->first_node) - jeb->first_node = raw; - if (jeb->last_node) - jeb->last_node->next_phys = raw; - jeb->last_node = raw; - - fd->raw = raw; + fd->raw = sum_link_node_ref(c, jeb, je32_to_cpu(spd->offset) | REF_UNCHECKED, + PAD(je32_to_cpu(spd->totlen)), ic); + fd->next = NULL; fd->version = je32_to_cpu(spd->version); fd->ino = je32_to_cpu(spd->ino); fd->nhash = full_name_hash(fd->name, spd->nsize); fd->type = spd->type; - USED_SPACE(PAD(je32_to_cpu(spd->totlen))); + jffs2_add_fd_to_list(c, fd, &ic->scan_dents); *pseudo_random += je32_to_cpu(spd->version); @@ -408,48 +464,100 @@ static int jffs2_sum_process_sum_data(st break; } +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: { + struct jffs2_xattr_datum *xd; + struct jffs2_sum_xattr_flash *spx; + + spx = (struct jffs2_sum_xattr_flash *)sp; + dbg_summary("xattr at %#08x-%#08x (xid=%u, version=%u)\n", + jeb->offset + je32_to_cpu(spx->offset), + jeb->offset + je32_to_cpu(spx->offset) + je32_to_cpu(spx->totlen), + je32_to_cpu(spx->xid), je32_to_cpu(spx->version)); + + xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid), + je32_to_cpu(spx->version)); + if (IS_ERR(xd)) + return PTR_ERR(xd); + if (xd->version > je32_to_cpu(spx->version)) { + /* node is not the newest one */ + struct jffs2_raw_node_ref *raw + = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, + PAD(je32_to_cpu(spx->totlen)), NULL); + raw->next_in_ino = xd->node->next_in_ino; + xd->node->next_in_ino = raw; + } else { + xd->version = je32_to_cpu(spx->version); + sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, + PAD(je32_to_cpu(spx->totlen)), (void *)xd); + } + *pseudo_random += je32_to_cpu(spx->xid); + sp += JFFS2_SUMMARY_XATTR_SIZE; + + break; + } + case JFFS2_NODETYPE_XREF: { + struct jffs2_xattr_ref *ref; + struct jffs2_sum_xref_flash *spr; + + spr = (struct jffs2_sum_xref_flash *)sp; + dbg_summary("xref at %#08x-%#08x\n", + jeb->offset + je32_to_cpu(spr->offset), + jeb->offset + je32_to_cpu(spr->offset) + + (uint32_t)PAD(sizeof(struct jffs2_raw_xref))); + + ref = jffs2_alloc_xattr_ref(); + if (!ref) { + JFFS2_NOTICE("allocation of xattr_datum failed\n"); + return -ENOMEM; + } + ref->next = c->xref_temp; + c->xref_temp = ref; + + sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED, + PAD(sizeof(struct jffs2_raw_xref)), (void *)ref); + + *pseudo_random += ref->node->flash_offset; + sp += JFFS2_SUMMARY_XREF_SIZE; + break; + } +#endif default : { - JFFS2_WARNING("Unsupported node type found in summary! Exiting..."); - kfree(summary); - return -EIO; + uint16_t nodetype = je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype); + JFFS2_WARNING("Unsupported node type %x found in summary! Exiting...\n", nodetype); + if ((nodetype & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_INCOMPAT) + return -EIO; + + /* For compatible node types, just fall back to the full scan */ + c->wasted_size -= jeb->wasted_size; + c->free_size += c->sector_size - jeb->free_size; + c->used_size -= jeb->used_size; + c->dirty_size -= jeb->dirty_size; + jeb->wasted_size = jeb->used_size = jeb->dirty_size = 0; + jeb->free_size = c->sector_size; + + jffs2_free_jeb_node_refs(c, jeb); + return -ENOTRECOVERABLE; } } } - - kfree(summary); return 0; } /* Process the summary node - called from jffs2_scan_eraseblock() */ - int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - uint32_t ofs, uint32_t *pseudo_random) + struct jffs2_raw_summary *summary, uint32_t sumsize, + uint32_t *pseudo_random) { struct jffs2_unknown_node crcnode; - struct jffs2_raw_node_ref *cache_ref; - struct jffs2_raw_summary *summary; - int ret, sumsize; + int ret, ofs; uint32_t crc; - sumsize = c->sector_size - ofs; - ofs += jeb->offset; + ofs = c->sector_size - sumsize; dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n", - jeb->offset, ofs, sumsize); - - summary = kmalloc(sumsize, GFP_KERNEL); - - if (!summary) { - return -ENOMEM; - } - - ret = jffs2_fill_scan_buf(c, (unsigned char *)summary, ofs, sumsize); - - if (ret) { - kfree(summary); - return ret; - } + jeb->offset, jeb->offset + ofs, sumsize); /* OK, now check for node validity and CRC */ crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -486,66 +594,49 @@ int jffs2_sum_scan_sumnode(struct jffs2_ dbg_summary("Summary : CLEANMARKER node \n"); + ret = jffs2_prealloc_raw_node_refs(c, jeb, 1); + if (ret) + return ret; + if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) { dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n", je32_to_cpu(summary->cln_mkr), c->cleanmarker_size); - UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr))); + if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr))))) + return ret; } else if (jeb->first_node) { dbg_summary("CLEANMARKER node not first node in block " "(0x%08x)\n", jeb->offset); - UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr))); + if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr))))) + return ret; } else { - struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); - - if (!marker_ref) { - JFFS2_NOTICE("Failed to allocate node ref for clean marker\n"); - kfree(summary); - return -ENOMEM; - } - - marker_ref->next_in_ino = NULL; - marker_ref->next_phys = NULL; - marker_ref->flash_offset = jeb->offset | REF_NORMAL; - marker_ref->__totlen = je32_to_cpu(summary->cln_mkr); - jeb->first_node = jeb->last_node = marker_ref; - - USED_SPACE( PAD(je32_to_cpu(summary->cln_mkr)) ); + jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, + je32_to_cpu(summary->cln_mkr), NULL); } } - if (je32_to_cpu(summary->padded)) { - DIRTY_SPACE(je32_to_cpu(summary->padded)); - } - ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random); + /* -ENOTRECOVERABLE isn't a fatal error -- it means we should do a full + scan of this eraseblock. So return zero */ + if (ret == -ENOTRECOVERABLE) + return 0; if (ret) - return ret; + return ret; /* real error */ /* for PARANOIA_CHECK */ - cache_ref = jffs2_alloc_raw_node_ref(); - - if (!cache_ref) { - JFFS2_NOTICE("Failed to allocate node ref for cache\n"); - return -ENOMEM; - } - - cache_ref->next_in_ino = NULL; - cache_ref->next_phys = NULL; - cache_ref->flash_offset = ofs | REF_NORMAL; - cache_ref->__totlen = sumsize; - - if (!jeb->first_node) - jeb->first_node = cache_ref; - if (jeb->last_node) - jeb->last_node->next_phys = cache_ref; - jeb->last_node = cache_ref; + ret = jffs2_prealloc_raw_node_refs(c, jeb, 2); + if (ret) + return ret; - USED_SPACE(sumsize); + sum_link_node_ref(c, jeb, ofs | REF_NORMAL, sumsize, NULL); - jeb->wasted_size += jeb->free_size; - c->wasted_size += jeb->free_size; - c->free_size -= jeb->free_size; - jeb->free_size = 0; + if (unlikely(jeb->free_size)) { + JFFS2_WARNING("Free size 0x%x bytes in eraseblock @0x%08x with summary?\n", + jeb->free_size, jeb->offset); + jeb->wasted_size += jeb->free_size; + c->wasted_size += jeb->free_size; + c->free_size -= jeb->free_size; + jeb->free_size = 0; + } return jffs2_scan_classify_jeb(c, jeb); @@ -564,6 +655,7 @@ static int jffs2_sum_write_data(struct j union jffs2_sum_mem *temp; struct jffs2_sum_marker *sm; struct kvec vecs[2]; + uint32_t sum_ofs; void *wpage; int ret; size_t retlen; @@ -581,16 +673,17 @@ static int jffs2_sum_write_data(struct j wpage = c->summary->sum_buf; while (c->summary->sum_num) { + temp = c->summary->sum_list_head; - switch (je16_to_cpu(c->summary->sum_list_head->u.nodetype)) { + switch (je16_to_cpu(temp->u.nodetype)) { case JFFS2_NODETYPE_INODE: { struct jffs2_sum_inode_flash *sino_ptr = wpage; - sino_ptr->nodetype = c->summary->sum_list_head->i.nodetype; - sino_ptr->inode = c->summary->sum_list_head->i.inode; - sino_ptr->version = c->summary->sum_list_head->i.version; - sino_ptr->offset = c->summary->sum_list_head->i.offset; - sino_ptr->totlen = c->summary->sum_list_head->i.totlen; + sino_ptr->nodetype = temp->i.nodetype; + sino_ptr->inode = temp->i.inode; + sino_ptr->version = temp->i.version; + sino_ptr->offset = temp->i.offset; + sino_ptr->totlen = temp->i.totlen; wpage += JFFS2_SUMMARY_INODE_SIZE; @@ -600,30 +693,60 @@ static int jffs2_sum_write_data(struct j case JFFS2_NODETYPE_DIRENT: { struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage; - sdrnt_ptr->nodetype = c->summary->sum_list_head->d.nodetype; - sdrnt_ptr->totlen = c->summary->sum_list_head->d.totlen; - sdrnt_ptr->offset = c->summary->sum_list_head->d.offset; - sdrnt_ptr->pino = c->summary->sum_list_head->d.pino; - sdrnt_ptr->version = c->summary->sum_list_head->d.version; - sdrnt_ptr->ino = c->summary->sum_list_head->d.ino; - sdrnt_ptr->nsize = c->summary->sum_list_head->d.nsize; - sdrnt_ptr->type = c->summary->sum_list_head->d.type; + sdrnt_ptr->nodetype = temp->d.nodetype; + sdrnt_ptr->totlen = temp->d.totlen; + sdrnt_ptr->offset = temp->d.offset; + sdrnt_ptr->pino = temp->d.pino; + sdrnt_ptr->version = temp->d.version; + sdrnt_ptr->ino = temp->d.ino; + sdrnt_ptr->nsize = temp->d.nsize; + sdrnt_ptr->type = temp->d.type; - memcpy(sdrnt_ptr->name, c->summary->sum_list_head->d.name, - c->summary->sum_list_head->d.nsize); + memcpy(sdrnt_ptr->name, temp->d.name, + temp->d.nsize); - wpage += JFFS2_SUMMARY_DIRENT_SIZE(c->summary->sum_list_head->d.nsize); + wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize); break; } +#ifdef CONFIG_JFFS2_FS_XATTR + case JFFS2_NODETYPE_XATTR: { + struct jffs2_sum_xattr_flash *sxattr_ptr = wpage; + + temp = c->summary->sum_list_head; + sxattr_ptr->nodetype = temp->x.nodetype; + sxattr_ptr->xid = temp->x.xid; + sxattr_ptr->version = temp->x.version; + sxattr_ptr->offset = temp->x.offset; + sxattr_ptr->totlen = temp->x.totlen; + + wpage += JFFS2_SUMMARY_XATTR_SIZE; + break; + } + case JFFS2_NODETYPE_XREF: { + struct jffs2_sum_xref_flash *sxref_ptr = wpage; + + temp = c->summary->sum_list_head; + sxref_ptr->nodetype = temp->r.nodetype; + sxref_ptr->offset = temp->r.offset; + wpage += JFFS2_SUMMARY_XREF_SIZE; + break; + } +#endif default : { - BUG(); /* unknown node in summary information */ + if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK) + == JFFS2_FEATURE_RWCOMPAT_COPY) { + dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n", + je16_to_cpu(temp->u.nodetype)); + jffs2_sum_disable_collecting(c->summary); + } else { + BUG(); /* unknown node in summary information */ + } } } - temp = c->summary->sum_list_head; - c->summary->sum_list_head = c->summary->sum_list_head->u.next; + c->summary->sum_list_head = temp->u.next; kfree(temp); c->summary->sum_num--; @@ -645,25 +768,34 @@ static int jffs2_sum_write_data(struct j vecs[1].iov_base = c->summary->sum_buf; vecs[1].iov_len = datasize; - dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n", - jeb->offset + c->sector_size - jeb->free_size); + sum_ofs = jeb->offset + c->sector_size - jeb->free_size; - spin_unlock(&c->erase_completion_lock); - ret = jffs2_flash_writev(c, vecs, 2, jeb->offset + c->sector_size - - jeb->free_size, &retlen, 0); - spin_lock(&c->erase_completion_lock); + dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n", + sum_ofs); + ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0); if (ret || (retlen != infosize)) { - JFFS2_WARNING("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", - infosize, jeb->offset + c->sector_size - jeb->free_size, ret, retlen); + + JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n", + infosize, sum_ofs, ret, retlen); + + if (retlen) { + /* Waste remaining space */ + spin_lock(&c->erase_completion_lock); + jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL); + spin_unlock(&c->erase_completion_lock); + } c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE; - WASTED_SPACE(infosize); - return 1; + return 0; } + spin_lock(&c->erase_completion_lock); + jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL); + spin_unlock(&c->erase_completion_lock); + return 0; } @@ -671,13 +803,16 @@ static int jffs2_sum_write_data(struct j int jffs2_sum_write_sumnode(struct jffs2_sb_info *c) { - struct jffs2_raw_node_ref *summary_ref; - int datasize, infosize, padsize, ret; + int datasize, infosize, padsize; struct jffs2_eraseblock *jeb; + int ret; dbg_summary("called\n"); + spin_unlock(&c->erase_completion_lock); + jeb = c->nextblock; + jffs2_prealloc_raw_node_refs(c, jeb, 1); if (!c->summary->sum_num || !c->summary->sum_list_head) { JFFS2_WARNING("Empty summary info!!!\n"); @@ -696,35 +831,11 @@ int jffs2_sum_write_sumnode(struct jffs2 jffs2_sum_disable_collecting(c->summary); JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize); + spin_lock(&c->erase_completion_lock); return 0; } ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize); - if (ret) - return 0; /* can't write out summary, block is marked as NOSUM_SIZE */ - - /* for ACCT_PARANOIA_CHECK */ - spin_unlock(&c->erase_completion_lock); - summary_ref = jffs2_alloc_raw_node_ref(); spin_lock(&c->erase_completion_lock); - - if (!summary_ref) { - JFFS2_NOTICE("Failed to allocate node ref for summary\n"); - return -ENOMEM; - } - - summary_ref->next_in_ino = NULL; - summary_ref->next_phys = NULL; - summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL; - summary_ref->__totlen = infosize; - - if (!jeb->first_node) - jeb->first_node = summary_ref; - if (jeb->last_node) - jeb->last_node->next_phys = summary_ref; - jeb->last_node = summary_ref; - - USED_SPACE(infosize); - - return 0; + return ret; } diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h index b7a678b..6bf1f6a 100644 --- a/fs/jffs2/summary.h +++ b/fs/jffs2/summary.h @@ -18,23 +18,6 @@ #define JFFS2_SUMMARY_H #include #include -#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ - c->free_size -= _x; c->dirty_size += _x; \ - jeb->free_size -= _x ; jeb->dirty_size += _x; \ - }while(0) -#define USED_SPACE(x) do { typeof(x) _x = (x); \ - c->free_size -= _x; c->used_size += _x; \ - jeb->free_size -= _x ; jeb->used_size += _x; \ - }while(0) -#define WASTED_SPACE(x) do { typeof(x) _x = (x); \ - c->free_size -= _x; c->wasted_size += _x; \ - jeb->free_size -= _x ; jeb->wasted_size += _x; \ - }while(0) -#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ - c->free_size -= _x; c->unchecked_size += _x; \ - jeb->free_size -= _x ; jeb->unchecked_size += _x; \ - }while(0) - #define BLK_STATE_ALLFF 0 #define BLK_STATE_CLEAN 1 #define BLK_STATE_PARTDIRTY 2 @@ -45,6 +28,8 @@ #define BLK_STATE_BADBLOCK 5 #define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff #define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash)) #define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x)) +#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash)) +#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash)) /* Summary structures used on flash */ @@ -75,11 +60,28 @@ struct jffs2_sum_dirent_flash uint8_t name[0]; /* dirent name */ } __attribute__((packed)); +struct jffs2_sum_xattr_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */ + jint32_t xid; /* xattr identifier */ + jint32_t version; /* version number */ + jint32_t offset; /* offset on jeb */ + jint32_t totlen; /* node length */ +} __attribute__((packed)); + +struct jffs2_sum_xref_flash +{ + jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */ + jint32_t offset; /* offset on jeb */ +} __attribute__((packed)); + union jffs2_sum_flash { struct jffs2_sum_unknown_flash u; struct jffs2_sum_inode_flash i; struct jffs2_sum_dirent_flash d; + struct jffs2_sum_xattr_flash x; + struct jffs2_sum_xref_flash r; }; /* Summary structures used in the memory */ @@ -114,11 +116,30 @@ struct jffs2_sum_dirent_mem uint8_t name[0]; /* dirent name */ } __attribute__((packed)); +struct jffs2_sum_xattr_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t xid; + jint32_t version; + jint32_t offset; + jint32_t totlen; +} __attribute__((packed)); + +struct jffs2_sum_xref_mem +{ + union jffs2_sum_mem *next; + jint16_t nodetype; + jint32_t offset; +} __attribute__((packed)); + union jffs2_sum_mem { struct jffs2_sum_unknown_mem u; struct jffs2_sum_inode_mem i; struct jffs2_sum_dirent_mem d; + struct jffs2_sum_xattr_mem x; + struct jffs2_sum_xref_mem r; }; /* Summary related information stored in superblock */ @@ -159,8 +180,11 @@ int jffs2_sum_write_sumnode(struct jffs2 int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size); int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs); int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs); +int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs); +int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs); int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, - uint32_t ofs, uint32_t *pseudo_random); + struct jffs2_raw_summary *summary, uint32_t sumlen, + uint32_t *pseudo_random); #else /* SUMMARY DISABLED */ @@ -176,7 +200,9 @@ #define jffs2_sum_write_sumnode(a) (0) #define jffs2_sum_add_padding_mem(a,b) #define jffs2_sum_add_inode_mem(a,b,c) #define jffs2_sum_add_dirent_mem(a,b,c) -#define jffs2_sum_scan_sumnode(a,b,c,d) (0) +#define jffs2_sum_add_xattr_mem(a,b,c) +#define jffs2_sum_add_xref_mem(a,b,c) +#define jffs2_sum_scan_sumnode(a,b,c,d,e) (0) #endif /* CONFIG_JFFS2_SUMMARY */ diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index ffd8e84..68e3953 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -11,7 +11,6 @@ * */ -#include #include #include #include @@ -111,9 +110,10 @@ static int jffs2_sb_set(struct super_blo return 0; } -static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data, struct mtd_info *mtd) +static int jffs2_get_sb_mtd(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct mtd_info *mtd, + struct vfsmount *mnt) { struct super_block *sb; struct jffs2_sb_info *c; @@ -121,19 +121,20 @@ static struct super_block *jffs2_get_sb_ c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) - return ERR_PTR(-ENOMEM); + return -ENOMEM; memset(c, 0, sizeof(*c)); c->mtd = mtd; sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); if (IS_ERR(sb)) - goto out_put; + goto out_error; if (sb->s_root) { /* New mountpoint for JFFS2 which is already mounted */ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", mtd->index, mtd->name)); + ret = simple_set_mnt(mnt, sb); goto out_put; } @@ -151,51 +152,57 @@ static struct super_block *jffs2_get_sb_ sb->s_op = &jffs2_super_operations; sb->s_flags = flags | MS_NOATIME; - + sb->s_xattr = jffs2_xattr_handlers; +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + sb->s_flags |= MS_POSIXACL; +#endif ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); if (ret) { /* Failure case... */ up_write(&sb->s_umount); deactivate_super(sb); - return ERR_PTR(ret); + return ret; } sb->s_flags |= MS_ACTIVE; - return sb; + return simple_set_mnt(mnt, sb); +out_error: + ret = PTR_ERR(sb); out_put: kfree(c); put_mtd_device(mtd); - return sb; + return ret; } -static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data, int mtdnr) +static int jffs2_get_sb_mtdnr(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, int mtdnr, + struct vfsmount *mnt) { struct mtd_info *mtd; mtd = get_mtd_device(NULL, mtdnr); if (!mtd) { D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); - return ERR_PTR(-EINVAL); + return -EINVAL; } - return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); + return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); } -static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int jffs2_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { int err; struct nameidata nd; int mtdnr; if (!dev_name) - return ERR_PTR(-EINVAL); + return -EINVAL; D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); @@ -217,7 +224,7 @@ static struct super_block *jffs2_get_sb( mtd = get_mtd_device(NULL, mtdnr); if (mtd) { if (!strcmp(mtd->name, dev_name+4)) - return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); + return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); put_mtd_device(mtd); } } @@ -230,7 +237,7 @@ static struct super_block *jffs2_get_sb( if (!*endptr) { /* It was a valid number */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); - return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); + return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt); } } } @@ -244,7 +251,7 @@ static struct super_block *jffs2_get_sb( err, nd.dentry->d_inode)); if (err) - return ERR_PTR(err); + return err; err = -EINVAL; @@ -266,11 +273,11 @@ static struct super_block *jffs2_get_sb( mtdnr = iminor(nd.dentry->d_inode); path_release(&nd); - return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); + return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt); out: path_release(&nd); - return ERR_PTR(err); + return err; } static void jffs2_put_super (struct super_block *sb) @@ -293,6 +300,7 @@ static void jffs2_put_super (struct supe kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); + jffs2_clear_xattr_subsystem(c); if (c->mtd->sync) c->mtd->sync(c->mtd); @@ -320,6 +328,18 @@ static int __init init_jffs2_fs(void) { int ret; + /* Paranoia checks for on-medium structures. If we ask GCC + to pack them with __attribute__((packed)) then it _also_ + assumes that they're not aligned -- so it emits crappy + code on some architectures. Ideally we want an attribute + which means just 'no padding', without the alignment + thing. But GCC doesn't have that -- we have to just + hope the structs are the right sizes, instead. */ + BUG_ON(sizeof(struct jffs2_unknown_node) != 12); + BUG_ON(sizeof(struct jffs2_raw_dirent) != 40); + BUG_ON(sizeof(struct jffs2_raw_inode) != 68); + BUG_ON(sizeof(struct jffs2_raw_summary) != 32); + printk(KERN_INFO "JFFS2 version 2.2." #ifdef CONFIG_JFFS2_FS_WRITEBUFFER " (NAND)" @@ -327,7 +347,7 @@ #endif #ifdef CONFIG_JFFS2_SUMMARY " (SUMMARY) " #endif - " (C) 2001-2003 Red Hat, Inc.\n"); + " (C) 2001-2006 Red Hat, Inc.\n"); jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index d55754f..fc211b6 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -24,7 +24,12 @@ struct inode_operations jffs2_symlink_in { .readlink = generic_readlink, .follow_link = jffs2_follow_link, - .setattr = jffs2_setattr + .permission = jffs2_permission, + .setattr = jffs2_setattr, + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr }; static void *jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 4cebf0e..b9b7007 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -156,69 +156,130 @@ static void jffs2_block_refile(struct jf jffs2_erase_pending_trigger(c); } - /* Adjust its size counts accordingly */ - c->wasted_size += jeb->free_size; - c->free_size -= jeb->free_size; - jeb->wasted_size += jeb->free_size; - jeb->free_size = 0; + if (!jffs2_prealloc_raw_node_refs(c, jeb, 1)) { + uint32_t oldfree = jeb->free_size; + + jffs2_link_node_ref(c, jeb, + (jeb->offset+c->sector_size-oldfree) | REF_OBSOLETE, + oldfree, NULL); + /* convert to wasted */ + c->wasted_size += oldfree; + jeb->wasted_size += oldfree; + c->dirty_size -= oldfree; + jeb->dirty_size -= oldfree; + } jffs2_dbg_dump_block_lists_nolock(c); jffs2_dbg_acct_sanity_check_nolock(c,jeb); jffs2_dbg_acct_paranoia_check_nolock(c, jeb); } +static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info *c, + struct jffs2_inode_info *f, + struct jffs2_raw_node_ref *raw, + union jffs2_node_union *node) +{ + struct jffs2_node_frag *frag; + struct jffs2_full_dirent *fd; + + dbg_noderef("incore_replace_raw: node at %p is {%04x,%04x}\n", + node, je16_to_cpu(node->u.magic), je16_to_cpu(node->u.nodetype)); + + BUG_ON(je16_to_cpu(node->u.magic) != 0x1985 && + je16_to_cpu(node->u.magic) != 0); + + switch (je16_to_cpu(node->u.nodetype)) { + case JFFS2_NODETYPE_INODE: + if (f->metadata && f->metadata->raw == raw) { + dbg_noderef("Will replace ->raw in f->metadata at %p\n", f->metadata); + return &f->metadata->raw; + } + frag = jffs2_lookup_node_frag(&f->fragtree, je32_to_cpu(node->i.offset)); + BUG_ON(!frag); + /* Find a frag which refers to the full_dnode we want to modify */ + while (!frag->node || frag->node->raw != raw) { + frag = frag_next(frag); + BUG_ON(!frag); + } + dbg_noderef("Will replace ->raw in full_dnode at %p\n", frag->node); + return &frag->node->raw; + + case JFFS2_NODETYPE_DIRENT: + for (fd = f->dents; fd; fd = fd->next) { + if (fd->raw == raw) { + dbg_noderef("Will replace ->raw in full_dirent at %p\n", fd); + return &fd->raw; + } + } + BUG(); + + default: + dbg_noderef("Don't care about replacing raw for nodetype %x\n", + je16_to_cpu(node->u.nodetype)); + break; + } + return NULL; +} + /* Recover from failure to write wbuf. Recover the nodes up to the * wbuf, not the one which we were starting to try to write. */ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) { struct jffs2_eraseblock *jeb, *new_jeb; - struct jffs2_raw_node_ref **first_raw, **raw; + struct jffs2_raw_node_ref *raw, *next, *first_raw = NULL; size_t retlen; int ret; + int nr_refile = 0; unsigned char *buf; uint32_t start, end, ofs, len; - spin_lock(&c->erase_completion_lock); - jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + spin_lock(&c->erase_completion_lock); jffs2_block_refile(c, jeb, REFILE_NOTEMPTY); + spin_unlock(&c->erase_completion_lock); + + BUG_ON(!ref_obsolete(jeb->last_node)); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ - first_raw = &jeb->first_node; - while (*first_raw && - (ref_obsolete(*first_raw) || - (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { - D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", - ref_offset(*first_raw), ref_flags(*first_raw), - (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), - c->wbuf_ofs)); - first_raw = &(*first_raw)->next_phys; + for (next = raw = jeb->first_node; next; raw = next) { + next = ref_next(raw); + + if (ref_obsolete(raw) || + (next && ref_offset(next) <= c->wbuf_ofs)) { + dbg_noderef("Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", + ref_offset(raw), ref_flags(raw), + (ref_offset(raw) + ref_totlen(c, jeb, raw)), + c->wbuf_ofs); + continue; + } + dbg_noderef("First node to be recovered is at 0x%08x(%d)-0x%08x\n", + ref_offset(raw), ref_flags(raw), + (ref_offset(raw) + ref_totlen(c, jeb, raw))); + + first_raw = raw; + break; } - if (!*first_raw) { + if (!first_raw) { /* All nodes were obsolete. Nothing to recover. */ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n")); - spin_unlock(&c->erase_completion_lock); + c->wbuf_len = 0; return; } - start = ref_offset(*first_raw); - end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); - - /* Find the last node to be recovered */ - raw = first_raw; - while ((*raw)) { - if (!ref_obsolete(*raw)) - end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); + start = ref_offset(first_raw); + end = ref_offset(jeb->last_node); + nr_refile = 1; - raw = &(*raw)->next_phys; - } - spin_unlock(&c->erase_completion_lock); + /* Count the number of refs which need to be copied */ + while ((raw = ref_next(raw)) != jeb->last_node) + nr_refile++; - D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end)); + dbg_noderef("wbuf recover %08x-%08x (%d bytes in %d nodes)\n", + start, end, end - start, nr_refile); buf = NULL; if (start < c->wbuf_ofs) { @@ -233,28 +294,37 @@ static void jffs2_wbuf_recover(struct jf } /* Do the read... */ - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); - else - ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); + ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); - if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { - /* ECC recovered */ + /* ECC recovered ? */ + if ((ret == -EUCLEAN || ret == -EBADMSG) && + (retlen == c->wbuf_ofs - start)) ret = 0; - } + if (ret || retlen != c->wbuf_ofs - start) { printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); kfree(buf); buf = NULL; read_failed: - first_raw = &(*first_raw)->next_phys; + first_raw = ref_next(first_raw); + nr_refile--; + while (first_raw && ref_obsolete(first_raw)) { + first_raw = ref_next(first_raw); + nr_refile--; + } + /* If this was the only node to be recovered, give up */ - if (!(*first_raw)) + if (!first_raw) { + c->wbuf_len = 0; return; + } /* It wasn't. Go on and try to recover nodes complete in the wbuf */ - start = ref_offset(*first_raw); + start = ref_offset(first_raw); + dbg_noderef("wbuf now recover %08x-%08x (%d bytes in %d nodes)\n", + start, end, end - start, nr_refile); + } else { /* Read succeeded. Copy the remaining data from the wbuf */ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); @@ -263,14 +333,23 @@ static void jffs2_wbuf_recover(struct jf /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. Either 'buf' contains the data, or we find it in the wbuf */ - /* ... and get an allocation of space from a shiny new block instead */ - ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len, JFFS2_SUMMARY_NOSUM_SIZE); + ret = jffs2_reserve_space_gc(c, end-start, &len, JFFS2_SUMMARY_NOSUM_SIZE); if (ret) { printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); kfree(buf); return; } + + ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, nr_refile); + if (ret) { + printk(KERN_WARNING "Failed to allocate node refs for wbuf recovery. Data loss ensues.\n"); + kfree(buf); + return; + } + + ofs = write_ofs(c); + if (end-start >= c->wbuf_pagesize) { /* Need to do another write immediately, but it's possible that this is just because the wbuf itself is completely @@ -288,36 +367,22 @@ #ifdef BREAKMEHEADER if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); breakme = 0; - c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, - brokenbuf, NULL, c->oobinfo); + c->mtd->write(c->mtd, ofs, towrite, &retlen, + brokenbuf); ret = -EIO; } else #endif - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, - rewrite_buf, NULL, c->oobinfo); - else - ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf); + ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, + rewrite_buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); kfree(buf); - if (retlen) { - struct jffs2_raw_node_ref *raw2; - - raw2 = jffs2_alloc_raw_node_ref(); - if (!raw2) - return; + if (retlen) + jffs2_add_physical_node_ref(c, ofs | REF_OBSOLETE, ref_totlen(c, jeb, first_raw), NULL); - raw2->flash_offset = ofs | REF_OBSOLETE; - raw2->__totlen = ref_totlen(c, jeb, *first_raw); - raw2->next_phys = NULL; - raw2->next_in_ino = NULL; - - jffs2_add_physical_node_ref(c, raw2); - } return; } printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); @@ -326,12 +391,10 @@ #endif c->wbuf_ofs = ofs + towrite; memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len); /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ - kfree(buf); } else { /* OK, now we're left with the dregs in whichever buffer we're using */ if (buf) { memcpy(c->wbuf, buf, end-start); - kfree(buf); } else { memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); } @@ -343,62 +406,110 @@ #endif new_jeb = &c->blocks[ofs / c->sector_size]; spin_lock(&c->erase_completion_lock); - if (new_jeb->first_node) { - /* Odd, but possible with ST flash later maybe */ - new_jeb->last_node->next_phys = *first_raw; - } else { - new_jeb->first_node = *first_raw; - } - - raw = first_raw; - while (*raw) { - uint32_t rawlen = ref_totlen(c, jeb, *raw); + for (raw = first_raw; raw != jeb->last_node; raw = ref_next(raw)) { + uint32_t rawlen = ref_totlen(c, jeb, raw); + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *new_ref; + struct jffs2_raw_node_ref **adjust_ref = NULL; + struct jffs2_inode_info *f = NULL; D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", - rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); + rawlen, ref_offset(raw), ref_flags(raw), ofs)); + + ic = jffs2_raw_ref_to_ic(raw); + + /* Ick. This XATTR mess should be fixed shortly... */ + if (ic && ic->class == RAWNODE_CLASS_XATTR_DATUM) { + struct jffs2_xattr_datum *xd = (void *)ic; + BUG_ON(xd->node != raw); + adjust_ref = &xd->node; + raw->next_in_ino = NULL; + ic = NULL; + } else if (ic && ic->class == RAWNODE_CLASS_XATTR_REF) { + struct jffs2_xattr_datum *xr = (void *)ic; + BUG_ON(xr->node != raw); + adjust_ref = &xr->node; + raw->next_in_ino = NULL; + ic = NULL; + } else if (ic && ic->class == RAWNODE_CLASS_INODE_CACHE) { + struct jffs2_raw_node_ref **p = &ic->nodes; + + /* Remove the old node from the per-inode list */ + while (*p && *p != (void *)ic) { + if (*p == raw) { + (*p) = (raw->next_in_ino); + raw->next_in_ino = NULL; + break; + } + p = &((*p)->next_in_ino); + } - if (ref_obsolete(*raw)) { - /* Shouldn't really happen much */ - new_jeb->dirty_size += rawlen; - new_jeb->free_size -= rawlen; - c->dirty_size += rawlen; - } else { - new_jeb->used_size += rawlen; - new_jeb->free_size -= rawlen; + if (ic->state == INO_STATE_PRESENT && !ref_obsolete(raw)) { + /* If it's an in-core inode, then we have to adjust any + full_dirent or full_dnode structure to point to the + new version instead of the old */ + f = jffs2_gc_fetch_inode(c, ic->ino, ic->nlink); + if (IS_ERR(f)) { + /* Should never happen; it _must_ be present */ + JFFS2_ERROR("Failed to iget() ino #%u, err %ld\n", + ic->ino, PTR_ERR(f)); + BUG(); + } + /* We don't lock f->sem. There's a number of ways we could + end up in here with it already being locked, and nobody's + going to modify it on us anyway because we hold the + alloc_sem. We're only changing one ->raw pointer too, + which we can get away with without upsetting readers. */ + adjust_ref = jffs2_incore_replace_raw(c, f, raw, + (void *)(buf?:c->wbuf) + (ref_offset(raw) - start)); + } else if (unlikely(ic->state != INO_STATE_PRESENT && + ic->state != INO_STATE_CHECKEDABSENT && + ic->state != INO_STATE_GC)) { + JFFS2_ERROR("Inode #%u is in strange state %d!\n", ic->ino, ic->state); + BUG(); + } + } + + new_ref = jffs2_link_node_ref(c, new_jeb, ofs | ref_flags(raw), rawlen, ic); + + if (adjust_ref) { + BUG_ON(*adjust_ref != raw); + *adjust_ref = new_ref; + } + if (f) + jffs2_gc_release_inode(c, f); + + if (!ref_obsolete(raw)) { jeb->dirty_size += rawlen; jeb->used_size -= rawlen; c->dirty_size += rawlen; + c->used_size -= rawlen; + raw->flash_offset = ref_offset(raw) | REF_OBSOLETE; + BUG_ON(raw->next_in_ino); } - c->free_size -= rawlen; - (*raw)->flash_offset = ofs | ref_flags(*raw); ofs += rawlen; - new_jeb->last_node = *raw; - - raw = &(*raw)->next_phys; } + kfree(buf); + /* Fix up the original jeb now it's on the bad_list */ - *first_raw = NULL; - if (first_raw == &jeb->first_node) { - jeb->last_node = NULL; + if (first_raw == jeb->first_node) { D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); - list_del(&jeb->list); - list_add(&jeb->list, &c->erase_pending_list); + list_move(&jeb->list, &c->erase_pending_list); c->nr_erasing_blocks++; jffs2_erase_pending_trigger(c); } - else - jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); jffs2_dbg_acct_sanity_check_nolock(c, jeb); - jffs2_dbg_acct_paranoia_check_nolock(c, jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, jeb); jffs2_dbg_acct_sanity_check_nolock(c, new_jeb); - jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); + jffs2_dbg_acct_paranoia_check_nolock(c, new_jeb); spin_unlock(&c->erase_completion_lock); - D1(printk(KERN_DEBUG "wbuf recovery completed OK\n")); + D1(printk(KERN_DEBUG "wbuf recovery completed OK. wbuf_ofs 0x%08x, len 0x%x\n", c->wbuf_ofs, c->wbuf_len)); + } /* Meaning of pad argument: @@ -412,6 +523,7 @@ #define PAD_ACCOUNTING 2 static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { + struct jffs2_eraseblock *wbuf_jeb; int ret; size_t retlen; @@ -429,6 +541,10 @@ static int __jffs2_flush_wbuf(struct jff if (!c->wbuf_len) /* already checked c->wbuf above */ return 0; + wbuf_jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + if (jffs2_prealloc_raw_node_refs(c, wbuf_jeb, c->nextblock->allocated_refs + 1)) + return -ENOMEM; + /* claim remaining space on the page this happens, if we have a change to a new block, or if fsync forces us to flush the writebuffer. @@ -458,15 +574,12 @@ #ifdef BREAKME if (breakme++ == 20) { printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs); breakme = 0; - c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, - &retlen, brokenbuf, NULL, c->oobinfo); + c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, + brokenbuf); ret = -EIO; } else #endif - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); - else ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); if (ret || retlen != c->wbuf_pagesize) { @@ -483,32 +596,34 @@ #endif return ret; } - spin_lock(&c->erase_completion_lock); - /* Adjust free size of the block if we padded. */ if (pad) { - struct jffs2_eraseblock *jeb; - - jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + uint32_t waste = c->wbuf_pagesize - c->wbuf_len; D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of %sblock at %08x\n", - (jeb==c->nextblock)?"next":"", jeb->offset)); + (wbuf_jeb==c->nextblock)?"next":"", wbuf_jeb->offset)); /* wbuf_pagesize - wbuf_len is the amount of space that's to be padded. If there is less free space in the block than that, something screwed up */ - if (jeb->free_size < (c->wbuf_pagesize - c->wbuf_len)) { + if (wbuf_jeb->free_size < waste) { printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", - c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); + c->wbuf_ofs, c->wbuf_len, waste); printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", - jeb->offset, jeb->free_size); + wbuf_jeb->offset, wbuf_jeb->free_size); BUG(); } - jeb->free_size -= (c->wbuf_pagesize - c->wbuf_len); - c->free_size -= (c->wbuf_pagesize - c->wbuf_len); - jeb->wasted_size += (c->wbuf_pagesize - c->wbuf_len); - c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); - } + + spin_lock(&c->erase_completion_lock); + + jffs2_link_node_ref(c, wbuf_jeb, (c->wbuf_ofs + c->wbuf_len) | REF_OBSOLETE, waste, NULL); + /* FIXME: that made it count as dirty. Convert to wasted */ + wbuf_jeb->dirty_size -= waste; + c->dirty_size -= waste; + wbuf_jeb->wasted_size += waste; + c->wasted_size += waste; + } else + spin_lock(&c->erase_completion_lock); /* Stick any now-obsoleted blocks on the erase_pending_list */ jffs2_refile_wbuf_blocks(c); @@ -603,20 +718,30 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb return ret; } -int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) + +static size_t jffs2_fill_wbuf(struct jffs2_sb_info *c, const uint8_t *buf, + size_t len) +{ + if (len && !c->wbuf_len && (len >= c->wbuf_pagesize)) + return 0; + + if (len > (c->wbuf_pagesize - c->wbuf_len)) + len = c->wbuf_pagesize - c->wbuf_len; + memcpy(c->wbuf + c->wbuf_len, buf, len); + c->wbuf_len += (uint32_t) len; + return len; +} + +int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, + unsigned long count, loff_t to, size_t *retlen, + uint32_t ino) { - struct kvec outvecs[3]; - uint32_t totlen = 0; - uint32_t split_ofs = 0; - uint32_t old_totlen; - int ret, splitvec = -1; - int invec, outvec; - size_t wbuf_retlen; - unsigned char *wbuf_ptr; - size_t donelen = 0; + struct jffs2_eraseblock *jeb; + size_t wbuf_retlen, donelen = 0; uint32_t outvec_to = to; + int ret, invec; - /* If not NAND flash, don't bother */ + /* If not writebuffered flash, don't bother */ if (!jffs2_is_writebuffered(c)) return jffs2_flash_direct_writev(c, invecs, count, to, retlen); @@ -629,34 +754,22 @@ int jffs2_flash_writev(struct jffs2_sb_i memset(c->wbuf,0xff,c->wbuf_pagesize); } - /* Fixup the wbuf if we are moving to a new eraseblock. The checks below - fail for ECC'd NOR because cleanmarker == 16, so a block starts at - xxx0010. */ - if (jffs2_nor_ecc(c)) { - if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) { - c->wbuf_ofs = PAGE_DIV(to); - c->wbuf_len = PAGE_MOD(to); - memset(c->wbuf,0xff,c->wbuf_pagesize); - } - } - - /* Sanity checks on target address. - It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), - and it's permitted to write at the beginning of a new - erase block. Anything else, and you die. - New block starts at xxx000c (0-b = block header) - */ + /* + * Sanity checks on target address. It's permitted to write + * at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to + * write at the beginning of a new erase block. Anything else, + * and you die. New block starts at xxx000c (0-b = block + * header) + */ if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) { /* It's a write to a new block */ if (c->wbuf_len) { - D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); + D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx " + "causes flush of wbuf at 0x%08x\n", + (unsigned long)to, c->wbuf_ofs)); ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); - if (ret) { - /* the underlying layer has to check wbuf_len to do the cleanup */ - D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); - *retlen = 0; - goto exit; - } + if (ret) + goto outerr; } /* set pointer to new block */ c->wbuf_ofs = PAGE_DIV(to); @@ -665,165 +778,70 @@ int jffs2_flash_writev(struct jffs2_sb_i if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { /* We're not writing immediately after the writebuffer. Bad. */ - printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); + printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write " + "to %08lx\n", (unsigned long)to); if (c->wbuf_len) printk(KERN_CRIT "wbuf was previously %08x-%08x\n", - c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); + c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); BUG(); } - /* Note outvecs[3] above. We know count is never greater than 2 */ - if (count > 2) { - printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); - BUG(); - } - - invec = 0; - outvec = 0; - - /* Fill writebuffer first, if already in use */ - if (c->wbuf_len) { - uint32_t invec_ofs = 0; - - /* adjust alignment offset */ - if (c->wbuf_len != PAGE_MOD(to)) { - c->wbuf_len = PAGE_MOD(to); - /* take care of alignment to next page */ - if (!c->wbuf_len) - c->wbuf_len = c->wbuf_pagesize; - } - - while(c->wbuf_len < c->wbuf_pagesize) { - uint32_t thislen; - - if (invec == count) - goto alldone; - - thislen = c->wbuf_pagesize - c->wbuf_len; - - if (thislen >= invecs[invec].iov_len) - thislen = invecs[invec].iov_len; - - invec_ofs = thislen; - - memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); - c->wbuf_len += thislen; - donelen += thislen; - /* Get next invec, if actual did not fill the buffer */ - if (c->wbuf_len < c->wbuf_pagesize) - invec++; - } - - /* write buffer is full, flush buffer */ - ret = __jffs2_flush_wbuf(c, NOPAD); - if (ret) { - /* the underlying layer has to check wbuf_len to do the cleanup */ - D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); - /* Retlen zero to make sure our caller doesn't mark the space dirty. - We've already done everything that's necessary */ - *retlen = 0; - goto exit; - } - outvec_to += donelen; - c->wbuf_ofs = outvec_to; - - /* All invecs done ? */ - if (invec == count) - goto alldone; - - /* Set up the first outvec, containing the remainder of the - invec we partially used */ - if (invecs[invec].iov_len > invec_ofs) { - outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; - totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; - if (totlen > c->wbuf_pagesize) { - splitvec = outvec; - split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); - } - outvec++; - } - invec++; - } - - /* OK, now we've flushed the wbuf and the start of the bits - we have been asked to write, now to write the rest.... */ - - /* totlen holds the amount of data still to be written */ - old_totlen = totlen; - for ( ; invec < count; invec++,outvec++ ) { - outvecs[outvec].iov_base = invecs[invec].iov_base; - totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; - if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { - splitvec = outvec; - split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); - old_totlen = totlen; + /* adjust alignment offset */ + if (c->wbuf_len != PAGE_MOD(to)) { + c->wbuf_len = PAGE_MOD(to); + /* take care of alignment to next page */ + if (!c->wbuf_len) { + c->wbuf_len = c->wbuf_pagesize; + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; } } - /* Now the outvecs array holds all the remaining data to write */ - /* Up to splitvec,split_ofs is to be written immediately. The rest - goes into the (now-empty) wbuf */ - - if (splitvec != -1) { - uint32_t remainder; - - remainder = outvecs[splitvec].iov_len - split_ofs; - outvecs[splitvec].iov_len = split_ofs; - - /* We did cross a page boundary, so we write some now */ - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); - else - ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); + for (invec = 0; invec < count; invec++) { + int vlen = invecs[invec].iov_len; + uint8_t *v = invecs[invec].iov_base; - if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { - /* At this point we have no problem, - c->wbuf is empty. However refile nextblock to avoid - writing again to same address. - */ - struct jffs2_eraseblock *jeb; + wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); - spin_lock(&c->erase_completion_lock); - - jeb = &c->blocks[outvec_to / c->sector_size]; - jffs2_block_refile(c, jeb, REFILE_ANYWAY); - - *retlen = 0; - spin_unlock(&c->erase_completion_lock); - goto exit; + if (c->wbuf_len == c->wbuf_pagesize) { + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; } - + vlen -= wbuf_retlen; + outvec_to += wbuf_retlen; donelen += wbuf_retlen; - c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); - - if (remainder) { - outvecs[splitvec].iov_base += split_ofs; - outvecs[splitvec].iov_len = remainder; - } else { - splitvec++; + v += wbuf_retlen; + + if (vlen >= c->wbuf_pagesize) { + ret = c->mtd->write(c->mtd, outvec_to, PAGE_DIV(vlen), + &wbuf_retlen, v); + if (ret < 0 || wbuf_retlen != PAGE_DIV(vlen)) + goto outfile; + + vlen -= wbuf_retlen; + outvec_to += wbuf_retlen; + c->wbuf_ofs = outvec_to; + donelen += wbuf_retlen; + v += wbuf_retlen; } - } else { - splitvec = 0; - } - - /* Now splitvec points to the start of the bits we have to copy - into the wbuf */ - wbuf_ptr = c->wbuf; + wbuf_retlen = jffs2_fill_wbuf(c, v, vlen); + if (c->wbuf_len == c->wbuf_pagesize) { + ret = __jffs2_flush_wbuf(c, NOPAD); + if (ret) + goto outerr; + } - for ( ; splitvec < outvec; splitvec++) { - /* Don't copy the wbuf into itself */ - if (outvecs[splitvec].iov_base == c->wbuf) - continue; - memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); - wbuf_ptr += outvecs[splitvec].iov_len; - donelen += outvecs[splitvec].iov_len; + outvec_to += wbuf_retlen; + donelen += wbuf_retlen; } - c->wbuf_len = wbuf_ptr - c->wbuf; - /* If there's a remainder in the wbuf and it's a non-GC write, - remember that the wbuf affects this ino */ -alldone: + /* + * If there's a remainder in the wbuf and it's a non-GC write, + * remember that the wbuf affects this ino + */ *retlen = donelen; if (jffs2_sum_active()) { @@ -836,8 +854,24 @@ alldone: jffs2_wbuf_dirties_inode(c, ino); ret = 0; + up_write(&c->wbuf_sem); + return ret; -exit: +outfile: + /* + * At this point we have no problem, c->wbuf is empty. However + * refile nextblock to avoid writing again to same address. + */ + + spin_lock(&c->erase_completion_lock); + + jeb = &c->blocks[outvec_to / c->sector_size]; + jffs2_block_refile(c, jeb, REFILE_ANYWAY); + + spin_unlock(&c->erase_completion_lock); + +outerr: + *retlen = 0; up_write(&c->wbuf_sem); return ret; } @@ -846,7 +880,8 @@ exit: * This is the entry for flash write. * Check, if we work on NAND FLASH, if so build an kvec and write it via vritev */ -int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) +int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf) { struct kvec vecs[1]; @@ -871,25 +906,23 @@ int jffs2_flash_read(struct jffs2_sb_inf /* Read flash */ down_read(&c->wbuf_sem); - if (jffs2_cleanmarker_oob(c)) - ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); - else - ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); - - if ( (ret == -EBADMSG) && (*retlen == len) ) { - printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", - len, ofs); + ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); + + if ( (ret == -EBADMSG || ret == -EUCLEAN) && (*retlen == len) ) { + if (ret == -EBADMSG) + printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx)" + " returned ECC error\n", len, ofs); /* - * We have the raw data without ECC correction in the buffer, maybe - * we are lucky and all data or parts are correct. We check the node. - * If data are corrupted node check will sort it out. - * We keep this block, it will fail on write or erase and the we - * mark it bad. Or should we do that now? But we should give him a chance. - * Maybe we had a system crash or power loss before the ecc write or - * a erase was completed. + * We have the raw data without ECC correction in the buffer, + * maybe we are lucky and all data or parts are correct. We + * check the node. If data are corrupted node check will sort + * it out. We keep this block, it will fail on write or erase + * and the we mark it bad. Or should we do that now? But we + * should give him a chance. Maybe we had a system crash or + * power loss before the ecc write or a erase was completed. * So we return success. :) */ - ret = 0; + ret = 0; } /* if no writebuffer available or write buffer empty, return */ @@ -911,7 +944,7 @@ int jffs2_flash_read(struct jffs2_sb_inf orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ if (orbf > len) /* is write beyond write buffer ? */ goto exit; - lwbf = len - orbf; /* number of bytes to copy */ + lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; } @@ -923,158 +956,159 @@ exit: return ret; } +#define NR_OOB_SCAN_PAGES 4 + /* - * Check, if the out of band area is empty + * Check, if the out of band area is empty */ -int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) +int jffs2_check_oob_empty(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb, int mode) { - unsigned char *buf; - int ret = 0; - int i,len,page; - size_t retlen; - int oob_size; - - /* allocate a buffer for all oob data in this sector */ - oob_size = c->mtd->oobsize; - len = 4 * oob_size; - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); - return -ENOMEM; - } - /* - * if mode = 0, we scan for a total empty oob area, else we have - * to take care of the cleanmarker in the first page of the block - */ - ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); + int i, page, ret; + int oobsize = c->mtd->oobsize; + struct mtd_oob_ops ops; + + ops.len = NR_OOB_SCAN_PAGES * oobsize; + ops.ooblen = oobsize; + ops.oobbuf = c->oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); if (ret) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - goto out; + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " + "failed %d for block at %08x\n", ret, jeb->offset)); + return ret; } - if (retlen < len) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " - "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); - ret = -EIO; - goto out; + if (ops.retlen < ops.len) { + D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " + "returned short read (%zd bytes not %d) for block " + "at %08x\n", ops.retlen, ops.len, jeb->offset)); + return -EIO; } /* Special check for first page */ - for(i = 0; i < oob_size ; i++) { + for(i = 0; i < oobsize ; i++) { /* Yeah, we know about the cleanmarker. */ if (mode && i >= c->fsdata_pos && i < c->fsdata_pos + c->fsdata_len) continue; - if (buf[i] != 0xFF) { - D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", - buf[i], i, jeb->offset)); - ret = 1; - goto out; + if (ops.oobbuf[i] != 0xFF) { + D2(printk(KERN_DEBUG "Found %02x at %x in OOB for " + "%08x\n", ops.oobbuf[i], i, jeb->offset)); + return 1; } } /* we know, we are aligned :) */ - for (page = oob_size; page < len; page += sizeof(long)) { - unsigned long dat = *(unsigned long *)(&buf[page]); - if(dat != -1) { - ret = 1; - goto out; - } + for (page = oobsize; page < ops.len; page += sizeof(long)) { + long dat = *(long *)(&ops.oobbuf[page]); + if(dat != -1) + return 1; } - -out: - kfree(buf); - - return ret; + return 0; } /* -* Scan for a valid cleanmarker and for bad blocks -* For virtual blocks (concatenated physical blocks) check the cleanmarker -* only in the first page of the first physical block, but scan for bad blocks in all -* physical blocks -*/ -int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + * Scan for a valid cleanmarker and for bad blocks + */ +int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { struct jffs2_unknown_node n; - unsigned char buf[2 * NAND_MAX_OOBSIZE]; - unsigned char *p; - int ret, i, cnt, retval = 0; - size_t retlen, offset; - int oob_size; - - offset = jeb->offset; - oob_size = c->mtd->oobsize; - - /* Loop through the physical blocks */ - for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { - /* Check first if the block is bad. */ - if (c->mtd->block_isbad (c->mtd, offset)) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); - return 2; - } - /* - * We read oob data from page 0 and 1 of the block. - * page 0 contains cleanmarker and badblock info - * page 1 contains failure count of this block - */ - ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); + struct mtd_oob_ops ops; + int oobsize = c->mtd->oobsize; + unsigned char *p,*b; + int i, ret; + size_t offset = jeb->offset; + + /* Check first if the block is bad. */ + if (c->mtd->block_isbad(c->mtd, offset)) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" + ": Bad block at %08x\n", jeb->offset)); + return 2; + } - if (ret) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); - return ret; - } - if (retlen < (oob_size << 1)) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); - return -EIO; - } + ops.len = oobsize; + ops.ooblen = oobsize; + ops.oobbuf = c->oobbuf; + ops.ooboffs = 0; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; - /* Check cleanmarker only on the first physical block */ - if (!cnt) { - n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32 (8); - p = (unsigned char *) &n; + ret = c->mtd->read_oob(c->mtd, offset, &ops); + if (ret) { + D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Read OOB failed %d for block at %08x\n", + ret, jeb->offset)); + return ret; + } - for (i = 0; i < c->fsdata_len; i++) { - if (buf[c->fsdata_pos + i] != p[i]) { - retval = 1; - } - } - D1(if (retval == 1) { - printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); - printk(KERN_WARNING "OOB at %08x was ", offset); - for (i=0; i < oob_size; i++) { - printk("%02x ", buf[i]); - } - printk("\n"); - }) - } - offset += c->mtd->erasesize; + if (ops.retlen < ops.len) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Read OOB return short read (%zd bytes not %d) " + "for block at %08x\n", ops.retlen, ops.len, + jeb->offset)); + return -EIO; } - return retval; + + n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); + n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); + n.totlen = cpu_to_je32 (8); + p = (unsigned char *) &n; + b = c->oobbuf + c->fsdata_pos; + + for (i = c->fsdata_len; i; i--) { + if (*b++ != *p++) + ret = 1; + } + + D1(if (ret == 1) { + printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " + "Cleanmarker node not detected in block at %08x\n", + offset); + printk(KERN_WARNING "OOB at %08zx was ", offset); + for (i=0; i < oobsize; i++) + printk("%02x ", c->oobbuf[i]); + printk("\n"); + }); + return ret; } -int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; - int ret; - size_t retlen; + struct jffs2_unknown_node n; + int ret; + struct mtd_oob_ops ops; n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); n.totlen = cpu_to_je32(8); - ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); + ops.len = c->fsdata_len; + ops.ooblen = c->fsdata_len;; + ops.oobbuf = (uint8_t *)&n; + ops.ooboffs = c->fsdata_pos; + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); if (ret) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " + "Write failed for block at %08x: error %d\n", + jeb->offset, ret)); return ret; } - if (retlen != c->fsdata_len) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); - return ret; + if (ops.retlen != ops.len) { + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " + "Short write for block at %08x: %zd not %d\n", + jeb->offset, ops.retlen, ops.len)); + return -EIO; } return 0; } @@ -1108,18 +1142,9 @@ int jffs2_write_nand_badblock(struct jff return 1; } -#define NAND_JFFS2_OOB16_FSDALEN 8 - -static struct nand_oobinfo jffs2_oobinfo_docecc = { - .useecc = MTD_NANDECC_PLACE, - .eccbytes = 6, - .eccpos = {0,1,2,3,4,5} -}; - - static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) { - struct nand_oobinfo *oinfo = &c->mtd->oobinfo; + struct nand_ecclayout *oinfo = c->mtd->ecclayout; /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) @@ -1129,33 +1154,23 @@ static int jffs2_nand_set_oobinfo(struct c->cleanmarker_size = 0; /* Should we use autoplacement ? */ - if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { - D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); - /* Get the position of the free bytes */ - if (!oinfo->oobfree[0][1]) { - printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); - return -ENOSPC; - } - c->fsdata_pos = oinfo->oobfree[0][0]; - c->fsdata_len = oinfo->oobfree[0][1]; - if (c->fsdata_len > 8) - c->fsdata_len = 8; - } else { - /* This is just a legacy fallback and should go away soon */ - switch(c->mtd->ecctype) { - case MTD_ECC_RS_DiskOnChip: - printk(KERN_WARNING "JFFS2 using DiskOnChip hardware ECC without autoplacement. Fix it!\n"); - c->oobinfo = &jffs2_oobinfo_docecc; - c->fsdata_pos = 6; - c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; - c->badblock_pos = 15; - break; + if (!oinfo) { + D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); + return -EINVAL; + } - default: - D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); - return -EINVAL; - } + D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); + /* Get the position of the free bytes */ + if (!oinfo->oobfree[0].length) { + printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep." + " Autoplacement selected and no empty space in oob\n"); + return -ENOSPC; } + c->fsdata_pos = oinfo->oobfree[0].offset; + c->fsdata_len = oinfo->oobfree[0].length; + if (c->fsdata_len > 8) + c->fsdata_len = 8; + return 0; } @@ -1165,13 +1180,17 @@ int jffs2_nand_flash_setup(struct jffs2_ /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); - c->wbuf_pagesize = c->mtd->oobblock; + c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) return -ENOMEM; + c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); + if (!c->oobbuf) + return -ENOMEM; + res = jffs2_nand_set_oobinfo(c); #ifdef BREAKME @@ -1189,6 +1208,7 @@ #endif void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); + kfree(c->oobbuf); } int jffs2_dataflash_setup(struct jffs2_sb_info *c) { @@ -1236,33 +1256,14 @@ void jffs2_dataflash_cleanup(struct jffs kfree(c->wbuf); } -int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) { - /* Cleanmarker is actually larger on the flashes */ - c->cleanmarker_size = 16; - - /* Initialize write buffer */ - init_rwsem(&c->wbuf_sem); - c->wbuf_pagesize = c->mtd->eccsize; - c->wbuf_ofs = 0xFFFFFFFF; - - c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); - if (!c->wbuf) - return -ENOMEM; - - return 0; -} - -void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { - kfree(c->wbuf); -} - int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c) { - /* Cleanmarker currently occupies a whole programming region */ - c->cleanmarker_size = MTD_PROGREGION_SIZE(c->mtd); + /* Cleanmarker currently occupies whole programming regions, + * either one or 2 for 8Byte STMicro flashes. */ + c->cleanmarker_size = max(16u, c->mtd->writesize); /* Initialize write buffer */ init_rwsem(&c->wbuf_sem); - c->wbuf_pagesize = MTD_PROGREGION_SIZE(c->mtd); + c->wbuf_pagesize = c->mtd->writesize; c->wbuf_ofs = 0xFFFFFFFF; c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 1342f01..6717679 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -37,7 +37,6 @@ int jffs2_do_new_inode(struct jffs2_sb_i f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; f->inocache->state = INO_STATE_PRESENT; - jffs2_add_ino_cache(c, f->inocache); D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ri->ino = cpu_to_je32(f->inocache->ino); @@ -57,12 +56,14 @@ int jffs2_do_new_inode(struct jffs2_sb_i /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, write it to the flash, link it into the existing inode/fragment list */ -struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode) +struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_raw_inode *ri, const unsigned char *data, + uint32_t datalen, int alloc_mode) { - struct jffs2_raw_node_ref *raw; struct jffs2_full_dnode *fn; size_t retlen; + uint32_t flash_ofs; struct kvec vecs[2]; int ret; int retried = 0; @@ -78,34 +79,21 @@ struct jffs2_full_dnode *jffs2_write_dno vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; - jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); - if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); } - raw = jffs2_alloc_raw_node_ref(); - if (!raw) - return ERR_PTR(-ENOMEM); fn = jffs2_alloc_full_dnode(); - if (!fn) { - jffs2_free_raw_node_ref(raw); + if (!fn) return ERR_PTR(-ENOMEM); - } - - fn->ofs = je32_to_cpu(ri->offset); - fn->size = je32_to_cpu(ri->dsize); - fn->frags = 0; /* check number of valid vecs */ if (!datalen || !data) cnt = 1; retry: - fn->raw = raw; + flash_ofs = write_ofs(c); - raw->flash_offset = flash_ofs; - raw->__totlen = PAD(sizeof(*ri)+datalen); - raw->next_phys = NULL; + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) { BUG_ON(!retried); @@ -125,22 +113,16 @@ struct jffs2_full_dnode *jffs2_write_dno /* Mark the space as dirtied */ if (retlen) { - /* Doesn't belong to any inode */ - raw->next_in_ino = NULL; - /* Don't change raw->size to match retlen. We may have written the node header already, and only the data will seem corrupted, in which case the scan would skip over any node we write before the original intended end of this node */ - raw->flash_offset |= REF_OBSOLETE; - jffs2_add_physical_node_ref(c, raw); - jffs2_mark_node_obsolete(c, raw); + jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*ri)+datalen), NULL); } else { - printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); - jffs2_free_raw_node_ref(raw); + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs); } - if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { + if (!retried && alloc_mode != ALLOC_NORETRY) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; @@ -153,19 +135,20 @@ struct jffs2_full_dnode *jffs2_write_dno jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { - ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, - &dummy, JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &dummy, + JFFS2_SUMMARY_INODE_SIZE); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, - &dummy, alloc_mode, JFFS2_SUMMARY_INODE_SIZE); + ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &dummy, + alloc_mode, JFFS2_SUMMARY_INODE_SIZE); down(&f->sem); } if (!ret) { + flash_ofs = write_ofs(c); D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); @@ -174,7 +157,6 @@ struct jffs2_full_dnode *jffs2_write_dno goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); - jffs2_free_raw_node_ref(raw); } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dnode(fn); @@ -188,20 +170,17 @@ struct jffs2_full_dnode *jffs2_write_dno if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { - raw->flash_offset |= REF_PRISTINE; + flash_ofs |= REF_PRISTINE; } else { - raw->flash_offset |= REF_NORMAL; + flash_ofs |= REF_NORMAL; } - jffs2_add_physical_node_ref(c, raw); - - /* Link into per-inode list */ - spin_lock(&c->erase_completion_lock); - raw->next_in_ino = f->inocache->nodes; - f->inocache->nodes = raw; - spin_unlock(&c->erase_completion_lock); + fn->raw = jffs2_add_physical_node_ref(c, flash_ofs, PAD(sizeof(*ri)+datalen), f->inocache); + fn->ofs = je32_to_cpu(ri->offset); + fn->size = je32_to_cpu(ri->dsize); + fn->frags = 0; D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", - flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), + flash_ofs & ~3, flash_ofs & 3, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); @@ -212,12 +191,14 @@ struct jffs2_full_dnode *jffs2_write_dno return fn; } -struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode) +struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, + struct jffs2_raw_dirent *rd, const unsigned char *name, + uint32_t namelen, int alloc_mode) { - struct jffs2_raw_node_ref *raw; struct jffs2_full_dirent *fd; size_t retlen; struct kvec vecs[2]; + uint32_t flash_ofs; int retried = 0; int ret; @@ -228,26 +209,16 @@ struct jffs2_full_dirent *jffs2_write_di D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); BUG(); - } - ); + }); vecs[0].iov_base = rd; vecs[0].iov_len = sizeof(*rd); vecs[1].iov_base = (unsigned char *)name; vecs[1].iov_len = namelen; - jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); - - raw = jffs2_alloc_raw_node_ref(); - - if (!raw) - return ERR_PTR(-ENOMEM); - fd = jffs2_alloc_full_dirent(namelen+1); - if (!fd) { - jffs2_free_raw_node_ref(raw); + if (!fd) return ERR_PTR(-ENOMEM); - } fd->version = je32_to_cpu(rd->version); fd->ino = je32_to_cpu(rd->ino); @@ -257,11 +228,9 @@ struct jffs2_full_dirent *jffs2_write_di fd->name[namelen]=0; retry: - fd->raw = raw; + flash_ofs = write_ofs(c); - raw->flash_offset = flash_ofs; - raw->__totlen = PAD(sizeof(*rd)+namelen); - raw->next_phys = NULL; + jffs2_dbg_prewrite_paranoia_check(c, flash_ofs, vecs[0].iov_len + vecs[1].iov_len); if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) { BUG_ON(!retried); @@ -280,15 +249,11 @@ struct jffs2_full_dirent *jffs2_write_di sizeof(*rd)+namelen, flash_ofs, ret, retlen); /* Mark the space as dirtied */ if (retlen) { - raw->next_in_ino = NULL; - raw->flash_offset |= REF_OBSOLETE; - jffs2_add_physical_node_ref(c, raw); - jffs2_mark_node_obsolete(c, raw); + jffs2_add_physical_node_ref(c, flash_ofs | REF_OBSOLETE, PAD(sizeof(*rd)+namelen), NULL); } else { - printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); - jffs2_free_raw_node_ref(raw); + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", flash_ofs); } - if (!retried && (raw = jffs2_alloc_raw_node_ref())) { + if (!retried) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; @@ -301,39 +266,33 @@ struct jffs2_full_dirent *jffs2_write_di jffs2_dbg_acct_paranoia_check(c, jeb); if (alloc_mode == ALLOC_GC) { - ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, - &dummy, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &dummy, + JFFS2_SUMMARY_DIRENT_SIZE(namelen)); } else { /* Locking pain */ up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, - &dummy, alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); + ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &dummy, + alloc_mode, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); down(&f->sem); } if (!ret) { + flash_ofs = write_ofs(c); D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); jffs2_dbg_acct_sanity_check(c,jeb); jffs2_dbg_acct_paranoia_check(c, jeb); goto retry; } D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); - jffs2_free_raw_node_ref(raw); } /* Release the full_dnode which is now useless, and return */ jffs2_free_full_dirent(fd); return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ - raw->flash_offset |= REF_PRISTINE; - jffs2_add_physical_node_ref(c, raw); - - spin_lock(&c->erase_completion_lock); - raw->next_in_ino = f->inocache->nodes; - f->inocache->nodes = raw; - spin_unlock(&c->erase_completion_lock); + fd->raw = jffs2_add_physical_node_ref(c, flash_ofs | REF_PRISTINE, PAD(sizeof(*rd)+namelen), f->inocache); if (retried) { jffs2_dbg_acct_sanity_check(c,NULL); @@ -359,14 +318,14 @@ int jffs2_write_inode_range(struct jffs2 struct jffs2_full_dnode *fn; unsigned char *comprbuf = NULL; uint16_t comprtype = JFFS2_COMPR_NONE; - uint32_t phys_ofs, alloclen; + uint32_t alloclen; uint32_t datalen, cdatalen; int retried = 0; retry: D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); - ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, + ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); if (ret) { D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); @@ -394,7 +353,7 @@ int jffs2_write_inode_range(struct jffs2 ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); - fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); + fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, ALLOC_NORETRY); jffs2_free_comprbuf(comprbuf, buf); @@ -448,13 +407,13 @@ int jffs2_do_create(struct jffs2_sb_info struct jffs2_raw_dirent *rd; struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret; /* Try to reserve enough space for both node and dirent. * Just the node will do for now, though */ - ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL, + ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE); D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); if (ret) { @@ -465,7 +424,7 @@ int jffs2_do_create(struct jffs2_sb_info ri->data_crc = cpu_to_je32(0); ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); - fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL); D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", jemode_to_cpu(ri->mode))); @@ -484,7 +443,7 @@ int jffs2_do_create(struct jffs2_sb_info up(&f->sem); jffs2_complete_reservation(c); - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { @@ -516,7 +475,7 @@ int jffs2_do_create(struct jffs2_sb_info rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); @@ -545,7 +504,7 @@ int jffs2_do_unlink(struct jffs2_sb_info { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret; if (1 /* alternative branch needs testing */ || @@ -556,7 +515,7 @@ int jffs2_do_unlink(struct jffs2_sb_info if (!rd) return -ENOMEM; - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_DELETION, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); @@ -580,7 +539,7 @@ int jffs2_do_unlink(struct jffs2_sb_info rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_DELETION); jffs2_free_raw_dirent(rd); @@ -659,14 +618,14 @@ int jffs2_do_link (struct jffs2_sb_info { struct jffs2_raw_dirent *rd; struct jffs2_full_dirent *fd; - uint32_t alloclen, phys_ofs; + uint32_t alloclen; int ret; rd = jffs2_alloc_raw_dirent(); if (!rd) return -ENOMEM; - ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); if (ret) { jffs2_free_raw_dirent(rd); @@ -692,7 +651,7 @@ int jffs2_do_link (struct jffs2_sb_info rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); - fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); + fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, ALLOC_NORMAL); jffs2_free_raw_dirent(rd); diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c new file mode 100644 index 0000000..25bc1ae --- /dev/null +++ b/fs/jffs2/xattr.c @@ -0,0 +1,1325 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" +/* -------- xdatum related functions ---------------- + * xattr_datum_hashkey(xprefix, xname, xvalue, xsize) + * is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is + * the index of the xattr name/value pair cache (c->xattrindex). + * is_xattr_datum_unchecked(c, xd) + * returns 1, if xdatum contains any unchecked raw nodes. if all raw nodes are not + * unchecked, it returns 0. + * unload_xattr_datum(c, xd) + * is used to release xattr name/value pair and detach from c->xattrindex. + * reclaim_xattr_datum(c) + * is used to reclaim xattr name/value pairs on the xattr name/value pair cache when + * memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold + * is hard coded as 32KiB. + * do_verify_xattr_datum(c, xd) + * is used to load the xdatum informations without name/value pair from the medium. + * It's necessary once, because those informations are not collected during mounting + * process when EBS is enabled. + * 0 will be returned, if success. An negative return value means recoverable error, and + * positive return value means unrecoverable error. Thus, caller must remove this xdatum + * and xref when it returned positive value. + * do_load_xattr_datum(c, xd) + * is used to load name/value pair from the medium. + * The meanings of return value is same as do_verify_xattr_datum(). + * load_xattr_datum(c, xd) + * is used to be as a wrapper of do_verify_xattr_datum() and do_load_xattr_datum(). + * If xd need to call do_verify_xattr_datum() at first, it's called before calling + * do_load_xattr_datum(). The meanings of return value is same as do_verify_xattr_datum(). + * save_xattr_datum(c, xd) + * is used to write xdatum to medium. xd->version will be incremented. + * create_xattr_datum(c, xprefix, xname, xvalue, xsize) + * is used to create new xdatum and write to medium. + * unrefer_xattr_datum(c, xd) + * is used to delete a xdatum. When nobody refers this xdatum, JFFS2_XFLAGS_DEAD + * is set on xd->flags and chained xattr_dead_list or release it immediately. + * In the first case, the garbage collector release it later. + * -------------------------------------------------- */ +static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize) +{ + int name_len = strlen(xname); + + return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize); +} + +static int is_xattr_datum_unchecked(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + struct jffs2_raw_node_ref *raw; + int rc = 0; + + spin_lock(&c->erase_completion_lock); + for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { + if (ref_flags(raw) == REF_UNCHECKED) { + rc = 1; + break; + } + } + spin_unlock(&c->erase_completion_lock); + return rc; +} + +static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + D1(dbg_xattr("%s: xid=%u, version=%u\n", __FUNCTION__, xd->xid, xd->version)); + if (xd->xname) { + c->xdatum_mem_usage -= (xd->name_len + 1 + xd->value_len); + kfree(xd->xname); + } + + list_del_init(&xd->xindex); + xd->hashkey = 0; + xd->xname = NULL; + xd->xvalue = NULL; +} + +static void reclaim_xattr_datum(struct jffs2_sb_info *c) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd, *_xd; + uint32_t target, before; + static int index = 0; + int count; + + if (c->xdatum_mem_threshold > c->xdatum_mem_usage) + return; + + before = c->xdatum_mem_usage; + target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */ + for (count = 0; count < XATTRINDEX_HASHSIZE; count++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) { + if (xd->flags & JFFS2_XFLAGS_HOT) { + xd->flags &= ~JFFS2_XFLAGS_HOT; + } else if (!(xd->flags & JFFS2_XFLAGS_BIND)) { + unload_xattr_datum(c, xd); + } + if (c->xdatum_mem_usage <= target) + goto out; + } + index = (index+1) % XATTRINDEX_HASHSIZE; + } + out: + JFFS2_NOTICE("xdatum_mem_usage from %u byte to %u byte (%u byte reclaimed)\n", + before, c->xdatum_mem_usage, before - c->xdatum_mem_usage); +} + +static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; + struct jffs2_raw_xattr rx; + size_t readlen; + uint32_t crc, offset, totlen; + int rc; + + spin_lock(&c->erase_completion_lock); + offset = ref_offset(xd->node); + if (ref_flags(xd->node) == REF_PRISTINE) + goto complete; + spin_unlock(&c->erase_completion_lock); + + rc = jffs2_flash_read(c, offset, sizeof(rx), &readlen, (char *)&rx); + if (rc || readlen != sizeof(rx)) { + JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n", + rc, sizeof(rx), readlen, offset); + return rc ? rc : -EIO; + } + crc = crc32(0, &rx, sizeof(rx) - 4); + if (crc != je32_to_cpu(rx.node_crc)) { + JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + offset, je32_to_cpu(rx.hdr_crc), crc); + xd->flags |= JFFS2_XFLAGS_INVALID; + return EIO; + } + totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len)); + if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK + || je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR + || je32_to_cpu(rx.totlen) != totlen + || je32_to_cpu(rx.xid) != xd->xid + || je32_to_cpu(rx.version) != xd->version) { + JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, " + "nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n", + offset, je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK, + je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR, + je32_to_cpu(rx.totlen), totlen, + je32_to_cpu(rx.xid), xd->xid, + je32_to_cpu(rx.version), xd->version); + xd->flags |= JFFS2_XFLAGS_INVALID; + return EIO; + } + xd->xprefix = rx.xprefix; + xd->name_len = rx.name_len; + xd->value_len = je16_to_cpu(rx.value_len); + xd->data_crc = je32_to_cpu(rx.data_crc); + + spin_lock(&c->erase_completion_lock); + complete: + for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { + jeb = &c->blocks[ref_offset(raw) / c->sector_size]; + totlen = PAD(ref_totlen(c, jeb, raw)); + if (ref_flags(raw) == REF_UNCHECKED) { + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + } + raw->flash_offset = ref_offset(raw) | ((xd->node==raw) ? REF_PRISTINE : REF_NORMAL); + } + spin_unlock(&c->erase_completion_lock); + + /* unchecked xdatum is chained with c->xattr_unchecked */ + list_del_init(&xd->xindex); + + dbg_xattr("success on verfying xdatum (xid=%u, version=%u)\n", + xd->xid, xd->version); + + return 0; +} + +static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + char *data; + size_t readlen; + uint32_t crc, length; + int i, ret, retry = 0; + + BUG_ON(ref_flags(xd->node) != REF_PRISTINE); + BUG_ON(!list_empty(&xd->xindex)); + retry: + length = xd->name_len + 1 + xd->value_len; + data = kmalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr), + length, &readlen, data); + + if (ret || length!=readlen) { + JFFS2_WARNING("jffs2_flash_read() returned %d, request=%d, readlen=%zu, at %#08x\n", + ret, length, readlen, ref_offset(xd->node)); + kfree(data); + return ret ? ret : -EIO; + } + + data[xd->name_len] = '\0'; + crc = crc32(0, data, length); + if (crc != xd->data_crc) { + JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)" + " at %#08x, read: 0x%08x calculated: 0x%08x\n", + ref_offset(xd->node), xd->data_crc, crc); + kfree(data); + xd->flags |= JFFS2_XFLAGS_INVALID; + return EIO; + } + + xd->flags |= JFFS2_XFLAGS_HOT; + xd->xname = data; + xd->xvalue = data + xd->name_len+1; + + c->xdatum_mem_usage += length; + + xd->hashkey = xattr_datum_hashkey(xd->xprefix, xd->xname, xd->xvalue, xd->value_len); + i = xd->hashkey % XATTRINDEX_HASHSIZE; + list_add(&xd->xindex, &c->xattrindex[i]); + if (!retry) { + retry = 1; + reclaim_xattr_datum(c); + if (!xd->xname) + goto retry; + } + + dbg_xattr("success on loading xdatum (xid=%u, xprefix=%u, xname='%s')\n", + xd->xid, xd->xprefix, xd->xname); + + return 0; +} + +static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem); + * rc < 0 : recoverable error, try again + * rc = 0 : success + * rc > 0 : Unrecoverable error, this node should be deleted. + */ + int rc = 0; + + BUG_ON(xd->flags & JFFS2_XFLAGS_DEAD); + if (xd->xname) + return 0; + if (xd->flags & JFFS2_XFLAGS_INVALID) + return EIO; + if (unlikely(is_xattr_datum_unchecked(c, xd))) + rc = do_verify_xattr_datum(c, xd); + if (!rc) + rc = do_load_xattr_datum(c, xd); + return rc; +} + +static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_raw_xattr rx; + struct kvec vecs[2]; + size_t length; + int rc, totlen; + uint32_t phys_ofs = write_ofs(c); + + BUG_ON(!xd->xname); + BUG_ON(xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID)); + + vecs[0].iov_base = ℞ + vecs[0].iov_len = sizeof(rx); + vecs[1].iov_base = xd->xname; + vecs[1].iov_len = xd->name_len + 1 + xd->value_len; + totlen = vecs[0].iov_len + vecs[1].iov_len; + + /* Setup raw-xattr */ + memset(&rx, 0, sizeof(rx)); + rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); + rx.totlen = cpu_to_je32(PAD(totlen)); + rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); + + rx.xid = cpu_to_je32(xd->xid); + rx.version = cpu_to_je32(++xd->version); + rx.xprefix = xd->xprefix; + rx.name_len = xd->name_len; + rx.value_len = cpu_to_je16(xd->value_len); + rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len)); + rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4)); + + rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + if (rc || totlen != length) { + JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%zu, at %#08x\n", + rc, totlen, length, phys_ofs); + rc = rc ? rc : -EIO; + if (length) + jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, PAD(totlen), NULL); + + return rc; + } + /* success */ + jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), (void *)xd); + + dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n", + xd->xid, xd->version, xd->xprefix, xd->xname); + + return 0; +} + +static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, + int xprefix, const char *xname, + const char *xvalue, int xsize) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd; + uint32_t hashkey, name_len; + char *data; + int i, rc; + + /* Search xattr_datum has same xname/xvalue by index */ + hashkey = xattr_datum_hashkey(xprefix, xname, xvalue, xsize); + i = hashkey % XATTRINDEX_HASHSIZE; + list_for_each_entry(xd, &c->xattrindex[i], xindex) { + if (xd->hashkey==hashkey + && xd->xprefix==xprefix + && xd->value_len==xsize + && !strcmp(xd->xname, xname) + && !memcmp(xd->xvalue, xvalue, xsize)) { + atomic_inc(&xd->refcnt); + return xd; + } + } + + /* Not found, Create NEW XATTR-Cache */ + name_len = strlen(xname); + + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + + data = kmalloc(name_len + 1 + xsize, GFP_KERNEL); + if (!data) { + jffs2_free_xattr_datum(xd); + return ERR_PTR(-ENOMEM); + } + strcpy(data, xname); + memcpy(data + name_len + 1, xvalue, xsize); + + atomic_set(&xd->refcnt, 1); + xd->xid = ++c->highest_xid; + xd->flags |= JFFS2_XFLAGS_HOT; + xd->xprefix = xprefix; + + xd->hashkey = hashkey; + xd->xname = data; + xd->xvalue = data + name_len + 1; + xd->name_len = name_len; + xd->value_len = xsize; + xd->data_crc = crc32(0, data, xd->name_len + 1 + xd->value_len); + + rc = save_xattr_datum(c, xd); + if (rc) { + kfree(xd->xname); + jffs2_free_xattr_datum(xd); + return ERR_PTR(rc); + } + + /* Insert Hash Index */ + i = hashkey % XATTRINDEX_HASHSIZE; + list_add(&xd->xindex, &c->xattrindex[i]); + + c->xdatum_mem_usage += (xd->name_len + 1 + xd->value_len); + reclaim_xattr_datum(c); + + return xd; +} + +static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) { + uint32_t xid = xd->xid, version = xd->version; + + unload_xattr_datum(c, xd); + xd->flags |= JFFS2_XFLAGS_DEAD; + if (xd->node == (void *)xd) { + BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID)); + jffs2_free_xattr_datum(xd); + } else { + list_add(&xd->xindex, &c->xattr_dead_list); + } + spin_unlock(&c->erase_completion_lock); + + dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version); + } +} + +/* -------- xref related functions ------------------ + * verify_xattr_ref(c, ref) + * is used to load xref information from medium. Because summary data does not + * contain xid/ino, it's necessary to verify once while mounting process. + * save_xattr_ref(c, ref) + * is used to write xref to medium. If delete marker is marked, it write + * a delete marker of xref into medium. + * create_xattr_ref(c, ic, xd) + * is used to create a new xref and write to medium. + * delete_xattr_ref(c, ref) + * is used to delete jffs2_xattr_ref. It marks xref XREF_DELETE_MARKER, + * and allows GC to reclaim those physical nodes. + * jffs2_xattr_delete_inode(c, ic) + * is called to remove xrefs related to obsolete inode when inode is unlinked. + * jffs2_xattr_free_inode(c, ic) + * is called to release xattr related objects when unmounting. + * check_xattr_ref_inode(c, ic) + * is used to confirm inode does not have duplicate xattr name/value pair. + * -------------------------------------------------- */ +static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; + struct jffs2_raw_xref rr; + size_t readlen; + uint32_t crc, offset, totlen; + int rc; + + spin_lock(&c->erase_completion_lock); + if (ref_flags(ref->node) != REF_UNCHECKED) + goto complete; + offset = ref_offset(ref->node); + spin_unlock(&c->erase_completion_lock); + + rc = jffs2_flash_read(c, offset, sizeof(rr), &readlen, (char *)&rr); + if (rc || sizeof(rr) != readlen) { + JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu, at %#08x\n", + rc, sizeof(rr), readlen, offset); + return rc ? rc : -EIO; + } + /* obsolete node */ + crc = crc32(0, &rr, sizeof(rr) - 4); + if (crc != je32_to_cpu(rr.node_crc)) { + JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + offset, je32_to_cpu(rr.node_crc), crc); + return EIO; + } + if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK + || je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF + || je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) { + JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, " + "nodetype=%#04x/%#04x, totlen=%u/%zu\n", + offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK, + je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF, + je32_to_cpu(rr.totlen), PAD(sizeof(rr))); + return EIO; + } + ref->ino = je32_to_cpu(rr.ino); + ref->xid = je32_to_cpu(rr.xid); + ref->xseqno = je32_to_cpu(rr.xseqno); + if (ref->xseqno > c->highest_xseqno) + c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER); + + spin_lock(&c->erase_completion_lock); + complete: + for (raw=ref->node; raw != (void *)ref; raw=raw->next_in_ino) { + jeb = &c->blocks[ref_offset(raw) / c->sector_size]; + totlen = PAD(ref_totlen(c, jeb, raw)); + if (ref_flags(raw) == REF_UNCHECKED) { + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + } + raw->flash_offset = ref_offset(raw) | ((ref->node==raw) ? REF_PRISTINE : REF_NORMAL); + } + spin_unlock(&c->erase_completion_lock); + + dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n", + ref->ino, ref->xid, ref_offset(ref->node)); + return 0; +} + +static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_raw_xref rr; + size_t length; + uint32_t xseqno, phys_ofs = write_ofs(c); + int ret; + + rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rr.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF); + rr.totlen = cpu_to_je32(PAD(sizeof(rr))); + rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4)); + + xseqno = (c->highest_xseqno += 2); + if (is_xattr_ref_dead(ref)) { + xseqno |= XREF_DELETE_MARKER; + rr.ino = cpu_to_je32(ref->ino); + rr.xid = cpu_to_je32(ref->xid); + } else { + rr.ino = cpu_to_je32(ref->ic->ino); + rr.xid = cpu_to_je32(ref->xd->xid); + } + rr.xseqno = cpu_to_je32(xseqno); + rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4)); + + ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); + if (ret || sizeof(rr) != length) { + JFFS2_WARNING("jffs2_flash_write() returned %d, request=%zu, retlen=%zu, at %#08x\n", + ret, sizeof(rr), length, phys_ofs); + ret = ret ? ret : -EIO; + if (length) + jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, PAD(sizeof(rr)), NULL); + + return ret; + } + /* success */ + ref->xseqno = xseqno; + jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), (void *)ref); + + dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid); + + return 0; +} + +static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, + struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_ref *ref; + int ret; + + ref = jffs2_alloc_xattr_ref(); + if (!ref) + return ERR_PTR(-ENOMEM); + ref->ic = ic; + ref->xd = xd; + + ret = save_xattr_ref(c, ref); + if (ret) { + jffs2_free_xattr_ref(ref); + return ERR_PTR(ret); + } + + /* Chain to inode */ + ref->next = ic->xref; + ic->xref = ref; + + return ref; /* success */ +} + +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd; + + xd = ref->xd; + ref->xseqno |= XREF_DELETE_MARKER; + ref->ino = ref->ic->ino; + ref->xid = ref->xd->xid; + spin_lock(&c->erase_completion_lock); + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + spin_unlock(&c->erase_completion_lock); + + dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n", + ref->ino, ref->xid, ref->xseqno); + + unrefer_xattr_datum(c, xd); +} + +void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + /* It's called from jffs2_clear_inode() on inode removing. + When an inode with XATTR is removed, those XATTRs must be removed. */ + struct jffs2_xattr_ref *ref, *_ref; + + if (!ic || ic->nlink > 0) + return; + + down_write(&c->xattr_sem); + for (ref = ic->xref; ref; ref = _ref) { + _ref = ref->next; + delete_xattr_ref(c, ref); + } + ic->xref = NULL; + up_write(&c->xattr_sem); +} + +void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + /* It's called from jffs2_free_ino_caches() until unmounting FS. */ + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref, *_ref; + + down_write(&c->xattr_sem); + for (ref = ic->xref; ref; ref = _ref) { + _ref = ref->next; + xd = ref->xd; + if (atomic_dec_and_test(&xd->refcnt)) { + unload_xattr_datum(c, xd); + jffs2_free_xattr_datum(xd); + } + jffs2_free_xattr_ref(ref); + } + ic->xref = NULL; + up_write(&c->xattr_sem); +} + +static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) +{ + /* success of check_xattr_ref_inode() means taht inode (ic) dose not have + * duplicate name/value pairs. If duplicate name/value pair would be found, + * one will be removed. + */ + struct jffs2_xattr_ref *ref, *cmp, **pref, **pcmp; + int rc = 0; + + if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED)) + return 0; + down_write(&c->xattr_sem); + retry: + rc = 0; + for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) { + if (!ref->xd->xname) { + rc = load_xattr_datum(c, ref->xd); + if (unlikely(rc > 0)) { + *pref = ref->next; + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + for (cmp=ref->next, pcmp=&ref->next; cmp; pcmp=&cmp->next, cmp=cmp->next) { + if (!cmp->xd->xname) { + ref->xd->flags |= JFFS2_XFLAGS_BIND; + rc = load_xattr_datum(c, cmp->xd); + ref->xd->flags &= ~JFFS2_XFLAGS_BIND; + if (unlikely(rc > 0)) { + *pcmp = cmp->next; + delete_xattr_ref(c, cmp); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + if (ref->xd->xprefix == cmp->xd->xprefix + && !strcmp(ref->xd->xname, cmp->xd->xname)) { + if (ref->xseqno > cmp->xseqno) { + *pcmp = cmp->next; + delete_xattr_ref(c, cmp); + } else { + *pref = ref->next; + delete_xattr_ref(c, ref); + } + goto retry; + } + } + } + ic->flags |= INO_FLAGS_XATTR_CHECKED; + out: + up_write(&c->xattr_sem); + + return rc; +} + +/* -------- xattr subsystem functions --------------- + * jffs2_init_xattr_subsystem(c) + * is used to initialize semaphore and list_head, and some variables. + * jffs2_find_xattr_datum(c, xid) + * is used to lookup xdatum while scanning process. + * jffs2_clear_xattr_subsystem(c) + * is used to release any xattr related objects. + * jffs2_build_xattr_subsystem(c) + * is used to associate xdatum and xref while super block building process. + * jffs2_setup_xattr_datum(c, xid, version) + * is used to insert xdatum while scanning process. + * -------------------------------------------------- */ +void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) +{ + int i; + + for (i=0; i < XATTRINDEX_HASHSIZE; i++) + INIT_LIST_HEAD(&c->xattrindex[i]); + INIT_LIST_HEAD(&c->xattr_unchecked); + INIT_LIST_HEAD(&c->xattr_dead_list); + c->xref_dead_list = NULL; + c->xref_temp = NULL; + + init_rwsem(&c->xattr_sem); + c->highest_xid = 0; + c->highest_xseqno = 0; + c->xdatum_mem_usage = 0; + c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */ +} + +static struct jffs2_xattr_datum *jffs2_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid) +{ + struct jffs2_xattr_datum *xd; + int i = xid % XATTRINDEX_HASHSIZE; + + /* It's only used in scanning/building process. */ + BUG_ON(!(c->flags & (JFFS2_SB_FLAG_SCANNING|JFFS2_SB_FLAG_BUILDING))); + + list_for_each_entry(xd, &c->xattrindex[i], xindex) { + if (xd->xid==xid) + return xd; + } + return NULL; +} + +void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_xattr_ref *ref, *_ref; + int i; + + for (ref=c->xref_temp; ref; ref = _ref) { + _ref = ref->next; + jffs2_free_xattr_ref(ref); + } + + for (ref=c->xref_dead_list; ref; ref = _ref) { + _ref = ref->next; + jffs2_free_xattr_ref(ref); + } + + for (i=0; i < XATTRINDEX_HASHSIZE; i++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { + list_del(&xd->xindex); + if (xd->xname) + kfree(xd->xname); + jffs2_free_xattr_datum(xd); + } + } + + list_for_each_entry_safe(xd, _xd, &c->xattr_dead_list, xindex) { + list_del(&xd->xindex); + jffs2_free_xattr_datum(xd); + } +} + +#define XREF_TMPHASH_SIZE (128) +void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_ref *ref, *_ref; + struct jffs2_xattr_ref *xref_tmphash[XREF_TMPHASH_SIZE]; + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_inode_cache *ic; + struct jffs2_raw_node_ref *raw; + int i, xdatum_count = 0, xdatum_unchecked_count = 0, xref_count = 0; + int xdatum_orphan_count = 0, xref_orphan_count = 0, xref_dead_count = 0; + + BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); + + /* Phase.1 : Merge same xref */ + for (i=0; i < XREF_TMPHASH_SIZE; i++) + xref_tmphash[i] = NULL; + for (ref=c->xref_temp; ref; ref=_ref) { + struct jffs2_xattr_ref *tmp; + + _ref = ref->next; + if (ref_flags(ref->node) != REF_PRISTINE) { + if (verify_xattr_ref(c, ref)) { + BUG_ON(ref->node->next_in_ino != (void *)ref); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); + jffs2_free_xattr_ref(ref); + continue; + } + } + + i = (ref->ino ^ ref->xid) % XREF_TMPHASH_SIZE; + for (tmp=xref_tmphash[i]; tmp; tmp=tmp->next) { + if (tmp->ino == ref->ino && tmp->xid == ref->xid) + break; + } + if (tmp) { + raw = ref->node; + if (ref->xseqno > tmp->xseqno) { + tmp->xseqno = ref->xseqno; + raw->next_in_ino = tmp->node; + tmp->node = raw; + } else { + raw->next_in_ino = tmp->node->next_in_ino; + tmp->node->next_in_ino = raw; + } + jffs2_free_xattr_ref(ref); + continue; + } else { + ref->next = xref_tmphash[i]; + xref_tmphash[i] = ref; + } + } + c->xref_temp = NULL; + + /* Phase.2 : Bind xref with inode_cache and xattr_datum */ + for (i=0; i < XREF_TMPHASH_SIZE; i++) { + for (ref=xref_tmphash[i]; ref; ref=_ref) { + xref_count++; + _ref = ref->next; + if (is_xattr_ref_dead(ref)) { + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + xref_dead_count++; + continue; + } + /* At this point, ref->xid and ref->ino contain XID and inode number. + ref->xd and ref->ic are not valid yet. */ + xd = jffs2_find_xattr_datum(c, ref->xid); + ic = jffs2_get_ino_cache(c, ref->ino); + if (!xd || !ic) { + dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) is orphan.\n", + ref->ino, ref->xid, ref->xseqno); + ref->xseqno |= XREF_DELETE_MARKER; + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + xref_orphan_count++; + continue; + } + ref->xd = xd; + ref->ic = ic; + atomic_inc(&xd->refcnt); + ref->next = ic->xref; + ic->xref = ref; + } + } + + /* Phase.3 : Link unchecked xdatum to xattr_unchecked list */ + for (i=0; i < XATTRINDEX_HASHSIZE; i++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { + xdatum_count++; + list_del_init(&xd->xindex); + if (!atomic_read(&xd->refcnt)) { + dbg_xattr("xdatum(xid=%u, version=%u) is orphan.\n", + xd->xid, xd->version); + xd->flags |= JFFS2_XFLAGS_DEAD; + list_add(&xd->xindex, &c->xattr_unchecked); + xdatum_orphan_count++; + continue; + } + if (is_xattr_datum_unchecked(c, xd)) { + dbg_xattr("unchecked xdatum(xid=%u, version=%u)\n", + xd->xid, xd->version); + list_add(&xd->xindex, &c->xattr_unchecked); + xdatum_unchecked_count++; + } + } + } + /* build complete */ + JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum" + " (%u unchecked, %u orphan) and " + "%u of xref (%u dead, %u orphan) found.\n", + xdatum_count, xdatum_unchecked_count, xdatum_orphan_count, + xref_count, xref_dead_count, xref_orphan_count); +} + +struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, + uint32_t xid, uint32_t version) +{ + struct jffs2_xattr_datum *xd; + + xd = jffs2_find_xattr_datum(c, xid); + if (!xd) { + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + xd->xid = xid; + xd->version = version; + if (xd->xid > c->highest_xid) + c->highest_xid = xd->xid; + list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]); + } + return xd; +} + +/* -------- xattr subsystem functions --------------- + * xprefix_to_handler(xprefix) + * is used to translate xprefix into xattr_handler. + * jffs2_listxattr(dentry, buffer, size) + * is an implementation of listxattr handler on jffs2. + * do_jffs2_getxattr(inode, xprefix, xname, buffer, size) + * is an implementation of getxattr handler on jffs2. + * do_jffs2_setxattr(inode, xprefix, xname, buffer, size, flags) + * is an implementation of setxattr handler on jffs2. + * -------------------------------------------------- */ +struct xattr_handler *jffs2_xattr_handlers[] = { + &jffs2_user_xattr_handler, +#ifdef CONFIG_JFFS2_FS_SECURITY + &jffs2_security_xattr_handler, +#endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + &jffs2_acl_access_xattr_handler, + &jffs2_acl_default_xattr_handler, +#endif + &jffs2_trusted_xattr_handler, + NULL +}; + +static struct xattr_handler *xprefix_to_handler(int xprefix) { + struct xattr_handler *ret; + + switch (xprefix) { + case JFFS2_XPREFIX_USER: + ret = &jffs2_user_xattr_handler; + break; +#ifdef CONFIG_JFFS2_FS_SECURITY + case JFFS2_XPREFIX_SECURITY: + ret = &jffs2_security_xattr_handler; + break; +#endif +#ifdef CONFIG_JFFS2_FS_POSIX_ACL + case JFFS2_XPREFIX_ACL_ACCESS: + ret = &jffs2_acl_access_xattr_handler; + break; + case JFFS2_XPREFIX_ACL_DEFAULT: + ret = &jffs2_acl_default_xattr_handler; + break; +#endif + case JFFS2_XPREFIX_TRUSTED: + ret = &jffs2_trusted_xattr_handler; + break; + default: + ret = NULL; + break; + } + return ret; +} + +ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct inode *inode = dentry->d_inode; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_cache *ic = f->inocache; + struct jffs2_xattr_ref *ref, **pref; + struct jffs2_xattr_datum *xd; + struct xattr_handler *xhandle; + ssize_t len, rc; + int retry = 0; + + rc = check_xattr_ref_inode(c, ic); + if (unlikely(rc)) + return rc; + + down_read(&c->xattr_sem); + retry: + len = 0; + for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) { + BUG_ON(ref->ic != ic); + xd = ref->xd; + if (!xd->xname) { + /* xdatum is unchached */ + if (!retry) { + retry = 1; + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + goto retry; + } else { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + *pref = ref->next; + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + } + xhandle = xprefix_to_handler(xd->xprefix); + if (!xhandle) + continue; + if (buffer) { + rc = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len); + } else { + rc = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len); + } + if (rc < 0) + goto out; + len += rc; + } + rc = len; + out: + if (!retry) { + up_read(&c->xattr_sem); + } else { + up_write(&c->xattr_sem); + } + return rc; +} + +int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, + char *buffer, size_t size) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_cache *ic = f->inocache; + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref, **pref; + int rc, retry = 0; + + rc = check_xattr_ref_inode(c, ic); + if (unlikely(rc)) + return rc; + + down_read(&c->xattr_sem); + retry: + for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) { + BUG_ON(ref->ic!=ic); + + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + if (!xd->xname) { + /* xdatum is unchached */ + if (!retry) { + retry = 1; + up_read(&c->xattr_sem); + down_write(&c->xattr_sem); + goto retry; + } else { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + *pref = ref->next; + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) { + goto out; + } + } + } + if (!strcmp(xname, xd->xname)) { + rc = xd->value_len; + if (buffer) { + if (size < rc) { + rc = -ERANGE; + } else { + memcpy(buffer, xd->xvalue, rc); + } + } + goto out; + } + } + rc = -ENODATA; + out: + if (!retry) { + up_read(&c->xattr_sem); + } else { + up_write(&c->xattr_sem); + } + return rc; +} + +int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, + const char *buffer, size_t size, int flags) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_cache *ic = f->inocache; + struct jffs2_xattr_datum *xd; + struct jffs2_xattr_ref *ref, *newref, **pref; + uint32_t length, request; + int rc; + + rc = check_xattr_ref_inode(c, ic); + if (unlikely(rc)) + return rc; + + request = PAD(sizeof(struct jffs2_raw_xattr) + strlen(xname) + 1 + size); + rc = jffs2_reserve_space(c, request, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_XATTR_SIZE); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); + return rc; + } + + /* Find existing xattr */ + down_write(&c->xattr_sem); + retry: + for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) { + xd = ref->xd; + if (xd->xprefix != xprefix) + continue; + if (!xd->xname) { + rc = load_xattr_datum(c, xd); + if (unlikely(rc > 0)) { + *pref = ref->next; + delete_xattr_ref(c, ref); + goto retry; + } else if (unlikely(rc < 0)) + goto out; + } + if (!strcmp(xd->xname, xname)) { + if (flags & XATTR_CREATE) { + rc = -EEXIST; + goto out; + } + if (!buffer) { + ref->ino = ic->ino; + ref->xid = xd->xid; + ref->xseqno |= XREF_DELETE_MARKER; + rc = save_xattr_ref(c, ref); + if (!rc) { + *pref = ref->next; + spin_lock(&c->erase_completion_lock); + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + spin_unlock(&c->erase_completion_lock); + unrefer_xattr_datum(c, xd); + } else { + ref->ic = ic; + ref->xd = xd; + ref->xseqno &= ~XREF_DELETE_MARKER; + } + goto out; + } + goto found; + } + } + /* not found */ + if (flags & XATTR_REPLACE) { + rc = -ENODATA; + goto out; + } + if (!buffer) { + rc = -ENODATA; + goto out; + } + found: + xd = create_xattr_datum(c, xprefix, xname, buffer, size); + if (IS_ERR(xd)) { + rc = PTR_ERR(xd); + goto out; + } + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + /* create xattr_ref */ + request = PAD(sizeof(struct jffs2_raw_xref)); + rc = jffs2_reserve_space(c, request, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE); + down_write(&c->xattr_sem); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); + unrefer_xattr_datum(c, xd); + up_write(&c->xattr_sem); + return rc; + } + if (ref) + *pref = ref->next; + newref = create_xattr_ref(c, ic, xd); + if (IS_ERR(newref)) { + if (ref) { + ref->next = ic->xref; + ic->xref = ref; + } + rc = PTR_ERR(newref); + unrefer_xattr_datum(c, xd); + } else if (ref) { + delete_xattr_ref(c, ref); + } + out: + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + return rc; +} + +/* -------- garbage collector functions ------------- + * jffs2_garbage_collect_xattr_datum(c, xd, raw) + * is used to move xdatum into new node. + * jffs2_garbage_collect_xattr_ref(c, ref, raw) + * is used to move xref into new node. + * jffs2_verify_xattr(c) + * is used to call do_verify_xattr_datum() before garbage collecting. + * jffs2_release_xattr_datum(c, xd) + * is used to release an in-memory object of xdatum. + * jffs2_release_xattr_ref(c, ref) + * is used to release an in-memory object of xref. + * -------------------------------------------------- */ +int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, + struct jffs2_raw_node_ref *raw) +{ + uint32_t totlen, length, old_ofs; + int rc = 0; + + down_write(&c->xattr_sem); + if (xd->node != raw) + goto out; + if (xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID)) + goto out; + + rc = load_xattr_datum(c, xd); + if (unlikely(rc)) { + rc = (rc > 0) ? 0 : rc; + goto out; + } + old_ofs = ref_offset(xd->node); + totlen = PAD(sizeof(struct jffs2_raw_xattr) + + xd->name_len + 1 + xd->value_len); + rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XATTR_SIZE); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u\n", rc, totlen); + rc = rc ? rc : -EBADFD; + goto out; + } + rc = save_xattr_datum(c, xd); + if (!rc) + dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n", + xd->xid, xd->version, old_ofs, ref_offset(xd->node)); + out: + if (!rc) + jffs2_mark_node_obsolete(c, raw); + up_write(&c->xattr_sem); + return rc; +} + +int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, + struct jffs2_raw_node_ref *raw) +{ + uint32_t totlen, length, old_ofs; + int rc = 0; + + down_write(&c->xattr_sem); + BUG_ON(!ref->node); + + if (ref->node != raw) + goto out; + if (is_xattr_ref_dead(ref) && (raw->next_in_ino == (void *)ref)) + goto out; + + old_ofs = ref_offset(ref->node); + totlen = ref_totlen(c, c->gcblock, ref->node); + + rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XREF_SIZE); + if (rc) { + JFFS2_WARNING("%s: jffs2_reserve_space_gc() = %d, request = %u\n", + __FUNCTION__, rc, totlen); + rc = rc ? rc : -EBADFD; + goto out; + } + rc = save_xattr_ref(c, ref); + if (!rc) + dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n", + ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node)); + out: + if (!rc) + jffs2_mark_node_obsolete(c, raw); + up_write(&c->xattr_sem); + return rc; +} + +int jffs2_verify_xattr(struct jffs2_sb_info *c) +{ + struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; + uint32_t totlen; + int rc; + + down_write(&c->xattr_sem); + list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) { + rc = do_verify_xattr_datum(c, xd); + if (rc < 0) + continue; + list_del_init(&xd->xindex); + spin_lock(&c->erase_completion_lock); + for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { + if (ref_flags(raw) != REF_UNCHECKED) + continue; + jeb = &c->blocks[ref_offset(raw) / c->sector_size]; + totlen = PAD(ref_totlen(c, jeb, raw)); + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + raw->flash_offset = ref_offset(raw) + | ((xd->node == (void *)raw) ? REF_PRISTINE : REF_NORMAL); + } + if (xd->flags & JFFS2_XFLAGS_DEAD) + list_add(&xd->xindex, &c->xattr_dead_list); + spin_unlock(&c->erase_completion_lock); + } + up_write(&c->xattr_sem); + return list_empty(&c->xattr_unchecked) ? 1 : 0; +} + +void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under spin_lock(&c->erase_completion_lock) */ + if (atomic_read(&xd->refcnt) || xd->node != (void *)xd) + return; + + list_del(&xd->xindex); + jffs2_free_xattr_datum(xd); +} + +void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under spin_lock(&c->erase_completion_lock) */ + struct jffs2_xattr_ref *tmp, **ptmp; + + if (ref->node != (void *)ref) + return; + + for (tmp=c->xref_dead_list, ptmp=&c->xref_dead_list; tmp; ptmp=&tmp->next, tmp=tmp->next) { + if (ref == tmp) { + *ptmp = tmp->next; + break; + } + } + jffs2_free_xattr_ref(ref); +} diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h new file mode 100644 index 0000000..06a5c69 --- /dev/null +++ b/fs/jffs2/xattr.h @@ -0,0 +1,129 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +#ifndef _JFFS2_FS_XATTR_H_ +#define _JFFS2_FS_XATTR_H_ + +#include +#include + +#define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */ +#define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */ +#define JFFS2_XFLAGS_DEAD (0x40) /* This datum is already dead */ +#define JFFS2_XFLAGS_INVALID (0x80) /* This datum contains crc error */ + +struct jffs2_xattr_datum +{ + void *always_null; + struct jffs2_raw_node_ref *node; + uint8_t class; + uint8_t flags; + uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */ + + struct list_head xindex; /* chained from c->xattrindex[n] */ + atomic_t refcnt; /* # of xattr_ref refers this */ + uint32_t xid; + uint32_t version; + + uint32_t data_crc; + uint32_t hashkey; + char *xname; /* XATTR name without prefix */ + uint32_t name_len; /* length of xname */ + char *xvalue; /* XATTR value */ + uint32_t value_len; /* length of xvalue */ +}; + +struct jffs2_inode_cache; +struct jffs2_xattr_ref +{ + void *always_null; + struct jffs2_raw_node_ref *node; + uint8_t class; + uint8_t flags; /* Currently unused */ + u16 unused; + + uint32_t xseqno; + union { + struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */ + uint32_t ino; /* only used in scanning/building */ + }; + union { + struct jffs2_xattr_datum *xd; /* reference to jffs2_xattr_datum */ + uint32_t xid; /* only used in sccanning/building */ + }; + struct jffs2_xattr_ref *next; /* chained from ic->xref_list */ +}; + +#define XREF_DELETE_MARKER (0x00000001) +static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref) +{ + return ((ref->xseqno & XREF_DELETE_MARKER) != 0); +} + +#ifdef CONFIG_JFFS2_FS_XATTR + +extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c); +extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c); +extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); + +extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, + uint32_t xid, uint32_t version); + +extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); +extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); + +extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, + struct jffs2_raw_node_ref *raw); +extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, + struct jffs2_raw_node_ref *raw); +extern int jffs2_verify_xattr(struct jffs2_sb_info *c); +extern void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); +extern void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); + +extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, + char *buffer, size_t size); +extern int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, + const char *buffer, size_t size, int flags); + +extern struct xattr_handler *jffs2_xattr_handlers[]; +extern struct xattr_handler jffs2_user_xattr_handler; +extern struct xattr_handler jffs2_trusted_xattr_handler; + +extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); +#define jffs2_getxattr generic_getxattr +#define jffs2_setxattr generic_setxattr +#define jffs2_removexattr generic_removexattr + +#else + +#define jffs2_init_xattr_subsystem(c) +#define jffs2_build_xattr_subsystem(c) +#define jffs2_clear_xattr_subsystem(c) + +#define jffs2_xattr_delete_inode(c, ic) +#define jffs2_xattr_free_inode(c, ic) +#define jffs2_verify_xattr(c) (1) + +#define jffs2_xattr_handlers NULL +#define jffs2_listxattr NULL +#define jffs2_getxattr NULL +#define jffs2_setxattr NULL +#define jffs2_removexattr NULL + +#endif /* CONFIG_JFFS2_FS_XATTR */ + +#ifdef CONFIG_JFFS2_FS_SECURITY +extern int jffs2_init_security(struct inode *inode, struct inode *dir); +extern struct xattr_handler jffs2_security_xattr_handler; +#else +#define jffs2_init_security(inode,dir) (0) +#endif /* CONFIG_JFFS2_FS_SECURITY */ + +#endif /* _JFFS2_FS_XATTR_H_ */ diff --git a/fs/jffs2/xattr_trusted.c b/fs/jffs2/xattr_trusted.c new file mode 100644 index 0000000..ed046e1 --- /dev/null +++ b/fs/jffs2/xattr_trusted.c @@ -0,0 +1,52 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +#include +#include +#include +#include +#include +#include "nodelist.h" + +static int jffs2_trusted_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_getxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size); +} + +static int jffs2_trusted_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_setxattr(inode, JFFS2_XPREFIX_TRUSTED, name, buffer, size, flags); +} + +static size_t jffs2_trusted_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + size_t retlen = XATTR_TRUSTED_PREFIX_LEN + name_len + 1; + + if (list && retlen<=list_size) { + strcpy(list, XATTR_TRUSTED_PREFIX); + strcpy(list + XATTR_TRUSTED_PREFIX_LEN, name); + } + + return retlen; +} + +struct xattr_handler jffs2_trusted_xattr_handler = { + .prefix = XATTR_TRUSTED_PREFIX, + .list = jffs2_trusted_listxattr, + .set = jffs2_trusted_setxattr, + .get = jffs2_trusted_getxattr +}; diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c new file mode 100644 index 0000000..2f8e9aa --- /dev/null +++ b/fs/jffs2/xattr_user.c @@ -0,0 +1,52 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2006 NEC Corporation + * + * Created by KaiGai Kohei + * + * For licensing information, see the file 'LICENCE' in this directory. + * + */ +#include +#include +#include +#include +#include +#include "nodelist.h" + +static int jffs2_user_getxattr(struct inode *inode, const char *name, + void *buffer, size_t size) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_getxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size); +} + +static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer, + size_t size, int flags) +{ + if (!strcmp(name, "")) + return -EINVAL; + return do_jffs2_setxattr(inode, JFFS2_XPREFIX_USER, name, buffer, size, flags); +} + +static size_t jffs2_user_listxattr(struct inode *inode, char *list, size_t list_size, + const char *name, size_t name_len) +{ + size_t retlen = XATTR_USER_PREFIX_LEN + name_len + 1; + + if (list && retlen <= list_size) { + strcpy(list, XATTR_USER_PREFIX); + strcpy(list + XATTR_USER_PREFIX_LEN, name); + } + + return retlen; +} + +struct xattr_handler jffs2_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .list = jffs2_user_listxattr, + .set = jffs2_user_setxattr, + .get = jffs2_user_getxattr +}; diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 04eb78f..43e3f56 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -305,7 +305,7 @@ static ssize_t jfs_direct_IO(int rw, str offset, nr_segs, jfs_get_block, NULL); } -struct address_space_operations jfs_aops = { +const struct address_space_operations jfs_aops = { .readpage = jfs_readpage, .readpages = jfs_readpages, .writepage = jfs_writepage, diff --git a/fs/jfs/jfs_extent.c b/fs/jfs/jfs_extent.c index 5549378..4d52593 100644 --- a/fs/jfs/jfs_extent.c +++ b/fs/jfs/jfs_extent.c @@ -126,7 +126,7 @@ extAlloc(struct inode *ip, s64 xlen, s64 /* allocate the disk blocks for the extent. initially, extBalloc() * will try to allocate disk blocks for the requested size (xlen). - * if this fails (xlen contigious free blocks not avaliable), it'll + * if this fails (xlen contiguous free blocks not avaliable), it'll * try to allocate a smaller number of blocks (producing a smaller * extent), with this smaller number of blocks consisting of the * requested number of blocks rounded down to the next smaller @@ -493,7 +493,7 @@ #endif /* _NOTYET */ * * initially, we will try to allocate disk blocks for the * requested size (nblocks). if this fails (nblocks - * contigious free blocks not avaliable), we'll try to allocate + * contiguous free blocks not avaliable), we'll try to allocate * a smaller number of blocks (producing a smaller extent), with * this smaller number of blocks consisting of the requested * number of blocks rounded down to the next smaller power of 2 @@ -529,7 +529,7 @@ extBalloc(struct inode *ip, s64 hint, s6 /* get the number of blocks to initially attempt to allocate. * we'll first try the number of blocks requested unless this - * number is greater than the maximum number of contigious free + * number is greater than the maximum number of contiguous free * blocks in the map. in that case, we'll start off with the * maximum free. */ @@ -586,7 +586,7 @@ #ifdef _NOTYET * in place. if this fails, we'll try to move the extent * to a new set of blocks. if moving the extent, we initially * will try to allocate disk blocks for the requested size - * (nnew). if this fails (nnew contigious free blocks not + * (nnew). if this fails (new contiguous free blocks not * avaliable), we'll try to allocate a smaller number of * blocks (producing a smaller extent), with this smaller * number of blocks consisting of the requested number of diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index c300726..b5c7da6 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -33,7 +33,7 @@ extern void jfs_free_zero_link(struct in extern struct dentry *jfs_get_parent(struct dentry *dentry); extern void jfs_set_inode_flags(struct inode *); -extern struct address_space_operations jfs_aops; +extern const struct address_space_operations jfs_aops; extern struct inode_operations jfs_dir_inode_operations; extern const struct file_operations jfs_dir_operations; extern struct inode_operations jfs_file_inode_operations; diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c index 2b220dd..e1e0a6e 100644 --- a/fs/jfs/jfs_metapage.c +++ b/fs/jfs/jfs_metapage.c @@ -577,7 +577,7 @@ static void metapage_invalidatepage(stru metapage_releasepage(page, 0); } -struct address_space_operations jfs_metapage_aops = { +const struct address_space_operations jfs_metapage_aops = { .readpage = metapage_readpage, .writepage = metapage_writepage, .sync_page = block_sync_page, @@ -632,10 +632,9 @@ struct metapage *__get_metapage(struct i } SetPageUptodate(page); } else { - page = read_cache_page(mapping, page_index, - (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, page_index, NULL); if (IS_ERR(page) || !PageUptodate(page)) { - jfs_err("read_cache_page failed!"); + jfs_err("read_mapping_page failed!"); return NULL; } lock_page(page); diff --git a/fs/jfs/jfs_metapage.h b/fs/jfs/jfs_metapage.h index f0b7d32..d17a329 100644 --- a/fs/jfs/jfs_metapage.h +++ b/fs/jfs/jfs_metapage.h @@ -139,7 +139,7 @@ static inline void metapage_homeok(struc put_metapage(mp); } -extern struct address_space_operations jfs_metapage_aops; +extern const struct address_space_operations jfs_metapage_aops; /* * This routines invalidate all pages for an extent. diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index ac3d669..10c4623 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -842,7 +842,7 @@ struct tlock *txLock(tid_t tid, struct i TXN_UNLOCK(); release_metapage(mp); TXN_LOCK(); - xtid = tlck->tid; /* reaquire after dropping TXN_LOCK */ + xtid = tlck->tid; /* reacquire after dropping TXN_LOCK */ jfs_info("txLock: in waitLock, tid = %d, xtid = %d, lid = %d", tid, xtid, lid); diff --git a/fs/jfs/super.c b/fs/jfs/super.c index db6f41d..4f6cfeb 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -18,7 +18,6 @@ */ #include -#include #include #include #include @@ -139,9 +138,9 @@ #endif kmem_cache_free(jfs_inode_cachep, ji); } -static int jfs_statfs(struct super_block *sb, struct kstatfs *buf) +static int jfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct jfs_sb_info *sbi = JFS_SBI(sb); + struct jfs_sb_info *sbi = JFS_SBI(dentry->d_sb); s64 maxinodes; struct inomap *imap = JFS_IP(sbi->ipimap)->i_imap; @@ -565,10 +564,11 @@ static void jfs_unlockfs(struct super_bl } } -static struct super_block *jfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int jfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, jfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, jfs_fill_super, + mnt); } static int jfs_sync_fs(struct super_block *sb, int wait) diff --git a/fs/libfs.c b/fs/libfs.c index 7145ba7..ac02ea6 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -20,9 +20,9 @@ int simple_getattr(struct vfsmount *mnt, return 0; } -int simple_statfs(struct super_block *sb, struct kstatfs *buf) +int simple_statfs(struct dentry *dentry, struct kstatfs *buf) { - buf->f_type = sb->s_magic; + buf->f_type = dentry->d_sb->s_magic; buf->f_bsize = PAGE_CACHE_SIZE; buf->f_namelen = NAME_MAX; return 0; @@ -149,10 +149,9 @@ int dcache_readdir(struct file * filp, v /* fallthrough */ default: spin_lock(&dcache_lock); - if (filp->f_pos == 2) { - list_del(q); - list_add(q, &dentry->d_subdirs); - } + if (filp->f_pos == 2) + list_move(q, &dentry->d_subdirs); + for (p=q->next; p != &dentry->d_subdirs; p=p->next) { struct dentry *next; next = list_entry(p, struct dentry, d_u.d_child); @@ -164,8 +163,7 @@ int dcache_readdir(struct file * filp, v return 0; spin_lock(&dcache_lock); /* next is still alive */ - list_del(q); - list_add(q, p); + list_move(q, p); p = q; filp->f_pos++; } @@ -196,9 +194,9 @@ struct inode_operations simple_dir_inode * Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that * will never be mountable) */ -struct super_block * -get_sb_pseudo(struct file_system_type *fs_type, char *name, - struct super_operations *ops, unsigned long magic) +int get_sb_pseudo(struct file_system_type *fs_type, char *name, + struct super_operations *ops, unsigned long magic, + struct vfsmount *mnt) { struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); static struct super_operations default_ops = {.statfs = simple_statfs}; @@ -207,7 +205,7 @@ get_sb_pseudo(struct file_system_type *f struct qstr d_name = {.name = name, .len = strlen(name)}; if (IS_ERR(s)) - return s; + return PTR_ERR(s); s->s_flags = MS_NOUSER; s->s_maxbytes = ~0ULL; @@ -232,12 +230,12 @@ get_sb_pseudo(struct file_system_type *f d_instantiate(dentry, root); s->s_root = dentry; s->s_flags |= MS_ACTIVE; - return s; + return simple_set_mnt(mnt, s); Enomem: up_write(&s->s_umount); deactivate_super(s); - return ERR_PTR(-ENOMEM); + return -ENOMEM; } int simple_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) @@ -424,13 +422,13 @@ out: static DEFINE_SPINLOCK(pin_fs_lock); -int simple_pin_fs(char *name, struct vfsmount **mount, int *count) +int simple_pin_fs(struct file_system_type *type, struct vfsmount **mount, int *count) { struct vfsmount *mnt = NULL; spin_lock(&pin_fs_lock); if (unlikely(!*mount)) { spin_unlock(&pin_fs_lock); - mnt = do_kern_mount(name, 0, name, NULL); + mnt = vfs_kern_mount(type, 0, type->name, NULL); if (IS_ERR(mnt)) return PTR_ERR(mnt); spin_lock(&pin_fs_lock); diff --git a/fs/lockd/clntlock.c b/fs/lockd/clntlock.c index bce7444..52774fe 100644 --- a/fs/lockd/clntlock.c +++ b/fs/lockd/clntlock.c @@ -147,11 +147,10 @@ u32 nlmclnt_grant(const struct sockaddr_ * Someone has sent us an SM_NOTIFY. Ensure we bind to the new port number, * that we mark locks for reclaiming, and that we bump the pseudo NSM state. */ -static inline -void nlmclnt_prepare_reclaim(struct nlm_host *host, u32 newstate) +static void nlmclnt_prepare_reclaim(struct nlm_host *host) { + down_write(&host->h_rwsem); host->h_monitored = 0; - host->h_nsmstate = newstate; host->h_state++; host->h_nextrebind = 0; nlm_rebind_host(host); @@ -164,6 +163,13 @@ void nlmclnt_prepare_reclaim(struct nlm_ dprintk("NLM: reclaiming locks for host %s", host->h_name); } +static void nlmclnt_finish_reclaim(struct nlm_host *host) +{ + host->h_reclaiming = 0; + up_write(&host->h_rwsem); + dprintk("NLM: done reclaiming locks for host %s", host->h_name); +} + /* * Reclaim all locks on server host. We do this by spawning a separate * reclaimer thread. @@ -171,12 +177,10 @@ void nlmclnt_prepare_reclaim(struct nlm_ void nlmclnt_recovery(struct nlm_host *host, u32 newstate) { - if (host->h_reclaiming++) { - if (host->h_nsmstate == newstate) - return; - nlmclnt_prepare_reclaim(host, newstate); - } else { - nlmclnt_prepare_reclaim(host, newstate); + if (host->h_nsmstate == newstate) + return; + host->h_nsmstate = newstate; + if (!host->h_reclaiming++) { nlm_get_host(host); __module_get(THIS_MODULE); if (kernel_thread(reclaimer, host, CLONE_KERNEL) < 0) @@ -190,6 +194,7 @@ reclaimer(void *ptr) struct nlm_host *host = (struct nlm_host *) ptr; struct nlm_wait *block; struct file_lock *fl, *next; + u32 nsmstate; daemonize("%s-reclaim", host->h_name); allow_signal(SIGKILL); @@ -199,19 +204,25 @@ reclaimer(void *ptr) lock_kernel(); lockd_up(); + nlmclnt_prepare_reclaim(host); /* First, reclaim all locks that have been marked. */ restart: + nsmstate = host->h_nsmstate; list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) { list_del_init(&fl->fl_u.nfs_fl.list); if (signalled()) continue; - if (nlmclnt_reclaim(host, fl) == 0) - list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); - goto restart; + if (nlmclnt_reclaim(host, fl) != 0) + continue; + list_add_tail(&fl->fl_u.nfs_fl.list, &host->h_granted); + if (host->h_nsmstate != nsmstate) { + /* Argh! The server rebooted again! */ + list_splice_init(&host->h_granted, &host->h_reclaim); + goto restart; + } } - - host->h_reclaiming = 0; + nlmclnt_finish_reclaim(host); /* Now, wake up all processes that sleep on a blocked lock */ list_for_each_entry(block, &nlm_blocked, b_list) { diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index f96e381..89ba0df 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -6,7 +6,6 @@ * Copyright (C) 1996, Olaf Kirch */ -#include #include #include #include @@ -455,7 +454,7 @@ static void nlmclnt_locks_init_private(s fl->fl_ops = &nlmclnt_lock_ops; } -static void do_vfs_lock(struct file_lock *fl) +static int do_vfs_lock(struct file_lock *fl) { int res = 0; switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { @@ -468,9 +467,7 @@ static void do_vfs_lock(struct file_lock default: BUG(); } - if (res < 0) - printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", - __FUNCTION__); + return res; } /* @@ -499,6 +496,7 @@ nlmclnt_lock(struct nlm_rqst *req, struc struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; struct nlm_wait *block = NULL; + unsigned char fl_flags = fl->fl_flags; int status = -ENOLCK; if (!host->h_monitored && nsm_monitor(host) < 0) { @@ -506,9 +504,16 @@ nlmclnt_lock(struct nlm_rqst *req, struc host->h_name); goto out; } + fl->fl_flags |= FL_ACCESS; + status = do_vfs_lock(fl); + if (status < 0) + goto out; block = nlmclnt_prepare_block(host, fl); +again: for(;;) { + /* Reboot protection */ + fl->fl_u.nfs_fl.state = host->h_state; status = nlmclnt_call(req, NLMPROC_LOCK); if (status < 0) goto out_unblock; @@ -531,10 +536,17 @@ nlmclnt_lock(struct nlm_rqst *req, struc } if (resp->status == NLM_LCK_GRANTED) { - fl->fl_u.nfs_fl.state = host->h_state; - fl->fl_flags |= FL_SLEEP; + down_read(&host->h_rwsem); + /* Check whether or not the server has rebooted */ + if (fl->fl_u.nfs_fl.state != host->h_state) { + up_read(&host->h_rwsem); + goto again; + } /* Ensure the resulting lock will get added to granted list */ - do_vfs_lock(fl); + fl->fl_flags = fl_flags | FL_SLEEP; + if (do_vfs_lock(fl) < 0) + printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); + up_read(&host->h_rwsem); } status = nlm_stat_to_errno(resp->status); out_unblock: @@ -544,6 +556,7 @@ out_unblock: nlmclnt_cancel(host, req->a_args.block, fl); out: nlm_release_call(req); + fl->fl_flags = fl_flags; return status; } @@ -596,15 +609,22 @@ nlmclnt_reclaim(struct nlm_host *host, s static int nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) { + struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; - int status; + int status = 0; /* * Note: the server is supposed to either grant us the unlock * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either * case, we want to unlock. */ - do_vfs_lock(fl); + fl->fl_flags |= FL_EXISTS; + down_read(&host->h_rwsem); + if (do_vfs_lock(fl) == -ENOENT) { + up_read(&host->h_rwsem); + goto out; + } + up_read(&host->h_rwsem); if (req->a_flags & RPC_TASK_ASYNC) return nlm_async_call(req, NLMPROC_UNLOCK, &nlmclnt_unlock_ops); @@ -613,7 +633,6 @@ nlmclnt_unlock(struct nlm_rqst *req, str if (status < 0) goto out; - status = 0; if (resp->status == NLM_LCK_GRANTED) goto out; diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 729ac42..38b0e8a 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -112,11 +112,12 @@ nlm_lookup_host(int server, struct socka host->h_version = version; host->h_proto = proto; host->h_rpcclnt = NULL; - init_MUTEX(&host->h_sema); + mutex_init(&host->h_mutex); host->h_nextrebind = jiffies + NLM_HOST_REBIND; host->h_expires = jiffies + NLM_HOST_EXPIRE; atomic_set(&host->h_count, 1); init_waitqueue_head(&host->h_gracewait); + init_rwsem(&host->h_rwsem); host->h_state = 0; /* pseudo NSM state */ host->h_nsmstate = 0; /* real NSM state */ host->h_server = server; @@ -172,7 +173,7 @@ nlm_bind_host(struct nlm_host *host) (unsigned)ntohl(host->h_addr.sin_addr.s_addr)); /* Lock host handle */ - down(&host->h_sema); + mutex_lock(&host->h_mutex); /* If we've already created an RPC client, check whether * RPC rebind is required @@ -204,12 +205,12 @@ nlm_bind_host(struct nlm_host *host) host->h_rpcclnt = clnt; } - up(&host->h_sema); + mutex_unlock(&host->h_mutex); return clnt; forgetit: printk("lockd: couldn't create RPC handle for %s\n", host->h_name); - up(&host->h_sema); + mutex_unlock(&host->h_mutex); return NULL; } diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index fd56c88..9a991b5 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -12,7 +12,6 @@ * Copyright (C) 1995, 1996 Olaf Kirch */ -#include #include #include #include diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c index 3ef7391..baf5ae5 100644 --- a/fs/lockd/svclock.c +++ b/fs/lockd/svclock.c @@ -20,7 +20,6 @@ * Copyright (C) 1996, Olaf Kirch */ -#include #include #include #include diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index d210cf3..dbb66a3 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -7,7 +7,6 @@ * Copyright (C) 1996, Olaf Kirch */ -#include #include #include #include diff --git a/fs/lockd/svcsubs.c b/fs/lockd/svcsubs.c index a570e5c..2a4df9b 100644 --- a/fs/lockd/svcsubs.c +++ b/fs/lockd/svcsubs.c @@ -6,7 +6,6 @@ * Copyright (C) 1996, Olaf Kirch */ -#include #include #include #include diff --git a/fs/lockd/xdr.c b/fs/lockd/xdr.c index f22a376..033ea4a 100644 --- a/fs/lockd/xdr.c +++ b/fs/lockd/xdr.c @@ -6,7 +6,6 @@ * Copyright (C) 1995, 1996 Olaf Kirch */ -#include #include #include #include diff --git a/fs/locks.c b/fs/locks.c index ab61a8b..b0b41a6 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -703,7 +703,7 @@ EXPORT_SYMBOL(posix_test_lock); * from a broken NFS client. But broken NFS clients have a lot more to * worry about than proper deadlock detection anyway... --okir */ -int posix_locks_deadlock(struct file_lock *caller_fl, +static int posix_locks_deadlock(struct file_lock *caller_fl, struct file_lock *block_fl) { struct list_head *tmp; @@ -722,11 +722,13 @@ next_task: return 0; } -EXPORT_SYMBOL(posix_locks_deadlock); - /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks * at the head of the list, but that's secret knowledge known only to * flock_lock_file and posix_lock_file. + * + * Note that if called with an FL_EXISTS argument, the caller may determine + * whether or not a lock was successfully freed by testing the return + * value for -ENOENT. */ static int flock_lock_file(struct file *filp, struct file_lock *request) { @@ -737,6 +739,8 @@ static int flock_lock_file(struct file * int found = 0; lock_kernel(); + if (request->fl_flags & FL_ACCESS) + goto find_conflict; for_each_lock(inode, before) { struct file_lock *fl = *before; if (IS_POSIX(fl)) @@ -752,8 +756,11 @@ static int flock_lock_file(struct file * break; } - if (request->fl_type == F_UNLCK) + if (request->fl_type == F_UNLCK) { + if ((request->fl_flags & FL_EXISTS) && !found) + error = -ENOENT; goto out; + } error = -ENOMEM; new_fl = locks_alloc_lock(); @@ -766,6 +773,7 @@ static int flock_lock_file(struct file * if (found) cond_resched(); +find_conflict: for_each_lock(inode, before) { struct file_lock *fl = *before; if (IS_POSIX(fl)) @@ -779,6 +787,8 @@ static int flock_lock_file(struct file * locks_insert_block(fl, request); goto out; } + if (request->fl_flags & FL_ACCESS) + goto out; locks_copy_lock(new_fl, request); locks_insert_lock(&inode->i_flock, new_fl); new_fl = NULL; @@ -794,7 +804,8 @@ out: static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request, struct file_lock *conflock) { struct file_lock *fl; - struct file_lock *new_fl, *new_fl2; + struct file_lock *new_fl = NULL; + struct file_lock *new_fl2 = NULL; struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; @@ -803,9 +814,15 @@ static int __posix_lock_file_conf(struct /* * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. + * + * In some cases we can be sure, that no new locks will be needed */ - new_fl = locks_alloc_lock(); - new_fl2 = locks_alloc_lock(); + if (!(request->fl_flags & FL_ACCESS) && + (request->fl_type != F_UNLCK || + request->fl_start != 0 || request->fl_end != OFFSET_MAX)) { + new_fl = locks_alloc_lock(); + new_fl2 = locks_alloc_lock(); + } lock_kernel(); if (request->fl_type != F_UNLCK) { @@ -834,14 +851,7 @@ static int __posix_lock_file_conf(struct if (request->fl_flags & FL_ACCESS) goto out; - error = -ENOLCK; /* "no luck" */ - if (!(new_fl && new_fl2)) - goto out; - /* - * We've allocated the new locks in advance, so there are no - * errors possible (and no blocking operations) from here on. - * * Find the first old lock with the same owner as the new lock. */ @@ -938,10 +948,28 @@ static int __posix_lock_file_conf(struct before = &fl->fl_next; } + /* + * The above code only modifies existing locks in case of + * merging or replacing. If new lock(s) need to be inserted + * all modifications are done bellow this, so it's safe yet to + * bail out. + */ + error = -ENOLCK; /* "no luck" */ + if (right && left == right && !new_fl2) + goto out; + error = 0; if (!added) { - if (request->fl_type == F_UNLCK) + if (request->fl_type == F_UNLCK) { + if (request->fl_flags & FL_EXISTS) + error = -ENOENT; goto out; + } + + if (!new_fl) { + error = -ENOLCK; + goto out; + } locks_copy_lock(new_fl, request); locks_insert_lock(before, new_fl); new_fl = NULL; @@ -983,6 +1011,10 @@ static int __posix_lock_file_conf(struct * Add a POSIX style lock to a file. * We merge adjacent & overlapping locks whenever possible. * POSIX locks are sorted by owner task, then by starting address + * + * Note that if called with an FL_EXISTS argument, the caller may determine + * whether or not a lock was successfully freed by testing the return + * value for -ENOENT. */ int posix_lock_file(struct file *filp, struct file_lock *fl) { @@ -1881,19 +1913,18 @@ #endif /* BITS_PER_LONG == 32 */ */ void locks_remove_posix(struct file *filp, fl_owner_t owner) { - struct file_lock lock, **before; + struct file_lock lock; /* * If there are no locks held on this file, we don't need to call * posix_lock_file(). Another process could be setting a lock on this * file at the same time, but we wouldn't remove that lock anyway. */ - before = &filp->f_dentry->d_inode->i_flock; - if (*before == NULL) + if (!filp->f_dentry->d_inode->i_flock) return; lock.fl_type = F_UNLCK; - lock.fl_flags = FL_POSIX; + lock.fl_flags = FL_POSIX | FL_CLOSE; lock.fl_start = 0; lock.fl_end = OFFSET_MAX; lock.fl_owner = owner; @@ -1902,25 +1933,11 @@ void locks_remove_posix(struct file *fil lock.fl_ops = NULL; lock.fl_lmops = NULL; - if (filp->f_op && filp->f_op->lock != NULL) { + if (filp->f_op && filp->f_op->lock != NULL) filp->f_op->lock(filp, F_SETLK, &lock); - goto out; - } + else + posix_lock_file(filp, &lock); - /* Can't use posix_lock_file here; we need to remove it no matter - * which pid we have. - */ - lock_kernel(); - while (*before != NULL) { - struct file_lock *fl = *before; - if (IS_POSIX(fl) && posix_same_owner(fl, &lock)) { - locks_delete_lock(before); - continue; - } - before = &fl->fl_next; - } - unlock_kernel(); -out: if (lock.fl_ops && lock.fl_ops->fl_release_private) lock.fl_ops->fl_release_private(&lock); } @@ -2206,63 +2223,6 @@ int lock_may_write(struct inode *inode, EXPORT_SYMBOL(lock_may_write); -static inline void __steal_locks(struct file *file, fl_owner_t from) -{ - struct inode *inode = file->f_dentry->d_inode; - struct file_lock *fl = inode->i_flock; - - while (fl) { - if (fl->fl_file == file && fl->fl_owner == from) - fl->fl_owner = current->files; - fl = fl->fl_next; - } -} - -/* When getting ready for executing a binary, we make sure that current - * has a files_struct on its own. Before dropping the old files_struct, - * we take over ownership of all locks for all file descriptors we own. - * Note that we may accidentally steal a lock for a file that a sibling - * has created since the unshare_files() call. - */ -void steal_locks(fl_owner_t from) -{ - struct files_struct *files = current->files; - int i, j; - struct fdtable *fdt; - - if (from == files) - return; - - lock_kernel(); - j = 0; - - /* - * We are not taking a ref to the file structures, so - * we need to acquire ->file_lock. - */ - spin_lock(&files->file_lock); - fdt = files_fdtable(files); - for (;;) { - unsigned long set; - i = j * __NFDBITS; - if (i >= fdt->max_fdset || i >= fdt->max_fds) - break; - set = fdt->open_fds->fds_bits[j++]; - while (set) { - if (set & 1) { - struct file *file = fdt->fd[i]; - if (file) - __steal_locks(file, from); - } - i++; - set >>= 1; - } - } - spin_unlock(&files->file_lock); - unlock_kernel(); -} -EXPORT_SYMBOL(steal_locks); - static int __init filelock_init(void) { filelock_cache = kmem_cache_create("file_lock_cache", diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 69224d1..2b0a389 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -60,8 +60,7 @@ static int dir_commit_chunk(struct page static struct page * dir_get_page(struct inode *dir, unsigned long n) { struct address_space *mapping = dir->i_mapping; - struct page *page = read_cache_page(mapping, n, - (filler_t*)mapping->a_ops->readpage, NULL); + struct page *page = read_mapping_page(mapping, n, NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); kmap(page); diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 2dcccf1..9ea91c5 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -19,7 +19,7 @@ #include static void minix_read_inode(struct inode * inode); static int minix_write_inode(struct inode * inode, int wait); -static int minix_statfs(struct super_block *sb, struct kstatfs *buf); +static int minix_statfs(struct dentry *dentry, struct kstatfs *buf); static int minix_remount (struct super_block * sb, int * flags, char * data); static void minix_delete_inode(struct inode *inode) @@ -296,11 +296,11 @@ out_bad_sb: return -EINVAL; } -static int minix_statfs(struct super_block *sb, struct kstatfs *buf) +static int minix_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct minix_sb_info *sbi = minix_sb(sb); - buf->f_type = sb->s_magic; - buf->f_bsize = sb->s_blocksize; + struct minix_sb_info *sbi = minix_sb(dentry->d_sb); + buf->f_type = dentry->d_sb->s_magic; + buf->f_bsize = dentry->d_sb->s_blocksize; buf->f_blocks = (sbi->s_nzones - sbi->s_firstdatazone) << sbi->s_log_zone_size; buf->f_bfree = minix_count_free_blocks(sbi); buf->f_bavail = buf->f_bfree; @@ -335,7 +335,7 @@ static sector_t minix_bmap(struct addres { return generic_block_bmap(mapping,block,minix_get_block); } -static struct address_space_operations minix_aops = { +static const struct address_space_operations minix_aops = { .readpage = minix_readpage, .writepage = minix_writepage, .sync_page = block_sync_page, @@ -559,10 +559,11 @@ void minix_truncate(struct inode * inode V2_minix_truncate(inode); } -static struct super_block *minix_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int minix_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, minix_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, minix_fill_super, + mnt); } static struct file_system_type minix_fs_type = { diff --git a/fs/mpage.c b/fs/mpage.c index 9bf2eb3..1e45982 100644 --- a/fs/mpage.c +++ b/fs/mpage.c @@ -707,9 +707,9 @@ mpage_writepages(struct address_space *m struct pagevec pvec; int nr_pages; pgoff_t index; - pgoff_t end = -1; /* Inclusive */ + pgoff_t end; /* Inclusive */ int scanned = 0; - int is_range = 0; + int range_whole = 0; if (wbc->nonblocking && bdi_write_congested(bdi)) { wbc->encountered_congestion = 1; @@ -721,16 +721,14 @@ mpage_writepages(struct address_space *m writepage = mapping->a_ops->writepage; pagevec_init(&pvec, 0); - if (wbc->sync_mode == WB_SYNC_NONE) { + if (wbc->range_cyclic) { index = mapping->writeback_index; /* Start from prev offset */ + end = -1; } else { - index = 0; /* whole-file sweep */ - scanned = 1; - } - if (wbc->start || wbc->end) { - index = wbc->start >> PAGE_CACHE_SHIFT; - end = wbc->end >> PAGE_CACHE_SHIFT; - is_range = 1; + index = wbc->range_start >> PAGE_CACHE_SHIFT; + end = wbc->range_end >> PAGE_CACHE_SHIFT; + if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) + range_whole = 1; scanned = 1; } retry: @@ -759,7 +757,7 @@ retry: continue; } - if (unlikely(is_range) && page->index > end) { + if (!wbc->range_cyclic && page->index > end) { done = 1; unlock_page(page); continue; @@ -810,7 +808,7 @@ retry: index = 0; goto retry; } - if (!is_range) + if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) mapping->writeback_index = index; if (bio) mpage_bio_submit(WRITE, bio); diff --git a/fs/msdos/namei.c b/fs/msdos/namei.c index 5b76ccd..9e44158 100644 --- a/fs/msdos/namei.c +++ b/fs/msdos/namei.c @@ -661,11 +661,12 @@ static int msdos_fill_super(struct super return 0; } -static struct super_block *msdos_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int msdos_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super, + mnt); } static struct file_system_type msdos_fs_type = { diff --git a/fs/namei.c b/fs/namei.c index d6e2ee2..c9750d7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1127,7 +1127,7 @@ out: if (likely(retval == 0)) { if (unlikely(current->audit_context && nd && nd->dentry && nd->dentry->d_inode)) - audit_inode(name, nd->dentry->d_inode, flags); + audit_inode(name, nd->dentry->d_inode); } out_fail: return retval; @@ -1423,7 +1423,7 @@ struct dentry *lock_rename(struct dentry struct dentry *p; if (p1 == p2) { - mutex_lock(&p1->d_inode->i_mutex); + mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); return NULL; } @@ -1431,22 +1431,22 @@ struct dentry *lock_rename(struct dentry for (p = p1; p->d_parent != p; p = p->d_parent) { if (p->d_parent == p2) { - mutex_lock(&p2->d_inode->i_mutex); - mutex_lock(&p1->d_inode->i_mutex); + mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); return p; } } for (p = p2; p->d_parent != p; p = p->d_parent) { if (p->d_parent == p1) { - mutex_lock(&p1->d_inode->i_mutex); - mutex_lock(&p2->d_inode->i_mutex); + mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); return p; } } - mutex_lock(&p1->d_inode->i_mutex); - mutex_lock(&p2->d_inode->i_mutex); + mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); return NULL; } @@ -1751,7 +1751,7 @@ struct dentry *lookup_create(struct name { struct dentry *dentry = ERR_PTR(-EEXIST); - mutex_lock(&nd->dentry->d_inode->i_mutex); + mutex_lock_nested(&nd->dentry->d_inode->i_mutex, I_MUTEX_PARENT); /* * Yucky last component or no last component at all? * (foo/., foo/.., /////) @@ -2008,7 +2008,7 @@ static long do_rmdir(int dfd, const char error = -EBUSY; goto exit1; } - mutex_lock(&nd.dentry->d_inode->i_mutex); + mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { @@ -2082,7 +2082,7 @@ static long do_unlinkat(int dfd, const c error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; - mutex_lock(&nd.dentry->d_inode->i_mutex); + mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { @@ -2243,14 +2243,16 @@ asmlinkage long sys_linkat(int olddfd, c int error; char * to; - if (flags != 0) + if ((flags & ~AT_SYMLINK_FOLLOW) != 0) return -EINVAL; to = getname(newname); if (IS_ERR(to)) return PTR_ERR(to); - error = __user_walk_fd(olddfd, oldname, 0, &old_nd); + error = __user_walk_fd(olddfd, oldname, + flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, + &old_nd); if (error) goto exit; error = do_path_lookup(newdfd, to, LOOKUP_PARENT, &nd); @@ -2577,8 +2579,7 @@ static char *page_getlink(struct dentry { struct page * page; struct address_space *mapping = dentry->d_inode->i_mapping; - page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, - NULL); + page = read_mapping_page(mapping, 0, NULL); if (IS_ERR(page)) goto sync_fail; wait_on_page_locked(page); diff --git a/fs/namespace.c b/fs/namespace.c index bf478ad..fa7ed6a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -8,7 +8,6 @@ * Heavily rewritten. */ -#include #include #include #include @@ -86,6 +85,15 @@ struct vfsmount *alloc_vfsmnt(const char return mnt; } +int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb) +{ + mnt->mnt_sb = sb; + mnt->mnt_root = dget(sb->s_root); + return 0; +} + +EXPORT_SYMBOL(simple_set_mnt); + void free_vfsmnt(struct vfsmount *mnt) { kfree(mnt->mnt_devname); @@ -517,10 +525,8 @@ void umount_tree(struct vfsmount *mnt, i { struct vfsmount *p; - for (p = mnt; p; p = next_mnt(p, mnt)) { - list_del(&p->mnt_hash); - list_add(&p->mnt_hash, kill); - } + for (p = mnt; p; p = next_mnt(p, mnt)) + list_move(&p->mnt_hash, kill); if (propagate) propagate_umount(kill); @@ -576,8 +582,8 @@ static int do_umount(struct vfsmount *mn */ lock_kernel(); - if ((flags & MNT_FORCE) && sb->s_op->umount_begin) - sb->s_op->umount_begin(sb); + if (sb->s_op->umount_begin) + sb->s_op->umount_begin(mnt, flags); unlock_kernel(); /* @@ -1163,13 +1169,46 @@ static void expire_mount(struct vfsmount } /* + * go through the vfsmounts we've just consigned to the graveyard to + * - check that they're still dead + * - delete the vfsmount from the appropriate namespace under lock + * - dispose of the corpse + */ +static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts) +{ + struct namespace *namespace; + struct vfsmount *mnt; + + while (!list_empty(graveyard)) { + LIST_HEAD(umounts); + mnt = list_entry(graveyard->next, struct vfsmount, mnt_expire); + list_del_init(&mnt->mnt_expire); + + /* don't do anything if the namespace is dead - all the + * vfsmounts from it are going away anyway */ + namespace = mnt->mnt_namespace; + if (!namespace || !namespace->root) + continue; + get_namespace(namespace); + + spin_unlock(&vfsmount_lock); + down_write(&namespace_sem); + expire_mount(mnt, mounts, &umounts); + up_write(&namespace_sem); + release_mounts(&umounts); + mntput(mnt); + put_namespace(namespace); + spin_lock(&vfsmount_lock); + } +} + +/* * process a list of expirable mountpoints with the intent of discarding any * mountpoints that aren't in use and haven't been touched since last we came * here */ void mark_mounts_for_expiry(struct list_head *mounts) { - struct namespace *namespace; struct vfsmount *mnt, *next; LIST_HEAD(graveyard); @@ -1193,38 +1232,79 @@ void mark_mounts_for_expiry(struct list_ list_move(&mnt->mnt_expire, &graveyard); } - /* - * go through the vfsmounts we've just consigned to the graveyard to - * - check that they're still dead - * - delete the vfsmount from the appropriate namespace under lock - * - dispose of the corpse - */ - while (!list_empty(&graveyard)) { - LIST_HEAD(umounts); - mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire); - list_del_init(&mnt->mnt_expire); + expire_mount_list(&graveyard, mounts); - /* don't do anything if the namespace is dead - all the - * vfsmounts from it are going away anyway */ - namespace = mnt->mnt_namespace; - if (!namespace || !namespace->root) + spin_unlock(&vfsmount_lock); +} + +EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); + +/* + * Ripoff of 'select_parent()' + * + * search the list of submounts for a given mountpoint, and move any + * shrinkable submounts to the 'graveyard' list. + */ +static int select_submounts(struct vfsmount *parent, struct list_head *graveyard) +{ + struct vfsmount *this_parent = parent; + struct list_head *next; + int found = 0; + +repeat: + next = this_parent->mnt_mounts.next; +resume: + while (next != &this_parent->mnt_mounts) { + struct list_head *tmp = next; + struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child); + + next = tmp->next; + if (!(mnt->mnt_flags & MNT_SHRINKABLE)) continue; - get_namespace(namespace); + /* + * Descend a level if the d_mounts list is non-empty. + */ + if (!list_empty(&mnt->mnt_mounts)) { + this_parent = mnt; + goto repeat; + } - spin_unlock(&vfsmount_lock); - down_write(&namespace_sem); - expire_mount(mnt, mounts, &umounts); - up_write(&namespace_sem); - release_mounts(&umounts); - mntput(mnt); - put_namespace(namespace); - spin_lock(&vfsmount_lock); + if (!propagate_mount_busy(mnt, 1)) { + mntget(mnt); + list_move_tail(&mnt->mnt_expire, graveyard); + found++; + } } + /* + * All done at this level ... ascend and resume the search + */ + if (this_parent != parent) { + next = this_parent->mnt_child.next; + this_parent = this_parent->mnt_parent; + goto resume; + } + return found; +} + +/* + * process a list of expirable mountpoints with the intent of discarding any + * submounts of a specific parent mountpoint + */ +void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts) +{ + LIST_HEAD(graveyard); + int found; + + spin_lock(&vfsmount_lock); + + /* extract submounts of 'mountpoint' from the expiration list */ + while ((found = select_submounts(mountpoint, &graveyard)) != 0) + expire_mount_list(&graveyard, mounts); spin_unlock(&vfsmount_lock); } -EXPORT_SYMBOL_GPL(mark_mounts_for_expiry); +EXPORT_SYMBOL_GPL(shrink_submounts); /* * Some copy_from_user() implementations do not return the exact number of diff --git a/fs/ncpfs/dir.c b/fs/ncpfs/dir.c index f0860c6..b4ee892 100644 --- a/fs/ncpfs/dir.c +++ b/fs/ncpfs/dir.c @@ -10,7 +10,6 @@ * */ -#include #include #include diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index a1f3e97..1ddf77b 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -9,7 +9,6 @@ * */ -#include #include #include @@ -39,7 +38,7 @@ #include "getopt.h" static void ncp_delete_inode(struct inode *); static void ncp_put_super(struct super_block *); -static int ncp_statfs(struct super_block *, struct kstatfs *); +static int ncp_statfs(struct dentry *, struct kstatfs *); static kmem_cache_t * ncp_inode_cachep; @@ -105,7 +104,7 @@ static struct super_operations ncp_sops extern struct dentry_operations ncp_root_dentry_operations; #if defined(CONFIG_NCPFS_EXTRAS) || defined(CONFIG_NCPFS_NFS_NS) -extern struct address_space_operations ncp_symlink_aops; +extern const struct address_space_operations ncp_symlink_aops; extern int ncp_symlink(struct inode*, struct dentry*, const char*); #endif @@ -724,13 +723,14 @@ #endif /* CONFIG_NCPFS_NLS */ kfree(server); } -static int ncp_statfs(struct super_block *sb, struct kstatfs *buf) +static int ncp_statfs(struct dentry *dentry, struct kstatfs *buf) { struct dentry* d; struct inode* i; struct ncp_inode_info* ni; struct ncp_server* s; struct ncp_volume_info vi; + struct super_block *sb = dentry->d_sb; int err; __u8 dh; @@ -957,10 +957,10 @@ out: return result; } -static struct super_block *ncp_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ncp_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, ncp_fill_super); + return get_sb_nodev(fs_type, flags, data, ncp_fill_super, mnt); } static struct file_system_type ncp_fs_type = { diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index eb3813a..42039fe 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -7,7 +7,6 @@ * */ -#include #include #include diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 52d60c3..e7d5a30 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -93,7 +93,7 @@ static struct page* ncp_file_mmap_nopage */ if (type) *type = VM_FAULT_MAJOR; - inc_page_state(pgmajfault); + count_vm_event(PGMAJFAULT); return page; } diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index d9ebf64..551e0ba 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -10,7 +10,6 @@ */ -#include #include "ncplib_kernel.h" diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 799e5c2..2441d1a 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -12,7 +12,6 @@ #ifndef _NCPLIB_H #define _NCPLIB_H -#include #include #include diff --git a/fs/ncpfs/ncpsign_kernel.c b/fs/ncpfs/ncpsign_kernel.c index a6ec90c..749a18d 100644 --- a/fs/ncpfs/ncpsign_kernel.c +++ b/fs/ncpfs/ncpsign_kernel.c @@ -5,7 +5,6 @@ * */ -#include #ifdef CONFIG_NCPFS_PACKET_SIGNING diff --git a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c index 8783eb7..11c2b25 100644 --- a/fs/ncpfs/sock.c +++ b/fs/ncpfs/sock.c @@ -8,7 +8,6 @@ * */ -#include #include #include diff --git a/fs/ncpfs/symlink.c b/fs/ncpfs/symlink.c index e935f1b..ca92c24 100644 --- a/fs/ncpfs/symlink.c +++ b/fs/ncpfs/symlink.c @@ -20,7 +20,6 @@ * */ -#include #include @@ -99,7 +98,7 @@ fail: /* * symlinks can't do much... */ -struct address_space_operations ncp_symlink_aops = { +const struct address_space_operations ncp_symlink_aops = { .readpage = ncp_symlink_readpage, }; diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index ec61fd5..0b572a0 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile @@ -4,14 +4,16 @@ # obj-$(CONFIG_NFS_FS) += nfs.o -nfs-y := dir.o file.o inode.o nfs2xdr.o pagelist.o \ - proc.o read.o symlink.o unlink.o write.o +nfs-y := dir.o file.o inode.o super.o nfs2xdr.o pagelist.o \ + proc.o read.o symlink.o unlink.o write.o \ + namespace.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ delegation.o idmap.o \ - callback.o callback_xdr.o callback_proc.o + callback.o callback_xdr.o callback_proc.o \ + nfs4namespace.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-objs := $(nfs-y) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 90c95ad..fe0a6b8 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -6,7 +6,6 @@ * NFSv4 callback handling */ -#include #include #include #include @@ -182,8 +181,6 @@ static int nfs_callback_authenticate(str /* * Define NFS4 callback program */ -extern struct svc_version nfs4_callback_version1; - static struct svc_version *nfs4_callback_version[] = { [1] = &nfs4_callback_version1, }; diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 462cfce..7719483 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -5,7 +5,6 @@ * * NFSv4 callback procedures */ -#include #include #include #include "nfs4_fs.h" diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 05c38cf..29f9321 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -5,7 +5,6 @@ * * NFSv4 callback encode/decode procedures */ -#include #include #include #include @@ -202,7 +201,7 @@ static unsigned decode_recall_args(struc status = decode_fh(xdr, &args->fh); out: dprintk("%s: exit with status = %d\n", __FUNCTION__, status); - return 0; + return status; } static unsigned encode_string(struct xdr_stream *xdr, unsigned int len, const char *str) diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index d3be923..9540a31 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -6,7 +6,6 @@ * NFS file delegation management * */ -#include #include #include #include diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index cae74dd..e7ffb4d 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -528,7 +528,7 @@ static int nfs_readdir(struct file *filp lock_kernel(); - res = nfs_revalidate_inode(NFS_SERVER(inode), inode); + res = nfs_revalidate_mapping(inode, filp->f_mapping); if (res < 0) { unlock_kernel(); return res; @@ -690,7 +690,9 @@ int nfs_lookup_verify_inode(struct inode goto out_force; /* This is an open(2) */ if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 && - !(server->flags & NFS_MOUNT_NOCTO)) + !(server->flags & NFS_MOUNT_NOCTO) && + (S_ISREG(inode->i_mode) || + S_ISDIR(inode->i_mode))) goto out_force; } return nfs_revalidate_inode(server, inode); @@ -868,6 +870,17 @@ int nfs_is_exclusive_create(struct inode return (nd->intent.open.flags & O_EXCL) != 0; } +static inline int nfs_reval_fsid(struct inode *dir, + struct nfs_fh *fh, struct nfs_fattr *fattr) +{ + struct nfs_server *server = NFS_SERVER(dir); + + if (!nfs_fsid_equal(&server->fsid, &fattr->fsid)) + /* Revalidate fsid on root dir */ + return __nfs_revalidate_inode(server, dir->i_sb->s_root->d_inode); + return 0; +} + static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) { struct dentry *res; @@ -900,6 +913,11 @@ static struct dentry *nfs_lookup(struct res = ERR_PTR(error); goto out_unlock; } + error = nfs_reval_fsid(dir, &fhandle, &fattr); + if (error < 0) { + res = ERR_PTR(error); + goto out_unlock; + } inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); res = (struct dentry *)inode; if (IS_ERR(res)) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 3c72b0c..fecd3b0 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -38,7 +38,6 @@ * */ -#include #include #include #include @@ -68,25 +67,19 @@ struct nfs_direct_req { struct kref kref; /* release manager */ /* I/O parameters */ - struct list_head list, /* nfs_read/write_data structs */ - rewrite_list; /* saved nfs_write_data structs */ struct nfs_open_context *ctx; /* file open context info */ struct kiocb * iocb; /* controlling i/o request */ struct inode * inode; /* target file of i/o */ - unsigned long user_addr; /* location of user's buffer */ - size_t user_count; /* total bytes to move */ - loff_t pos; /* starting offset in file */ - struct page ** pages; /* pages in our buffer */ - unsigned int npages; /* count of pages */ /* completion state */ + atomic_t io_count; /* i/os we're waiting for */ spinlock_t lock; /* protect completion state */ - int outstanding; /* i/os we're waiting for */ ssize_t count, /* bytes actually processed */ error; /* any reported error */ struct completion completion; /* wait for i/o completion */ /* commit state */ + struct list_head rewrite_list; /* saved nfs_write_data structs */ struct nfs_write_data * commit_data; /* special write_data for commits */ int flags; #define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */ @@ -94,8 +87,37 @@ #define NFS_ODIRECT_RESCHED_WRITES (2) / struct nfs_writeverf verf; /* unstable write verifier */ }; -static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync); static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode); +static const struct rpc_call_ops nfs_write_direct_ops; + +static inline void get_dreq(struct nfs_direct_req *dreq) +{ + atomic_inc(&dreq->io_count); +} + +static inline int put_dreq(struct nfs_direct_req *dreq) +{ + return atomic_dec_and_test(&dreq->io_count); +} + +/* + * "size" is never larger than rsize or wsize. + */ +static inline int nfs_direct_count_pages(unsigned long user_addr, size_t size) +{ + int page_count; + + page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT; + page_count -= user_addr >> PAGE_SHIFT; + BUG_ON(page_count < 0); + + return page_count; +} + +static inline unsigned int nfs_max_pages(unsigned int size) +{ + return (size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; +} /** * nfs_direct_IO - NFS address space operation for direct I/O @@ -119,50 +141,21 @@ ssize_t nfs_direct_IO(int rw, struct kio return -EINVAL; } -static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty) +static void nfs_direct_dirty_pages(struct page **pages, int npages) { int i; for (i = 0; i < npages; i++) { struct page *page = pages[i]; - if (do_dirty && !PageCompound(page)) + if (!PageCompound(page)) set_page_dirty_lock(page); - page_cache_release(page); } - kfree(pages); } -static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t size, struct page ***pages) +static void nfs_direct_release_pages(struct page **pages, int npages) { - int result = -ENOMEM; - unsigned long page_count; - size_t array_size; - - page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT; - page_count -= user_addr >> PAGE_SHIFT; - - array_size = (page_count * sizeof(struct page *)); - *pages = kmalloc(array_size, GFP_KERNEL); - if (*pages) { - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - page_count, (rw == READ), 0, - *pages, NULL); - up_read(¤t->mm->mmap_sem); - if (result != page_count) { - /* - * If we got fewer pages than expected from - * get_user_pages(), the user buffer runs off the - * end of a mapping; return EFAULT. - */ - if (result >= 0) { - nfs_free_user_pages(*pages, result, 0); - result = -EFAULT; - } else - kfree(*pages); - *pages = NULL; - } - } - return result; + int i; + for (i = 0; i < npages; i++) + page_cache_release(pages[i]); } static inline struct nfs_direct_req *nfs_direct_req_alloc(void) @@ -174,13 +167,13 @@ static inline struct nfs_direct_req *nfs return NULL; kref_init(&dreq->kref); + kref_get(&dreq->kref); init_completion(&dreq->completion); - INIT_LIST_HEAD(&dreq->list); INIT_LIST_HEAD(&dreq->rewrite_list); dreq->iocb = NULL; dreq->ctx = NULL; spin_lock_init(&dreq->lock); - dreq->outstanding = 0; + atomic_set(&dreq->io_count, 0); dreq->count = 0; dreq->error = 0; dreq->flags = 0; @@ -221,18 +214,11 @@ out: } /* - * We must hold a reference to all the pages in this direct read request - * until the RPCs complete. This could be long *after* we are woken up in - * nfs_direct_wait (for instance, if someone hits ^C on a slow server). - * - * In addition, synchronous I/O uses a stack-allocated iocb. Thus we - * can't trust the iocb is still valid here if this is a synchronous - * request. If the waiter is woken prematurely, the iocb is long gone. + * Synchronous I/O uses a stack-allocated iocb. Thus we can't trust + * the iocb is still valid here if this is a synchronous request. */ static void nfs_direct_complete(struct nfs_direct_req *dreq) { - nfs_free_user_pages(dreq->pages, dreq->npages, 1); - if (dreq->iocb) { long res = (long) dreq->error; if (!res) @@ -245,48 +231,10 @@ static void nfs_direct_complete(struct n } /* - * Note we also set the number of requests we have in the dreq when we are - * done. This prevents races with I/O completion so we will always wait - * until all requests have been dispatched and completed. + * We must hold a reference to all the pages in this direct read request + * until the RPCs complete. This could be long *after* we are woken up in + * nfs_direct_wait (for instance, if someone hits ^C on a slow server). */ -static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, size_t rsize) -{ - struct list_head *list; - struct nfs_direct_req *dreq; - unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - dreq = nfs_direct_req_alloc(); - if (!dreq) - return NULL; - - list = &dreq->list; - for(;;) { - struct nfs_read_data *data = nfs_readdata_alloc(rpages); - - if (unlikely(!data)) { - while (!list_empty(list)) { - data = list_entry(list->next, - struct nfs_read_data, pages); - list_del(&data->pages); - nfs_readdata_free(data); - } - kref_put(&dreq->kref, nfs_direct_req_release); - return NULL; - } - - INIT_LIST_HEAD(&data->pages); - list_add(&data->pages, list); - - data->req = (struct nfs_page *) dreq; - dreq->outstanding++; - if (nbytes <= rsize) - break; - nbytes -= rsize; - } - kref_get(&dreq->kref); - return dreq; -} - static void nfs_direct_read_result(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; @@ -295,6 +243,9 @@ static void nfs_direct_read_result(struc if (nfs_readpage_result(task, data) != 0) return; + nfs_direct_dirty_pages(data->pagevec, data->npages); + nfs_direct_release_pages(data->pagevec, data->npages); + spin_lock(&dreq->lock); if (likely(task->tk_status >= 0)) @@ -302,13 +253,10 @@ static void nfs_direct_read_result(struc else dreq->error = task->tk_status; - if (--dreq->outstanding) { - spin_unlock(&dreq->lock); - return; - } - spin_unlock(&dreq->lock); - nfs_direct_complete(dreq); + + if (put_dreq(dreq)) + nfs_direct_complete(dreq); } static const struct rpc_call_ops nfs_read_direct_ops = { @@ -317,41 +265,60 @@ static const struct rpc_call_ops nfs_rea }; /* - * For each nfs_read_data struct that was allocated on the list, dispatch - * an NFS READ operation + * For each rsize'd chunk of the user's buffer, dispatch an NFS READ + * operation. If nfs_readdata_alloc() or get_user_pages() fails, + * bail and stop sending more reads. Read length accounting is + * handled automatically by nfs_direct_read_result(). Otherwise, if + * no requests have been sent, just return an error. */ -static void nfs_direct_read_schedule(struct nfs_direct_req *dreq) +static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) { struct nfs_open_context *ctx = dreq->ctx; struct inode *inode = ctx->dentry->d_inode; - struct list_head *list = &dreq->list; - struct page **pages = dreq->pages; - size_t count = dreq->user_count; - loff_t pos = dreq->pos; size_t rsize = NFS_SERVER(inode)->rsize; - unsigned int curpage, pgbase; + unsigned int rpages = nfs_max_pages(rsize); + unsigned int pgbase; + int result; + ssize_t started = 0; + + get_dreq(dreq); - curpage = 0; - pgbase = dreq->user_addr & ~PAGE_MASK; + pgbase = user_addr & ~PAGE_MASK; do { struct nfs_read_data *data; size_t bytes; + result = -ENOMEM; + data = nfs_readdata_alloc(rpages); + if (unlikely(!data)) + break; + bytes = rsize; if (count < rsize) bytes = count; - BUG_ON(list_empty(list)); - data = list_entry(list->next, struct nfs_read_data, pages); - list_del_init(&data->pages); + data->npages = nfs_direct_count_pages(user_addr, bytes); + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, + data->npages, 1, 0, data->pagevec, NULL); + up_read(¤t->mm->mmap_sem); + if (unlikely(result < data->npages)) { + if (result > 0) + nfs_direct_release_pages(data->pagevec, result); + nfs_readdata_release(data); + break; + } + + get_dreq(dreq); + data->req = (struct nfs_page *) dreq; data->inode = inode; data->cred = ctx->cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; data->args.offset = pos; data->args.pgbase = pgbase; - data->args.pages = &pages[curpage]; + data->args.pages = data->pagevec; data->args.count = bytes; data->res.fattr = &data->fattr; data->res.eof = 0; @@ -374,33 +341,35 @@ static void nfs_direct_read_schedule(str bytes, (unsigned long long)data->args.offset); + started += bytes; + user_addr += bytes; pos += bytes; pgbase += bytes; - curpage += pgbase >> PAGE_SHIFT; pgbase &= ~PAGE_MASK; count -= bytes; } while (count != 0); - BUG_ON(!list_empty(list)); + + if (put_dreq(dreq)) + nfs_direct_complete(dreq); + + if (started) + return 0; + return result < 0 ? (ssize_t) result : -EFAULT; } -static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, unsigned int nr_pages) +static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos) { - ssize_t result; + ssize_t result = 0; sigset_t oldset; struct inode *inode = iocb->ki_filp->f_mapping->host; struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_direct_req *dreq; - dreq = nfs_direct_read_alloc(count, NFS_SERVER(inode)->rsize); + dreq = nfs_direct_req_alloc(); if (!dreq) return -ENOMEM; - dreq->user_addr = user_addr; - dreq->user_count = count; - dreq->pos = pos; - dreq->pages = pages; - dreq->npages = nr_pages; dreq->inode = inode; dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data); if (!is_sync_kiocb(iocb)) @@ -408,8 +377,9 @@ static ssize_t nfs_direct_read(struct ki nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count); rpc_clnt_sigmask(clnt, &oldset); - nfs_direct_read_schedule(dreq); - result = nfs_direct_wait(dreq); + result = nfs_direct_read_schedule(dreq, user_addr, count, pos); + if (!result) + result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); return result; @@ -417,10 +387,10 @@ static ssize_t nfs_direct_read(struct ki static void nfs_direct_free_writedata(struct nfs_direct_req *dreq) { - list_splice_init(&dreq->rewrite_list, &dreq->list); - while (!list_empty(&dreq->list)) { - struct nfs_write_data *data = list_entry(dreq->list.next, struct nfs_write_data, pages); + while (!list_empty(&dreq->rewrite_list)) { + struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages); list_del(&data->pages); + nfs_direct_release_pages(data->pagevec, data->npages); nfs_writedata_release(data); } } @@ -428,14 +398,51 @@ static void nfs_direct_free_writedata(st #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) { - struct list_head *pos; + struct inode *inode = dreq->inode; + struct list_head *p; + struct nfs_write_data *data; - list_splice_init(&dreq->rewrite_list, &dreq->list); - list_for_each(pos, &dreq->list) - dreq->outstanding++; dreq->count = 0; + get_dreq(dreq); + + list_for_each(p, &dreq->rewrite_list) { + data = list_entry(p, struct nfs_write_data, pages); + + get_dreq(dreq); + + /* + * Reset data->res. + */ + nfs_fattr_init(&data->fattr); + data->res.count = data->args.count; + memset(&data->verf, 0, sizeof(data->verf)); + + /* + * Reuse data->task; data->args should not have changed + * since the original request was sent. + */ + rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, + &nfs_write_direct_ops, data); + NFS_PROTO(inode)->write_setup(data, FLUSH_STABLE); + + data->task.tk_priority = RPC_PRIORITY_NORMAL; + data->task.tk_cookie = (unsigned long) inode; + + /* + * We're called via an RPC callback, so BKL is already held. + */ + rpc_execute(&data->task); + + dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n", + data->task.tk_pid, + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + data->args.count, + (unsigned long long)data->args.offset); + } - nfs_direct_write_schedule(dreq, FLUSH_STABLE); + if (put_dreq(dreq)) + nfs_direct_write_complete(dreq, inode); } static void nfs_direct_commit_result(struct rpc_task *task, void *calldata) @@ -472,8 +479,8 @@ static void nfs_direct_commit_schedule(s data->cred = dreq->ctx->cred; data->args.fh = NFS_FH(data->inode); - data->args.offset = dreq->pos; - data->args.count = dreq->user_count; + data->args.offset = 0; + data->args.count = 0; data->res.count = 0; data->res.fattr = &data->fattr; data->res.verf = &data->verf; @@ -535,47 +542,6 @@ static void nfs_direct_write_complete(st } #endif -static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize) -{ - struct list_head *list; - struct nfs_direct_req *dreq; - unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - dreq = nfs_direct_req_alloc(); - if (!dreq) - return NULL; - - list = &dreq->list; - for(;;) { - struct nfs_write_data *data = nfs_writedata_alloc(wpages); - - if (unlikely(!data)) { - while (!list_empty(list)) { - data = list_entry(list->next, - struct nfs_write_data, pages); - list_del(&data->pages); - nfs_writedata_free(data); - } - kref_put(&dreq->kref, nfs_direct_req_release); - return NULL; - } - - INIT_LIST_HEAD(&data->pages); - list_add(&data->pages, list); - - data->req = (struct nfs_page *) dreq; - dreq->outstanding++; - if (nbytes <= wsize) - break; - nbytes -= wsize; - } - - nfs_alloc_commit_data(dreq); - - kref_get(&dreq->kref); - return dreq; -} - static void nfs_direct_write_result(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; @@ -605,8 +571,6 @@ static void nfs_direct_write_result(stru } } } - /* In case we have to resend */ - data->args.stable = NFS_FILE_SYNC; spin_unlock(&dreq->lock); } @@ -620,14 +584,8 @@ static void nfs_direct_write_release(voi struct nfs_write_data *data = calldata; struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; - spin_lock(&dreq->lock); - if (--dreq->outstanding) { - spin_unlock(&dreq->lock); - return; - } - spin_unlock(&dreq->lock); - - nfs_direct_write_complete(dreq, data->inode); + if (put_dreq(dreq)) + nfs_direct_write_complete(dreq, data->inode); } static const struct rpc_call_ops nfs_write_direct_ops = { @@ -636,41 +594,62 @@ static const struct rpc_call_ops nfs_wri }; /* - * For each nfs_write_data struct that was allocated on the list, dispatch - * an NFS WRITE operation + * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE + * operation. If nfs_writedata_alloc() or get_user_pages() fails, + * bail and stop sending more writes. Write length accounting is + * handled automatically by nfs_direct_write_result(). Otherwise, if + * no requests have been sent, just return an error. */ -static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync) +static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync) { struct nfs_open_context *ctx = dreq->ctx; struct inode *inode = ctx->dentry->d_inode; - struct list_head *list = &dreq->list; - struct page **pages = dreq->pages; - size_t count = dreq->user_count; - loff_t pos = dreq->pos; size_t wsize = NFS_SERVER(inode)->wsize; - unsigned int curpage, pgbase; + unsigned int wpages = nfs_max_pages(wsize); + unsigned int pgbase; + int result; + ssize_t started = 0; - curpage = 0; - pgbase = dreq->user_addr & ~PAGE_MASK; + get_dreq(dreq); + + pgbase = user_addr & ~PAGE_MASK; do { struct nfs_write_data *data; size_t bytes; + result = -ENOMEM; + data = nfs_writedata_alloc(wpages); + if (unlikely(!data)) + break; + bytes = wsize; if (count < wsize) bytes = count; - BUG_ON(list_empty(list)); - data = list_entry(list->next, struct nfs_write_data, pages); + data->npages = nfs_direct_count_pages(user_addr, bytes); + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, + data->npages, 0, 0, data->pagevec, NULL); + up_read(¤t->mm->mmap_sem); + if (unlikely(result < data->npages)) { + if (result > 0) + nfs_direct_release_pages(data->pagevec, result); + nfs_writedata_release(data); + break; + } + + get_dreq(dreq); + list_move_tail(&data->pages, &dreq->rewrite_list); + data->req = (struct nfs_page *) dreq; data->inode = inode; data->cred = ctx->cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; data->args.offset = pos; data->args.pgbase = pgbase; - data->args.pages = &pages[curpage]; + data->args.pages = data->pagevec; data->args.count = bytes; data->res.fattr = &data->fattr; data->res.count = bytes; @@ -694,19 +673,26 @@ static void nfs_direct_write_schedule(st bytes, (unsigned long long)data->args.offset); + started += bytes; + user_addr += bytes; pos += bytes; pgbase += bytes; - curpage += pgbase >> PAGE_SHIFT; pgbase &= ~PAGE_MASK; count -= bytes; } while (count != 0); - BUG_ON(!list_empty(list)); + + if (put_dreq(dreq)) + nfs_direct_write_complete(dreq, inode); + + if (started) + return 0; + return result < 0 ? (ssize_t) result : -EFAULT; } -static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, int nr_pages) +static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos) { - ssize_t result; + ssize_t result = 0; sigset_t oldset; struct inode *inode = iocb->ki_filp->f_mapping->host; struct rpc_clnt *clnt = NFS_CLIENT(inode); @@ -714,17 +700,14 @@ static ssize_t nfs_direct_write(struct k size_t wsize = NFS_SERVER(inode)->wsize; int sync = 0; - dreq = nfs_direct_write_alloc(count, wsize); + dreq = nfs_direct_req_alloc(); if (!dreq) return -ENOMEM; + nfs_alloc_commit_data(dreq); + if (dreq->commit_data == NULL || count < wsize) sync = FLUSH_STABLE; - dreq->user_addr = user_addr; - dreq->user_count = count; - dreq->pos = pos; - dreq->pages = pages; - dreq->npages = nr_pages; dreq->inode = inode; dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data); if (!is_sync_kiocb(iocb)) @@ -735,8 +718,9 @@ static ssize_t nfs_direct_write(struct k nfs_begin_data_update(inode); rpc_clnt_sigmask(clnt, &oldset); - nfs_direct_write_schedule(dreq, sync); - result = nfs_direct_wait(dreq); + result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync); + if (!result) + result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); return result; @@ -766,8 +750,6 @@ static ssize_t nfs_direct_write(struct k ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos) { ssize_t retval = -EINVAL; - int page_count; - struct page **pages; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -789,14 +771,7 @@ ssize_t nfs_file_direct_read(struct kioc if (retval) goto out; - retval = nfs_get_user_pages(READ, (unsigned long) buf, - count, &pages); - if (retval < 0) - goto out; - page_count = retval; - - retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos, - pages, page_count); + retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos); if (retval > 0) iocb->ki_pos = pos + retval; @@ -832,8 +807,6 @@ out: ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos) { ssize_t retval; - int page_count; - struct page **pages; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -861,14 +834,7 @@ ssize_t nfs_file_direct_write(struct kio if (retval) goto out; - retval = nfs_get_user_pages(WRITE, (unsigned long) buf, - count, &pages); - if (retval < 0) - goto out; - page_count = retval; - - retval = nfs_direct_write(iocb, (unsigned long) buf, count, - pos, pages, page_count); + retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos); /* * XXX: nfs_end_data_update() already ensures this file's @@ -892,7 +858,7 @@ out: * nfs_init_directcache - create a slab cache for nfs_direct_req structures * */ -int nfs_init_directcache(void) +int __init nfs_init_directcache(void) { nfs_direct_cachep = kmem_cache_create("nfs_direct_cache", sizeof(struct nfs_direct_req), @@ -906,7 +872,7 @@ int nfs_init_directcache(void) } /** - * nfs_init_directcache - destroy the slab cache for nfs_direct_req structures + * nfs_destroy_directcache - destroy the slab cache for nfs_direct_req structures * */ void nfs_destroy_directcache(void) diff --git a/fs/nfs/file.c b/fs/nfs/file.c index fade02c..cc2b874 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -43,7 +43,7 @@ static int nfs_file_mmap(struct file *, static ssize_t nfs_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); static ssize_t nfs_file_read(struct kiocb *, char __user *, size_t, loff_t); static ssize_t nfs_file_write(struct kiocb *, const char __user *, size_t, loff_t); -static int nfs_file_flush(struct file *); +static int nfs_file_flush(struct file *, fl_owner_t id); static int nfs_fsync(struct file *, struct dentry *dentry, int datasync); static int nfs_check_flags(int flags); static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl); @@ -127,23 +127,6 @@ nfs_file_release(struct inode *inode, st } /** - * nfs_revalidate_file - Revalidate the page cache & related metadata - * @inode - pointer to inode struct - * @file - pointer to file - */ -static int nfs_revalidate_file(struct inode *inode, struct file *filp) -{ - struct nfs_inode *nfsi = NFS_I(inode); - int retval = 0; - - if ((nfsi->cache_validity & (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATTR)) - || nfs_attribute_timeout(inode)) - retval = __nfs_revalidate_inode(NFS_SERVER(inode), inode); - nfs_revalidate_mapping(inode, filp->f_mapping); - return 0; -} - -/** * nfs_revalidate_size - Revalidate the file size * @inode - pointer to inode struct * @file - pointer to struct file @@ -188,7 +171,7 @@ static loff_t nfs_file_llseek(struct fil * */ static int -nfs_file_flush(struct file *file) +nfs_file_flush(struct file *file, fl_owner_t id) { struct nfs_open_context *ctx = (struct nfs_open_context *)file->private_data; struct inode *inode = file->f_dentry->d_inode; @@ -228,7 +211,7 @@ #endif dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long) pos); - result = nfs_revalidate_file(inode, iocb->ki_filp); + result = nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); nfs_add_stats(inode, NFSIOS_NORMALREADBYTES, count); if (!result) result = generic_file_aio_read(iocb, buf, count, pos); @@ -247,7 +230,7 @@ nfs_file_sendfile(struct file *filp, lof dentry->d_parent->d_name.name, dentry->d_name.name, (unsigned long) count, (unsigned long long) *ppos); - res = nfs_revalidate_file(inode, filp); + res = nfs_revalidate_mapping(inode, filp->f_mapping); if (!res) res = generic_file_sendfile(filp, ppos, count, actor, target); return res; @@ -263,7 +246,7 @@ nfs_file_mmap(struct file * file, struct dfprintk(VFS, "nfs: mmap(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); - status = nfs_revalidate_file(inode, file); + status = nfs_revalidate_mapping(inode, file->f_mapping); if (!status) status = generic_file_mmap(file, vma); return status; @@ -320,7 +303,11 @@ static int nfs_commit_write(struct file static void nfs_invalidate_page(struct page *page, unsigned long offset) { - /* FIXME: we really should cancel any unstarted writes on this page */ + struct inode *inode = page->mapping->host; + + /* Cancel any unstarted writes on this page */ + if (offset == 0) + nfs_sync_inode_wait(inode, page->index, 1, FLUSH_INVALIDATE); } static int nfs_release_page(struct page *page, gfp_t gfp) @@ -328,7 +315,7 @@ static int nfs_release_page(struct page return !nfs_wb_page(page->mapping->host, page); } -struct address_space_operations nfs_file_aops = { +const struct address_space_operations nfs_file_aops = { .readpage = nfs_readpage, .readpages = nfs_readpages, .set_page_dirty = __set_page_dirty_nobuffers, @@ -373,7 +360,6 @@ #endif if (result) goto out; } - nfs_revalidate_mapping(inode, iocb->ki_filp->f_mapping); result = count; if (!count) diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 3fab5b0..b81e7ed 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index d0b991a..d349fb2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -13,7 +13,6 @@ * */ -#include #include #include @@ -36,6 +35,8 @@ #include #include #include #include +#include +#include #include #include @@ -44,89 +45,17 @@ #include "nfs4_fs.h" #include "callback.h" #include "delegation.h" #include "iostat.h" +#include "internal.h" #define NFSDBG_FACILITY NFSDBG_VFS #define NFS_PARANOIA 1 -/* Maximum number of readahead requests - * FIXME: this should really be a sysctl so that users may tune it to suit - * their needs. People that do NFS over a slow network, might for - * instance want to reduce it to something closer to 1 for improved - * interactive response. - */ -#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) - static void nfs_invalidate_inode(struct inode *); static int nfs_update_inode(struct inode *, struct nfs_fattr *); -static struct inode *nfs_alloc_inode(struct super_block *sb); -static void nfs_destroy_inode(struct inode *); -static int nfs_write_inode(struct inode *,int); -static void nfs_delete_inode(struct inode *); -static void nfs_clear_inode(struct inode *); -static void nfs_umount_begin(struct super_block *); -static int nfs_statfs(struct super_block *, struct kstatfs *); -static int nfs_show_options(struct seq_file *, struct vfsmount *); -static int nfs_show_stats(struct seq_file *, struct vfsmount *); static void nfs_zap_acl_cache(struct inode *); -static struct rpc_program nfs_program; - -static struct super_operations nfs_sops = { - .alloc_inode = nfs_alloc_inode, - .destroy_inode = nfs_destroy_inode, - .write_inode = nfs_write_inode, - .delete_inode = nfs_delete_inode, - .statfs = nfs_statfs, - .clear_inode = nfs_clear_inode, - .umount_begin = nfs_umount_begin, - .show_options = nfs_show_options, - .show_stats = nfs_show_stats, -}; - -/* - * RPC cruft for NFS - */ -static struct rpc_stat nfs_rpcstat = { - .program = &nfs_program -}; -static struct rpc_version * nfs_version[] = { - NULL, - NULL, - &nfs_version2, -#if defined(CONFIG_NFS_V3) - &nfs_version3, -#elif defined(CONFIG_NFS_V4) - NULL, -#endif -#if defined(CONFIG_NFS_V4) - &nfs_version4, -#endif -}; - -static struct rpc_program nfs_program = { - .name = "nfs", - .number = NFS_PROGRAM, - .nrvers = ARRAY_SIZE(nfs_version), - .version = nfs_version, - .stats = &nfs_rpcstat, - .pipe_dir_name = "/nfs", -}; - -#ifdef CONFIG_NFS_V3_ACL -static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; -static struct rpc_version * nfsacl_version[] = { - [3] = &nfsacl_version3, -}; - -struct rpc_program nfsacl_program = { - .name = "nfsacl", - .number = NFS_ACL_PROGRAM, - .nrvers = ARRAY_SIZE(nfsacl_version), - .version = nfsacl_version, - .stats = &nfsacl_rpcstat, -}; -#endif /* CONFIG_NFS_V3_ACL */ +static kmem_cache_t * nfs_inode_cachep; static inline unsigned long nfs_fattr_to_ino_t(struct nfs_fattr *fattr) @@ -134,8 +63,7 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fat return nfs_fileid_to_ino_t(fattr->fileid); } -static int -nfs_write_inode(struct inode *inode, int sync) +int nfs_write_inode(struct inode *inode, int sync) { int flags = sync ? FLUSH_SYNC : 0; int ret; @@ -146,31 +74,15 @@ nfs_write_inode(struct inode *inode, int return 0; } -static void -nfs_delete_inode(struct inode * inode) +void nfs_clear_inode(struct inode *inode) { - dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino); - - truncate_inode_pages(&inode->i_data, 0); + struct nfs_inode *nfsi = NFS_I(inode); + struct rpc_cred *cred; - nfs_wb_all(inode); /* * The following should never happen... */ - if (nfs_have_writebacks(inode)) { - printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino); - } - - clear_inode(inode); -} - -static void -nfs_clear_inode(struct inode *inode) -{ - struct nfs_inode *nfsi = NFS_I(inode); - struct rpc_cred *cred; - - nfs_wb_all(inode); + BUG_ON(nfs_have_writebacks(inode)); BUG_ON (!list_empty(&nfsi->open_files)); nfs_zap_acl_cache(inode); cred = nfsi->cache_access.cred; @@ -179,554 +91,6 @@ nfs_clear_inode(struct inode *inode) BUG_ON(atomic_read(&nfsi->data_updates) != 0); } -void -nfs_umount_begin(struct super_block *sb) -{ - struct rpc_clnt *rpc = NFS_SB(sb)->client; - - /* -EIO all pending I/O */ - if (!IS_ERR(rpc)) - rpc_killall_tasks(rpc); - rpc = NFS_SB(sb)->client_acl; - if (!IS_ERR(rpc)) - rpc_killall_tasks(rpc); -} - - -static inline unsigned long -nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp) -{ - /* make sure blocksize is a power of two */ - if ((bsize & (bsize - 1)) || nrbitsp) { - unsigned char nrbits; - - for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--) - ; - bsize = 1 << nrbits; - if (nrbitsp) - *nrbitsp = nrbits; - } - - return bsize; -} - -/* - * Calculate the number of 512byte blocks used. - */ -static inline unsigned long -nfs_calc_block_size(u64 tsize) -{ - loff_t used = (tsize + 511) >> 9; - return (used > ULONG_MAX) ? ULONG_MAX : used; -} - -/* - * Compute and set NFS server blocksize - */ -static inline unsigned long -nfs_block_size(unsigned long bsize, unsigned char *nrbitsp) -{ - if (bsize < NFS_MIN_FILE_IO_SIZE) - bsize = NFS_DEF_FILE_IO_SIZE; - else if (bsize >= NFS_MAX_FILE_IO_SIZE) - bsize = NFS_MAX_FILE_IO_SIZE; - - return nfs_block_bits(bsize, nrbitsp); -} - -/* - * Obtain the root inode of the file system. - */ -static struct inode * -nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo) -{ - struct nfs_server *server = NFS_SB(sb); - int error; - - error = server->rpc_ops->getroot(server, rootfh, fsinfo); - if (error < 0) { - dprintk("nfs_get_root: getattr error = %d\n", -error); - return ERR_PTR(error); - } - - return nfs_fhget(sb, rootfh, fsinfo->fattr); -} - -/* - * Do NFS version-independent mount processing, and sanity checking - */ -static int -nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) -{ - struct nfs_server *server; - struct inode *root_inode; - struct nfs_fattr fattr; - struct nfs_fsinfo fsinfo = { - .fattr = &fattr, - }; - struct nfs_pathconf pathinfo = { - .fattr = &fattr, - }; - int no_root_error = 0; - unsigned long max_rpc_payload; - - /* We probably want something more informative here */ - snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); - - server = NFS_SB(sb); - - sb->s_magic = NFS_SUPER_MAGIC; - - server->io_stats = nfs_alloc_iostats(); - if (server->io_stats == NULL) - return -ENOMEM; - - root_inode = nfs_get_root(sb, &server->fh, &fsinfo); - /* Did getting the root inode fail? */ - if (IS_ERR(root_inode)) { - no_root_error = PTR_ERR(root_inode); - goto out_no_root; - } - sb->s_root = d_alloc_root(root_inode); - if (!sb->s_root) { - no_root_error = -ENOMEM; - goto out_no_root; - } - sb->s_root->d_op = server->rpc_ops->dentry_ops; - - /* mount time stamp, in seconds */ - server->mount_time = jiffies; - - /* Get some general file system info */ - if (server->namelen == 0 && - server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) - server->namelen = pathinfo.max_namelen; - /* Work out a lot of parameters */ - if (server->rsize == 0) - server->rsize = nfs_block_size(fsinfo.rtpref, NULL); - if (server->wsize == 0) - server->wsize = nfs_block_size(fsinfo.wtpref, NULL); - - if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax) - server->rsize = nfs_block_size(fsinfo.rtmax, NULL); - if (fsinfo.wtmax >= 512 && server->wsize > fsinfo.wtmax) - server->wsize = nfs_block_size(fsinfo.wtmax, NULL); - - max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); - if (server->rsize > max_rpc_payload) - server->rsize = max_rpc_payload; - if (server->rsize > NFS_MAX_FILE_IO_SIZE) - server->rsize = NFS_MAX_FILE_IO_SIZE; - server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - if (server->wsize > max_rpc_payload) - server->wsize = max_rpc_payload; - if (server->wsize > NFS_MAX_FILE_IO_SIZE) - server->wsize = NFS_MAX_FILE_IO_SIZE; - server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - if (sb->s_blocksize == 0) - sb->s_blocksize = nfs_block_bits(server->wsize, - &sb->s_blocksize_bits); - server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL); - - server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); - if (server->dtsize > PAGE_CACHE_SIZE) - server->dtsize = PAGE_CACHE_SIZE; - if (server->dtsize > server->rsize) - server->dtsize = server->rsize; - - if (server->flags & NFS_MOUNT_NOAC) { - server->acregmin = server->acregmax = 0; - server->acdirmin = server->acdirmax = 0; - sb->s_flags |= MS_SYNCHRONOUS; - } - server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; - - sb->s_maxbytes = fsinfo.maxfilesize; - if (sb->s_maxbytes > MAX_LFS_FILESIZE) - sb->s_maxbytes = MAX_LFS_FILESIZE; - - server->client->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; - server->client->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; - - /* We're airborne Set socket buffersize */ - rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); - return 0; - /* Yargs. It didn't work out. */ -out_no_root: - dprintk("nfs_sb_init: get root inode failed: errno %d\n", -no_root_error); - if (!IS_ERR(root_inode)) - iput(root_inode); - return no_root_error; -} - -static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, unsigned int timeo, unsigned int retrans) -{ - to->to_initval = timeo * HZ / 10; - to->to_retries = retrans; - if (!to->to_retries) - to->to_retries = 2; - - switch (proto) { - case IPPROTO_TCP: - if (!to->to_initval) - to->to_initval = 60 * HZ; - if (to->to_initval > NFS_MAX_TCP_TIMEOUT) - to->to_initval = NFS_MAX_TCP_TIMEOUT; - to->to_increment = to->to_initval; - to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); - to->to_exponential = 0; - break; - case IPPROTO_UDP: - default: - if (!to->to_initval) - to->to_initval = 11 * HZ / 10; - if (to->to_initval > NFS_MAX_UDP_TIMEOUT) - to->to_initval = NFS_MAX_UDP_TIMEOUT; - to->to_maxval = NFS_MAX_UDP_TIMEOUT; - to->to_exponential = 1; - break; - } -} - -/* - * Create an RPC client handle. - */ -static struct rpc_clnt * -nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) -{ - struct rpc_timeout timeparms; - struct rpc_xprt *xprt = NULL; - struct rpc_clnt *clnt = NULL; - int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; - - nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans); - - server->retrans_timeo = timeparms.to_initval; - server->retrans_count = timeparms.to_retries; - - /* create transport and client */ - xprt = xprt_create_proto(proto, &server->addr, &timeparms); - if (IS_ERR(xprt)) { - dprintk("%s: cannot create RPC transport. Error = %ld\n", - __FUNCTION__, PTR_ERR(xprt)); - return (struct rpc_clnt *)xprt; - } - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - server->rpc_ops->version, data->pseudoflavor); - if (IS_ERR(clnt)) { - dprintk("%s: cannot create RPC client. Error = %ld\n", - __FUNCTION__, PTR_ERR(xprt)); - goto out_fail; - } - - clnt->cl_intr = 1; - clnt->cl_softrtry = 1; - - return clnt; - -out_fail: - return clnt; -} - -/* - * The way this works is that the mount process passes a structure - * in the data argument which contains the server's IP address - * and the root file handle obtained from the server's mount - * daemon. We stash these away in the private superblock fields. - */ -static int -nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) -{ - struct nfs_server *server; - rpc_authflavor_t authflavor; - - server = NFS_SB(sb); - sb->s_blocksize_bits = 0; - sb->s_blocksize = 0; - if (data->bsize) - sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); - if (data->rsize) - server->rsize = nfs_block_size(data->rsize, NULL); - if (data->wsize) - server->wsize = nfs_block_size(data->wsize, NULL); - server->flags = data->flags & NFS_MOUNT_FLAGMASK; - - server->acregmin = data->acregmin*HZ; - server->acregmax = data->acregmax*HZ; - server->acdirmin = data->acdirmin*HZ; - server->acdirmax = data->acdirmax*HZ; - - /* Start lockd here, before we might error out */ - if (!(server->flags & NFS_MOUNT_NONLM)) - lockd_up(); - - server->namelen = data->namlen; - server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL); - if (!server->hostname) - return -ENOMEM; - strcpy(server->hostname, data->hostname); - - /* Check NFS protocol revision and initialize RPC op vector - * and file handle pool. */ -#ifdef CONFIG_NFS_V3 - if (server->flags & NFS_MOUNT_VER3) { - server->rpc_ops = &nfs_v3_clientops; - server->caps |= NFS_CAP_READDIRPLUS; - } else { - server->rpc_ops = &nfs_v2_clientops; - } -#else - server->rpc_ops = &nfs_v2_clientops; -#endif - - /* Fill in pseudoflavor for mount version < 5 */ - if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) - data->pseudoflavor = RPC_AUTH_UNIX; - authflavor = data->pseudoflavor; /* save for sb_init() */ - /* XXX maybe we want to add a server->pseudoflavor field */ - - /* Create RPC client handles */ - server->client = nfs_create_client(server, data); - if (IS_ERR(server->client)) - return PTR_ERR(server->client); - /* RFC 2623, sec 2.3.2 */ - if (authflavor != RPC_AUTH_UNIX) { - struct rpc_auth *auth; - - server->client_sys = rpc_clone_client(server->client); - if (IS_ERR(server->client_sys)) - return PTR_ERR(server->client_sys); - auth = rpcauth_create(RPC_AUTH_UNIX, server->client_sys); - if (IS_ERR(auth)) - return PTR_ERR(auth); - } else { - atomic_inc(&server->client->cl_count); - server->client_sys = server->client; - } - if (server->flags & NFS_MOUNT_VER3) { -#ifdef CONFIG_NFS_V3_ACL - if (!(server->flags & NFS_MOUNT_NOACL)) { - server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); - /* No errors! Assume that Sun nfsacls are supported */ - if (!IS_ERR(server->client_acl)) - server->caps |= NFS_CAP_ACLS; - } -#else - server->flags &= ~NFS_MOUNT_NOACL; -#endif /* CONFIG_NFS_V3_ACL */ - /* - * The VFS shouldn't apply the umask to mode bits. We will - * do so ourselves when necessary. - */ - sb->s_flags |= MS_POSIXACL; - if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) - server->namelen = NFS3_MAXNAMLEN; - sb->s_time_gran = 1; - } else { - if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) - server->namelen = NFS2_MAXNAMLEN; - } - - sb->s_op = &nfs_sops; - return nfs_sb_init(sb, authflavor); -} - -static int -nfs_statfs(struct super_block *sb, struct kstatfs *buf) -{ - struct nfs_server *server = NFS_SB(sb); - unsigned char blockbits; - unsigned long blockres; - struct nfs_fh *rootfh = NFS_FH(sb->s_root->d_inode); - struct nfs_fattr fattr; - struct nfs_fsstat res = { - .fattr = &fattr, - }; - int error; - - lock_kernel(); - - error = server->rpc_ops->statfs(server, rootfh, &res); - buf->f_type = NFS_SUPER_MAGIC; - if (error < 0) - goto out_err; - - /* - * Current versions of glibc do not correctly handle the - * case where f_frsize != f_bsize. Eventually we want to - * report the value of wtmult in this field. - */ - buf->f_frsize = sb->s_blocksize; - - /* - * On most *nix systems, f_blocks, f_bfree, and f_bavail - * are reported in units of f_frsize. Linux hasn't had - * an f_frsize field in its statfs struct until recently, - * thus historically Linux's sys_statfs reports these - * fields in units of f_bsize. - */ - buf->f_bsize = sb->s_blocksize; - blockbits = sb->s_blocksize_bits; - blockres = (1 << blockbits) - 1; - buf->f_blocks = (res.tbytes + blockres) >> blockbits; - buf->f_bfree = (res.fbytes + blockres) >> blockbits; - buf->f_bavail = (res.abytes + blockres) >> blockbits; - - buf->f_files = res.tfiles; - buf->f_ffree = res.afiles; - - buf->f_namelen = server->namelen; - out: - unlock_kernel(); - return 0; - - out_err: - dprintk("%s: statfs error = %d\n", __FUNCTION__, -error); - buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; - goto out; - -} - -static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults) -{ - static struct proc_nfs_info { - int flag; - char *str; - char *nostr; - } nfs_info[] = { - { NFS_MOUNT_SOFT, ",soft", ",hard" }, - { NFS_MOUNT_INTR, ",intr", "" }, - { NFS_MOUNT_NOCTO, ",nocto", "" }, - { NFS_MOUNT_NOAC, ",noac", "" }, - { NFS_MOUNT_NONLM, ",nolock", "" }, - { NFS_MOUNT_NOACL, ",noacl", "" }, - { 0, NULL, NULL } - }; - struct proc_nfs_info *nfs_infop; - char buf[12]; - char *proto; - - seq_printf(m, ",vers=%d", nfss->rpc_ops->version); - seq_printf(m, ",rsize=%d", nfss->rsize); - seq_printf(m, ",wsize=%d", nfss->wsize); - if (nfss->acregmin != 3*HZ || showdefaults) - seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ); - if (nfss->acregmax != 60*HZ || showdefaults) - seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ); - if (nfss->acdirmin != 30*HZ || showdefaults) - seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ); - if (nfss->acdirmax != 60*HZ || showdefaults) - seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ); - for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) { - if (nfss->flags & nfs_infop->flag) - seq_puts(m, nfs_infop->str); - else - seq_puts(m, nfs_infop->nostr); - } - switch (nfss->client->cl_xprt->prot) { - case IPPROTO_TCP: - proto = "tcp"; - break; - case IPPROTO_UDP: - proto = "udp"; - break; - default: - snprintf(buf, sizeof(buf), "%u", nfss->client->cl_xprt->prot); - proto = buf; - } - seq_printf(m, ",proto=%s", proto); - seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ); - seq_printf(m, ",retrans=%u", nfss->retrans_count); -} - -static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) -{ - struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); - - nfs_show_mount_options(m, nfss, 0); - - seq_puts(m, ",addr="); - seq_escape(m, nfss->hostname, " \t\n\\"); - - return 0; -} - -static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) -{ - int i, cpu; - struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); - struct rpc_auth *auth = nfss->client->cl_auth; - struct nfs_iostats totals = { }; - - seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS); - - /* - * Display all mount option settings - */ - seq_printf(m, "\n\topts:\t"); - seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw"); - seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : ""); - seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : ""); - seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : ""); - nfs_show_mount_options(m, nfss, 1); - - seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ); - - seq_printf(m, "\n\tcaps:\t"); - seq_printf(m, "caps=0x%x", nfss->caps); - seq_printf(m, ",wtmult=%d", nfss->wtmult); - seq_printf(m, ",dtsize=%d", nfss->dtsize); - seq_printf(m, ",bsize=%d", nfss->bsize); - seq_printf(m, ",namelen=%d", nfss->namelen); - -#ifdef CONFIG_NFS_V4 - if (nfss->rpc_ops->version == 4) { - seq_printf(m, "\n\tnfsv4:\t"); - seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); - seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); - seq_printf(m, ",acl=0x%x", nfss->acl_bitmask); - } -#endif - - /* - * Display security flavor in effect for this mount - */ - seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor); - if (auth->au_flavor) - seq_printf(m, ",pseudoflavor=%d", auth->au_flavor); - - /* - * Display superblock I/O counters - */ - for_each_possible_cpu(cpu) { - struct nfs_iostats *stats; - - preempt_disable(); - stats = per_cpu_ptr(nfss->io_stats, cpu); - - for (i = 0; i < __NFSIOS_COUNTSMAX; i++) - totals.events[i] += stats->events[i]; - for (i = 0; i < __NFSIOS_BYTESMAX; i++) - totals.bytes[i] += stats->bytes[i]; - - preempt_enable(); - } - - seq_printf(m, "\n\tevents:\t"); - for (i = 0; i < __NFSIOS_COUNTSMAX; i++) - seq_printf(m, "%lu ", totals.events[i]); - seq_printf(m, "\n\tbytes:\t"); - for (i = 0; i < __NFSIOS_BYTESMAX; i++) - seq_printf(m, "%Lu ", totals.bytes[i]); - seq_printf(m, "\n"); - - rpc_print_iostats(m, nfss->client); - - return 0; -} - /** * nfs_sync_mapping - helper to flush all mmapped dirty data to disk */ @@ -889,6 +253,14 @@ nfs_fhget(struct super_block *sb, struct if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS) && fattr->size <= NFS_LIMIT_READDIRPLUS) set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); + /* Deal with crossing mountpoints */ + if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) { + if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) + inode->i_op = &nfs_referral_inode_operations; + else + inode->i_op = &nfs_mountpoint_inode_operations; + inode->i_fop = NULL; + } } else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; else @@ -1207,6 +579,7 @@ __nfs_revalidate_inode(struct nfs_server dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n", inode->i_sb->s_id, (long long)NFS_FILEID(inode)); + nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); lock_kernel(); if (!inode || is_bad_inode(inode)) goto out_nowait; @@ -1220,7 +593,7 @@ __nfs_revalidate_inode(struct nfs_server status = -ESTALE; /* Do we trust the cached ESTALE? */ if (NFS_ATTRTIMEO(inode) != 0) { - if (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ATIME)) { + if (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME)) { /* no */ } else goto out; @@ -1251,8 +624,6 @@ __nfs_revalidate_inode(struct nfs_server } spin_unlock(&inode->i_lock); - nfs_revalidate_mapping(inode, inode->i_mapping); - if (nfsi->cache_validity & NFS_INO_INVALID_ACL) nfs_zap_acl_cache(inode); @@ -1286,8 +657,7 @@ int nfs_attribute_timeout(struct inode * */ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) { - nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE); - if (!(NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) + if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR) && !nfs_attribute_timeout(inode)) return NFS_STALE(inode) ? -ESTALE : 0; return __nfs_revalidate_inode(server, inode); @@ -1298,9 +668,16 @@ int nfs_revalidate_inode(struct nfs_serv * @inode - pointer to host inode * @mapping - pointer to mapping */ -void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) +int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping) { struct nfs_inode *nfsi = NFS_I(inode); + int ret = 0; + + if (NFS_STALE(inode)) + ret = -ESTALE; + if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE) + || nfs_attribute_timeout(inode)) + ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode); if (nfsi->cache_validity & NFS_INO_INVALID_DATA) { nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE); @@ -1321,6 +698,7 @@ void nfs_revalidate_mapping(struct inode inode->i_sb->s_id, (long long)NFS_FILEID(inode)); } + return ret; } /** @@ -1360,12 +738,6 @@ static void nfs_wcc_update_inode(struct { struct nfs_inode *nfsi = NFS_I(inode); - if ((fattr->valid & NFS_ATTR_PRE_CHANGE) != 0 - && nfsi->change_attr == fattr->pre_change_attr) { - nfsi->change_attr = fattr->change_attr; - nfsi->cache_change_attribute = jiffies; - } - /* If we have atomic WCC data, we may update some attributes */ if ((fattr->valid & NFS_ATTR_WCC) != 0) { if (timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) { @@ -1399,9 +771,6 @@ static int nfs_check_inode_attributes(st int data_unstable; - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - /* Has the inode gone and changed behind our back? */ if (nfsi->fileid != fattr->fileid || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) { @@ -1414,20 +783,13 @@ static int nfs_check_inode_attributes(st /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); - if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0) { - if (nfsi->change_attr == fattr->change_attr) - goto out; - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; - if (!data_unstable) - nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE; - } + if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && + nfsi->change_attr != fattr->change_attr) + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; /* Verify a few of the more important attributes */ - if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; - if (!data_unstable) - nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE; - } + if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; cur_size = i_size_read(inode); new_isize = nfs_size_to_loff_t(fattr->size); @@ -1444,7 +806,6 @@ static int nfs_check_inode_attributes(st if (inode->i_nlink != fattr->nlink) nfsi->cache_validity |= NFS_INO_INVALID_ATTR; -out: if (!timespec_equal(&inode->i_atime, &fattr->atime)) nfsi->cache_validity |= NFS_INO_INVALID_ATIME; @@ -1470,7 +831,6 @@ int nfs_refresh_inode(struct inode *inod if ((fattr->valid & NFS_ATTR_FATTR) == 0) return 0; spin_lock(&inode->i_lock); - nfsi->cache_validity &= ~NFS_INO_REVAL_PAGECACHE; if (time_after(fattr->time_start, nfsi->last_updated)) status = nfs_update_inode(inode, fattr); else @@ -1495,7 +855,7 @@ int nfs_post_op_update_inode(struct inod spin_lock(&inode->i_lock); if (unlikely((fattr->valid & NFS_ATTR_FATTR) == 0)) { - nfsi->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS; + nfsi->cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE; goto out; } status = nfs_update_inode(inode, fattr); @@ -1518,6 +878,7 @@ out: */ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) { + struct nfs_server *server; struct nfs_inode *nfsi = NFS_I(inode); loff_t cur_isize, new_isize; unsigned int invalid = 0; @@ -1527,9 +888,6 @@ static int nfs_update_inode(struct inode __FUNCTION__, inode->i_sb->s_id, inode->i_ino, atomic_read(&inode->i_count), fattr->valid); - if ((fattr->valid & NFS_ATTR_FATTR) == 0) - return 0; - if (nfsi->fileid != fattr->fileid) goto out_fileid; @@ -1539,6 +897,12 @@ static int nfs_update_inode(struct inode if ((inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) goto out_changed; + server = NFS_SERVER(inode); + /* Update the fsid if and only if this is the root directory */ + if (inode == inode->i_sb->s_root->d_inode + && !nfs_fsid_equal(&server->fsid, &fattr->fsid)) + server->fsid = fattr->fsid; + /* * Update the read time so we don't revalidate too often. */ @@ -1548,7 +912,7 @@ static int nfs_update_inode(struct inode /* Are we racing with known updates of the metadata on the server? */ data_stable = nfs_verify_change_attribute(inode, fattr->time_start); if (data_stable) - nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME); + nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATIME); /* Do atomic weak cache consistency updates */ nfs_wcc_update_inode(inode, fattr); @@ -1612,15 +976,13 @@ static int nfs_update_inode(struct inode inode->i_blksize = fattr->du.nfs2.blocksize; } - if ((fattr->valid & NFS_ATTR_FATTR_V4)) { - if (nfsi->change_attr != fattr->change_attr) { - dprintk("NFS: change_attr change on server for file %s/%ld\n", - inode->i_sb->s_id, inode->i_ino); - nfsi->change_attr = fattr->change_attr; - invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; - nfsi->cache_change_attribute = jiffies; - } else - invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA); + if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 && + nfsi->change_attr != fattr->change_attr) { + dprintk("NFS: change_attr change on server for file %s/%ld\n", + inode->i_sb->s_id, inode->i_ino); + nfsi->change_attr = fattr->change_attr; + invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL; + nfsi->cache_change_attribute = jiffies; } /* Update attrtimeo value if we're out of the unstable period */ @@ -1668,190 +1030,15 @@ #endif goto out_err; } -/* - * File system information - */ - -static int nfs_set_super(struct super_block *s, void *data) -{ - s->s_fs_info = data; - return set_anon_super(s, data); -} - -static int nfs_compare_super(struct super_block *sb, void *data) -{ - struct nfs_server *server = data; - struct nfs_server *old = NFS_SB(sb); - - if (old->addr.sin_addr.s_addr != server->addr.sin_addr.s_addr) - return 0; - if (old->addr.sin_port != server->addr.sin_port) - return 0; - return !nfs_compare_fh(&old->fh, &server->fh); -} - -static struct super_block *nfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data) -{ - int error; - struct nfs_server *server = NULL; - struct super_block *s; - struct nfs_fh *root; - struct nfs_mount_data *data = raw_data; - - s = ERR_PTR(-EINVAL); - if (data == NULL) { - dprintk("%s: missing data argument\n", __FUNCTION__); - goto out_err; - } - if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) { - dprintk("%s: bad mount version\n", __FUNCTION__); - goto out_err; - } - switch (data->version) { - case 1: - data->namlen = 0; - case 2: - data->bsize = 0; - case 3: - if (data->flags & NFS_MOUNT_VER3) { - dprintk("%s: mount structure version %d does not support NFSv3\n", - __FUNCTION__, - data->version); - goto out_err; - } - data->root.size = NFS2_FHSIZE; - memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); - case 4: - if (data->flags & NFS_MOUNT_SECFLAVOUR) { - dprintk("%s: mount structure version %d does not support strong security\n", - __FUNCTION__, - data->version); - goto out_err; - } - case 5: - memset(data->context, 0, sizeof(data->context)); - } -#ifndef CONFIG_NFS_V3 - /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */ - s = ERR_PTR(-EPROTONOSUPPORT); - if (data->flags & NFS_MOUNT_VER3) { - dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__); - goto out_err; - } -#endif /* CONFIG_NFS_V3 */ - - s = ERR_PTR(-ENOMEM); - server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); - if (!server) - goto out_err; - /* Zero out the NFS state stuff */ - init_nfsv4_state(server); - server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); - - root = &server->fh; - if (data->flags & NFS_MOUNT_VER3) - root->size = data->root.size; - else - root->size = NFS2_FHSIZE; - s = ERR_PTR(-EINVAL); - if (root->size > sizeof(root->data)) { - dprintk("%s: invalid root filehandle\n", __FUNCTION__); - goto out_err; - } - memcpy(root->data, data->root.data, root->size); - - /* We now require that the mount process passes the remote address */ - memcpy(&server->addr, &data->addr, sizeof(server->addr)); - if (server->addr.sin_addr.s_addr == INADDR_ANY) { - dprintk("%s: mount program didn't pass remote address!\n", - __FUNCTION__); - goto out_err; - } - - /* Fire up rpciod if not yet running */ - s = ERR_PTR(rpciod_up()); - if (IS_ERR(s)) { - dprintk("%s: couldn't start rpciod! Error = %ld\n", - __FUNCTION__, PTR_ERR(s)); - goto out_err; - } - - s = sget(fs_type, nfs_compare_super, nfs_set_super, server); - if (IS_ERR(s) || s->s_root) - goto out_rpciod_down; - - s->s_flags = flags; - - error = nfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0); - if (error) { - up_write(&s->s_umount); - deactivate_super(s); - return ERR_PTR(error); - } - s->s_flags |= MS_ACTIVE; - return s; -out_rpciod_down: - rpciod_down(); -out_err: - kfree(server); - return s; -} - -static void nfs_kill_super(struct super_block *s) -{ - struct nfs_server *server = NFS_SB(s); - - kill_anon_super(s); - - if (!IS_ERR(server->client)) - rpc_shutdown_client(server->client); - if (!IS_ERR(server->client_sys)) - rpc_shutdown_client(server->client_sys); - if (!IS_ERR(server->client_acl)) - rpc_shutdown_client(server->client_acl); - - if (!(server->flags & NFS_MOUNT_NONLM)) - lockd_down(); /* release rpc.lockd */ - - rpciod_down(); /* release rpciod */ - - nfs_free_iostats(server->io_stats); - kfree(server->hostname); - kfree(server); -} - -static struct file_system_type nfs_fs_type = { - .owner = THIS_MODULE, - .name = "nfs", - .get_sb = nfs_get_sb, - .kill_sb = nfs_kill_super, - .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; #ifdef CONFIG_NFS_V4 -static void nfs4_clear_inode(struct inode *); - - -static struct super_operations nfs4_sops = { - .alloc_inode = nfs_alloc_inode, - .destroy_inode = nfs_destroy_inode, - .write_inode = nfs_write_inode, - .delete_inode = nfs_delete_inode, - .statfs = nfs_statfs, - .clear_inode = nfs4_clear_inode, - .umount_begin = nfs_umount_begin, - .show_options = nfs_show_options, - .show_stats = nfs_show_stats, -}; - /* * Clean out any remaining NFSv4 state that might be left over due * to open() calls that passed nfs_atomic_lookup, but failed to call * nfs_open(). */ -static void nfs4_clear_inode(struct inode *inode) +void nfs4_clear_inode(struct inode *inode) { struct nfs_inode *nfsi = NFS_I(inode); @@ -1875,357 +1062,9 @@ static void nfs4_clear_inode(struct inod nfs4_close_state(state, state->state); } } - - -static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) -{ - struct nfs_server *server; - struct nfs4_client *clp = NULL; - struct rpc_xprt *xprt = NULL; - struct rpc_clnt *clnt = NULL; - struct rpc_timeout timeparms; - rpc_authflavor_t authflavour; - int err = -EIO; - - sb->s_blocksize_bits = 0; - sb->s_blocksize = 0; - server = NFS_SB(sb); - if (data->rsize != 0) - server->rsize = nfs_block_size(data->rsize, NULL); - if (data->wsize != 0) - server->wsize = nfs_block_size(data->wsize, NULL); - server->flags = data->flags & NFS_MOUNT_FLAGMASK; - server->caps = NFS_CAP_ATOMIC_OPEN; - - server->acregmin = data->acregmin*HZ; - server->acregmax = data->acregmax*HZ; - server->acdirmin = data->acdirmin*HZ; - server->acdirmax = data->acdirmax*HZ; - - server->rpc_ops = &nfs_v4_clientops; - - nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans); - - server->retrans_timeo = timeparms.to_initval; - server->retrans_count = timeparms.to_retries; - - clp = nfs4_get_client(&server->addr.sin_addr); - if (!clp) { - dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__); - return -EIO; - } - - /* Now create transport and client */ - authflavour = RPC_AUTH_UNIX; - if (data->auth_flavourlen != 0) { - if (data->auth_flavourlen != 1) { - dprintk("%s: Invalid number of RPC auth flavours %d.\n", - __FUNCTION__, data->auth_flavourlen); - err = -EINVAL; - goto out_fail; - } - if (copy_from_user(&authflavour, data->auth_flavours, sizeof(authflavour))) { - err = -EFAULT; - goto out_fail; - } - } - - down_write(&clp->cl_sem); - if (IS_ERR(clp->cl_rpcclient)) { - xprt = xprt_create_proto(data->proto, &server->addr, &timeparms); - if (IS_ERR(xprt)) { - up_write(&clp->cl_sem); - err = PTR_ERR(xprt); - dprintk("%s: cannot create RPC transport. Error = %d\n", - __FUNCTION__, err); - goto out_fail; - } - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - server->rpc_ops->version, authflavour); - if (IS_ERR(clnt)) { - up_write(&clp->cl_sem); - err = PTR_ERR(clnt); - dprintk("%s: cannot create RPC client. Error = %d\n", - __FUNCTION__, err); - goto out_fail; - } - clnt->cl_intr = 1; - clnt->cl_softrtry = 1; - clp->cl_rpcclient = clnt; - memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); - nfs_idmap_new(clp); - } - list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); - clnt = rpc_clone_client(clp->cl_rpcclient); - if (!IS_ERR(clnt)) - server->nfs4_state = clp; - up_write(&clp->cl_sem); - clp = NULL; - - if (IS_ERR(clnt)) { - err = PTR_ERR(clnt); - dprintk("%s: cannot create RPC client. Error = %d\n", - __FUNCTION__, err); - return err; - } - - server->client = clnt; - - if (server->nfs4_state->cl_idmap == NULL) { - dprintk("%s: failed to create idmapper.\n", __FUNCTION__); - return -ENOMEM; - } - - if (clnt->cl_auth->au_flavor != authflavour) { - struct rpc_auth *auth; - - auth = rpcauth_create(authflavour, clnt); - if (IS_ERR(auth)) { - dprintk("%s: couldn't create credcache!\n", __FUNCTION__); - return PTR_ERR(auth); - } - } - - sb->s_time_gran = 1; - - sb->s_op = &nfs4_sops; - err = nfs_sb_init(sb, authflavour); - if (err == 0) - return 0; -out_fail: - if (clp) - nfs4_put_client(clp); - return err; -} - -static int nfs4_compare_super(struct super_block *sb, void *data) -{ - struct nfs_server *server = data; - struct nfs_server *old = NFS_SB(sb); - - if (strcmp(server->hostname, old->hostname) != 0) - return 0; - if (strcmp(server->mnt_path, old->mnt_path) != 0) - return 0; - return 1; -} - -static void * -nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) -{ - void *p = NULL; - - if (!src->len) - return ERR_PTR(-EINVAL); - if (src->len < maxlen) - maxlen = src->len; - if (dst == NULL) { - p = dst = kmalloc(maxlen + 1, GFP_KERNEL); - if (p == NULL) - return ERR_PTR(-ENOMEM); - } - if (copy_from_user(dst, src->data, maxlen)) { - kfree(p); - return ERR_PTR(-EFAULT); - } - dst[maxlen] = '\0'; - return dst; -} - -static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *raw_data) -{ - int error; - struct nfs_server *server; - struct super_block *s; - struct nfs4_mount_data *data = raw_data; - void *p; - - if (data == NULL) { - dprintk("%s: missing data argument\n", __FUNCTION__); - return ERR_PTR(-EINVAL); - } - if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) { - dprintk("%s: bad mount version\n", __FUNCTION__); - return ERR_PTR(-EINVAL); - } - - server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); - if (!server) - return ERR_PTR(-ENOMEM); - /* Zero out the NFS state stuff */ - init_nfsv4_state(server); - server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); - - p = nfs_copy_user_string(NULL, &data->hostname, 256); - if (IS_ERR(p)) - goto out_err; - server->hostname = p; - - p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); - if (IS_ERR(p)) - goto out_err; - server->mnt_path = p; - - p = nfs_copy_user_string(server->ip_addr, &data->client_addr, - sizeof(server->ip_addr) - 1); - if (IS_ERR(p)) - goto out_err; - - /* We now require that the mount process passes the remote address */ - if (data->host_addrlen != sizeof(server->addr)) { - s = ERR_PTR(-EINVAL); - goto out_free; - } - if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) { - s = ERR_PTR(-EFAULT); - goto out_free; - } - if (server->addr.sin_family != AF_INET || - server->addr.sin_addr.s_addr == INADDR_ANY) { - dprintk("%s: mount program didn't pass remote IP address!\n", - __FUNCTION__); - s = ERR_PTR(-EINVAL); - goto out_free; - } - - /* Fire up rpciod if not yet running */ - s = ERR_PTR(rpciod_up()); - if (IS_ERR(s)) { - dprintk("%s: couldn't start rpciod! Error = %ld\n", - __FUNCTION__, PTR_ERR(s)); - goto out_free; - } - - s = sget(fs_type, nfs4_compare_super, nfs_set_super, server); - - if (IS_ERR(s) || s->s_root) - goto out_free; - - s->s_flags = flags; - - error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0); - if (error) { - up_write(&s->s_umount); - deactivate_super(s); - return ERR_PTR(error); - } - s->s_flags |= MS_ACTIVE; - return s; -out_err: - s = (struct super_block *)p; -out_free: - kfree(server->mnt_path); - kfree(server->hostname); - kfree(server); - return s; -} - -static void nfs4_kill_super(struct super_block *sb) -{ - struct nfs_server *server = NFS_SB(sb); - - nfs_return_all_delegations(sb); - kill_anon_super(sb); - - nfs4_renewd_prepare_shutdown(server); - - if (server->client != NULL && !IS_ERR(server->client)) - rpc_shutdown_client(server->client); - - destroy_nfsv4_state(server); - - rpciod_down(); - - nfs_free_iostats(server->io_stats); - kfree(server->hostname); - kfree(server); -} - -static struct file_system_type nfs4_fs_type = { - .owner = THIS_MODULE, - .name = "nfs4", - .get_sb = nfs4_get_sb, - .kill_sb = nfs4_kill_super, - .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, -}; - -static const int nfs_set_port_min = 0; -static const int nfs_set_port_max = 65535; -static int param_set_port(const char *val, struct kernel_param *kp) -{ - char *endp; - int num = simple_strtol(val, &endp, 0); - if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max) - return -EINVAL; - *((int *)kp->arg) = num; - return 0; -} - -module_param_call(callback_tcpport, param_set_port, param_get_int, - &nfs_callback_set_tcpport, 0644); - -static int param_set_idmap_timeout(const char *val, struct kernel_param *kp) -{ - char *endp; - int num = simple_strtol(val, &endp, 0); - int jif = num * HZ; - if (endp == val || *endp || num < 0 || jif < num) - return -EINVAL; - *((int *)kp->arg) = jif; - return 0; -} - -module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int, - &nfs_idmap_cache_timeout, 0644); - -#define nfs4_init_once(nfsi) \ - do { \ - INIT_LIST_HEAD(&(nfsi)->open_states); \ - nfsi->delegation = NULL; \ - nfsi->delegation_state = 0; \ - init_rwsem(&nfsi->rwsem); \ - } while(0) - -static inline int register_nfs4fs(void) -{ - int ret; - - ret = nfs_register_sysctl(); - if (ret != 0) - return ret; - ret = register_filesystem(&nfs4_fs_type); - if (ret != 0) - nfs_unregister_sysctl(); - return ret; -} - -static inline void unregister_nfs4fs(void) -{ - unregister_filesystem(&nfs4_fs_type); - nfs_unregister_sysctl(); -} -#else -#define nfs4_init_once(nfsi) \ - do { } while (0) -#define register_nfs4fs() (0) -#define unregister_nfs4fs() #endif -extern int nfs_init_nfspagecache(void); -extern void nfs_destroy_nfspagecache(void); -extern int nfs_init_readpagecache(void); -extern void nfs_destroy_readpagecache(void); -extern int nfs_init_writepagecache(void); -extern void nfs_destroy_writepagecache(void); -#ifdef CONFIG_NFS_DIRECTIO -extern int nfs_init_directcache(void); -extern void nfs_destroy_directcache(void); -#endif - -static kmem_cache_t * nfs_inode_cachep; - -static struct inode *nfs_alloc_inode(struct super_block *sb) +struct inode *nfs_alloc_inode(struct super_block *sb) { struct nfs_inode *nfsi; nfsi = (struct nfs_inode *)kmem_cache_alloc(nfs_inode_cachep, SLAB_KERNEL); @@ -2244,11 +1083,21 @@ #endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; } -static void nfs_destroy_inode(struct inode *inode) +void nfs_destroy_inode(struct inode *inode) { kmem_cache_free(nfs_inode_cachep, NFS_I(inode)); } +static inline void nfs4_init_once(struct nfs_inode *nfsi) +{ +#ifdef CONFIG_NFS_V4 + INIT_LIST_HEAD(&nfsi->open_states); + nfsi->delegation = NULL; + nfsi->delegation_state = 0; + init_rwsem(&nfsi->rwsem); +#endif +} + static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) { struct nfs_inode *nfsi = (struct nfs_inode *) foo; @@ -2269,7 +1118,7 @@ static void init_once(void * foo, kmem_c } } -static int nfs_init_inodecache(void) +static int __init nfs_init_inodecache(void) { nfs_inode_cachep = kmem_cache_create("nfs_inode_cache", sizeof(struct nfs_inode), @@ -2311,29 +1160,22 @@ static int __init init_nfs_fs(void) if (err) goto out1; -#ifdef CONFIG_NFS_DIRECTIO err = nfs_init_directcache(); if (err) goto out0; -#endif #ifdef CONFIG_PROC_FS rpc_proc_register(&nfs_rpcstat); #endif - err = register_filesystem(&nfs_fs_type); - if (err) - goto out; - if ((err = register_nfs4fs()) != 0) + if ((err = register_nfs_fs()) != 0) goto out; return 0; out: #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif -#ifdef CONFIG_NFS_DIRECTIO nfs_destroy_directcache(); out0: -#endif nfs_destroy_writepagecache(); out1: nfs_destroy_readpagecache(); @@ -2347,9 +1189,7 @@ out4: static void __exit exit_nfs_fs(void) { -#ifdef CONFIG_NFS_DIRECTIO nfs_destroy_directcache(); -#endif nfs_destroy_writepagecache(); nfs_destroy_readpagecache(); nfs_destroy_inodecache(); @@ -2357,8 +1197,7 @@ #endif #ifdef CONFIG_PROC_FS rpc_proc_unregister("nfs"); #endif - unregister_filesystem(&nfs_fs_type); - unregister_nfs4fs(); + unregister_nfs_fs(); } /* Not quite true; I just maintain it */ diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h new file mode 100644 index 0000000..e4f4e5d --- /dev/null +++ b/fs/nfs/internal.h @@ -0,0 +1,186 @@ +/* + * NFS internal definitions + */ + +#include + +struct nfs_clone_mount { + const struct super_block *sb; + const struct dentry *dentry; + struct nfs_fh *fh; + struct nfs_fattr *fattr; + char *hostname; + char *mnt_path; + struct sockaddr_in *addr; + rpc_authflavor_t authflavor; +}; + +/* namespace-nfs4.c */ +#ifdef CONFIG_NFS_V4 +extern struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry); +#else +static inline +struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) +{ + return ERR_PTR(-ENOENT); +} +#endif + +/* callback_xdr.c */ +extern struct svc_version nfs4_callback_version1; + +/* pagelist.c */ +extern int __init nfs_init_nfspagecache(void); +extern void nfs_destroy_nfspagecache(void); +extern int __init nfs_init_readpagecache(void); +extern void nfs_destroy_readpagecache(void); +extern int __init nfs_init_writepagecache(void); +extern void nfs_destroy_writepagecache(void); + +#ifdef CONFIG_NFS_DIRECTIO +extern int __init nfs_init_directcache(void); +extern void nfs_destroy_directcache(void); +#else +#define nfs_init_directcache() (0) +#define nfs_destroy_directcache() do {} while(0) +#endif + +/* nfs2xdr.c */ +extern struct rpc_procinfo nfs_procedures[]; +extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); + +/* nfs3xdr.c */ +extern struct rpc_procinfo nfs3_procedures[]; +extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); + +/* nfs4xdr.c */ +extern int nfs_stat_to_errno(int); +extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); + +/* nfs4proc.c */ +#ifdef CONFIG_NFS_V4 +extern struct rpc_procinfo nfs4_procedures[]; + +extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, + struct nfs4_fs_locations *fs_locations, + struct page *page); +#endif + +/* inode.c */ +extern struct inode *nfs_alloc_inode(struct super_block *sb); +extern void nfs_destroy_inode(struct inode *); +extern int nfs_write_inode(struct inode *,int); +extern void nfs_clear_inode(struct inode *); +#ifdef CONFIG_NFS_V4 +extern void nfs4_clear_inode(struct inode *); +#endif + +/* super.c */ +extern struct file_system_type nfs_referral_nfs4_fs_type; +extern struct file_system_type clone_nfs_fs_type; +#ifdef CONFIG_NFS_V4 +extern struct file_system_type clone_nfs4_fs_type; +#endif + +extern struct rpc_stat nfs_rpcstat; + +extern int __init register_nfs_fs(void); +extern void __exit unregister_nfs_fs(void); + +/* namespace.c */ +extern char *nfs_path(const char *base, const struct dentry *dentry, + char *buffer, ssize_t buflen); + +/* + * Determine the mount path as a string + */ +static inline char * +nfs4_path(const struct dentry *dentry, char *buffer, ssize_t buflen) +{ +#ifdef CONFIG_NFS_V4 + return nfs_path(NFS_SB(dentry->d_sb)->mnt_path, dentry, buffer, buflen); +#else + return NULL; +#endif +} + +/* + * Determine the device name as a string + */ +static inline char *nfs_devname(const struct vfsmount *mnt_parent, + const struct dentry *dentry, + char *buffer, ssize_t buflen) +{ + return nfs_path(mnt_parent->mnt_devname, dentry, buffer, buflen); +} + +/* + * Determine the actual block size (and log2 thereof) + */ +static inline +unsigned long nfs_block_bits(unsigned long bsize, unsigned char *nrbitsp) +{ + /* make sure blocksize is a power of two */ + if ((bsize & (bsize - 1)) || nrbitsp) { + unsigned char nrbits; + + for (nrbits = 31; nrbits && !(bsize & (1 << nrbits)); nrbits--) + ; + bsize = 1 << nrbits; + if (nrbitsp) + *nrbitsp = nrbits; + } + + return bsize; +} + +/* + * Calculate the number of 512byte blocks used. + */ +static inline unsigned long nfs_calc_block_size(u64 tsize) +{ + loff_t used = (tsize + 511) >> 9; + return (used > ULONG_MAX) ? ULONG_MAX : used; +} + +/* + * Compute and set NFS server blocksize + */ +static inline +unsigned long nfs_block_size(unsigned long bsize, unsigned char *nrbitsp) +{ + if (bsize < NFS_MIN_FILE_IO_SIZE) + bsize = NFS_DEF_FILE_IO_SIZE; + else if (bsize >= NFS_MAX_FILE_IO_SIZE) + bsize = NFS_MAX_FILE_IO_SIZE; + + return nfs_block_bits(bsize, nrbitsp); +} + +/* + * Determine the maximum file size for a superblock + */ +static inline +void nfs_super_set_maxbytes(struct super_block *sb, __u64 maxfilesize) +{ + sb->s_maxbytes = (loff_t)maxfilesize; + if (sb->s_maxbytes > MAX_LFS_FILESIZE || sb->s_maxbytes <= 0) + sb->s_maxbytes = MAX_LFS_FILESIZE; +} + +/* + * Check if the string represents a "valid" IPv4 address + */ +static inline int valid_ipaddr4(const char *buf) +{ + int rc, count, in[4]; + + rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]); + if (rc != 4) + return -EINVAL; + for (count = 0; count < 4; count++) { + if (in[count] > 255) + return -EINVAL; + } + return 0; +} diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c new file mode 100644 index 0000000..19b98ca --- /dev/null +++ b/fs/nfs/namespace.c @@ -0,0 +1,229 @@ +/* + * linux/fs/nfs/namespace.c + * + * Copyright (C) 2005 Trond Myklebust + * + * NFS namespace + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#define NFSDBG_FACILITY NFSDBG_VFS + +static void nfs_expire_automounts(void *list); + +LIST_HEAD(nfs_automount_list); +static DECLARE_WORK(nfs_automount_task, nfs_expire_automounts, &nfs_automount_list); +int nfs_mountpoint_expiry_timeout = 500 * HZ; + +/* + * nfs_path - reconstruct the path given an arbitrary dentry + * @base - arbitrary string to prepend to the path + * @dentry - pointer to dentry + * @buffer - result buffer + * @buflen - length of buffer + * + * Helper function for constructing the path from the + * root dentry to an arbitrary hashed dentry. + * + * This is mainly for use in figuring out the path on the + * server side when automounting on top of an existing partition. + */ +char *nfs_path(const char *base, const struct dentry *dentry, + char *buffer, ssize_t buflen) +{ + char *end = buffer+buflen; + int namelen; + + *--end = '\0'; + buflen--; + spin_lock(&dcache_lock); + while (!IS_ROOT(dentry)) { + namelen = dentry->d_name.len; + buflen -= namelen + 1; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, dentry->d_name.name, namelen); + *--end = '/'; + dentry = dentry->d_parent; + } + spin_unlock(&dcache_lock); + namelen = strlen(base); + /* Strip off excess slashes in base string */ + while (namelen > 0 && base[namelen - 1] == '/') + namelen--; + buflen -= namelen; + if (buflen < 0) + goto Elong; + end -= namelen; + memcpy(end, base, namelen); + return end; +Elong: + return ERR_PTR(-ENAMETOOLONG); +} + +/* + * nfs_follow_mountpoint - handle crossing a mountpoint on the server + * @dentry - dentry of mountpoint + * @nd - nameidata info + * + * When we encounter a mountpoint on the server, we want to set up + * a mountpoint on the client too, to prevent inode numbers from + * colliding, and to allow "df" to work properly. + * On NFSv4, we also want to allow for the fact that different + * filesystems may be migrated to different servers in a failover + * situation, and that different filesystems may want to use + * different security flavours. + */ +static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +{ + struct vfsmount *mnt; + struct nfs_server *server = NFS_SERVER(dentry->d_inode); + struct dentry *parent; + struct nfs_fh fh; + struct nfs_fattr fattr; + int err; + + BUG_ON(IS_ROOT(dentry)); + dprintk("%s: enter\n", __FUNCTION__); + dput(nd->dentry); + nd->dentry = dget(dentry); + if (d_mountpoint(nd->dentry)) + goto out_follow; + /* Look it up again */ + parent = dget_parent(nd->dentry); + err = server->rpc_ops->lookup(parent->d_inode, &nd->dentry->d_name, &fh, &fattr); + dput(parent); + if (err != 0) + goto out_err; + + if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) + mnt = nfs_do_refmount(nd->mnt, nd->dentry); + else + mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); + err = PTR_ERR(mnt); + if (IS_ERR(mnt)) + goto out_err; + + mntget(mnt); + err = do_add_mount(mnt, nd, nd->mnt->mnt_flags|MNT_SHRINKABLE, &nfs_automount_list); + if (err < 0) { + mntput(mnt); + if (err == -EBUSY) + goto out_follow; + goto out_err; + } + mntput(nd->mnt); + dput(nd->dentry); + nd->mnt = mnt; + nd->dentry = dget(mnt->mnt_root); + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); +out: + dprintk("%s: done, returned %d\n", __FUNCTION__, err); + return ERR_PTR(err); +out_err: + path_release(nd); + goto out; +out_follow: + while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) + ; + err = 0; + goto out; +} + +struct inode_operations nfs_mountpoint_inode_operations = { + .follow_link = nfs_follow_mountpoint, + .getattr = nfs_getattr, +}; + +struct inode_operations nfs_referral_inode_operations = { + .follow_link = nfs_follow_mountpoint, +}; + +static void nfs_expire_automounts(void *data) +{ + struct list_head *list = (struct list_head *)data; + + mark_mounts_for_expiry(list); + if (!list_empty(list)) + schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); +} + +void nfs_release_automount_timer(void) +{ + if (list_empty(&nfs_automount_list)) { + cancel_delayed_work(&nfs_automount_task); + flush_scheduled_work(); + } +} + +/* + * Clone a mountpoint of the appropriate type + */ +static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, char *devname, + struct nfs_clone_mount *mountdata) +{ +#ifdef CONFIG_NFS_V4 + struct vfsmount *mnt = NULL; + switch (server->rpc_ops->version) { + case 2: + case 3: + mnt = vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata); + break; + case 4: + mnt = vfs_kern_mount(&clone_nfs4_fs_type, 0, devname, mountdata); + } + return mnt; +#else + return vfs_kern_mount(&clone_nfs_fs_type, 0, devname, mountdata); +#endif +} + +/** + * nfs_do_submount - set up mountpoint when crossing a filesystem boundary + * @mnt_parent - mountpoint of parent directory + * @dentry - parent directory + * @fh - filehandle for new root dentry + * @fattr - attributes for new root inode + * + */ +struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, + const struct dentry *dentry, struct nfs_fh *fh, + struct nfs_fattr *fattr) +{ + struct nfs_clone_mount mountdata = { + .sb = mnt_parent->mnt_sb, + .dentry = dentry, + .fh = fh, + .fattr = fattr, + }; + struct vfsmount *mnt = ERR_PTR(-ENOMEM); + char *page = (char *) __get_free_page(GFP_USER); + char *devname; + + dprintk("%s: submounting on %s/%s\n", __FUNCTION__, + dentry->d_parent->d_name.name, + dentry->d_name.name); + if (page == NULL) + goto out; + devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); + mnt = (struct vfsmount *)devname; + if (IS_ERR(devname)) + goto free_page; + mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata); +free_page: + free_page((unsigned long)page); +out: + dprintk("%s: done\n", __FUNCTION__); + return mnt; +} diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index f0015fa..67391ee 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -23,12 +23,11 @@ #include #include #include #include +#include "internal.h" #define NFSDBG_FACILITY NFSDBG_XDR /* #define NFS_PARANOIA 1 */ -extern int nfs_stat_to_errno(int stat); - /* Mapping from NFS error code to "errno" error code. */ #define errno_NFSERR_IO EIO @@ -131,7 +130,8 @@ xdr_decode_fattr(u32 *p, struct nfs_fatt fattr->du.nfs2.blocksize = ntohl(*p++); rdev = ntohl(*p++); fattr->du.nfs2.blocks = ntohl(*p++); - fattr->fsid_u.nfs3 = ntohl(*p++); + fattr->fsid.major = ntohl(*p++); + fattr->fsid.minor = 0; fattr->fileid = ntohl(*p++); p = xdr_decode_time(p, &fattr->atime); p = xdr_decode_time(p, &fattr->mtime); diff --git a/fs/nfs/nfs3acl.c b/fs/nfs/nfs3acl.c index 3328787..7322da4 100644 --- a/fs/nfs/nfs3acl.c +++ b/fs/nfs/nfs3acl.c @@ -172,8 +172,10 @@ static void nfs3_cache_acls(struct inode inode->i_ino, acl, dfacl); spin_lock(&inode->i_lock); __nfs3_forget_cached_acls(NFS_I(inode)); - nfsi->acl_access = posix_acl_dup(acl); - nfsi->acl_default = posix_acl_dup(dfacl); + if (!IS_ERR(acl)) + nfsi->acl_access = posix_acl_dup(acl); + if (!IS_ERR(dfacl)) + nfsi->acl_default = posix_acl_dup(dfacl); spin_unlock(&inode->i_lock); } @@ -254,7 +256,9 @@ struct posix_acl *nfs3_proc_getacl(struc res.acl_access = NULL; } } - nfs3_cache_acls(inode, res.acl_access, res.acl_default); + nfs3_cache_acls(inode, + (res.mask & NFS_ACL) ? res.acl_access : ERR_PTR(-EINVAL), + (res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL)); switch(type) { case ACL_TYPE_ACCESS: @@ -329,6 +333,7 @@ static int nfs3_proc_setacls(struct inod switch (status) { case 0: status = nfs_refresh_inode(inode, &fattr); + nfs3_cache_acls(inode, acl, dfacl); break; case -EPFNOSUPPORT: case -EPROTONOSUPPORT: diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index cf186f0..7143b1f 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c @@ -20,11 +20,10 @@ #include #include #include "iostat.h" +#include "internal.h" #define NFSDBG_FACILITY NFSDBG_PROC -extern struct rpc_procinfo nfs3_procedures[]; - /* A wrapper to handle the EJUKEBOX error message */ static int nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) @@ -809,8 +808,6 @@ nfs3_proc_pathconf(struct nfs_server *se return status; } -extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); - static int nfs3_read_done(struct rpc_task *task, struct nfs_read_data *data) { if (nfs3_async_handle_jukebox(task, data->inode)) diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index ec23361..0250269 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -22,14 +22,13 @@ #include #include #include #include +#include "internal.h" #define NFSDBG_FACILITY NFSDBG_XDR /* Mapping from NFS error code to "errno" error code. */ #define errno_NFSERR_IO EIO -extern int nfs_stat_to_errno(int); - /* * Declare the space requirements for NFS arguments and replies as * number of 32bit-words @@ -166,7 +165,8 @@ xdr_decode_fattr(u32 *p, struct nfs_fatt if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor) fattr->rdev = 0; - p = xdr_decode_hyper(p, &fattr->fsid_u.nfs3); + p = xdr_decode_hyper(p, &fattr->fsid.major); + fattr->fsid.minor = 0; p = xdr_decode_hyper(p, &fattr->fileid); p = xdr_decode_time3(p, &fattr->atime); p = xdr_decode_time3(p, &fattr->mtime); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 0f5e4e7..9a10286 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -217,6 +217,9 @@ extern int nfs4_proc_renew(struct nfs4_c extern int nfs4_do_close(struct inode *inode, struct nfs4_state *state); extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); +extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); +extern int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, + struct nfs4_fs_locations *fs_locations, struct page *page); extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops; extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops; @@ -225,6 +228,7 @@ extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2]; extern const u32 nfs4_pathconf_bitmap[2]; extern const u32 nfs4_fsinfo_bitmap[2]; +extern const u32 nfs4_fs_locations_bitmap[2]; /* nfs4renewd.c */ extern void nfs4_schedule_state_renewal(struct nfs4_client *); diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c new file mode 100644 index 0000000..ea38d27 --- /dev/null +++ b/fs/nfs/nfs4namespace.c @@ -0,0 +1,201 @@ +/* + * linux/fs/nfs/nfs4namespace.c + * + * Copyright (C) 2005 Trond Myklebust + * + * NFSv4 namespace + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +#define NFSDBG_FACILITY NFSDBG_VFS + +/* + * Check if fs_root is valid + */ +static inline char *nfs4_pathname_string(struct nfs4_pathname *pathname, + char *buffer, ssize_t buflen) +{ + char *end = buffer + buflen; + int n; + + *--end = '\0'; + buflen--; + + n = pathname->ncomponents; + while (--n >= 0) { + struct nfs4_string *component = &pathname->components[n]; + buflen -= component->len + 1; + if (buflen < 0) + goto Elong; + end -= component->len; + memcpy(end, component->data, component->len); + *--end = '/'; + } + return end; +Elong: + return ERR_PTR(-ENAMETOOLONG); +} + + +/** + * nfs_follow_referral - set up mountpoint when hitting a referral on moved error + * @mnt_parent - mountpoint of parent directory + * @dentry - parent directory + * @fspath - fs path returned in fs_locations + * @mntpath - mount path to new server + * @hostname - hostname of new server + * @addr - host addr of new server + * + */ +static struct vfsmount *nfs_follow_referral(const struct vfsmount *mnt_parent, + const struct dentry *dentry, + struct nfs4_fs_locations *locations) +{ + struct vfsmount *mnt = ERR_PTR(-ENOENT); + struct nfs_clone_mount mountdata = { + .sb = mnt_parent->mnt_sb, + .dentry = dentry, + .authflavor = NFS_SB(mnt_parent->mnt_sb)->client->cl_auth->au_flavor, + }; + char *page, *page2; + char *path, *fs_path; + char *devname; + int loc, s; + + if (locations == NULL || locations->nlocations <= 0) + goto out; + + dprintk("%s: referral at %s/%s\n", __FUNCTION__, + dentry->d_parent->d_name.name, dentry->d_name.name); + + /* Ensure fs path is a prefix of current dentry path */ + page = (char *) __get_free_page(GFP_USER); + if (page == NULL) + goto out; + page2 = (char *) __get_free_page(GFP_USER); + if (page2 == NULL) + goto out; + + path = nfs4_path(dentry, page, PAGE_SIZE); + if (IS_ERR(path)) + goto out_free; + + fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); + if (IS_ERR(fs_path)) + goto out_free; + + if (strncmp(path, fs_path, strlen(fs_path)) != 0) { + dprintk("%s: path %s does not begin with fsroot %s\n", __FUNCTION__, path, fs_path); + goto out_free; + } + + devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); + if (IS_ERR(devname)) { + mnt = (struct vfsmount *)devname; + goto out_free; + } + + loc = 0; + while (loc < locations->nlocations && IS_ERR(mnt)) { + struct nfs4_fs_location *location = &locations->locations[loc]; + char *mnt_path; + + if (location == NULL || location->nservers <= 0 || + location->rootpath.ncomponents == 0) { + loc++; + continue; + } + + mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); + if (IS_ERR(mnt_path)) { + loc++; + continue; + } + mountdata.mnt_path = mnt_path; + + s = 0; + while (s < location->nservers) { + struct sockaddr_in addr = {}; + + if (location->servers[s].len <= 0 || + valid_ipaddr4(location->servers[s].data) < 0) { + s++; + continue; + } + + mountdata.hostname = location->servers[s].data; + addr.sin_addr.s_addr = in_aton(mountdata.hostname); + addr.sin_family = AF_INET; + addr.sin_port = htons(NFS_PORT); + mountdata.addr = &addr; + + mnt = vfs_kern_mount(&nfs_referral_nfs4_fs_type, 0, devname, &mountdata); + if (!IS_ERR(mnt)) { + break; + } + s++; + } + loc++; + } + +out_free: + free_page((unsigned long)page); + free_page((unsigned long)page2); +out: + dprintk("%s: done\n", __FUNCTION__); + return mnt; +} + +/* + * nfs_do_refmount - handle crossing a referral on server + * @dentry - dentry of referral + * @nd - nameidata info + * + */ +struct vfsmount *nfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry) +{ + struct vfsmount *mnt = ERR_PTR(-ENOENT); + struct dentry *parent; + struct nfs4_fs_locations *fs_locations = NULL; + struct page *page; + int err; + + /* BUG_ON(IS_ROOT(dentry)); */ + dprintk("%s: enter\n", __FUNCTION__); + + page = alloc_page(GFP_KERNEL); + if (page == NULL) + goto out; + + fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); + if (fs_locations == NULL) + goto out_free; + + /* Get locations */ + parent = dget_parent(dentry); + dprintk("%s: getting locations for %s/%s\n", __FUNCTION__, parent->d_name.name, dentry->d_name.name); + err = nfs4_proc_fs_locations(parent->d_inode, dentry, fs_locations, page); + dput(parent); + if (err != 0 || fs_locations->nlocations <= 0 || + fs_locations->fs_path.ncomponents <= 0) + goto out_free; + + mnt = nfs_follow_referral(mnt_parent, dentry, fs_locations); +out_free: + __free_page(page); + kfree(fs_locations); +out: + dprintk("%s: done\n", __FUNCTION__); + return mnt; +} diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d86c0db..e6ee97f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -65,8 +65,6 @@ static int nfs4_async_handle_error(struc static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception); static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs4_client *clp); -extern u32 *nfs4_decode_dirent(u32 *p, struct nfs_entry *entry, int plus); -extern struct rpc_procinfo nfs4_procedures[]; /* Prevent leaks of NFSv4 errors into userland */ int nfs4_map_errors(int err) @@ -121,6 +119,25 @@ const u32 nfs4_fsinfo_bitmap[2] = { FATT 0 }; +const u32 nfs4_fs_locations_bitmap[2] = { + FATTR4_WORD0_TYPE + | FATTR4_WORD0_CHANGE + | FATTR4_WORD0_SIZE + | FATTR4_WORD0_FSID + | FATTR4_WORD0_FILEID + | FATTR4_WORD0_FS_LOCATIONS, + FATTR4_WORD1_MODE + | FATTR4_WORD1_NUMLINKS + | FATTR4_WORD1_OWNER + | FATTR4_WORD1_OWNER_GROUP + | FATTR4_WORD1_RAWDEV + | FATTR4_WORD1_SPACE_USED + | FATTR4_WORD1_TIME_ACCESS + | FATTR4_WORD1_TIME_METADATA + | FATTR4_WORD1_TIME_MODIFY + | FATTR4_WORD1_MOUNTED_ON_FILEID +}; + static void nfs4_setup_readdir(u64 cookie, u32 *verifier, struct dentry *dentry, struct nfs4_readdir_arg *readdir) { @@ -185,15 +202,15 @@ static void renew_lease(const struct nfs spin_unlock(&clp->cl_lock); } -static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo) +static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) { - struct nfs_inode *nfsi = NFS_I(inode); + struct nfs_inode *nfsi = NFS_I(dir); - spin_lock(&inode->i_lock); - nfsi->cache_validity |= NFS_INO_INVALID_ATTR; + spin_lock(&dir->i_lock); + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; if (cinfo->before == nfsi->change_attr && cinfo->atomic) nfsi->change_attr = cinfo->after; - spin_unlock(&inode->i_lock); + spin_unlock(&dir->i_lock); } struct nfs4_opendata { @@ -1331,7 +1348,7 @@ static int _nfs4_server_capabilities(str return status; } -static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) +int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle) { struct nfs4_exception exception = { }; int err; @@ -1443,6 +1460,50 @@ out: return nfs4_map_errors(status); } +/* + * Get locations and (maybe) other attributes of a referral. + * Note that we'll actually follow the referral later when + * we detect fsid mismatch in inode revalidation + */ +static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle) +{ + int status = -ENOMEM; + struct page *page = NULL; + struct nfs4_fs_locations *locations = NULL; + struct dentry dentry = {}; + + page = alloc_page(GFP_KERNEL); + if (page == NULL) + goto out; + locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); + if (locations == NULL) + goto out; + + dentry.d_name.name = name->name; + dentry.d_name.len = name->len; + status = nfs4_proc_fs_locations(dir, &dentry, locations, page); + if (status != 0) + goto out; + /* Make sure server returned a different fsid for the referral */ + if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) { + dprintk("%s: server did not return a different fsid for a referral at %s\n", __FUNCTION__, name->name); + status = -EIO; + goto out; + } + + memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr)); + fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL; + if (!fattr->mode) + fattr->mode = S_IFDIR; + memset(fhandle, 0, sizeof(struct nfs_fh)); +out: + if (page) + __free_page(page); + if (locations) + kfree(locations); + return status; +} + static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { struct nfs4_getattr_arg args = { @@ -1547,6 +1608,8 @@ static int _nfs4_proc_lookup(struct inod dprintk("NFS call lookup %s\n", name->name); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); + if (status == -NFS4ERR_MOVED) + status = nfs4_get_referral(dir, name, fattr, fhandle); dprintk("NFS reply lookup: %d\n", status); return status; } @@ -2008,7 +2071,7 @@ static int _nfs4_proc_link(struct inode if (!status) { update_changeattr(dir, &res.cinfo); nfs_post_op_update_inode(dir, res.dir_attr); - nfs_refresh_inode(inode, res.fattr); + nfs_post_op_update_inode(inode, res.fattr); } return status; @@ -3081,9 +3144,6 @@ static int do_vfs_lock(struct file *file default: BUG(); } - if (res < 0) - printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", - __FUNCTION__); return res; } @@ -3195,8 +3255,6 @@ static struct rpc_task *nfs4_do_unlck(st return ERR_PTR(-ENOMEM); } - /* Unlock _before_ we do the RPC call */ - do_vfs_lock(fl->fl_file, fl); return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data); } @@ -3207,30 +3265,28 @@ static int nfs4_proc_unlck(struct nfs4_s struct rpc_task *task; int status = 0; - /* Is this a delegated lock? */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - goto out_unlock; - /* Is this open_owner holding any locks on the server? */ - if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) - goto out_unlock; - status = nfs4_set_lock_state(state, request); + /* Unlock _before_ we do the RPC call */ + request->fl_flags |= FL_EXISTS; + if (do_vfs_lock(request->fl_file, request) == -ENOENT) + goto out; if (status != 0) - goto out_unlock; + goto out; + /* Is this a delegated lock? */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) + goto out; lsp = request->fl_u.nfs4_fl.owner; - status = -ENOMEM; seqid = nfs_alloc_seqid(&lsp->ls_seqid); + status = -ENOMEM; if (seqid == NULL) - goto out_unlock; + goto out; task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid); status = PTR_ERR(task); if (IS_ERR(task)) - goto out_unlock; + goto out; status = nfs4_wait_for_completion_rpc_task(task); rpc_release_task(task); - return status; -out_unlock: - do_vfs_lock(request->fl_file, request); +out: return status; } @@ -3398,10 +3454,10 @@ static int nfs4_lock_reclaim(struct nfs4 struct nfs4_exception exception = { }; int err; - /* Cache the lock if possible... */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - return 0; do { + /* Cache the lock if possible... */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) + return 0; err = _nfs4_do_setlk(state, F_SETLK, request, 1); if (err != -NFS4ERR_DELAY) break; @@ -3420,6 +3476,8 @@ static int nfs4_lock_expired(struct nfs4 if (err != 0) return err; do { + if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) + return 0; err = _nfs4_do_setlk(state, F_SETLK, request, 0); if (err != -NFS4ERR_DELAY) break; @@ -3431,29 +3489,42 @@ static int nfs4_lock_expired(struct nfs4 static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) { struct nfs4_client *clp = state->owner->so_client; + unsigned char fl_flags = request->fl_flags; int status; /* Is this a delegated open? */ - if (NFS_I(state->inode)->delegation_state != 0) { - /* Yes: cache locks! */ - status = do_vfs_lock(request->fl_file, request); - /* ...but avoid races with delegation recall... */ - if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags)) - return status; - } - down_read(&clp->cl_sem); status = nfs4_set_lock_state(state, request); if (status != 0) goto out; + request->fl_flags |= FL_ACCESS; + status = do_vfs_lock(request->fl_file, request); + if (status < 0) + goto out; + down_read(&clp->cl_sem); + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { + struct nfs_inode *nfsi = NFS_I(state->inode); + /* Yes: cache locks! */ + down_read(&nfsi->rwsem); + /* ...but avoid races with delegation recall... */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { + request->fl_flags = fl_flags & ~FL_SLEEP; + status = do_vfs_lock(request->fl_file, request); + up_read(&nfsi->rwsem); + goto out_unlock; + } + up_read(&nfsi->rwsem); + } status = _nfs4_do_setlk(state, cmd, request, 0); if (status != 0) - goto out; + goto out_unlock; /* Note: we always want to sleep here! */ - request->fl_flags |= FL_SLEEP; + request->fl_flags = fl_flags | FL_SLEEP; if (do_vfs_lock(request->fl_file, request) < 0) printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); -out: +out_unlock: up_read(&clp->cl_sem); +out: + request->fl_flags = fl_flags; return status; } @@ -3570,6 +3641,36 @@ ssize_t nfs4_listxattr(struct dentry *de return len; } +int nfs4_proc_fs_locations(struct inode *dir, struct dentry *dentry, + struct nfs4_fs_locations *fs_locations, struct page *page) +{ + struct nfs_server *server = NFS_SERVER(dir); + u32 bitmask[2] = { + [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS, + [1] = FATTR4_WORD1_MOUNTED_ON_FILEID, + }; + struct nfs4_fs_locations_arg args = { + .dir_fh = NFS_FH(dir), + .name = &dentry->d_name, + .page = page, + .bitmask = bitmask, + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FS_LOCATIONS], + .rpc_argp = &args, + .rpc_resp = fs_locations, + }; + int status; + + dprintk("%s: start\n", __FUNCTION__); + fs_locations->fattr.valid = 0; + fs_locations->server = server; + fs_locations->nlocations = 0; + status = rpc_call_sync(server->client, &msg, 0); + dprintk("%s: returned status = %d\n", __FUNCTION__, status); + return status; +} + struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = { .recover_open = nfs4_open_reclaim, .recover_lock = nfs4_lock_reclaim, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 96e5b82..090a36b 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -38,7 +38,6 @@ * subsequent patch. */ -#include #include #include #include diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 7c5d70e..1750d99 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -411,6 +411,15 @@ #define NFS4_enc_setacl_sz (compound_enc #define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) +#define NFS4_enc_fs_locations_sz \ + (compound_encode_hdr_maxsz + \ + encode_putfh_maxsz + \ + encode_getattr_maxsz) +#define NFS4_dec_fs_locations_sz \ + (compound_decode_hdr_maxsz + \ + decode_putfh_maxsz + \ + op_decode_hdr_maxsz + \ + nfs4_fattr_bitmap_maxsz) static struct { unsigned int mode; @@ -722,6 +731,13 @@ static int encode_fsinfo(struct xdr_stre bitmask[1] & nfs4_fsinfo_bitmap[1]); } +static int encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask) +{ + return encode_getattr_two(xdr, + bitmask[0] & nfs4_fs_locations_bitmap[0], + bitmask[1] & nfs4_fs_locations_bitmap[1]); +} + static int encode_getfh(struct xdr_stream *xdr) { uint32_t *p; @@ -2003,6 +2019,38 @@ out: } /* + * Encode FS_LOCATIONS request + */ +static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, uint32_t *p, struct nfs4_fs_locations_arg *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .nops = 3, + }; + struct rpc_auth *auth = req->rq_task->tk_auth; + int replen; + int status; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, &hdr); + if ((status = encode_putfh(&xdr, args->dir_fh)) != 0) + goto out; + if ((status = encode_lookup(&xdr, args->name)) != 0) + goto out; + if ((status = encode_fs_locations(&xdr, args->bitmask)) != 0) + goto out; + /* set up reply + * toplevel_status + OP_PUTFH + status + * + OP_LOOKUP + status + OP_GETATTR + status = 7 + */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page, + 0, PAGE_SIZE); +out: + return status; +} + +/* * START OF "GENERIC" DECODE ROUTINES. * These may look a little ugly since they are imported from a "generic" * set of XDR encode/decode routines which are intended to be shared by @@ -2036,7 +2084,7 @@ #define READ_BUF(nbytes) do { \ } \ } while (0) -static int decode_opaque_inline(struct xdr_stream *xdr, uint32_t *len, char **string) +static int decode_opaque_inline(struct xdr_stream *xdr, unsigned int *len, char **string) { uint32_t *p; @@ -2087,7 +2135,7 @@ static int decode_op_hdr(struct xdr_stre static int decode_ace(struct xdr_stream *xdr, void *ace, struct nfs4_client *clp) { uint32_t *p; - uint32_t strlen; + unsigned int strlen; char *str; READ_BUF(12); @@ -2217,7 +2265,7 @@ static int decode_attr_symlink_support(s return 0; } -static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fsid *fsid) +static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs_fsid *fsid) { uint32_t *p; @@ -2285,6 +2333,22 @@ static int decode_attr_fileid(struct xdr return 0; } +static int decode_attr_mounted_on_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid) +{ + uint32_t *p; + + *fileid = 0; + if (unlikely(bitmap[1] & (FATTR4_WORD1_MOUNTED_ON_FILEID - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_MOUNTED_ON_FILEID)) { + READ_BUF(8); + READ64(*fileid); + bitmap[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; + } + dprintk("%s: fileid=%Lu\n", __FUNCTION__, (unsigned long long)*fileid); + return 0; +} + static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) { uint32_t *p; @@ -2336,6 +2400,116 @@ static int decode_attr_files_total(struc return status; } +static int decode_pathname(struct xdr_stream *xdr, struct nfs4_pathname *path) +{ + int n; + uint32_t *p; + int status = 0; + + READ_BUF(4); + READ32(n); + if (n < 0) + goto out_eio; + if (n == 0) + goto root_path; + dprintk("path "); + path->ncomponents = 0; + while (path->ncomponents < n) { + struct nfs4_string *component = &path->components[path->ncomponents]; + status = decode_opaque_inline(xdr, &component->len, &component->data); + if (unlikely(status != 0)) + goto out_eio; + if (path->ncomponents != n) + dprintk("/"); + dprintk("%s", component->data); + if (path->ncomponents < NFS4_PATHNAME_MAXCOMPONENTS) + path->ncomponents++; + else { + dprintk("cannot parse %d components in path\n", n); + goto out_eio; + } + } +out: + dprintk("\n"); + return status; +root_path: +/* a root pathname is sent as a zero component4 */ + path->ncomponents = 1; + path->components[0].len=0; + path->components[0].data=NULL; + dprintk("path /\n"); + goto out; +out_eio: + dprintk(" status %d", status); + status = -EIO; + goto out; +} + +static int decode_attr_fs_locations(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fs_locations *res) +{ + int n; + uint32_t *p; + int status = -EIO; + + if (unlikely(bitmap[0] & (FATTR4_WORD0_FS_LOCATIONS -1U))) + goto out; + status = 0; + if (unlikely(!(bitmap[0] & FATTR4_WORD0_FS_LOCATIONS))) + goto out; + dprintk("%s: fsroot ", __FUNCTION__); + status = decode_pathname(xdr, &res->fs_path); + if (unlikely(status != 0)) + goto out; + READ_BUF(4); + READ32(n); + if (n <= 0) + goto out_eio; + res->nlocations = 0; + while (res->nlocations < n) { + int m; + struct nfs4_fs_location *loc = &res->locations[res->nlocations]; + + READ_BUF(4); + READ32(m); + if (m <= 0) + goto out_eio; + + loc->nservers = 0; + dprintk("%s: servers ", __FUNCTION__); + while (loc->nservers < m) { + struct nfs4_string *server = &loc->servers[loc->nservers]; + status = decode_opaque_inline(xdr, &server->len, &server->data); + if (unlikely(status != 0)) + goto out_eio; + dprintk("%s ", server->data); + if (loc->nservers < NFS4_FS_LOCATION_MAXSERVERS) + loc->nservers++; + else { + int i; + dprintk("%s: using first %d of %d servers returned for location %d\n", __FUNCTION__, NFS4_FS_LOCATION_MAXSERVERS, m, res->nlocations); + for (i = loc->nservers; i < m; i++) { + int len; + char *data; + status = decode_opaque_inline(xdr, &len, &data); + if (unlikely(status != 0)) + goto out_eio; + } + } + } + status = decode_pathname(xdr, &loc->rootpath); + if (unlikely(status != 0)) + goto out_eio; + if (res->nlocations < NFS4_FS_LOCATIONS_MAXENTRIES) + res->nlocations++; + } +out: + dprintk("%s: fs_locations done, error = %d\n", __FUNCTION__, status); + return status; +out_eio: + status = -EIO; + goto out; +} + static int decode_attr_maxfilesize(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) { uint32_t *p; @@ -2841,6 +3015,7 @@ static int decode_getfattr(struct xdr_st bitmap[2] = {0}, type; int status, fmode = 0; + uint64_t fileid; if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) goto xdr_error; @@ -2863,10 +3038,14 @@ static int decode_getfattr(struct xdr_st goto xdr_error; if ((status = decode_attr_size(xdr, bitmap, &fattr->size)) != 0) goto xdr_error; - if ((status = decode_attr_fsid(xdr, bitmap, &fattr->fsid_u.nfs4)) != 0) + if ((status = decode_attr_fsid(xdr, bitmap, &fattr->fsid)) != 0) goto xdr_error; if ((status = decode_attr_fileid(xdr, bitmap, &fattr->fileid)) != 0) goto xdr_error; + if ((status = decode_attr_fs_locations(xdr, bitmap, container_of(fattr, + struct nfs4_fs_locations, + fattr))) != 0) + goto xdr_error; if ((status = decode_attr_mode(xdr, bitmap, &fattr->mode)) != 0) goto xdr_error; fattr->mode |= fmode; @@ -2886,6 +3065,10 @@ static int decode_getfattr(struct xdr_st goto xdr_error; if ((status = decode_attr_time_modify(xdr, bitmap, &fattr->mtime)) != 0) goto xdr_error; + if ((status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid)) != 0) + goto xdr_error; + if (fattr->fileid == 0 && fileid != 0) + fattr->fileid = fileid; if ((status = verify_attr_len(xdr, savep, attrlen)) == 0) fattr->valid = NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3 | NFS_ATTR_FATTR_V4; xdr_error: @@ -3350,8 +3533,7 @@ static int decode_getacl(struct xdr_stre attrlen, recvd); return -EINVAL; } - if (attrlen <= *acl_len) - xdr_read_pages(xdr, attrlen); + xdr_read_pages(xdr, attrlen); *acl_len = attrlen; } else status = -EOPNOTSUPP; @@ -4211,6 +4393,29 @@ out: return status; } +/* + * FS_LOCATIONS request + */ +static int nfs4_xdr_dec_fs_locations(struct rpc_rqst *req, uint32_t *p, struct nfs4_fs_locations *res) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &req->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (status != 0) + goto out; + if ((status = decode_putfh(&xdr)) != 0) + goto out; + if ((status = decode_lookup(&xdr)) != 0) + goto out; + xdr_enter_page(&xdr, PAGE_SIZE); + status = decode_getfattr(&xdr, &res->fattr, res->server); +out: + return status; +} + uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus) { uint32_t bitmap[2] = {0}; @@ -4382,6 +4587,7 @@ struct rpc_procinfo nfs4_procedures[] = PROC(DELEGRETURN, enc_delegreturn, dec_delegreturn), PROC(GETACL, enc_getacl, dec_getacl), PROC(SETACL, enc_setacl, dec_setacl), + PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), }; struct rpc_version nfs_version4 = { diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index 106aca3..36e902a 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -9,7 +9,6 @@ * */ -#include #include #include #include @@ -315,6 +314,7 @@ nfs_scan_lock_dirty(struct nfs_inode *nf req->wb_index, NFS_PAGE_TAG_DIRTY); nfs_list_remove_request(req); nfs_list_add_request(req, dst); + dec_zone_page_state(req->wb_page, NR_FILE_DIRTY); res++; } } @@ -325,6 +325,7 @@ out: /** * nfs_scan_list - Scan a list for matching requests + * @nfsi: NFS inode * @head: One of the NFS inode request lists * @dst: Destination list * @idx_start: lower bound of page->index to scan @@ -336,14 +337,15 @@ out: * The requests are *not* checked to ensure that they form a contiguous set. * You must be holding the inode's req_lock when calling this function */ -int -nfs_scan_list(struct list_head *head, struct list_head *dst, - unsigned long idx_start, unsigned int npages) +int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, + struct list_head *dst, unsigned long idx_start, + unsigned int npages) { - struct list_head *pos, *tmp; - struct nfs_page *req; - unsigned long idx_end; - int res; + struct nfs_page *pgvec[NFS_SCAN_MAXENTRIES]; + struct nfs_page *req; + unsigned long idx_end; + int found, i; + int res; res = 0; if (npages == 0) @@ -351,25 +353,32 @@ nfs_scan_list(struct list_head *head, st else idx_end = idx_start + npages - 1; - list_for_each_safe(pos, tmp, head) { - - req = nfs_list_entry(pos); - - if (req->wb_index < idx_start) - continue; - if (req->wb_index > idx_end) + for (;;) { + found = radix_tree_gang_lookup(&nfsi->nfs_page_tree, + (void **)&pgvec[0], idx_start, + NFS_SCAN_MAXENTRIES); + if (found <= 0) break; + for (i = 0; i < found; i++) { + req = pgvec[i]; + if (req->wb_index > idx_end) + goto out; + idx_start = req->wb_index + 1; + if (req->wb_list_head != head) + continue; + if (nfs_set_page_writeback_locked(req)) { + nfs_list_remove_request(req); + nfs_list_add_request(req, dst); + res++; + } + } - if (!nfs_set_page_writeback_locked(req)) - continue; - nfs_list_remove_request(req); - nfs_list_add_request(req, dst); - res++; } +out: return res; } -int nfs_init_nfspagecache(void) +int __init nfs_init_nfspagecache(void) { nfs_page_cachep = kmem_cache_create("nfs_page", sizeof(struct nfs_page), diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 9dd85ca..b3899ea 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c @@ -44,11 +44,10 @@ #include #include #include #include +#include "internal.h" #define NFSDBG_FACILITY NFSDBG_PROC -extern struct rpc_procinfo nfs_procedures[]; - /* * Bare-bones access to getattr: this is for nfs_read_super. */ @@ -611,8 +610,6 @@ nfs_proc_pathconf(struct nfs_server *ser return 0; } -extern u32 * nfs_decode_dirent(u32 *, struct nfs_entry *, int); - static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data) { if (task->tk_status >= 0) { diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 624ca71..52bf634 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -15,7 +15,6 @@ * within the RPC code when root squashing is suspected. */ -#include #include #include #include @@ -51,14 +50,11 @@ struct nfs_read_data *nfs_readdata_alloc if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); - if (pagecount < NFS_PAGEVEC_SIZE) - p->pagevec = &p->page_array[0]; + if (pagecount <= ARRAY_SIZE(p->page_array)) + p->pagevec = p->page_array; else { - size_t size = ++pagecount * sizeof(struct page *); - p->pagevec = kmalloc(size, GFP_NOFS); - if (p->pagevec) { - memset(p->pagevec, 0, size); - } else { + p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); + if (!p->pagevec) { mempool_free(p, nfs_rdata_mempool); p = NULL; } @@ -104,6 +100,28 @@ int nfs_return_empty_page(struct page *p return 0; } +static void nfs_readpage_truncate_uninitialised_page(struct nfs_read_data *data) +{ + unsigned int remainder = data->args.count - data->res.count; + unsigned int base = data->args.pgbase + data->res.count; + unsigned int pglen; + struct page **pages; + + if (data->res.eof == 0 || remainder == 0) + return; + /* + * Note: "remainder" can never be negative, since we check for + * this in the XDR code. + */ + pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; + base &= ~PAGE_CACHE_MASK; + pglen = PAGE_CACHE_SIZE - base; + if (pglen < remainder) + memclear_highpage_flush(*pages, base, pglen); + else + memclear_highpage_flush(*pages, base, remainder); +} + /* * Read a page synchronously. */ @@ -177,11 +195,9 @@ static int nfs_readpage_sync(struct nfs_ NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME; spin_unlock(&inode->i_lock); - if (count) - memclear_highpage_flush(page, rdata->args.pgbase, count); - SetPageUptodate(page); - if (PageError(page)) - ClearPageError(page); + nfs_readpage_truncate_uninitialised_page(rdata); + if (rdata->res.eof || rdata->res.count == rdata->args.count) + SetPageUptodate(page); result = 0; io_error: @@ -436,20 +452,12 @@ static void nfs_readpage_result_partial( struct nfs_page *req = data->req; struct page *page = req->wb_page; + if (likely(task->tk_status >= 0)) + nfs_readpage_truncate_uninitialised_page(data); + else + SetPageError(page); if (nfs_readpage_result(task, data) != 0) return; - if (task->tk_status >= 0) { - unsigned int request = data->args.count; - unsigned int result = data->res.count; - - if (result < request) { - memclear_highpage_flush(page, - data->args.pgbase + result, - request - result); - } - } else - SetPageError(page); - if (atomic_dec_and_test(&req->wb_complete)) { if (!PageError(page)) SetPageUptodate(page); @@ -462,6 +470,40 @@ static const struct rpc_call_ops nfs_rea .rpc_release = nfs_readdata_release, }; +static void nfs_readpage_set_pages_uptodate(struct nfs_read_data *data) +{ + unsigned int count = data->res.count; + unsigned int base = data->args.pgbase; + struct page **pages; + + if (unlikely(count == 0)) + return; + pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; + base &= ~PAGE_CACHE_MASK; + count += base; + for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++) + SetPageUptodate(*pages); + /* + * Was this an eof or a short read? If the latter, don't mark the page + * as uptodate yet. + */ + if (count > 0 && (data->res.eof || data->args.count == data->res.count)) + SetPageUptodate(*pages); +} + +static void nfs_readpage_set_pages_error(struct nfs_read_data *data) +{ + unsigned int count = data->args.count; + unsigned int base = data->args.pgbase; + struct page **pages; + + pages = &data->args.pages[base >> PAGE_CACHE_SHIFT]; + base &= ~PAGE_CACHE_MASK; + count += base; + for (;count >= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE, pages++) + SetPageError(*pages); +} + /* * This is the callback from RPC telling us whether a reply was * received or some error occurred (timeout or socket shutdown). @@ -469,27 +511,24 @@ static const struct rpc_call_ops nfs_rea static void nfs_readpage_result_full(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; - unsigned int count = data->res.count; + /* + * Note: nfs_readpage_result may change the values of + * data->args. In the multi-page case, we therefore need + * to ensure that we call the next nfs_readpage_set_page_uptodate() + * first in the multi-page case. + */ + if (likely(task->tk_status >= 0)) { + nfs_readpage_truncate_uninitialised_page(data); + nfs_readpage_set_pages_uptodate(data); + } else + nfs_readpage_set_pages_error(data); if (nfs_readpage_result(task, data) != 0) return; while (!list_empty(&data->pages)) { struct nfs_page *req = nfs_list_entry(data->pages.next); - struct page *page = req->wb_page; - nfs_list_remove_request(req); - if (task->tk_status >= 0) { - if (count < PAGE_CACHE_SIZE) { - if (count < req->wb_bytes) - memclear_highpage_flush(page, - req->wb_pgbase + count, - req->wb_bytes - count); - count = 0; - } else - count -= PAGE_CACHE_SIZE; - SetPageUptodate(page); - } else - SetPageError(page); + nfs_list_remove_request(req); nfs_readpage_release(req); } } @@ -654,7 +693,7 @@ int nfs_readpages(struct file *filp, str return ret; } -int nfs_init_readpagecache(void) +int __init nfs_init_readpagecache(void) { nfs_rdata_cachep = kmem_cache_create("nfs_read_data", sizeof(struct nfs_read_data), diff --git a/fs/nfs/super.c b/fs/nfs/super.c new file mode 100644 index 0000000..e8a9bee --- /dev/null +++ b/fs/nfs/super.c @@ -0,0 +1,1537 @@ +/* + * linux/fs/nfs/super.c + * + * Copyright (C) 1992 Rick Sladkey + * + * nfs superblock handling functions + * + * Modularised by Alan Cox , while hacking some + * experimental NFS changes. Modularisation taken straight from SYS5 fs. + * + * Change to nfs_read_super() to permit NFS mounts to multi-homed hosts. + * J.S.Peatfield@damtp.cam.ac.uk + * + * Split from inode.c by David Howells + * + */ + +#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 "nfs4_fs.h" +#include "callback.h" +#include "delegation.h" +#include "iostat.h" +#include "internal.h" + +#define NFSDBG_FACILITY NFSDBG_VFS + +/* Maximum number of readahead requests + * FIXME: this should really be a sysctl so that users may tune it to suit + * their needs. People that do NFS over a slow network, might for + * instance want to reduce it to something closer to 1 for improved + * interactive response. + */ +#define NFS_MAX_READAHEAD (RPC_DEF_SLOT_TABLE - 1) + +/* + * RPC cruft for NFS + */ +static struct rpc_version * nfs_version[] = { + NULL, + NULL, + &nfs_version2, +#if defined(CONFIG_NFS_V3) + &nfs_version3, +#elif defined(CONFIG_NFS_V4) + NULL, +#endif +#if defined(CONFIG_NFS_V4) + &nfs_version4, +#endif +}; + +static struct rpc_program nfs_program = { + .name = "nfs", + .number = NFS_PROGRAM, + .nrvers = ARRAY_SIZE(nfs_version), + .version = nfs_version, + .stats = &nfs_rpcstat, + .pipe_dir_name = "/nfs", +}; + +struct rpc_stat nfs_rpcstat = { + .program = &nfs_program +}; + + +#ifdef CONFIG_NFS_V3_ACL +static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; +static struct rpc_version * nfsacl_version[] = { + [3] = &nfsacl_version3, +}; + +struct rpc_program nfsacl_program = { + .name = "nfsacl", + .number = NFS_ACL_PROGRAM, + .nrvers = ARRAY_SIZE(nfsacl_version), + .version = nfsacl_version, + .stats = &nfsacl_rpcstat, +}; +#endif /* CONFIG_NFS_V3_ACL */ + +static void nfs_umount_begin(struct vfsmount *, int); +static int nfs_statfs(struct dentry *, struct kstatfs *); +static int nfs_show_options(struct seq_file *, struct vfsmount *); +static int nfs_show_stats(struct seq_file *, struct vfsmount *); +static int nfs_get_sb(struct file_system_type *, int, const char *, void *, struct vfsmount *); +static int nfs_clone_nfs_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); +static void nfs_kill_super(struct super_block *); + +static struct file_system_type nfs_fs_type = { + .owner = THIS_MODULE, + .name = "nfs", + .get_sb = nfs_get_sb, + .kill_sb = nfs_kill_super, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + +struct file_system_type clone_nfs_fs_type = { + .owner = THIS_MODULE, + .name = "nfs", + .get_sb = nfs_clone_nfs_sb, + .kill_sb = nfs_kill_super, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + +static struct super_operations nfs_sops = { + .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, + .write_inode = nfs_write_inode, + .statfs = nfs_statfs, + .clear_inode = nfs_clear_inode, + .umount_begin = nfs_umount_begin, + .show_options = nfs_show_options, + .show_stats = nfs_show_stats, +}; + +#ifdef CONFIG_NFS_V4 +static int nfs4_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); +static int nfs_clone_nfs4_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); +static int nfs_referral_nfs4_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt); +static void nfs4_kill_super(struct super_block *sb); + +static struct file_system_type nfs4_fs_type = { + .owner = THIS_MODULE, + .name = "nfs4", + .get_sb = nfs4_get_sb, + .kill_sb = nfs4_kill_super, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + +struct file_system_type clone_nfs4_fs_type = { + .owner = THIS_MODULE, + .name = "nfs4", + .get_sb = nfs_clone_nfs4_sb, + .kill_sb = nfs4_kill_super, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + +struct file_system_type nfs_referral_nfs4_fs_type = { + .owner = THIS_MODULE, + .name = "nfs4", + .get_sb = nfs_referral_nfs4_sb, + .kill_sb = nfs4_kill_super, + .fs_flags = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA, +}; + +static struct super_operations nfs4_sops = { + .alloc_inode = nfs_alloc_inode, + .destroy_inode = nfs_destroy_inode, + .write_inode = nfs_write_inode, + .statfs = nfs_statfs, + .clear_inode = nfs4_clear_inode, + .umount_begin = nfs_umount_begin, + .show_options = nfs_show_options, + .show_stats = nfs_show_stats, +}; +#endif + +#ifdef CONFIG_NFS_V4 +static const int nfs_set_port_min = 0; +static const int nfs_set_port_max = 65535; + +static int param_set_port(const char *val, struct kernel_param *kp) +{ + char *endp; + int num = simple_strtol(val, &endp, 0); + if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max) + return -EINVAL; + *((int *)kp->arg) = num; + return 0; +} + +module_param_call(callback_tcpport, param_set_port, param_get_int, + &nfs_callback_set_tcpport, 0644); +#endif + +#ifdef CONFIG_NFS_V4 +static int param_set_idmap_timeout(const char *val, struct kernel_param *kp) +{ + char *endp; + int num = simple_strtol(val, &endp, 0); + int jif = num * HZ; + if (endp == val || *endp || num < 0 || jif < num) + return -EINVAL; + *((int *)kp->arg) = jif; + return 0; +} + +module_param_call(idmap_cache_timeout, param_set_idmap_timeout, param_get_int, + &nfs_idmap_cache_timeout, 0644); +#endif + +/* + * Register the NFS filesystems + */ +int __init register_nfs_fs(void) +{ + int ret; + + ret = register_filesystem(&nfs_fs_type); + if (ret < 0) + goto error_0; + +#ifdef CONFIG_NFS_V4 + ret = nfs_register_sysctl(); + if (ret < 0) + goto error_1; + ret = register_filesystem(&nfs4_fs_type); + if (ret < 0) + goto error_2; +#endif + return 0; + +#ifdef CONFIG_NFS_V4 +error_2: + nfs_unregister_sysctl(); +error_1: + unregister_filesystem(&nfs_fs_type); +#endif +error_0: + return ret; +} + +/* + * Unregister the NFS filesystems + */ +void __exit unregister_nfs_fs(void) +{ +#ifdef CONFIG_NFS_V4 + unregister_filesystem(&nfs4_fs_type); + nfs_unregister_sysctl(); +#endif + unregister_filesystem(&nfs_fs_type); +} + +/* + * Deliver file system statistics to userspace + */ +static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct nfs_server *server = NFS_SB(sb); + unsigned char blockbits; + unsigned long blockres; + struct nfs_fh *rootfh = NFS_FH(sb->s_root->d_inode); + struct nfs_fattr fattr; + struct nfs_fsstat res = { + .fattr = &fattr, + }; + int error; + + lock_kernel(); + + error = server->rpc_ops->statfs(server, rootfh, &res); + buf->f_type = NFS_SUPER_MAGIC; + if (error < 0) + goto out_err; + + /* + * Current versions of glibc do not correctly handle the + * case where f_frsize != f_bsize. Eventually we want to + * report the value of wtmult in this field. + */ + buf->f_frsize = sb->s_blocksize; + + /* + * On most *nix systems, f_blocks, f_bfree, and f_bavail + * are reported in units of f_frsize. Linux hasn't had + * an f_frsize field in its statfs struct until recently, + * thus historically Linux's sys_statfs reports these + * fields in units of f_bsize. + */ + buf->f_bsize = sb->s_blocksize; + blockbits = sb->s_blocksize_bits; + blockres = (1 << blockbits) - 1; + buf->f_blocks = (res.tbytes + blockres) >> blockbits; + buf->f_bfree = (res.fbytes + blockres) >> blockbits; + buf->f_bavail = (res.abytes + blockres) >> blockbits; + + buf->f_files = res.tfiles; + buf->f_ffree = res.afiles; + + buf->f_namelen = server->namelen; + out: + unlock_kernel(); + return 0; + + out_err: + dprintk("%s: statfs error = %d\n", __FUNCTION__, -error); + buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; + goto out; + +} + +static const char *nfs_pseudoflavour_to_name(rpc_authflavor_t flavour) +{ + static struct { + rpc_authflavor_t flavour; + const char *str; + } sec_flavours[] = { + { RPC_AUTH_NULL, "null" }, + { RPC_AUTH_UNIX, "sys" }, + { RPC_AUTH_GSS_KRB5, "krb5" }, + { RPC_AUTH_GSS_KRB5I, "krb5i" }, + { RPC_AUTH_GSS_KRB5P, "krb5p" }, + { RPC_AUTH_GSS_LKEY, "lkey" }, + { RPC_AUTH_GSS_LKEYI, "lkeyi" }, + { RPC_AUTH_GSS_LKEYP, "lkeyp" }, + { RPC_AUTH_GSS_SPKM, "spkm" }, + { RPC_AUTH_GSS_SPKMI, "spkmi" }, + { RPC_AUTH_GSS_SPKMP, "spkmp" }, + { -1, "unknown" } + }; + int i; + + for (i=0; sec_flavours[i].flavour != -1; i++) { + if (sec_flavours[i].flavour == flavour) + break; + } + return sec_flavours[i].str; +} + +/* + * Describe the mount options in force on this server representation + */ +static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults) +{ + static struct proc_nfs_info { + int flag; + char *str; + char *nostr; + } nfs_info[] = { + { NFS_MOUNT_SOFT, ",soft", ",hard" }, + { NFS_MOUNT_INTR, ",intr", "" }, + { NFS_MOUNT_NOCTO, ",nocto", "" }, + { NFS_MOUNT_NOAC, ",noac", "" }, + { NFS_MOUNT_NONLM, ",nolock", "" }, + { NFS_MOUNT_NOACL, ",noacl", "" }, + { 0, NULL, NULL } + }; + struct proc_nfs_info *nfs_infop; + char buf[12]; + char *proto; + + seq_printf(m, ",vers=%d", nfss->rpc_ops->version); + seq_printf(m, ",rsize=%d", nfss->rsize); + seq_printf(m, ",wsize=%d", nfss->wsize); + if (nfss->acregmin != 3*HZ || showdefaults) + seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ); + if (nfss->acregmax != 60*HZ || showdefaults) + seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ); + if (nfss->acdirmin != 30*HZ || showdefaults) + seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ); + if (nfss->acdirmax != 60*HZ || showdefaults) + seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ); + for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) { + if (nfss->flags & nfs_infop->flag) + seq_puts(m, nfs_infop->str); + else + seq_puts(m, nfs_infop->nostr); + } + switch (nfss->client->cl_xprt->prot) { + case IPPROTO_TCP: + proto = "tcp"; + break; + case IPPROTO_UDP: + proto = "udp"; + break; + default: + snprintf(buf, sizeof(buf), "%u", nfss->client->cl_xprt->prot); + proto = buf; + } + seq_printf(m, ",proto=%s", proto); + seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ); + seq_printf(m, ",retrans=%u", nfss->retrans_count); + seq_printf(m, ",sec=%s", nfs_pseudoflavour_to_name(nfss->client->cl_auth->au_flavor)); +} + +/* + * Describe the mount options on this VFS mountpoint + */ +static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); + + nfs_show_mount_options(m, nfss, 0); + + seq_puts(m, ",addr="); + seq_escape(m, nfss->hostname, " \t\n\\"); + + return 0; +} + +/* + * Present statistical information for this VFS mountpoint + */ +static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt) +{ + int i, cpu; + struct nfs_server *nfss = NFS_SB(mnt->mnt_sb); + struct rpc_auth *auth = nfss->client->cl_auth; + struct nfs_iostats totals = { }; + + seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS); + + /* + * Display all mount option settings + */ + seq_printf(m, "\n\topts:\t"); + seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw"); + seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : ""); + seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : ""); + seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : ""); + nfs_show_mount_options(m, nfss, 1); + + seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ); + + seq_printf(m, "\n\tcaps:\t"); + seq_printf(m, "caps=0x%x", nfss->caps); + seq_printf(m, ",wtmult=%d", nfss->wtmult); + seq_printf(m, ",dtsize=%d", nfss->dtsize); + seq_printf(m, ",bsize=%d", nfss->bsize); + seq_printf(m, ",namelen=%d", nfss->namelen); + +#ifdef CONFIG_NFS_V4 + if (nfss->rpc_ops->version == 4) { + seq_printf(m, "\n\tnfsv4:\t"); + seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]); + seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]); + seq_printf(m, ",acl=0x%x", nfss->acl_bitmask); + } +#endif + + /* + * Display security flavor in effect for this mount + */ + seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor); + if (auth->au_flavor) + seq_printf(m, ",pseudoflavor=%d", auth->au_flavor); + + /* + * Display superblock I/O counters + */ + for_each_possible_cpu(cpu) { + struct nfs_iostats *stats; + + preempt_disable(); + stats = per_cpu_ptr(nfss->io_stats, cpu); + + for (i = 0; i < __NFSIOS_COUNTSMAX; i++) + totals.events[i] += stats->events[i]; + for (i = 0; i < __NFSIOS_BYTESMAX; i++) + totals.bytes[i] += stats->bytes[i]; + + preempt_enable(); + } + + seq_printf(m, "\n\tevents:\t"); + for (i = 0; i < __NFSIOS_COUNTSMAX; i++) + seq_printf(m, "%lu ", totals.events[i]); + seq_printf(m, "\n\tbytes:\t"); + for (i = 0; i < __NFSIOS_BYTESMAX; i++) + seq_printf(m, "%Lu ", totals.bytes[i]); + seq_printf(m, "\n"); + + rpc_print_iostats(m, nfss->client); + + return 0; +} + +/* + * Begin unmount by attempting to remove all automounted mountpoints we added + * in response to traversals + */ +static void nfs_umount_begin(struct vfsmount *vfsmnt, int flags) +{ + struct nfs_server *server; + struct rpc_clnt *rpc; + + shrink_submounts(vfsmnt, &nfs_automount_list); + if (!(flags & MNT_FORCE)) + return; + /* -EIO all pending I/O */ + server = NFS_SB(vfsmnt->mnt_sb); + rpc = server->client; + if (!IS_ERR(rpc)) + rpc_killall_tasks(rpc); + rpc = server->client_acl; + if (!IS_ERR(rpc)) + rpc_killall_tasks(rpc); +} + +/* + * Obtain the root inode of the file system. + */ +static struct inode * +nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo) +{ + struct nfs_server *server = NFS_SB(sb); + int error; + + error = server->rpc_ops->getroot(server, rootfh, fsinfo); + if (error < 0) { + dprintk("nfs_get_root: getattr error = %d\n", -error); + return ERR_PTR(error); + } + + server->fsid = fsinfo->fattr->fsid; + return nfs_fhget(sb, rootfh, fsinfo->fattr); +} + +/* + * Do NFS version-independent mount processing, and sanity checking + */ +static int +nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor) +{ + struct nfs_server *server; + struct inode *root_inode; + struct nfs_fattr fattr; + struct nfs_fsinfo fsinfo = { + .fattr = &fattr, + }; + struct nfs_pathconf pathinfo = { + .fattr = &fattr, + }; + int no_root_error = 0; + unsigned long max_rpc_payload; + + /* We probably want something more informative here */ + snprintf(sb->s_id, sizeof(sb->s_id), "%x:%x", MAJOR(sb->s_dev), MINOR(sb->s_dev)); + + server = NFS_SB(sb); + + sb->s_magic = NFS_SUPER_MAGIC; + + server->io_stats = nfs_alloc_iostats(); + if (server->io_stats == NULL) + return -ENOMEM; + + root_inode = nfs_get_root(sb, &server->fh, &fsinfo); + /* Did getting the root inode fail? */ + if (IS_ERR(root_inode)) { + no_root_error = PTR_ERR(root_inode); + goto out_no_root; + } + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) { + no_root_error = -ENOMEM; + goto out_no_root; + } + sb->s_root->d_op = server->rpc_ops->dentry_ops; + + /* mount time stamp, in seconds */ + server->mount_time = jiffies; + + /* Get some general file system info */ + if (server->namelen == 0 && + server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0) + server->namelen = pathinfo.max_namelen; + /* Work out a lot of parameters */ + if (server->rsize == 0) + server->rsize = nfs_block_size(fsinfo.rtpref, NULL); + if (server->wsize == 0) + server->wsize = nfs_block_size(fsinfo.wtpref, NULL); + + if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax) + server->rsize = nfs_block_size(fsinfo.rtmax, NULL); + if (fsinfo.wtmax >= 512 && server->wsize > fsinfo.wtmax) + server->wsize = nfs_block_size(fsinfo.wtmax, NULL); + + max_rpc_payload = nfs_block_size(rpc_max_payload(server->client), NULL); + if (server->rsize > max_rpc_payload) + server->rsize = max_rpc_payload; + if (server->rsize > NFS_MAX_FILE_IO_SIZE) + server->rsize = NFS_MAX_FILE_IO_SIZE; + server->rpages = (server->rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + + if (server->wsize > max_rpc_payload) + server->wsize = max_rpc_payload; + if (server->wsize > NFS_MAX_FILE_IO_SIZE) + server->wsize = NFS_MAX_FILE_IO_SIZE; + server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + + if (sb->s_blocksize == 0) + sb->s_blocksize = nfs_block_bits(server->wsize, + &sb->s_blocksize_bits); + server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL); + + server->dtsize = nfs_block_size(fsinfo.dtpref, NULL); + if (server->dtsize > PAGE_CACHE_SIZE) + server->dtsize = PAGE_CACHE_SIZE; + if (server->dtsize > server->rsize) + server->dtsize = server->rsize; + + if (server->flags & NFS_MOUNT_NOAC) { + server->acregmin = server->acregmax = 0; + server->acdirmin = server->acdirmax = 0; + sb->s_flags |= MS_SYNCHRONOUS; + } + server->backing_dev_info.ra_pages = server->rpages * NFS_MAX_READAHEAD; + + nfs_super_set_maxbytes(sb, fsinfo.maxfilesize); + + server->client->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; + server->client->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; + + /* We're airborne Set socket buffersize */ + rpc_setbufsize(server->client, server->wsize + 100, server->rsize + 100); + return 0; + /* Yargs. It didn't work out. */ +out_no_root: + dprintk("nfs_sb_init: get root inode failed: errno %d\n", -no_root_error); + if (!IS_ERR(root_inode)) + iput(root_inode); + return no_root_error; +} + +/* + * Initialise the timeout values for a connection + */ +static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, unsigned int timeo, unsigned int retrans) +{ + to->to_initval = timeo * HZ / 10; + to->to_retries = retrans; + if (!to->to_retries) + to->to_retries = 2; + + switch (proto) { + case IPPROTO_TCP: + if (!to->to_initval) + to->to_initval = 60 * HZ; + if (to->to_initval > NFS_MAX_TCP_TIMEOUT) + to->to_initval = NFS_MAX_TCP_TIMEOUT; + to->to_increment = to->to_initval; + to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); + to->to_exponential = 0; + break; + case IPPROTO_UDP: + default: + if (!to->to_initval) + to->to_initval = 11 * HZ / 10; + if (to->to_initval > NFS_MAX_UDP_TIMEOUT) + to->to_initval = NFS_MAX_UDP_TIMEOUT; + to->to_maxval = NFS_MAX_UDP_TIMEOUT; + to->to_exponential = 1; + break; + } +} + +/* + * Create an RPC client handle. + */ +static struct rpc_clnt * +nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data) +{ + struct rpc_timeout timeparms; + struct rpc_xprt *xprt = NULL; + struct rpc_clnt *clnt = NULL; + int proto = (data->flags & NFS_MOUNT_TCP) ? IPPROTO_TCP : IPPROTO_UDP; + + nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans); + + server->retrans_timeo = timeparms.to_initval; + server->retrans_count = timeparms.to_retries; + + /* create transport and client */ + xprt = xprt_create_proto(proto, &server->addr, &timeparms); + if (IS_ERR(xprt)) { + dprintk("%s: cannot create RPC transport. Error = %ld\n", + __FUNCTION__, PTR_ERR(xprt)); + return (struct rpc_clnt *)xprt; + } + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + server->rpc_ops->version, data->pseudoflavor); + if (IS_ERR(clnt)) { + dprintk("%s: cannot create RPC client. Error = %ld\n", + __FUNCTION__, PTR_ERR(xprt)); + goto out_fail; + } + + clnt->cl_intr = 1; + clnt->cl_softrtry = 1; + + return clnt; + +out_fail: + return clnt; +} + +/* + * Clone a server record + */ +static struct nfs_server *nfs_clone_server(struct super_block *sb, struct nfs_clone_mount *data) +{ + struct nfs_server *server = NFS_SB(sb); + struct nfs_server *parent = NFS_SB(data->sb); + struct inode *root_inode; + struct nfs_fsinfo fsinfo; + void *err = ERR_PTR(-ENOMEM); + + sb->s_op = data->sb->s_op; + sb->s_blocksize = data->sb->s_blocksize; + sb->s_blocksize_bits = data->sb->s_blocksize_bits; + sb->s_maxbytes = data->sb->s_maxbytes; + + server->client_sys = server->client_acl = ERR_PTR(-EINVAL); + server->io_stats = nfs_alloc_iostats(); + if (server->io_stats == NULL) + goto out; + + server->client = rpc_clone_client(parent->client); + if (IS_ERR((err = server->client))) + goto out; + + if (!IS_ERR(parent->client_sys)) { + server->client_sys = rpc_clone_client(parent->client_sys); + if (IS_ERR((err = server->client_sys))) + goto out; + } + if (!IS_ERR(parent->client_acl)) { + server->client_acl = rpc_clone_client(parent->client_acl); + if (IS_ERR((err = server->client_acl))) + goto out; + } + root_inode = nfs_fhget(sb, data->fh, data->fattr); + if (!root_inode) + goto out; + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) + goto out_put_root; + fsinfo.fattr = data->fattr; + if (NFS_PROTO(root_inode)->fsinfo(server, data->fh, &fsinfo) == 0) + nfs_super_set_maxbytes(sb, fsinfo.maxfilesize); + sb->s_root->d_op = server->rpc_ops->dentry_ops; + sb->s_flags |= MS_ACTIVE; + return server; +out_put_root: + iput(root_inode); +out: + return err; +} + +/* + * Copy an existing superblock and attach revised data + */ +static int nfs_clone_generic_sb(struct nfs_clone_mount *data, + struct super_block *(*fill_sb)(struct nfs_server *, struct nfs_clone_mount *), + struct nfs_server *(*fill_server)(struct super_block *, struct nfs_clone_mount *), + struct vfsmount *mnt) +{ + struct nfs_server *server; + struct nfs_server *parent = NFS_SB(data->sb); + struct super_block *sb = ERR_PTR(-EINVAL); + char *hostname; + int error = -ENOMEM; + int len; + + server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL); + if (server == NULL) + goto out_err; + memcpy(server, parent, sizeof(*server)); + hostname = (data->hostname != NULL) ? data->hostname : parent->hostname; + len = strlen(hostname) + 1; + server->hostname = kmalloc(len, GFP_KERNEL); + if (server->hostname == NULL) + goto free_server; + memcpy(server->hostname, hostname, len); + error = rpciod_up(); + if (error != 0) + goto free_hostname; + + sb = fill_sb(server, data); + if (IS_ERR(sb)) { + error = PTR_ERR(sb); + goto kill_rpciod; + } + + if (sb->s_root) + goto out_rpciod_down; + + server = fill_server(sb, data); + if (IS_ERR(server)) { + error = PTR_ERR(server); + goto out_deactivate; + } + return simple_set_mnt(mnt, sb); +out_deactivate: + up_write(&sb->s_umount); + deactivate_super(sb); + return error; +out_rpciod_down: + rpciod_down(); + kfree(server->hostname); + kfree(server); + return simple_set_mnt(mnt, sb); +kill_rpciod: + rpciod_down(); +free_hostname: + kfree(server->hostname); +free_server: + kfree(server); +out_err: + return error; +} + +/* + * Set up an NFS2/3 superblock + * + * The way this works is that the mount process passes a structure + * in the data argument which contains the server's IP address + * and the root file handle obtained from the server's mount + * daemon. We stash these away in the private superblock fields. + */ +static int +nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) +{ + struct nfs_server *server; + rpc_authflavor_t authflavor; + + server = NFS_SB(sb); + sb->s_blocksize_bits = 0; + sb->s_blocksize = 0; + if (data->bsize) + sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); + if (data->rsize) + server->rsize = nfs_block_size(data->rsize, NULL); + if (data->wsize) + server->wsize = nfs_block_size(data->wsize, NULL); + server->flags = data->flags & NFS_MOUNT_FLAGMASK; + + server->acregmin = data->acregmin*HZ; + server->acregmax = data->acregmax*HZ; + server->acdirmin = data->acdirmin*HZ; + server->acdirmax = data->acdirmax*HZ; + + /* Start lockd here, before we might error out */ + if (!(server->flags & NFS_MOUNT_NONLM)) + lockd_up(); + + server->namelen = data->namlen; + server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL); + if (!server->hostname) + return -ENOMEM; + strcpy(server->hostname, data->hostname); + + /* Check NFS protocol revision and initialize RPC op vector + * and file handle pool. */ +#ifdef CONFIG_NFS_V3 + if (server->flags & NFS_MOUNT_VER3) { + server->rpc_ops = &nfs_v3_clientops; + server->caps |= NFS_CAP_READDIRPLUS; + } else { + server->rpc_ops = &nfs_v2_clientops; + } +#else + server->rpc_ops = &nfs_v2_clientops; +#endif + + /* Fill in pseudoflavor for mount version < 5 */ + if (!(data->flags & NFS_MOUNT_SECFLAVOUR)) + data->pseudoflavor = RPC_AUTH_UNIX; + authflavor = data->pseudoflavor; /* save for sb_init() */ + /* XXX maybe we want to add a server->pseudoflavor field */ + + /* Create RPC client handles */ + server->client = nfs_create_client(server, data); + if (IS_ERR(server->client)) + return PTR_ERR(server->client); + /* RFC 2623, sec 2.3.2 */ + if (authflavor != RPC_AUTH_UNIX) { + struct rpc_auth *auth; + + server->client_sys = rpc_clone_client(server->client); + if (IS_ERR(server->client_sys)) + return PTR_ERR(server->client_sys); + auth = rpcauth_create(RPC_AUTH_UNIX, server->client_sys); + if (IS_ERR(auth)) + return PTR_ERR(auth); + } else { + atomic_inc(&server->client->cl_count); + server->client_sys = server->client; + } + if (server->flags & NFS_MOUNT_VER3) { +#ifdef CONFIG_NFS_V3_ACL + if (!(server->flags & NFS_MOUNT_NOACL)) { + server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); + /* No errors! Assume that Sun nfsacls are supported */ + if (!IS_ERR(server->client_acl)) + server->caps |= NFS_CAP_ACLS; + } +#else + server->flags &= ~NFS_MOUNT_NOACL; +#endif /* CONFIG_NFS_V3_ACL */ + /* + * The VFS shouldn't apply the umask to mode bits. We will + * do so ourselves when necessary. + */ + sb->s_flags |= MS_POSIXACL; + if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) + server->namelen = NFS3_MAXNAMLEN; + sb->s_time_gran = 1; + } else { + if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN) + server->namelen = NFS2_MAXNAMLEN; + } + + sb->s_op = &nfs_sops; + return nfs_sb_init(sb, authflavor); +} + +static int nfs_set_super(struct super_block *s, void *data) +{ + s->s_fs_info = data; + return set_anon_super(s, data); +} + +static int nfs_compare_super(struct super_block *sb, void *data) +{ + struct nfs_server *server = data; + struct nfs_server *old = NFS_SB(sb); + + if (old->addr.sin_addr.s_addr != server->addr.sin_addr.s_addr) + return 0; + if (old->addr.sin_port != server->addr.sin_port) + return 0; + return !nfs_compare_fh(&old->fh, &server->fh); +} + +static int nfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) +{ + int error; + struct nfs_server *server = NULL; + struct super_block *s; + struct nfs_fh *root; + struct nfs_mount_data *data = raw_data; + + error = -EINVAL; + if (data == NULL) { + dprintk("%s: missing data argument\n", __FUNCTION__); + goto out_err_noserver; + } + if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) { + dprintk("%s: bad mount version\n", __FUNCTION__); + goto out_err_noserver; + } + switch (data->version) { + case 1: + data->namlen = 0; + case 2: + data->bsize = 0; + case 3: + if (data->flags & NFS_MOUNT_VER3) { + dprintk("%s: mount structure version %d does not support NFSv3\n", + __FUNCTION__, + data->version); + goto out_err_noserver; + } + data->root.size = NFS2_FHSIZE; + memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE); + case 4: + if (data->flags & NFS_MOUNT_SECFLAVOUR) { + dprintk("%s: mount structure version %d does not support strong security\n", + __FUNCTION__, + data->version); + goto out_err_noserver; + } + case 5: + memset(data->context, 0, sizeof(data->context)); + } +#ifndef CONFIG_NFS_V3 + /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */ + error = -EPROTONOSUPPORT; + if (data->flags & NFS_MOUNT_VER3) { + dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__); + goto out_err_noserver; + } +#endif /* CONFIG_NFS_V3 */ + + error = -ENOMEM; + server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); + if (!server) + goto out_err_noserver; + /* Zero out the NFS state stuff */ + init_nfsv4_state(server); + server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); + + root = &server->fh; + if (data->flags & NFS_MOUNT_VER3) + root->size = data->root.size; + else + root->size = NFS2_FHSIZE; + error = -EINVAL; + if (root->size > sizeof(root->data)) { + dprintk("%s: invalid root filehandle\n", __FUNCTION__); + goto out_err; + } + memcpy(root->data, data->root.data, root->size); + + /* We now require that the mount process passes the remote address */ + memcpy(&server->addr, &data->addr, sizeof(server->addr)); + if (server->addr.sin_addr.s_addr == INADDR_ANY) { + dprintk("%s: mount program didn't pass remote address!\n", + __FUNCTION__); + goto out_err; + } + + /* Fire up rpciod if not yet running */ + error = rpciod_up(); + if (error < 0) { + dprintk("%s: couldn't start rpciod! Error = %d\n", + __FUNCTION__, error); + goto out_err; + } + + s = sget(fs_type, nfs_compare_super, nfs_set_super, server); + if (IS_ERR(s)) { + error = PTR_ERR(s); + goto out_err_rpciod; + } + + if (s->s_root) + goto out_rpciod_down; + + s->s_flags = flags; + + error = nfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0); + if (error) { + up_write(&s->s_umount); + deactivate_super(s); + return error; + } + s->s_flags |= MS_ACTIVE; + return simple_set_mnt(mnt, s); + +out_rpciod_down: + rpciod_down(); + kfree(server); + return simple_set_mnt(mnt, s); + +out_err_rpciod: + rpciod_down(); +out_err: + kfree(server); +out_err_noserver: + return error; +} + +static void nfs_kill_super(struct super_block *s) +{ + struct nfs_server *server = NFS_SB(s); + + kill_anon_super(s); + + if (!IS_ERR(server->client)) + rpc_shutdown_client(server->client); + if (!IS_ERR(server->client_sys)) + rpc_shutdown_client(server->client_sys); + if (!IS_ERR(server->client_acl)) + rpc_shutdown_client(server->client_acl); + + if (!(server->flags & NFS_MOUNT_NONLM)) + lockd_down(); /* release rpc.lockd */ + + rpciod_down(); /* release rpciod */ + + nfs_free_iostats(server->io_stats); + kfree(server->hostname); + kfree(server); + nfs_release_automount_timer(); +} + +static struct super_block *nfs_clone_sb(struct nfs_server *server, struct nfs_clone_mount *data) +{ + struct super_block *sb; + + server->fsid = data->fattr->fsid; + nfs_copy_fh(&server->fh, data->fh); + sb = sget(&nfs_fs_type, nfs_compare_super, nfs_set_super, server); + if (!IS_ERR(sb) && sb->s_root == NULL && !(server->flags & NFS_MOUNT_NONLM)) + lockd_up(); + return sb; +} + +static int nfs_clone_nfs_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) +{ + struct nfs_clone_mount *data = raw_data; + return nfs_clone_generic_sb(data, nfs_clone_sb, nfs_clone_server, mnt); +} + +#ifdef CONFIG_NFS_V4 +static struct rpc_clnt *nfs4_create_client(struct nfs_server *server, + struct rpc_timeout *timeparms, int proto, rpc_authflavor_t flavor) +{ + struct nfs4_client *clp; + struct rpc_xprt *xprt = NULL; + struct rpc_clnt *clnt = NULL; + int err = -EIO; + + clp = nfs4_get_client(&server->addr.sin_addr); + if (!clp) { + dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__); + return ERR_PTR(err); + } + + /* Now create transport and client */ + down_write(&clp->cl_sem); + if (IS_ERR(clp->cl_rpcclient)) { + xprt = xprt_create_proto(proto, &server->addr, timeparms); + if (IS_ERR(xprt)) { + up_write(&clp->cl_sem); + err = PTR_ERR(xprt); + dprintk("%s: cannot create RPC transport. Error = %d\n", + __FUNCTION__, err); + goto out_fail; + } + /* Bind to a reserved port! */ + xprt->resvport = 1; + clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + server->rpc_ops->version, flavor); + if (IS_ERR(clnt)) { + up_write(&clp->cl_sem); + err = PTR_ERR(clnt); + dprintk("%s: cannot create RPC client. Error = %d\n", + __FUNCTION__, err); + goto out_fail; + } + clnt->cl_intr = 1; + clnt->cl_softrtry = 1; + clp->cl_rpcclient = clnt; + memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr)); + nfs_idmap_new(clp); + } + list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); + clnt = rpc_clone_client(clp->cl_rpcclient); + if (!IS_ERR(clnt)) + server->nfs4_state = clp; + up_write(&clp->cl_sem); + clp = NULL; + + if (IS_ERR(clnt)) { + dprintk("%s: cannot create RPC client. Error = %d\n", + __FUNCTION__, err); + return clnt; + } + + if (server->nfs4_state->cl_idmap == NULL) { + dprintk("%s: failed to create idmapper.\n", __FUNCTION__); + return ERR_PTR(-ENOMEM); + } + + if (clnt->cl_auth->au_flavor != flavor) { + struct rpc_auth *auth; + + auth = rpcauth_create(flavor, clnt); + if (IS_ERR(auth)) { + dprintk("%s: couldn't create credcache!\n", __FUNCTION__); + return (struct rpc_clnt *)auth; + } + } + return clnt; + + out_fail: + if (clp) + nfs4_put_client(clp); + return ERR_PTR(err); +} + +/* + * Set up an NFS4 superblock + */ +static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) +{ + struct nfs_server *server; + struct rpc_timeout timeparms; + rpc_authflavor_t authflavour; + int err = -EIO; + + sb->s_blocksize_bits = 0; + sb->s_blocksize = 0; + server = NFS_SB(sb); + if (data->rsize != 0) + server->rsize = nfs_block_size(data->rsize, NULL); + if (data->wsize != 0) + server->wsize = nfs_block_size(data->wsize, NULL); + server->flags = data->flags & NFS_MOUNT_FLAGMASK; + server->caps = NFS_CAP_ATOMIC_OPEN; + + server->acregmin = data->acregmin*HZ; + server->acregmax = data->acregmax*HZ; + server->acdirmin = data->acdirmin*HZ; + server->acdirmax = data->acdirmax*HZ; + + server->rpc_ops = &nfs_v4_clientops; + + nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans); + + server->retrans_timeo = timeparms.to_initval; + server->retrans_count = timeparms.to_retries; + + /* Now create transport and client */ + authflavour = RPC_AUTH_UNIX; + if (data->auth_flavourlen != 0) { + if (data->auth_flavourlen != 1) { + dprintk("%s: Invalid number of RPC auth flavours %d.\n", + __FUNCTION__, data->auth_flavourlen); + err = -EINVAL; + goto out_fail; + } + if (copy_from_user(&authflavour, data->auth_flavours, sizeof(authflavour))) { + err = -EFAULT; + goto out_fail; + } + } + + server->client = nfs4_create_client(server, &timeparms, data->proto, authflavour); + if (IS_ERR(server->client)) { + err = PTR_ERR(server->client); + dprintk("%s: cannot create RPC client. Error = %d\n", + __FUNCTION__, err); + goto out_fail; + } + + sb->s_time_gran = 1; + + sb->s_op = &nfs4_sops; + err = nfs_sb_init(sb, authflavour); + + out_fail: + return err; +} + +static int nfs4_compare_super(struct super_block *sb, void *data) +{ + struct nfs_server *server = data; + struct nfs_server *old = NFS_SB(sb); + + if (strcmp(server->hostname, old->hostname) != 0) + return 0; + if (strcmp(server->mnt_path, old->mnt_path) != 0) + return 0; + return 1; +} + +static void * +nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen) +{ + void *p = NULL; + + if (!src->len) + return ERR_PTR(-EINVAL); + if (src->len < maxlen) + maxlen = src->len; + if (dst == NULL) { + p = dst = kmalloc(maxlen + 1, GFP_KERNEL); + if (p == NULL) + return ERR_PTR(-ENOMEM); + } + if (copy_from_user(dst, src->data, maxlen)) { + kfree(p); + return ERR_PTR(-EFAULT); + } + dst[maxlen] = '\0'; + return dst; +} + +static int nfs4_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) +{ + int error; + struct nfs_server *server; + struct super_block *s; + struct nfs4_mount_data *data = raw_data; + void *p; + + if (data == NULL) { + dprintk("%s: missing data argument\n", __FUNCTION__); + return -EINVAL; + } + if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) { + dprintk("%s: bad mount version\n", __FUNCTION__); + return -EINVAL; + } + + server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL); + if (!server) + return -ENOMEM; + /* Zero out the NFS state stuff */ + init_nfsv4_state(server); + server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); + + p = nfs_copy_user_string(NULL, &data->hostname, 256); + if (IS_ERR(p)) + goto out_err; + server->hostname = p; + + p = nfs_copy_user_string(NULL, &data->mnt_path, 1024); + if (IS_ERR(p)) + goto out_err; + server->mnt_path = p; + + p = nfs_copy_user_string(server->ip_addr, &data->client_addr, + sizeof(server->ip_addr) - 1); + if (IS_ERR(p)) + goto out_err; + + /* We now require that the mount process passes the remote address */ + if (data->host_addrlen != sizeof(server->addr)) { + error = -EINVAL; + goto out_free; + } + if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) { + error = -EFAULT; + goto out_free; + } + if (server->addr.sin_family != AF_INET || + server->addr.sin_addr.s_addr == INADDR_ANY) { + dprintk("%s: mount program didn't pass remote IP address!\n", + __FUNCTION__); + error = -EINVAL; + goto out_free; + } + + /* Fire up rpciod if not yet running */ + error = rpciod_up(); + if (error < 0) { + dprintk("%s: couldn't start rpciod! Error = %d\n", + __FUNCTION__, error); + goto out_free; + } + + s = sget(fs_type, nfs4_compare_super, nfs_set_super, server); + + if (IS_ERR(s)) { + error = PTR_ERR(s); + goto out_free; + } + + if (s->s_root) { + kfree(server->mnt_path); + kfree(server->hostname); + kfree(server); + return simple_set_mnt(mnt, s); + } + + s->s_flags = flags; + + error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0); + if (error) { + up_write(&s->s_umount); + deactivate_super(s); + return error; + } + s->s_flags |= MS_ACTIVE; + return simple_set_mnt(mnt, s); +out_err: + error = PTR_ERR(p); +out_free: + kfree(server->mnt_path); + kfree(server->hostname); + kfree(server); + return error; +} + +static void nfs4_kill_super(struct super_block *sb) +{ + struct nfs_server *server = NFS_SB(sb); + + nfs_return_all_delegations(sb); + kill_anon_super(sb); + + nfs4_renewd_prepare_shutdown(server); + + if (server->client != NULL && !IS_ERR(server->client)) + rpc_shutdown_client(server->client); + + destroy_nfsv4_state(server); + + rpciod_down(); + + nfs_free_iostats(server->io_stats); + kfree(server->hostname); + kfree(server); + nfs_release_automount_timer(); +} + +/* + * Constructs the SERVER-side path + */ +static inline char *nfs4_dup_path(const struct dentry *dentry) +{ + char *page = (char *) __get_free_page(GFP_USER); + char *path; + + path = nfs4_path(dentry, page, PAGE_SIZE); + if (!IS_ERR(path)) { + int len = PAGE_SIZE + page - path; + char *tmp = path; + + path = kmalloc(len, GFP_KERNEL); + if (path) + memcpy(path, tmp, len); + else + path = ERR_PTR(-ENOMEM); + } + free_page((unsigned long)page); + return path; +} + +static struct super_block *nfs4_clone_sb(struct nfs_server *server, struct nfs_clone_mount *data) +{ + const struct dentry *dentry = data->dentry; + struct nfs4_client *clp = server->nfs4_state; + struct super_block *sb; + + server->fsid = data->fattr->fsid; + nfs_copy_fh(&server->fh, data->fh); + server->mnt_path = nfs4_dup_path(dentry); + if (IS_ERR(server->mnt_path)) { + sb = (struct super_block *)server->mnt_path; + goto err; + } + sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server); + if (IS_ERR(sb) || sb->s_root) + goto free_path; + nfs4_server_capabilities(server, &server->fh); + + down_write(&clp->cl_sem); + atomic_inc(&clp->cl_count); + list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks); + up_write(&clp->cl_sem); + return sb; +free_path: + kfree(server->mnt_path); +err: + server->mnt_path = NULL; + return sb; +} + +static int nfs_clone_nfs4_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) +{ + struct nfs_clone_mount *data = raw_data; + return nfs_clone_generic_sb(data, nfs4_clone_sb, nfs_clone_server, mnt); +} + +static struct super_block *nfs4_referral_sb(struct nfs_server *server, struct nfs_clone_mount *data) +{ + struct super_block *sb = ERR_PTR(-ENOMEM); + int len; + + len = strlen(data->mnt_path) + 1; + server->mnt_path = kmalloc(len, GFP_KERNEL); + if (server->mnt_path == NULL) + goto err; + memcpy(server->mnt_path, data->mnt_path, len); + memcpy(&server->addr, data->addr, sizeof(struct sockaddr_in)); + + sb = sget(&nfs4_fs_type, nfs4_compare_super, nfs_set_super, server); + if (IS_ERR(sb) || sb->s_root) + goto free_path; + return sb; +free_path: + kfree(server->mnt_path); +err: + server->mnt_path = NULL; + return sb; +} + +static struct nfs_server *nfs4_referral_server(struct super_block *sb, struct nfs_clone_mount *data) +{ + struct nfs_server *server = NFS_SB(sb); + struct rpc_timeout timeparms; + int proto, timeo, retrans; + void *err; + + proto = IPPROTO_TCP; + /* Since we are following a referral and there may be alternatives, + set the timeouts and retries to low values */ + timeo = 2; + retrans = 1; + nfs_init_timeout_values(&timeparms, proto, timeo, retrans); + + server->client = nfs4_create_client(server, &timeparms, proto, data->authflavor); + if (IS_ERR((err = server->client))) + goto out_err; + + sb->s_time_gran = 1; + sb->s_op = &nfs4_sops; + err = ERR_PTR(nfs_sb_init(sb, data->authflavor)); + if (!IS_ERR(err)) + return server; +out_err: + return (struct nfs_server *)err; +} + +static int nfs_referral_nfs4_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt) +{ + struct nfs_clone_mount *data = raw_data; + return nfs_clone_generic_sb(data, nfs4_referral_sb, nfs4_referral_server, mnt); +} + +#endif diff --git a/fs/nfs/symlink.c b/fs/nfs/symlink.c index 18dc95b..600bbe6 100644 --- a/fs/nfs/symlink.c +++ b/fs/nfs/symlink.c @@ -52,7 +52,7 @@ static void *nfs_follow_link(struct dent { struct inode *inode = dentry->d_inode; struct page *page; - void *err = ERR_PTR(nfs_revalidate_inode(NFS_SERVER(inode), inode)); + void *err = ERR_PTR(nfs_revalidate_mapping(inode, inode->i_mapping)); if (err) goto read_failed; page = read_cache_page(&inode->i_data, 0, @@ -75,22 +75,13 @@ read_failed: return NULL; } -static void nfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) -{ - if (cookie) { - struct page *page = cookie; - kunmap(page); - page_cache_release(page); - } -} - /* * symlinks can't do much... */ struct inode_operations nfs_symlink_inode_operations = { .readlink = generic_readlink, .follow_link = nfs_follow_link, - .put_link = nfs_put_link, + .put_link = page_put_link, .getattr = nfs_getattr, .setattr = nfs_setattr, }; diff --git a/fs/nfs/sysctl.c b/fs/nfs/sysctl.c index 4c486eb..2fe3403 100644 --- a/fs/nfs/sysctl.c +++ b/fs/nfs/sysctl.c @@ -3,7 +3,6 @@ * * Sysctl interface to NFS parameters */ -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include #include +#include #include "callback.h" @@ -46,6 +46,15 @@ #ifdef CONFIG_NFS_V4 .strategy = &sysctl_jiffies, }, #endif + { + .ctl_name = CTL_UNNUMBERED, + .procname = "nfs_mountpoint_timeout", + .data = &nfs_mountpoint_expiry_timeout, + .maxlen = sizeof(nfs_mountpoint_expiry_timeout), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, + }, { .ctl_name = 0 } }; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 4cfada2..86bac6a 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -46,7 +46,6 @@ * Copyright (C) 1996, 1997, Olaf Kirch */ -#include #include #include #include @@ -98,11 +97,10 @@ struct nfs_write_data *nfs_commit_alloc( if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); - if (pagecount < NFS_PAGEVEC_SIZE) - p->pagevec = &p->page_array[0]; + if (pagecount <= ARRAY_SIZE(p->page_array)) + p->pagevec = p->page_array; else { - size_t size = ++pagecount * sizeof(struct page *); - p->pagevec = kzalloc(size, GFP_NOFS); + p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); if (!p->pagevec) { mempool_free(p, nfs_commit_mempool); p = NULL; @@ -126,14 +124,11 @@ struct nfs_write_data *nfs_writedata_all if (p) { memset(p, 0, sizeof(*p)); INIT_LIST_HEAD(&p->pages); - if (pagecount < NFS_PAGEVEC_SIZE) - p->pagevec = &p->page_array[0]; + if (pagecount <= ARRAY_SIZE(p->page_array)) + p->pagevec = p->page_array; else { - size_t size = ++pagecount * sizeof(struct page *); - p->pagevec = kmalloc(size, GFP_NOFS); - if (p->pagevec) { - memset(p->pagevec, 0, size); - } else { + p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); + if (!p->pagevec) { mempool_free(p, nfs_wdata_mempool); p = NULL; } @@ -501,7 +496,7 @@ nfs_mark_request_dirty(struct nfs_page * nfs_list_add_request(req, &nfsi->dirty); nfsi->ndirty++; spin_unlock(&nfsi->req_lock); - inc_page_state(nr_dirty); + inc_zone_page_state(req->wb_page, NR_FILE_DIRTY); mark_inode_dirty(inode); } @@ -529,7 +524,7 @@ nfs_mark_request_commit(struct nfs_page nfs_list_add_request(req, &nfsi->commit); nfsi->ncommit++; spin_unlock(&nfsi->req_lock); - inc_page_state(nr_unstable); + inc_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); mark_inode_dirty(inode); } #endif @@ -583,6 +578,30 @@ static int nfs_wait_on_requests(struct i return ret; } +static void nfs_cancel_dirty_list(struct list_head *head) +{ + struct nfs_page *req; + while(!list_empty(head)) { + req = nfs_list_entry(head->next); + nfs_list_remove_request(req); + nfs_inode_remove_request(req); + nfs_clear_page_writeback(req); + } +} + +static void nfs_cancel_commit_list(struct list_head *head) +{ + struct nfs_page *req; + + while(!list_empty(head)) { + req = nfs_list_entry(head->next); + nfs_list_remove_request(req); + nfs_inode_remove_request(req); + nfs_clear_page_writeback(req); + dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); + } +} + /* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan @@ -602,7 +621,6 @@ nfs_scan_dirty(struct inode *inode, stru if (nfsi->ndirty != 0) { res = nfs_scan_lock_dirty(nfsi, dst, idx_start, npages); nfsi->ndirty -= res; - sub_page_state(nr_dirty,res); if ((nfsi->ndirty == 0) != list_empty(&nfsi->dirty)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ndirty.\n"); } @@ -627,7 +645,7 @@ nfs_scan_commit(struct inode *inode, str int res = 0; if (nfsi->ncommit != 0) { - res = nfs_scan_list(&nfsi->commit, dst, idx_start, npages); + res = nfs_scan_list(nfsi, &nfsi->commit, dst, idx_start, npages); nfsi->ncommit -= res; if ((nfsi->ncommit == 0) != list_empty(&nfsi->commit)) printk(KERN_ERR "NFS: desynchronized value of nfs_i.ncommit.\n"); @@ -1376,6 +1394,7 @@ nfs_commit_list(struct inode *inode, str nfs_list_remove_request(req); nfs_mark_request_commit(req); nfs_clear_page_writeback(req); + dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); } return -ENOMEM; } @@ -1387,7 +1406,6 @@ static void nfs_commit_done(struct rpc_t { struct nfs_write_data *data = calldata; struct nfs_page *req; - int res = 0; dprintk("NFS: %4d nfs_commit_done (status %d)\n", task->tk_pid, task->tk_status); @@ -1399,6 +1417,7 @@ static void nfs_commit_done(struct rpc_t while (!list_empty(&data->pages)) { req = nfs_list_entry(data->pages.next); nfs_list_remove_request(req); + dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); dprintk("NFS: commit (%s/%Ld %d@%Ld)", req->wb_context->dentry->d_inode->i_sb->s_id, @@ -1425,9 +1444,7 @@ static void nfs_commit_done(struct rpc_t nfs_mark_request_dirty(req); next: nfs_clear_page_writeback(req); - res++; } - sub_page_state(nr_unstable,res); } static const struct rpc_call_ops nfs_commit_ops = { @@ -1495,15 +1512,25 @@ int nfs_sync_inode_wait(struct inode *in pages = nfs_scan_dirty(inode, &head, idx_start, npages); if (pages != 0) { spin_unlock(&nfsi->req_lock); - ret = nfs_flush_list(inode, &head, pages, how); + if (how & FLUSH_INVALIDATE) + nfs_cancel_dirty_list(&head); + else + ret = nfs_flush_list(inode, &head, pages, how); spin_lock(&nfsi->req_lock); continue; } if (nocommit) break; - pages = nfs_scan_commit(inode, &head, 0, 0); + pages = nfs_scan_commit(inode, &head, idx_start, npages); if (pages == 0) break; + if (how & FLUSH_INVALIDATE) { + spin_unlock(&nfsi->req_lock); + nfs_cancel_commit_list(&head); + spin_lock(&nfsi->req_lock); + continue; + } + pages += nfs_scan_commit(inode, &head, 0, 0); spin_unlock(&nfsi->req_lock); ret = nfs_commit_list(inode, &head, how); spin_lock(&nfsi->req_lock); @@ -1512,7 +1539,7 @@ int nfs_sync_inode_wait(struct inode *in return ret; } -int nfs_init_writepagecache(void) +int __init nfs_init_writepagecache(void) { nfs_wdata_cachep = kmem_cache_create("nfs_write_data", sizeof(struct nfs_write_data), diff --git a/fs/nfsctl.c b/fs/nfsctl.c index a5a18d4..c043136 100644 --- a/fs/nfsctl.c +++ b/fs/nfsctl.c @@ -4,7 +4,6 @@ * This should eventually move to userland. * */ -#include #include #include #include diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 3eec300..01bc68c 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -126,7 +126,7 @@ static int expkey_parse(struct cache_det if (*ep) goto out; dprintk("found fsidtype %d\n", fsidtype); - if (fsidtype > 2) + if (key_len(fsidtype)==0) /* invalid type */ goto out; if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) goto out; diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index dbaf3f9..54b37b1 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -33,7 +33,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include #include diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 4b6aa60..bea6b94 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c @@ -34,7 +34,6 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 96c7578..9daa0b9 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -123,7 +123,7 @@ static void release_stateid(struct nfs4_ */ /* recall_lock protects the del_recall_lru */ -static spinlock_t recall_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(recall_lock); static struct list_head del_recall_lru; static void @@ -529,8 +529,7 @@ move_to_confirmed(struct nfs4_client *cl dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); list_del_init(&clp->cl_strhash); - list_del_init(&clp->cl_idhash); - list_add(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); + list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); strhashval = clientstr_hashval(clp->cl_recdir); list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); renew_client(clp); @@ -1238,8 +1237,15 @@ find_file(struct inode *ino) return NULL; } -#define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0) -#define TEST_DENY(x) ((x >= 0 || x < 5)?1:0) +static int access_valid(u32 x) +{ + return (x > 0 && x < 4); +} + +static int deny_valid(u32 x) +{ + return (x >= 0 && x < 5); +} static void set_access(unsigned int *access, unsigned long bmap) { @@ -1746,7 +1752,8 @@ nfsd4_process_open2(struct svc_rqst *rqs int status; status = nfserr_inval; - if (!TEST_ACCESS(open->op_share_access) || !TEST_DENY(open->op_share_deny)) + if (!access_valid(open->op_share_access) + || !deny_valid(open->op_share_deny)) goto out; /* * Lookup file; if found, lookup stateid and check open request, @@ -1783,10 +1790,10 @@ nfsd4_process_open2(struct svc_rqst *rqs } else { /* Stateid was not found, this is a new OPEN */ int flags = 0; + if (open->op_share_access & NFS4_SHARE_ACCESS_READ) + flags |= MAY_READ; if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) - flags = MAY_WRITE; - else - flags = MAY_READ; + flags |= MAY_WRITE; status = nfs4_new_open(rqstp, &stp, dp, current_fh, flags); if (status) goto out; @@ -2071,16 +2078,12 @@ nfs4_preprocess_stateid_op(struct svc_fh if (!stateid->si_fileid) { /* delegation stateid */ if(!(dp = find_delegation_stateid(ino, stateid))) { dprintk("NFSD: delegation stateid not found\n"); - if (nfs4_in_grace()) - status = nfserr_grace; goto out; } stidp = &dp->dl_stateid; } else { /* open or lock stateid */ if (!(stp = find_stateid(stateid, flags))) { dprintk("NFSD: open or lock stateid not found\n"); - if (nfs4_in_grace()) - status = nfserr_grace; goto out; } if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) @@ -2253,8 +2256,9 @@ nfsd4_open_confirm(struct svc_rqst *rqst (int)current_fh->fh_dentry->d_name.len, current_fh->fh_dentry->d_name.name); - if ((status = fh_verify(rqstp, current_fh, S_IFREG, 0))) - goto out; + status = fh_verify(rqstp, current_fh, S_IFREG, 0); + if (status) + return status; nfs4_lock_state(); @@ -2321,7 +2325,8 @@ nfsd4_open_downgrade(struct svc_rqst *rq (int)current_fh->fh_dentry->d_name.len, current_fh->fh_dentry->d_name.name); - if (!TEST_ACCESS(od->od_share_access) || !TEST_DENY(od->od_share_deny)) + if (!access_valid(od->od_share_access) + || !deny_valid(od->od_share_deny)) return nfserr_inval; nfs4_lock_state(); diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index de3998f..5446a08 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1310,7 +1310,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s if ((bmval0 & (FATTR4_WORD0_FILES_FREE | FATTR4_WORD0_FILES_TOTAL)) || (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL))) { - status = vfs_statfs(dentry->d_inode->i_sb, &statfs); + status = vfs_statfs(dentry, &statfs); if (status) goto out_nfserr; } diff --git a/fs/nfsd/nfscache.c b/fs/nfsd/nfscache.c index d852ebb..fdf7cf3 100644 --- a/fs/nfsd/nfscache.c +++ b/fs/nfsd/nfscache.c @@ -103,8 +103,7 @@ nfsd_cache_shutdown(void) static void lru_put_end(struct svc_cacherep *rp) { - list_del(&rp->c_lru); - list_add_tail(&rp->c_lru, &lru_head); + list_move_tail(&rp->c_lru, &lru_head); } /* diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 3ef017b..7046ac9 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -6,7 +6,6 @@ * Copyright (C) 1995, 1996 Olaf Kirch */ -#include #include #include @@ -494,10 +493,10 @@ #endif return simple_fill_super(sb, 0x6e667364, nfsd_files); } -static struct super_block *nfsd_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int nfsd_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, nfsd_fill_super); + return get_sb_single(fs_type, flags, data, nfsd_fill_super, mnt); } static struct file_system_type nfsd_fs_type = { diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 3f2ec2e..ecc439d 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -187,13 +187,6 @@ fh_verify(struct svc_rqst *rqstp, struct goto out; } - /* Set user creds for this exportpoint */ - error = nfsd_setuser(rqstp, exp); - if (error) { - error = nfserrno(error); - goto out; - } - /* * Look up the dentry using the NFS file handle. */ @@ -251,6 +244,14 @@ #endif } cache_get(&exp->h); + /* Set user creds for this exportpoint; necessary even in the "just + * checking" case because this may be a filehandle that was created by + * fh_compose, and that is about to be used in another nfsv4 compound + * operation */ + error = nfserrno(nfsd_setuser(rqstp, exp)); + if (error) + goto out; + error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type); if (error) goto out; @@ -312,8 +313,8 @@ int fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh) { /* ref_fh is a reference file handle. - * if it is non-null, then we should compose a filehandle which is - * of the same version, where possible. + * if it is non-null and for the same filesystem, then we should compose + * a filehandle which is of the same version, where possible. * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca * Then create a 32byte filehandle using nfs_fhbase_old * @@ -332,7 +333,7 @@ fh_compose(struct svc_fh *fhp, struct sv parent->d_name.name, dentry->d_name.name, (inode ? inode->i_ino : 0)); - if (ref_fh) { + if (ref_fh && ref_fh->fh_export == exp) { ref_fh_version = ref_fh->fh_handle.fh_version; if (ref_fh_version == 0xca) ref_fh_fsid_type = 0; @@ -461,7 +462,7 @@ fh_update(struct svc_fh *fhp) } else { int size; if (fhp->fh_handle.fh_fileid_type != 0) - goto out_uptodate; + goto out; datap = fhp->fh_handle.fh_auth+ fhp->fh_handle.fh_size/4 -1; size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; @@ -481,10 +482,6 @@ out_negative: printk(KERN_ERR "fh_update: %s/%s still negative!\n", dentry->d_parent->d_name.name, dentry->d_name.name); goto out; -out_uptodate: - printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - goto out; } /* diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 3790727..ec1decf 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -8,7 +8,6 @@ * Copyright (C) 1995, 1996, 1997 Olaf Kirch */ -#include #include #include diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 1d65f13..c9e3b5a 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -16,7 +16,6 @@ #define MSNFS /* HACK HACK */ * Zerocpy NFS support (C) 2002 Hirokazu Takahashi */ -#include #include #include #include @@ -673,7 +672,10 @@ nfsd_open(struct svc_rqst *rqstp, struct goto out_nfserr; if (access & MAY_WRITE) { - flags = O_WRONLY|O_LARGEFILE; + if (access & MAY_READ) + flags = O_RDWR|O_LARGEFILE; + else + flags = O_WRONLY|O_LARGEFILE; DQUOT_INIT(inode); } @@ -834,7 +836,7 @@ #endif if (ra && ra->p_set) file->f_ra = ra->p_ra; - if (file->f_op->sendfile) { + if (file->f_op->sendfile && rqstp->rq_sendfile_ok) { svc_pushback_unused_pages(rqstp); err = file->f_op->sendfile(file, &offset, *count, nfsd_read_actor, rqstp); @@ -1517,14 +1519,15 @@ nfsd_link(struct svc_rqst *rqstp, struct err = nfserrno(err); } - fh_unlock(ffhp); dput(dnew); +out_unlock: + fh_unlock(ffhp); out: return err; out_nfserr: err = nfserrno(err); - goto out; + goto out_unlock; } /* @@ -1553,7 +1556,7 @@ nfsd_rename(struct svc_rqst *rqstp, stru tdir = tdentry->d_inode; err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev; - if (fdir->i_sb != tdir->i_sb) + if (ffhp->fh_export != tfhp->fh_export) goto out; err = nfserr_perm; @@ -1737,7 +1740,7 @@ int nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat) { int err = fh_verify(rqstp, fhp, 0, MAY_NOP); - if (!err && vfs_statfs(fhp->fh_dentry->d_inode->i_sb,stat)) + if (!err && vfs_statfs(fhp->fh_dentry,stat)) err = nfserr_io; return err; } diff --git a/fs/nls/nls_base.c b/fs/nls/nls_base.c index a912deb..9de6b49 100644 --- a/fs/nls/nls_base.c +++ b/fs/nls/nls_base.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index 580412d..bc579bf 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -1544,7 +1544,7 @@ #endif /* NTFS_RW */ /** * ntfs_aops - general address space operations for inodes and attributes */ -struct address_space_operations ntfs_aops = { +const struct address_space_operations ntfs_aops = { .readpage = ntfs_readpage, /* Fill page with data. */ .sync_page = block_sync_page, /* Currently, just unplugs the disk request queue. */ @@ -1560,7 +1560,7 @@ #endif /* NTFS_RW */ * ntfs_mst_aops - general address space operations for mst protecteed inodes * and attributes */ -struct address_space_operations ntfs_mst_aops = { +const struct address_space_operations ntfs_mst_aops = { .readpage = ntfs_readpage, /* Fill page with data. */ .sync_page = block_sync_page, /* Currently, just unplugs the disk request queue. */ diff --git a/fs/ntfs/aops.h b/fs/ntfs/aops.h index 3b74e66..325ce26 100644 --- a/fs/ntfs/aops.h +++ b/fs/ntfs/aops.h @@ -86,8 +86,7 @@ static inline void ntfs_unmap_page(struc static inline struct page *ntfs_map_page(struct address_space *mapping, unsigned long index) { - struct page *page = read_cache_page(mapping, index, - (filler_t*)mapping->a_ops->readpage, NULL); + struct page *page = read_mapping_page(mapping, index, NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 1663f5c..6708e1d 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -2529,8 +2529,7 @@ int ntfs_attr_set(ntfs_inode *ni, const end >>= PAGE_CACHE_SHIFT; /* If there is a first partial page, need to do it the slow way. */ if (start_ofs) { - page = read_cache_page(mapping, idx, - (filler_t*)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, idx, NULL); if (IS_ERR(page)) { ntfs_error(vol->sb, "Failed to read first partial " "page (sync error, index 0x%lx).", idx); @@ -2600,8 +2599,7 @@ int ntfs_attr_set(ntfs_inode *ni, const } /* If there is a last partial page, need to do it the slow way. */ if (end_ofs) { - page = read_cache_page(mapping, idx, - (filler_t*)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, idx, NULL); if (IS_ERR(page)) { ntfs_error(vol->sb, "Failed to read last partial page " "(sync error, index 0x%lx).", idx); diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index c63a83e..2e42c2d 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -231,8 +231,7 @@ do_non_resident_extend: * Read the page. If the page is not present, this will zero * the uninitialized regions for us. */ - page = read_cache_page(mapping, index, - (filler_t*)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, index, NULL); if (IS_ERR(page)) { err = PTR_ERR(page); goto init_err_out; @@ -1359,7 +1358,7 @@ err_out: goto out; } -static size_t __ntfs_copy_from_user_iovec(char *vaddr, +static size_t __ntfs_copy_from_user_iovec_inatomic(char *vaddr, const struct iovec *iov, size_t iov_ofs, size_t bytes) { size_t total = 0; @@ -1377,10 +1376,6 @@ static size_t __ntfs_copy_from_user_iove bytes -= len; vaddr += len; if (unlikely(left)) { - /* - * Zero the rest of the target like __copy_from_user(). - */ - memset(vaddr, 0, bytes); total -= left; break; } @@ -1421,11 +1416,13 @@ static inline void ntfs_set_next_iovec(c * pages (out to offset + bytes), to emulate ntfs_copy_from_user()'s * single-segment behaviour. * - * We call the same helper (__ntfs_copy_from_user_iovec()) both when atomic and - * when not atomic. This is ok because __ntfs_copy_from_user_iovec() calls - * __copy_from_user_inatomic() and it is ok to call this when non-atomic. In - * fact, the only difference between __copy_from_user_inatomic() and - * __copy_from_user() is that the latter calls might_sleep(). And on many + * We call the same helper (__ntfs_copy_from_user_iovec_inatomic()) both + * when atomic and when not atomic. This is ok because + * __ntfs_copy_from_user_iovec_inatomic() calls __copy_from_user_inatomic() + * and it is ok to call this when non-atomic. + * Infact, the only difference between __copy_from_user_inatomic() and + * __copy_from_user() is that the latter calls might_sleep() and the former + * should not zero the tail of the buffer on error. And on many * architectures __copy_from_user_inatomic() is just defined to * __copy_from_user() so it makes no difference at all on those architectures. */ @@ -1442,14 +1439,18 @@ static inline size_t ntfs_copy_from_user if (len > bytes) len = bytes; kaddr = kmap_atomic(*pages, KM_USER0); - copied = __ntfs_copy_from_user_iovec(kaddr + ofs, + copied = __ntfs_copy_from_user_iovec_inatomic(kaddr + ofs, *iov, *iov_ofs, len); kunmap_atomic(kaddr, KM_USER0); if (unlikely(copied != len)) { /* Do it the slow way. */ kaddr = kmap(*pages); - copied = __ntfs_copy_from_user_iovec(kaddr + ofs, + copied = __ntfs_copy_from_user_iovec_inatomic(kaddr + ofs, *iov, *iov_ofs, len); + /* + * Zero the rest of the target like __copy_from_user(). + */ + memset(kaddr + ofs + copied, 0, len - copied); kunmap(*pages); if (unlikely(copied != len)) goto err_out; @@ -1484,14 +1485,15 @@ static inline void ntfs_flush_dcache_pag unsigned nr_pages) { BUG_ON(!nr_pages); + /* + * Warning: Do not do the decrement at the same time as the call to + * flush_dcache_page() because it is a NULL macro on i386 and hence the + * decrement never happens so the loop never terminates. + */ do { - /* - * Warning: Do not do the decrement at the same time as the - * call because flush_dcache_page() is a NULL macro on i386 - * and hence the decrement never happens. - */ + --nr_pages; flush_dcache_page(pages[nr_pages]); - } while (--nr_pages > 0); + } while (nr_pages > 0); } /** diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 4c86b7e..d313f35 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -367,6 +367,12 @@ static void ntfs_destroy_extent_inode(nt kmem_cache_free(ntfs_inode_cache, ni); } +/* + * The attribute runlist lock has separate locking rules from the + * normal runlist lock, so split the two lock-classes: + */ +static struct lock_class_key attr_list_rl_lock_class; + /** * __ntfs_init_inode - initialize ntfs specific part of an inode * @sb: super block of mounted volume @@ -394,6 +400,8 @@ void __ntfs_init_inode(struct super_bloc ni->attr_list_size = 0; ni->attr_list = NULL; ntfs_init_runlist(&ni->attr_list_rl); + lockdep_set_class(&ni->attr_list_rl.lock, + &attr_list_rl_lock_class); ni->itype.index.bmp_ino = NULL; ni->itype.index.block_size = 0; ni->itype.index.vcn_size = 0; @@ -405,6 +413,13 @@ void __ntfs_init_inode(struct super_bloc ni->ext.base_ntfs_ino = NULL; } +/* + * Extent inodes get MFT-mapped in a nested way, while the base inode + * is still mapped. Teach this nesting to the lock validator by creating + * a separate class for nested inode's mrec_lock's: + */ +static struct lock_class_key extent_inode_mrec_lock_key; + inline ntfs_inode *ntfs_new_extent_inode(struct super_block *sb, unsigned long mft_no) { @@ -413,6 +428,7 @@ inline ntfs_inode *ntfs_new_extent_inode ntfs_debug("Entering."); if (likely(ni != NULL)) { __ntfs_init_inode(sb, ni); + lockdep_set_class(&ni->mrec_lock, &extent_inode_mrec_lock_key); ni->mft_no = mft_no; ni->type = AT_UNUSED; ni->name = NULL; @@ -1722,6 +1738,15 @@ err_out: return err; } +/* + * The MFT inode has special locking, so teach the lock validator + * about this by splitting off the locking rules of the MFT from + * the locking rules of other inodes. The MFT inode can never be + * accessed from the VFS side (or even internally), only by the + * map_mft functions. + */ +static struct lock_class_key mft_ni_runlist_lock_key, mft_ni_mrec_lock_key; + /** * ntfs_read_inode_mount - special read_inode for mount time use only * @vi: inode to read @@ -2148,6 +2173,14 @@ int ntfs_read_inode_mount(struct inode * ntfs_attr_put_search_ctx(ctx); ntfs_debug("Done."); ntfs_free(m); + + /* + * Split the locking rules of the MFT inode from the + * locking rules of other inodes: + */ + lockdep_set_class(&ni->runlist.lock, &mft_ni_runlist_lock_key); + lockdep_set_class(&ni->mrec_lock, &mft_ni_mrec_lock_key); + return 0; em_put_err_out: diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h index bf7b3d7..ddd3d50 100644 --- a/fs/ntfs/ntfs.h +++ b/fs/ntfs/ntfs.h @@ -57,8 +57,8 @@ extern struct kmem_cache *ntfs_attr_ctx_ extern struct kmem_cache *ntfs_index_ctx_cache; /* The various operations structs defined throughout the driver files. */ -extern struct address_space_operations ntfs_aops; -extern struct address_space_operations ntfs_mst_aops; +extern const struct address_space_operations ntfs_aops; +extern const struct address_space_operations ntfs_mst_aops; extern const struct file_operations ntfs_file_ops; extern struct inode_operations ntfs_file_inode_ops; diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 27833f6..74e0ee8 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -1724,6 +1724,14 @@ upcase_failed: return FALSE; } +/* + * The lcn and mft bitmap inodes are NTFS-internal inodes with + * their own special locking rules: + */ +static struct lock_class_key + lcnbmp_runlist_lock_key, lcnbmp_mrec_lock_key, + mftbmp_runlist_lock_key, mftbmp_mrec_lock_key; + /** * load_system_files - open the system files using normal functions * @vol: ntfs super block describing device whose system files to load @@ -1780,6 +1788,10 @@ #endif /* NTFS_RW */ ntfs_error(sb, "Failed to load $MFT/$BITMAP attribute."); goto iput_mirr_err_out; } + lockdep_set_class(&NTFS_I(vol->mftbmp_ino)->runlist.lock, + &mftbmp_runlist_lock_key); + lockdep_set_class(&NTFS_I(vol->mftbmp_ino)->mrec_lock, + &mftbmp_mrec_lock_key); /* Read upcase table and setup @vol->upcase and @vol->upcase_len. */ if (!load_and_init_upcase(vol)) goto iput_mftbmp_err_out; @@ -1802,6 +1814,11 @@ #endif /* NTFS_RW */ iput(vol->lcnbmp_ino); goto bitmap_failed; } + lockdep_set_class(&NTFS_I(vol->lcnbmp_ino)->runlist.lock, + &lcnbmp_runlist_lock_key); + lockdep_set_class(&NTFS_I(vol->lcnbmp_ino)->mrec_lock, + &lcnbmp_mrec_lock_key); + NInoSetSparseDisabled(NTFS_I(vol->lcnbmp_ino)); if ((vol->nr_clusters + 7) >> 3 > i_size_read(vol->lcnbmp_ino)) { iput(vol->lcnbmp_ino); @@ -2601,10 +2618,10 @@ static unsigned long __get_nr_free_mft_r /** * ntfs_statfs - return information about mounted NTFS volume - * @sb: super block of mounted volume + * @dentry: dentry from mounted volume * @sfs: statfs structure in which to return the information * - * Return information about the mounted NTFS volume @sb in the statfs structure + * Return information about the mounted NTFS volume @dentry in the statfs structure * pointed to by @sfs (this is initialized with zeros before ntfs_statfs is * called). We interpret the values to be correct of the moment in time at * which we are called. Most values are variable otherwise and this isn't just @@ -2617,8 +2634,9 @@ static unsigned long __get_nr_free_mft_r * * Return 0 on success or -errno on error. */ -static int ntfs_statfs(struct super_block *sb, struct kstatfs *sfs) +static int ntfs_statfs(struct dentry *dentry, struct kstatfs *sfs) { + struct super_block *sb = dentry->d_sb; s64 size; ntfs_volume *vol = NTFS_SB(sb); ntfs_inode *mft_ni = NTFS_I(vol->mft_ino); @@ -2742,6 +2760,17 @@ static int ntfs_fill_super(struct super_ struct inode *tmp_ino; int blocksize, result; + /* + * We do a pretty difficult piece of bootstrap by reading the + * MFT (and other metadata) from disk into memory. We'll only + * release this metadata during umount, so the locking patterns + * observed during bootstrap do not count. So turn off the + * observation of locking patterns (strictly for this context + * only) while mounting NTFS. [The validator is still active + * otherwise, even for this context: it will for example record + * lock class registrations.] + */ + lockdep_off(); ntfs_debug("Entering."); #ifndef NTFS_RW sb->s_flags |= MS_RDONLY; @@ -2753,6 +2782,7 @@ #endif /* ! NTFS_RW */ if (!silent) ntfs_error(sb, "Allocation of NTFS volume structure " "failed. Aborting mount..."); + lockdep_on(); return -ENOMEM; } /* Initialize ntfs_volume structure. */ @@ -2939,6 +2969,7 @@ #endif /* ! NTFS_RW */ mutex_unlock(&ntfs_lock); sb->s_export_op = &ntfs_export_ops; lock_kernel(); + lockdep_on(); return 0; } ntfs_error(sb, "Failed to allocate root directory."); @@ -3058,6 +3089,7 @@ err_out_now: sb->s_fs_info = NULL; kfree(vol); ntfs_debug("Failed, returning -EINVAL."); + lockdep_on(); return -EINVAL; } @@ -3093,10 +3125,11 @@ struct kmem_cache *ntfs_index_ctx_cache; /* Driver wide mutex. */ DEFINE_MUTEX(ntfs_lock); -static struct super_block *ntfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ntfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, ntfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, ntfs_fill_super, + mnt); } static struct file_system_type ntfs_fs_type = { diff --git a/fs/ntfs/sysctl.h b/fs/ntfs/sysctl.h index c8064ca..beda5bf 100644 --- a/fs/ntfs/sysctl.h +++ b/fs/ntfs/sysctl.h @@ -24,7 +24,6 @@ #ifndef _LINUX_NTFS_SYSCTL_H #define _LINUX_NTFS_SYSCTL_H -#include #if defined(DEBUG) && defined(CONFIG_SYSCTL) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 47152bf..f1d1c34 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -558,16 +558,9 @@ static int ocfs2_direct_IO_get_blocks(st u64 vbo_max; /* file offset, max_blocks from iblock */ u64 p_blkno; int contig_blocks; - unsigned char blocksize_bits; + unsigned char blocksize_bits = inode->i_sb->s_blocksize_bits; unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; - if (!inode || !bh_result) { - mlog(ML_ERROR, "inode or bh_result is null\n"); - return -EIO; - } - - blocksize_bits = inode->i_sb->s_blocksize_bits; - /* This function won't even be called if the request isn't all * nicely aligned and of the right size, so there's no need * for us to check any of that. */ @@ -666,7 +659,7 @@ out: return ret; } -struct address_space_operations ocfs2_aops = { +const struct address_space_operations ocfs2_aops = { .readpage = ocfs2_readpage, .writepage = ocfs2_writepage, .prepare_write = ocfs2_prepare_write, diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 21f38ac..504595d 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -54,7 +54,7 @@ static DECLARE_RWSEM(o2hb_callback_sem); * multiple hb threads are watching multiple regions. A node is live * whenever any of the threads sees activity from the node in its region. */ -static spinlock_t o2hb_live_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(o2hb_live_lock); static struct list_head o2hb_live_slots[O2NM_MAX_NODES]; static unsigned long o2hb_live_node_bitmap[BITS_TO_LONGS(O2NM_MAX_NODES)]; static LIST_HEAD(o2hb_node_events); @@ -517,6 +517,7 @@ static inline void o2hb_prepare_block(st hb_block->hb_seq = cpu_to_le64(cputime); hb_block->hb_node = node_num; hb_block->hb_generation = cpu_to_le64(generation); + hb_block->hb_dead_ms = cpu_to_le32(o2hb_dead_threshold * O2HB_REGION_TIMEOUT_MS); /* This step must always happen last! */ hb_block->hb_cksum = cpu_to_le32(o2hb_compute_block_crc_le(reg, @@ -645,6 +646,8 @@ static int o2hb_check_slot(struct o2hb_r struct o2nm_node *node; struct o2hb_disk_heartbeat_block *hb_block = reg->hr_tmp_block; u64 cputime; + unsigned int dead_ms = o2hb_dead_threshold * O2HB_REGION_TIMEOUT_MS; + unsigned int slot_dead_ms; memcpy(hb_block, slot->ds_raw_block, reg->hr_block_bytes); @@ -733,6 +736,23 @@ fire_callbacks: &o2hb_live_slots[slot->ds_node_num]); slot->ds_equal_samples = 0; + + /* We want to be sure that all nodes agree on the + * number of milliseconds before a node will be + * considered dead. The self-fencing timeout is + * computed from this value, and a discrepancy might + * result in heartbeat calling a node dead when it + * hasn't self-fenced yet. */ + slot_dead_ms = le32_to_cpu(hb_block->hb_dead_ms); + if (slot_dead_ms && slot_dead_ms != dead_ms) { + /* TODO: Perhaps we can fail the region here. */ + mlog(ML_ERROR, "Node %d on device %s has a dead count " + "of %u ms, but our count is %u ms.\n" + "Please double check your configuration values " + "for 'O2CB_HEARTBEAT_THRESHOLD'\n", + slot->ds_node_num, reg->hr_dev_name, slot_dead_ms, + dead_ms); + } goto out; } diff --git a/fs/ocfs2/cluster/masklog.h b/fs/ocfs2/cluster/masklog.h index 73edad7..a42628b 100644 --- a/fs/ocfs2/cluster/masklog.h +++ b/fs/ocfs2/cluster/masklog.h @@ -123,6 +123,17 @@ #ifndef MLOG_MASK_PREFIX #define MLOG_MASK_PREFIX 0 #endif +/* + * When logging is disabled, force the bit test to 0 for anything other + * than errors and notices, allowing gcc to remove the code completely. + * When enabled, allow all masks. + */ +#if defined(CONFIG_OCFS2_DEBUG_MASKLOG) +#define ML_ALLOWED_BITS ~0 +#else +#define ML_ALLOWED_BITS (ML_ERROR|ML_NOTICE) +#endif + #define MLOG_MAX_BITS 64 struct mlog_bits { @@ -187,7 +198,8 @@ #define __mlog_printk(level, fmt, args.. #define mlog(mask, fmt, args...) do { \ u64 __m = MLOG_MASK_PREFIX | (mask); \ - if (__mlog_test_u64(__m, mlog_and_bits) && \ + if ((__m & ML_ALLOWED_BITS) && \ + __mlog_test_u64(__m, mlog_and_bits) && \ !__mlog_test_u64(__m, mlog_not_bits)) { \ if (__m & ML_ERROR) \ __mlog_printk(KERN_ERR, "ERROR: "fmt , ##args); \ @@ -204,6 +216,7 @@ #define mlog_errno(st) do { \ mlog(ML_ERROR, "status = %lld\n", (long long)_st); \ } while (0) +#if defined(CONFIG_OCFS2_DEBUG_MASKLOG) #define mlog_entry(fmt, args...) do { \ mlog(ML_ENTRY, "ENTRY:" fmt , ##args); \ } while (0) @@ -247,6 +260,13 @@ #define mlog_exit_ptr(ptr) do { \ #define mlog_exit_void() do { \ mlog(ML_EXIT, "EXIT\n"); \ } while (0) +#else +#define mlog_entry(...) do { } while (0) +#define mlog_entry_void(...) do { } while (0) +#define mlog_exit(...) do { } while (0) +#define mlog_exit_ptr(...) do { } while (0) +#define mlog_exit_void(...) do { } while (0) +#endif /* defined(CONFIG_OCFS2_DEBUG_MASKLOG) */ #define mlog_bug_on_msg(cond, fmt, args...) do { \ if (cond) { \ diff --git a/fs/ocfs2/cluster/ocfs2_heartbeat.h b/fs/ocfs2/cluster/ocfs2_heartbeat.h index 9409606..3f4151d 100644 --- a/fs/ocfs2/cluster/ocfs2_heartbeat.h +++ b/fs/ocfs2/cluster/ocfs2_heartbeat.h @@ -32,6 +32,7 @@ struct o2hb_disk_heartbeat_block { __u8 hb_pad1[3]; __le32 hb_cksum; __le64 hb_generation; + __le32 hb_dead_ms; }; #endif /* _OCFS2_HEARTBEAT_H */ diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 0f60cc0..b650efa 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -108,7 +108,7 @@ #define sclog(sc, fmt, args...) do { ##args); \ } while (0) -static rwlock_t o2net_handler_lock = RW_LOCK_UNLOCKED; +static DEFINE_RWLOCK(o2net_handler_lock); static struct rb_root o2net_handler_tree = RB_ROOT; static struct o2net_node o2net_nodes[O2NM_MAX_NODES]; @@ -396,8 +396,8 @@ static void o2net_set_nn_state(struct o2 } if (was_valid && !valid) { - mlog(ML_NOTICE, "no longer connected to " SC_NODEF_FMT "\n", - SC_NODEF_ARGS(old_sc)); + printk(KERN_INFO "o2net: no longer connected to " + SC_NODEF_FMT "\n", SC_NODEF_ARGS(old_sc)); o2net_complete_nodes_nsw(nn); } @@ -409,10 +409,10 @@ static void o2net_set_nn_state(struct o2 * the only way to start connecting again is to down * heartbeat and bring it back up. */ cancel_delayed_work(&nn->nn_connect_expired); - mlog(ML_NOTICE, "%s " SC_NODEF_FMT "\n", - o2nm_this_node() > sc->sc_node->nd_num ? - "connected to" : "accepted connection from", - SC_NODEF_ARGS(sc)); + printk(KERN_INFO "o2net: %s " SC_NODEF_FMT "\n", + o2nm_this_node() > sc->sc_node->nd_num ? + "connected to" : "accepted connection from", + SC_NODEF_ARGS(sc)); } /* trigger the connecting worker func as long as we're not valid, @@ -1280,7 +1280,7 @@ static void o2net_idle_timer(unsigned lo do_gettimeofday(&now); - mlog(ML_NOTICE, "connection to " SC_NODEF_FMT " has been idle for 10 " + printk(KERN_INFO "o2net: connection to " SC_NODEF_FMT " has been idle for 10 " "seconds, shutting it down.\n", SC_NODEF_ARGS(sc)); mlog(ML_NOTICE, "here are some times that might help debug the " "situation: (tmr %ld.%ld now %ld.%ld dr %ld.%ld adv " diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index ae47f45..3d494d1 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -213,11 +213,9 @@ int ocfs2_find_files_on_disk(const char struct ocfs2_dir_entry **dirent) { int status = -ENOENT; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - mlog_entry("(osb=%p, parent=%llu, name='%.*s', blkno=%p, inode=%p)\n", - osb, (unsigned long long)OCFS2_I(inode)->ip_blkno, - namelen, name, blkno, inode); + mlog_entry("(name=%.*s, blkno=%p, inode=%p, dirent_bh=%p, dirent=%p)\n", + namelen, name, blkno, inode, dirent_bh, dirent); *dirent_bh = ocfs2_find_entry(name, namelen, inode, dirent); if (!*dirent_bh || !*dirent) { diff --git a/fs/ocfs2/dlm/dlmast.c b/fs/ocfs2/dlm/dlmast.c index 355593d..42775e2 100644 --- a/fs/ocfs2/dlm/dlmast.c +++ b/fs/ocfs2/dlm/dlmast.c @@ -197,12 +197,14 @@ static void dlm_update_lvb(struct dlm_ct lock->ml.node == dlm->node_num ? "master" : "remote"); memcpy(lksb->lvb, res->lvb, DLM_LVB_LEN); - } else if (lksb->flags & DLM_LKSB_PUT_LVB) { - mlog(0, "setting lvb from lockres for %s node\n", - lock->ml.node == dlm->node_num ? "master" : - "remote"); - memcpy(res->lvb, lksb->lvb, DLM_LVB_LEN); } + /* Do nothing for lvb put requests - they should be done in + * place when the lock is downconverted - otherwise we risk + * racing gets and puts which could result in old lvb data + * being propagated. We leave the put flag set and clear it + * here. In the future we might want to clear it at the time + * the put is actually done. + */ spin_unlock(&res->spinlock); } @@ -381,8 +383,7 @@ do_ast: ret = DLM_NORMAL; if (past->type == DLM_AST) { /* do not alter lock refcount. switching lists. */ - list_del_init(&lock->list); - list_add_tail(&lock->list, &res->granted); + list_move_tail(&lock->list, &res->granted); mlog(0, "ast: adding to granted list... type=%d, " "convert_type=%d\n", lock->ml.type, lock->ml.convert_type); if (lock->ml.convert_type != LKM_IVMODE) { diff --git a/fs/ocfs2/dlm/dlmcommon.h b/fs/ocfs2/dlm/dlmcommon.h index 88cc43d..14530ee 100644 --- a/fs/ocfs2/dlm/dlmcommon.h +++ b/fs/ocfs2/dlm/dlmcommon.h @@ -37,7 +37,17 @@ #define DLM_LOCK_RES_OWNER_UNKNOWN O #define DLM_THREAD_SHUFFLE_INTERVAL 5 // flush everything every 5 passes #define DLM_THREAD_MS 200 // flush at least every 200 ms -#define DLM_HASH_BUCKETS (PAGE_SIZE / sizeof(struct hlist_head)) +#define DLM_HASH_SIZE_DEFAULT (1 << 14) +#if DLM_HASH_SIZE_DEFAULT < PAGE_SIZE +# define DLM_HASH_PAGES 1 +#else +# define DLM_HASH_PAGES (DLM_HASH_SIZE_DEFAULT / PAGE_SIZE) +#endif +#define DLM_BUCKETS_PER_PAGE (PAGE_SIZE / sizeof(struct hlist_head)) +#define DLM_HASH_BUCKETS (DLM_HASH_PAGES * DLM_BUCKETS_PER_PAGE) + +/* Intended to make it easier for us to switch out hash functions */ +#define dlm_lockid_hash(_n, _l) full_name_hash(_n, _l) enum dlm_ast_type { DLM_AST = 0, @@ -61,7 +71,8 @@ static inline int dlm_is_recovery_lock(c return 0; } -#define DLM_RECO_STATE_ACTIVE 0x0001 +#define DLM_RECO_STATE_ACTIVE 0x0001 +#define DLM_RECO_STATE_FINALIZE 0x0002 struct dlm_recovery_ctxt { @@ -85,7 +96,7 @@ enum dlm_ctxt_state { struct dlm_ctxt { struct list_head list; - struct hlist_head *lockres_hash; + struct hlist_head **lockres_hash; struct list_head dirty_list; struct list_head purge_list; struct list_head pending_asts; @@ -120,6 +131,7 @@ struct dlm_ctxt struct o2hb_callback_func dlm_hb_down; struct task_struct *dlm_thread_task; struct task_struct *dlm_reco_thread_task; + struct workqueue_struct *dlm_worker; wait_queue_head_t dlm_thread_wq; wait_queue_head_t dlm_reco_thread_wq; wait_queue_head_t ast_wq; @@ -132,6 +144,11 @@ struct dlm_ctxt struct list_head dlm_eviction_callbacks; }; +static inline struct hlist_head *dlm_lockres_hash(struct dlm_ctxt *dlm, unsigned i) +{ + return dlm->lockres_hash[(i / DLM_BUCKETS_PER_PAGE) % DLM_HASH_PAGES] + (i % DLM_BUCKETS_PER_PAGE); +} + /* these keventd work queue items are for less-frequently * called functions that cannot be directly called from the * net message handlers for some reason, usually because @@ -216,20 +233,29 @@ struct dlm_lock_resource /* WARNING: Please see the comment in dlm_init_lockres before * adding fields here. */ struct hlist_node hash_node; + struct qstr lockname; struct kref refs; - /* please keep these next 3 in this order - * some funcs want to iterate over all lists */ + /* + * Please keep granted, converting, and blocked in this order, + * as some funcs want to iterate over all lists. + * + * All four lists are protected by the hash's reference. + */ struct list_head granted; struct list_head converting; struct list_head blocked; + struct list_head purge; + /* + * These two lists require you to hold an additional reference + * while they are on the list. + */ struct list_head dirty; struct list_head recovering; // dlm_recovery_ctxt.resources list /* unused lock resources have their last_used stamped and are * put on a list for the dlm thread to run. */ - struct list_head purge; unsigned long last_used; unsigned migration_pending:1; @@ -238,7 +264,6 @@ struct dlm_lock_resource wait_queue_head_t wq; u8 owner; //node which owns the lock resource, or unknown u16 state; - struct qstr lockname; char lvb[DLM_LVB_LEN]; }; @@ -300,6 +325,15 @@ enum dlm_lockres_list { DLM_BLOCKED_LIST }; +static inline int dlm_lvb_is_empty(char *lvb) +{ + int i; + for (i=0; irefs); +} void dlm_lockres_put(struct dlm_lock_resource *res); void __dlm_unhash_lockres(struct dlm_lock_resource *res); void __dlm_insert_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res); struct dlm_lock_resource * __dlm_lookup_lockres(struct dlm_ctxt *dlm, const char *name, - unsigned int len); + unsigned int len, + unsigned int hash); struct dlm_lock_resource * dlm_lookup_lockres(struct dlm_ctxt *dlm, const char *name, unsigned int len); @@ -780,8 +822,6 @@ int dlm_begin_reco_handler(struct o2net_ int dlm_finalize_reco_handler(struct o2net_msg *msg, u32 len, void *data); int dlm_do_master_requery(struct dlm_ctxt *dlm, struct dlm_lock_resource *res, u8 nodenum, u8 *real_master); -int dlm_lockres_master_requery(struct dlm_ctxt *dlm, - struct dlm_lock_resource *res, u8 *real_master); int dlm_dispatch_assert_master(struct dlm_ctxt *dlm, @@ -819,6 +859,7 @@ void dlm_clean_master_list(struct dlm_ct u8 dead_node); int dlm_lock_basts_flushed(struct dlm_ctxt *dlm, struct dlm_lock *lock); +int __dlm_lockres_unused(struct dlm_lock_resource *res); static inline const char * dlm_lock_mode_name(int mode) { diff --git a/fs/ocfs2/dlm/dlmconvert.c b/fs/ocfs2/dlm/dlmconvert.c index 8285228..c764dc8 100644 --- a/fs/ocfs2/dlm/dlmconvert.c +++ b/fs/ocfs2/dlm/dlmconvert.c @@ -214,6 +214,9 @@ grant: if (lock->ml.node == dlm->node_num) mlog(0, "doing in-place convert for nonlocal lock\n"); lock->ml.type = type; + if (lock->lksb->flags & DLM_LKSB_PUT_LVB) + memcpy(res->lvb, lock->lksb->lvb, DLM_LVB_LEN); + status = DLM_NORMAL; *call_ast = 1; goto unlock_exit; @@ -231,8 +234,7 @@ switch_queues: lock->ml.convert_type = type; /* do not alter lock refcount. switching lists. */ - list_del_init(&lock->list); - list_add_tail(&lock->list, &res->converting); + list_move_tail(&lock->list, &res->converting); unlock_exit: spin_unlock(&lock->spinlock); @@ -248,8 +250,7 @@ void dlm_revert_pending_convert(struct d struct dlm_lock *lock) { /* do not alter lock refcount. switching lists. */ - list_del_init(&lock->list); - list_add_tail(&lock->list, &res->granted); + list_move_tail(&lock->list, &res->granted); lock->ml.convert_type = LKM_IVMODE; lock->lksb->flags &= ~(DLM_LKSB_GET_LVB|DLM_LKSB_PUT_LVB); } @@ -294,8 +295,7 @@ enum dlm_status dlmconvert_remote(struct res->state |= DLM_LOCK_RES_IN_PROGRESS; /* move lock to local convert queue */ /* do not alter lock refcount. switching lists. */ - list_del_init(&lock->list); - list_add_tail(&lock->list, &res->converting); + list_move_tail(&lock->list, &res->converting); lock->convert_pending = 1; lock->ml.convert_type = type; @@ -464,6 +464,12 @@ int dlm_convert_lock_handler(struct o2ne } spin_lock(&res->spinlock); + status = __dlm_lockres_state_to_status(res); + if (status != DLM_NORMAL) { + spin_unlock(&res->spinlock); + dlm_error(status); + goto leave; + } list_for_each(iter, &res->granted) { lock = list_entry(iter, struct dlm_lock, list); if (lock->ml.cookie == cnv->cookie && @@ -473,6 +479,21 @@ int dlm_convert_lock_handler(struct o2ne } lock = NULL; } + if (!lock) { + __dlm_print_one_lock_resource(res); + list_for_each(iter, &res->granted) { + lock = list_entry(iter, struct dlm_lock, list); + if (lock->ml.node == cnv->node_idx) { + mlog(ML_ERROR, "There is something here " + "for node %u, lock->ml.cookie=%llu, " + "cnv->cookie=%llu\n", cnv->node_idx, + (unsigned long long)lock->ml.cookie, + (unsigned long long)cnv->cookie); + break; + } + } + lock = NULL; + } spin_unlock(&res->spinlock); if (!lock) { status = DLM_IVLOCKID; diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index c7eae5d..3f6c8d8 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -37,10 +37,8 @@ #include "cluster/tcp.h" #include "dlmapi.h" #include "dlmcommon.h" -#include "dlmdebug.h" #include "dlmdomain.h" -#include "dlmdebug.h" #define MLOG_MASK_PREFIX ML_DLM #include "cluster/masklog.h" @@ -120,6 +118,7 @@ void dlm_print_one_lock(struct dlm_lock } EXPORT_SYMBOL_GPL(dlm_print_one_lock); +#if 0 void dlm_dump_lock_resources(struct dlm_ctxt *dlm) { struct dlm_lock_resource *res; @@ -136,12 +135,13 @@ void dlm_dump_lock_resources(struct dlm_ spin_lock(&dlm->spinlock); for (i=0; ilockres_hash[i]); + bucket = dlm_lockres_hash(dlm, i); hlist_for_each_entry(res, iter, bucket, hash_node) dlm_print_one_lock_resource(res); } spin_unlock(&dlm->spinlock); } +#endif /* 0 */ static const char *dlm_errnames[] = { [DLM_NORMAL] = "DLM_NORMAL", diff --git a/fs/ocfs2/dlm/dlmdebug.h b/fs/ocfs2/dlm/dlmdebug.h deleted file mode 100644 index 6858510..0000000 --- a/fs/ocfs2/dlm/dlmdebug.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8; -*- - * vim: noexpandtab sw=8 ts=8 sts=0: - * - * dlmdebug.h - * - * Copyright (C) 2004 Oracle. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 021110-1307, USA. - * - */ - -#ifndef DLMDEBUG_H -#define DLMDEBUG_H - -void dlm_dump_lock_resources(struct dlm_ctxt *dlm); - -#endif diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 8f3a9e3..8d1065f 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -41,7 +41,6 @@ #include "cluster/tcp.h" #include "dlmapi.h" #include "dlmcommon.h" -#include "dlmdebug.h" #include "dlmdomain.h" #include "dlmver.h" @@ -49,6 +48,33 @@ #include "dlmver.h" #define MLOG_MASK_PREFIX (ML_DLM|ML_DLM_DOMAIN) #include "cluster/masklog.h" +static void dlm_free_pagevec(void **vec, int pages) +{ + while (pages--) + free_page((unsigned long)vec[pages]); + kfree(vec); +} + +static void **dlm_alloc_pagevec(int pages) +{ + void **vec = kmalloc(pages * sizeof(void *), GFP_KERNEL); + int i; + + if (!vec) + return NULL; + + for (i = 0; i < pages; i++) + if (!(vec[i] = (void *)__get_free_page(GFP_KERNEL))) + goto out_free; + + mlog(0, "Allocated DLM hash pagevec; %d pages (%lu expected), %lu buckets per page\n", + pages, DLM_HASH_PAGES, (unsigned long)DLM_BUCKETS_PER_PAGE); + return vec; +out_free: + dlm_free_pagevec(vec, i); + return NULL; +} + /* * * spinlock lock ordering: if multiple locks are needed, obey this ordering: @@ -62,7 +88,7 @@ #include "cluster/masklog.h" * */ -spinlock_t dlm_domain_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(dlm_domain_lock); LIST_HEAD(dlm_domains); static DECLARE_WAIT_QUEUE_HEAD(dlm_domain_events); @@ -90,8 +116,7 @@ void __dlm_insert_lockres(struct dlm_ctx assert_spin_locked(&dlm->spinlock); q = &res->lockname; - q->hash = full_name_hash(q->name, q->len); - bucket = &(dlm->lockres_hash[q->hash % DLM_HASH_BUCKETS]); + bucket = dlm_lockres_hash(dlm, q->hash); /* get a reference for our hashtable */ dlm_lockres_get(res); @@ -100,34 +125,32 @@ void __dlm_insert_lockres(struct dlm_ctx } struct dlm_lock_resource * __dlm_lookup_lockres(struct dlm_ctxt *dlm, - const char *name, - unsigned int len) + const char *name, + unsigned int len, + unsigned int hash) { - unsigned int hash; - struct hlist_node *iter; - struct dlm_lock_resource *tmpres=NULL; struct hlist_head *bucket; + struct hlist_node *list; mlog_entry("%.*s\n", len, name); assert_spin_locked(&dlm->spinlock); - hash = full_name_hash(name, len); - - bucket = &(dlm->lockres_hash[hash % DLM_HASH_BUCKETS]); - - /* check for pre-existing lock */ - hlist_for_each(iter, bucket) { - tmpres = hlist_entry(iter, struct dlm_lock_resource, hash_node); - if (tmpres->lockname.len == len && - memcmp(tmpres->lockname.name, name, len) == 0) { - dlm_lockres_get(tmpres); - break; - } + bucket = dlm_lockres_hash(dlm, hash); - tmpres = NULL; + hlist_for_each(list, bucket) { + struct dlm_lock_resource *res = hlist_entry(list, + struct dlm_lock_resource, hash_node); + if (res->lockname.name[0] != name[0]) + continue; + if (unlikely(res->lockname.len != len)) + continue; + if (memcmp(res->lockname.name + 1, name + 1, len - 1)) + continue; + dlm_lockres_get(res); + return res; } - return tmpres; + return NULL; } struct dlm_lock_resource * dlm_lookup_lockres(struct dlm_ctxt *dlm, @@ -135,9 +158,10 @@ struct dlm_lock_resource * dlm_lookup_lo unsigned int len) { struct dlm_lock_resource *res; + unsigned int hash = dlm_lockid_hash(name, len); spin_lock(&dlm->spinlock); - res = __dlm_lookup_lockres(dlm, name, len); + res = __dlm_lookup_lockres(dlm, name, len, hash); spin_unlock(&dlm->spinlock); return res; } @@ -194,7 +218,7 @@ static int dlm_wait_on_domain_helper(con static void dlm_free_ctxt_mem(struct dlm_ctxt *dlm) { if (dlm->lockres_hash) - free_page((unsigned long) dlm->lockres_hash); + dlm_free_pagevec((void **)dlm->lockres_hash, DLM_HASH_PAGES); if (dlm->name) kfree(dlm->name); @@ -278,11 +302,21 @@ int dlm_domain_fully_joined(struct dlm_c return ret; } +static void dlm_destroy_dlm_worker(struct dlm_ctxt *dlm) +{ + if (dlm->dlm_worker) { + flush_workqueue(dlm->dlm_worker); + destroy_workqueue(dlm->dlm_worker); + dlm->dlm_worker = NULL; + } +} + static void dlm_complete_dlm_shutdown(struct dlm_ctxt *dlm) { dlm_unregister_domain_handlers(dlm); dlm_complete_thread(dlm); dlm_complete_recovery_thread(dlm); + dlm_destroy_dlm_worker(dlm); /* We've left the domain. Now we can take ourselves out of the * list and allow the kref stuff to help us free the @@ -304,8 +338,8 @@ static void dlm_migrate_all_locks(struct restart: spin_lock(&dlm->spinlock); for (i = 0; i < DLM_HASH_BUCKETS; i++) { - while (!hlist_empty(&dlm->lockres_hash[i])) { - res = hlist_entry(dlm->lockres_hash[i].first, + while (!hlist_empty(dlm_lockres_hash(dlm, i))) { + res = hlist_entry(dlm_lockres_hash(dlm, i)->first, struct dlm_lock_resource, hash_node); /* need reference when manually grabbing lockres */ dlm_lockres_get(res); @@ -374,12 +408,13 @@ static void __dlm_print_nodes(struct dlm assert_spin_locked(&dlm->spinlock); - mlog(ML_NOTICE, "Nodes in my domain (\"%s\"):\n", dlm->name); + printk(KERN_INFO "ocfs2_dlm: Nodes in domain (\"%s\"): ", dlm->name); while ((node = find_next_bit(dlm->domain_map, O2NM_MAX_NODES, node + 1)) < O2NM_MAX_NODES) { - mlog(ML_NOTICE, " node %d\n", node); + printk("%d ", node); } + printk("\n"); } static int dlm_exit_domain_handler(struct o2net_msg *msg, u32 len, void *data) @@ -395,7 +430,7 @@ static int dlm_exit_domain_handler(struc node = exit_msg->node_idx; - mlog(0, "Node %u leaves domain %s\n", node, dlm->name); + printk(KERN_INFO "ocfs2_dlm: Node %u leaves domain %s\n", node, dlm->name); spin_lock(&dlm->spinlock); clear_bit(node, dlm->domain_map); @@ -644,6 +679,8 @@ static int dlm_assert_joined_handler(str set_bit(assert->node_idx, dlm->domain_map); __dlm_set_joining_node(dlm, DLM_LOCK_RES_OWNER_UNKNOWN); + printk(KERN_INFO "ocfs2_dlm: Node %u joins domain %s\n", + assert->node_idx, dlm->name); __dlm_print_nodes(dlm); /* notify anything attached to the heartbeat events */ @@ -1126,6 +1163,13 @@ static int dlm_join_domain(struct dlm_ct goto bail; } + dlm->dlm_worker = create_singlethread_workqueue("dlm_wq"); + if (!dlm->dlm_worker) { + status = -ENOMEM; + mlog_errno(status); + goto bail; + } + do { unsigned int backoff; status = dlm_try_to_join_domain(dlm); @@ -1166,6 +1210,7 @@ bail: dlm_unregister_domain_handlers(dlm); dlm_complete_thread(dlm); dlm_complete_recovery_thread(dlm); + dlm_destroy_dlm_worker(dlm); } return status; @@ -1191,7 +1236,7 @@ static struct dlm_ctxt *dlm_alloc_ctxt(c goto leave; } - dlm->lockres_hash = (struct hlist_head *) __get_free_page(GFP_KERNEL); + dlm->lockres_hash = (struct hlist_head **)dlm_alloc_pagevec(DLM_HASH_PAGES); if (!dlm->lockres_hash) { mlog_errno(-ENOMEM); kfree(dlm->name); @@ -1200,8 +1245,8 @@ static struct dlm_ctxt *dlm_alloc_ctxt(c goto leave; } - for (i=0; ilockres_hash[i]); + for (i = 0; i < DLM_HASH_BUCKETS; i++) + INIT_HLIST_HEAD(dlm_lockres_hash(dlm, i)); strcpy(dlm->name, domain); dlm->key = key; @@ -1231,6 +1276,7 @@ static struct dlm_ctxt *dlm_alloc_ctxt(c dlm->dlm_thread_task = NULL; dlm->dlm_reco_thread_task = NULL; + dlm->dlm_worker = NULL; init_waitqueue_head(&dlm->dlm_thread_wq); init_waitqueue_head(&dlm->dlm_reco_thread_wq); init_waitqueue_head(&dlm->reco.event); diff --git a/fs/ocfs2/dlm/dlmfs.c b/fs/ocfs2/dlm/dlmfs.c index 7e88e24..033ad17 100644 --- a/fs/ocfs2/dlm/dlmfs.c +++ b/fs/ocfs2/dlm/dlmfs.c @@ -116,7 +116,7 @@ static int dlmfs_file_open(struct inode * doesn't make sense for LVB writes. */ file->f_flags &= ~O_APPEND; - fp = kmalloc(sizeof(*fp), GFP_KERNEL); + fp = kmalloc(sizeof(*fp), GFP_NOFS); if (!fp) { status = -ENOMEM; goto bail; @@ -196,7 +196,7 @@ static ssize_t dlmfs_file_read(struct fi else readlen = count - *ppos; - lvb_buf = kmalloc(readlen, GFP_KERNEL); + lvb_buf = kmalloc(readlen, GFP_NOFS); if (!lvb_buf) return -ENOMEM; @@ -240,7 +240,7 @@ static ssize_t dlmfs_file_write(struct f else writelen = count - *ppos; - lvb_buf = kmalloc(writelen, GFP_KERNEL); + lvb_buf = kmalloc(writelen, GFP_NOFS); if (!lvb_buf) return -ENOMEM; @@ -574,10 +574,10 @@ static struct inode_operations dlmfs_fil .getattr = simple_getattr, }; -static struct super_block *dlmfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int dlmfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, dlmfs_fill_super); + return get_sb_nodev(fs_type, flags, data, dlmfs_fill_super, mnt); } static struct file_system_type dlmfs_fs_type = { diff --git a/fs/ocfs2/dlm/dlmlock.c b/fs/ocfs2/dlm/dlmlock.c index 6fea283..5ca57ec 100644 --- a/fs/ocfs2/dlm/dlmlock.c +++ b/fs/ocfs2/dlm/dlmlock.c @@ -53,7 +53,7 @@ #include "dlmconvert.h" #define MLOG_MASK_PREFIX ML_DLM #include "cluster/masklog.h" -static spinlock_t dlm_cookie_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dlm_cookie_lock); static u64 dlm_next_cookie = 1; static enum dlm_status dlm_send_remote_lock_request(struct dlm_ctxt *dlm, @@ -201,6 +201,7 @@ static enum dlm_status dlmlock_remote(st struct dlm_lock *lock, int flags) { enum dlm_status status = DLM_DENIED; + int lockres_changed = 1; mlog_entry("type=%d\n", lock->ml.type); mlog(0, "lockres %.*s, flags = 0x%x\n", res->lockname.len, @@ -226,8 +227,25 @@ static enum dlm_status dlmlock_remote(st res->state &= ~DLM_LOCK_RES_IN_PROGRESS; lock->lock_pending = 0; if (status != DLM_NORMAL) { - if (status != DLM_NOTQUEUED) + if (status == DLM_RECOVERING && + dlm_is_recovery_lock(res->lockname.name, + res->lockname.len)) { + /* recovery lock was mastered by dead node. + * we need to have calc_usage shoot down this + * lockres and completely remaster it. */ + mlog(0, "%s: recovery lock was owned by " + "dead node %u, remaster it now.\n", + dlm->name, res->owner); + } else if (status != DLM_NOTQUEUED) { + /* + * DO NOT call calc_usage, as this would unhash + * the remote lockres before we ever get to use + * it. treat as if we never made any change to + * the lockres. + */ + lockres_changed = 0; dlm_error(status); + } dlm_revert_pending_lock(res, lock); dlm_lock_put(lock); } else if (dlm_is_recovery_lock(res->lockname.name, @@ -239,12 +257,12 @@ static enum dlm_status dlmlock_remote(st mlog(0, "%s: $RECOVERY lock for this node (%u) is " "mastered by %u; got lock, manually granting (no ast)\n", dlm->name, dlm->node_num, res->owner); - list_del_init(&lock->list); - list_add_tail(&lock->list, &res->granted); + list_move_tail(&lock->list, &res->granted); } spin_unlock(&res->spinlock); - dlm_lockres_calc_usage(dlm, res); + if (lockres_changed) + dlm_lockres_calc_usage(dlm, res); wake_up(&res->wq); return status; @@ -281,6 +299,14 @@ static enum dlm_status dlm_send_remote_l if (tmpret >= 0) { // successfully sent and received ret = status; // this is already a dlm_status + if (ret == DLM_REJECTED) { + mlog(ML_ERROR, "%s:%.*s: BUG. this is a stale lockres " + "no longer owned by %u. that node is coming back " + "up currently.\n", dlm->name, create.namelen, + create.name, res->owner); + dlm_print_one_lock_resource(res); + BUG(); + } } else { mlog_errno(tmpret); if (dlm_is_host_down(tmpret)) { @@ -382,13 +408,13 @@ struct dlm_lock * dlm_new_lock(int type, struct dlm_lock *lock; int kernel_allocated = 0; - lock = kcalloc(1, sizeof(*lock), GFP_KERNEL); + lock = kcalloc(1, sizeof(*lock), GFP_NOFS); if (!lock) return NULL; if (!lksb) { /* zero memory only if kernel-allocated */ - lksb = kcalloc(1, sizeof(*lksb), GFP_KERNEL); + lksb = kcalloc(1, sizeof(*lksb), GFP_NOFS); if (!lksb) { kfree(lock); return NULL; @@ -429,11 +455,16 @@ int dlm_create_lock_handler(struct o2net if (!dlm_grab(dlm)) return DLM_REJECTED; - mlog_bug_on_msg(!dlm_domain_fully_joined(dlm), - "Domain %s not fully joined!\n", dlm->name); - name = create->name; namelen = create->namelen; + status = DLM_REJECTED; + if (!dlm_domain_fully_joined(dlm)) { + mlog(ML_ERROR, "Domain %s not fully joined, but node %u is " + "sending a create_lock message for lock %.*s!\n", + dlm->name, create->node_idx, namelen, name); + dlm_error(status); + goto leave; + } status = DLM_IVBUFLEN; if (namelen > DLM_LOCKID_NAME_MAX) { @@ -669,18 +700,22 @@ retry_lock: msleep(100); /* no waiting for dlm_reco_thread */ if (recovery) { - if (status == DLM_RECOVERING) { - mlog(0, "%s: got RECOVERING " - "for $REOCVERY lock, master " - "was %u\n", dlm->name, - res->owner); - dlm_wait_for_node_death(dlm, res->owner, - DLM_NODE_DEATH_WAIT_MAX); - } + if (status != DLM_RECOVERING) + goto retry_lock; + + mlog(0, "%s: got RECOVERING " + "for $RECOVERY lock, master " + "was %u\n", dlm->name, + res->owner); + /* wait to see the node go down, then + * drop down and allow the lockres to + * get cleaned up. need to remaster. */ + dlm_wait_for_node_death(dlm, res->owner, + DLM_NODE_DEATH_WAIT_MAX); } else { dlm_wait_for_recovery(dlm); + goto retry_lock; } - goto retry_lock; } if (status != DLM_NORMAL) { diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index 940be4c..1b8346d 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -47,7 +47,6 @@ #include "cluster/tcp.h" #include "dlmapi.h" #include "dlmcommon.h" -#include "dlmdebug.h" #include "dlmdomain.h" #define MLOG_MASK_PREFIX (ML_DLM|ML_DLM_MASTER) @@ -74,6 +73,7 @@ struct dlm_master_list_entry wait_queue_head_t wq; atomic_t woken; struct kref mle_refs; + int inuse; unsigned long maybe_map[BITS_TO_LONGS(O2NM_MAX_NODES)]; unsigned long vote_map[BITS_TO_LONGS(O2NM_MAX_NODES)]; unsigned long response_map[BITS_TO_LONGS(O2NM_MAX_NODES)]; @@ -127,18 +127,30 @@ static inline int dlm_mle_equal(struct d return 1; } -#if 0 -/* Code here is included but defined out as it aids debugging */ +#define dlm_print_nodemap(m) _dlm_print_nodemap(m,#m) +static void _dlm_print_nodemap(unsigned long *map, const char *mapname) +{ + int i; + printk("%s=[ ", mapname); + for (i=0; imaybe_map, + *vote = mle->vote_map, + *resp = mle->response_map, + *node = mle->node_map; k = &mle->mle_refs; if (mle->type == DLM_MLE_BLOCK) @@ -159,18 +171,29 @@ void dlm_print_one_mle(struct dlm_master name = mle->u.res->lockname.name; } - mlog(ML_NOTICE, " #%3d: %3s %3d %3u %3u %c (%d)%.*s\n", - i, type, refs, master, mle->new_master, attached, - namelen, namelen, name); + mlog(ML_NOTICE, "%.*s: %3s refs=%3d mas=%3u new=%3u evt=%c inuse=%d ", + namelen, name, type, refs, master, mle->new_master, attached, + mle->inuse); + dlm_print_nodemap(maybe); + printk(", "); + dlm_print_nodemap(vote); + printk(", "); + dlm_print_nodemap(resp); + printk(", "); + dlm_print_nodemap(node); + printk(", "); + printk("\n"); } +#if 0 +/* Code here is included but defined out as it aids debugging */ + static void dlm_dump_mles(struct dlm_ctxt *dlm) { struct dlm_master_list_entry *mle; struct list_head *iter; mlog(ML_NOTICE, "dumping all mles for domain %s:\n", dlm->name); - mlog(ML_NOTICE, " ####: type refs owner new events? lockname nodemap votemap respmap maybemap\n"); spin_lock(&dlm->master_lock); list_for_each(iter, &dlm->master_list) { mle = list_entry(iter, struct dlm_master_list_entry, list); @@ -314,6 +337,31 @@ static inline void dlm_mle_detach_hb_eve spin_unlock(&dlm->spinlock); } +static void dlm_get_mle_inuse(struct dlm_master_list_entry *mle) +{ + struct dlm_ctxt *dlm; + dlm = mle->dlm; + + assert_spin_locked(&dlm->spinlock); + assert_spin_locked(&dlm->master_lock); + mle->inuse++; + kref_get(&mle->mle_refs); +} + +static void dlm_put_mle_inuse(struct dlm_master_list_entry *mle) +{ + struct dlm_ctxt *dlm; + dlm = mle->dlm; + + spin_lock(&dlm->spinlock); + spin_lock(&dlm->master_lock); + mle->inuse--; + __dlm_put_mle(mle); + spin_unlock(&dlm->master_lock); + spin_unlock(&dlm->spinlock); + +} + /* remove from list and free */ static void __dlm_put_mle(struct dlm_master_list_entry *mle) { @@ -322,9 +370,14 @@ static void __dlm_put_mle(struct dlm_mas assert_spin_locked(&dlm->spinlock); assert_spin_locked(&dlm->master_lock); - BUG_ON(!atomic_read(&mle->mle_refs.refcount)); - - kref_put(&mle->mle_refs, dlm_mle_release); + if (!atomic_read(&mle->mle_refs.refcount)) { + /* this may or may not crash, but who cares. + * it's a BUG. */ + mlog(ML_ERROR, "bad mle: %p\n", mle); + dlm_print_one_mle(mle); + BUG(); + } else + kref_put(&mle->mle_refs, dlm_mle_release); } @@ -367,6 +420,7 @@ static void dlm_init_mle(struct dlm_mast memset(mle->response_map, 0, sizeof(mle->response_map)); mle->master = O2NM_MAX_NODES; mle->new_master = O2NM_MAX_NODES; + mle->inuse = 0; if (mle->type == DLM_MLE_MASTER) { BUG_ON(!res); @@ -564,6 +618,28 @@ static void dlm_lockres_release(struct k mlog(0, "destroying lockres %.*s\n", res->lockname.len, res->lockname.name); + if (!hlist_unhashed(&res->hash_node) || + !list_empty(&res->granted) || + !list_empty(&res->converting) || + !list_empty(&res->blocked) || + !list_empty(&res->dirty) || + !list_empty(&res->recovering) || + !list_empty(&res->purge)) { + mlog(ML_ERROR, + "Going to BUG for resource %.*s." + " We're on a list! [%c%c%c%c%c%c%c]\n", + res->lockname.len, res->lockname.name, + !hlist_unhashed(&res->hash_node) ? 'H' : ' ', + !list_empty(&res->granted) ? 'G' : ' ', + !list_empty(&res->converting) ? 'C' : ' ', + !list_empty(&res->blocked) ? 'B' : ' ', + !list_empty(&res->dirty) ? 'D' : ' ', + !list_empty(&res->recovering) ? 'R' : ' ', + !list_empty(&res->purge) ? 'P' : ' '); + + dlm_print_one_lock_resource(res); + } + /* By the time we're ready to blow this guy away, we shouldn't * be on any lists. */ BUG_ON(!hlist_unhashed(&res->hash_node)); @@ -579,11 +655,6 @@ static void dlm_lockres_release(struct k kfree(res); } -void dlm_lockres_get(struct dlm_lock_resource *res) -{ - kref_get(&res->refs); -} - void dlm_lockres_put(struct dlm_lock_resource *res) { kref_put(&res->refs, dlm_lockres_release); @@ -603,7 +674,7 @@ static void dlm_init_lockres(struct dlm_ memcpy(qname, name, namelen); res->lockname.len = namelen; - res->lockname.hash = full_name_hash(name, namelen); + res->lockname.hash = dlm_lockid_hash(name, namelen); init_waitqueue_head(&res->wq); spin_lock_init(&res->spinlock); @@ -637,11 +708,11 @@ struct dlm_lock_resource *dlm_new_lockre { struct dlm_lock_resource *res; - res = kmalloc(sizeof(struct dlm_lock_resource), GFP_KERNEL); + res = kmalloc(sizeof(struct dlm_lock_resource), GFP_NOFS); if (!res) return NULL; - res->lockname.name = kmalloc(namelen, GFP_KERNEL); + res->lockname.name = kmalloc(namelen, GFP_NOFS); if (!res->lockname.name) { kfree(res); return NULL; @@ -677,19 +748,20 @@ struct dlm_lock_resource * dlm_get_lock_ int blocked = 0; int ret, nodenum; struct dlm_node_iter iter; - unsigned int namelen; + unsigned int namelen, hash; int tries = 0; int bit, wait_on_recovery = 0; BUG_ON(!lockid); namelen = strlen(lockid); + hash = dlm_lockid_hash(lockid, namelen); mlog(0, "get lockres %s (len %d)\n", lockid, namelen); lookup: spin_lock(&dlm->spinlock); - tmpres = __dlm_lookup_lockres(dlm, lockid, namelen); + tmpres = __dlm_lookup_lockres(dlm, lockid, namelen, hash); if (tmpres) { spin_unlock(&dlm->spinlock); mlog(0, "found in hash!\n"); @@ -704,7 +776,7 @@ lookup: mlog(0, "allocating a new resource\n"); /* nothing found and we need to allocate one. */ alloc_mle = (struct dlm_master_list_entry *) - kmem_cache_alloc(dlm_mle_cache, GFP_KERNEL); + kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); if (!alloc_mle) goto leave; res = dlm_new_lockres(dlm, lockid, namelen); @@ -790,10 +862,11 @@ lookup: * if so, the creator of the BLOCK may try to put the last * ref at this time in the assert master handler, so we * need an extra one to keep from a bad ptr deref. */ - dlm_get_mle(mle); + dlm_get_mle_inuse(mle); spin_unlock(&dlm->master_lock); spin_unlock(&dlm->spinlock); +redo_request: while (wait_on_recovery) { /* any cluster changes that occurred after dropping the * dlm spinlock would be detectable be a change on the mle, @@ -812,7 +885,7 @@ lookup: } dlm_kick_recovery_thread(dlm); - msleep(100); + msleep(1000); dlm_wait_for_recovery(dlm); spin_lock(&dlm->spinlock); @@ -825,13 +898,15 @@ lookup: } else wait_on_recovery = 0; spin_unlock(&dlm->spinlock); + + if (wait_on_recovery) + dlm_wait_for_node_recovery(dlm, bit, 10000); } /* must wait for lock to be mastered elsewhere */ if (blocked) goto wait; -redo_request: ret = -EINVAL; dlm_node_iter_init(mle->vote_map, &iter); while ((nodenum = dlm_node_iter_next(&iter)) >= 0) { @@ -856,6 +931,7 @@ wait: /* keep going until the response map includes all nodes */ ret = dlm_wait_for_lock_mastery(dlm, res, mle, &blocked); if (ret < 0) { + wait_on_recovery = 1; mlog(0, "%s:%.*s: node map changed, redo the " "master request now, blocked=%d\n", dlm->name, res->lockname.len, @@ -866,7 +942,7 @@ wait: dlm->name, res->lockname.len, res->lockname.name, blocked); dlm_print_one_lock_resource(res); - /* dlm_print_one_mle(mle); */ + dlm_print_one_mle(mle); tries = 0; } goto redo_request; @@ -880,7 +956,7 @@ wait: dlm_mle_detach_hb_events(dlm, mle); dlm_put_mle(mle); /* put the extra ref */ - dlm_put_mle(mle); + dlm_put_mle_inuse(mle); wake_waiters: spin_lock(&res->spinlock); @@ -921,12 +997,14 @@ recheck: spin_unlock(&res->spinlock); /* this will cause the master to re-assert across * the whole cluster, freeing up mles */ - ret = dlm_do_master_request(mle, res->owner); - if (ret < 0) { - /* give recovery a chance to run */ - mlog(ML_ERROR, "link to %u went down?: %d\n", res->owner, ret); - msleep(500); - goto recheck; + if (res->owner != dlm->node_num) { + ret = dlm_do_master_request(mle, res->owner); + if (ret < 0) { + /* give recovery a chance to run */ + mlog(ML_ERROR, "link to %u went down?: %d\n", res->owner, ret); + msleep(500); + goto recheck; + } } ret = 0; goto leave; @@ -962,6 +1040,12 @@ recheck: "rechecking now\n", dlm->name, res->lockname.len, res->lockname.name); goto recheck; + } else { + if (!voting_done) { + mlog(0, "map not changed and voting not done " + "for %s:%.*s\n", dlm->name, res->lockname.len, + res->lockname.name); + } } if (m != O2NM_MAX_NODES) { @@ -1129,18 +1213,6 @@ static int dlm_restart_lock_mastery(stru set_bit(node, mle->vote_map); } else { mlog(ML_ERROR, "node down! %d\n", node); - - /* if the node wasn't involved in mastery skip it, - * but clear it out from the maps so that it will - * not affect mastery of this lockres */ - clear_bit(node, mle->response_map); - clear_bit(node, mle->vote_map); - if (!test_bit(node, mle->maybe_map)) - goto next; - - /* if we're already blocked on lock mastery, and the - * dead node wasn't the expected master, or there is - * another node in the maybe_map, keep waiting */ if (blocked) { int lowest = find_next_bit(mle->maybe_map, O2NM_MAX_NODES, 0); @@ -1148,54 +1220,53 @@ static int dlm_restart_lock_mastery(stru /* act like it was never there */ clear_bit(node, mle->maybe_map); - if (node != lowest) - goto next; - - mlog(ML_ERROR, "expected master %u died while " - "this node was blocked waiting on it!\n", - node); - lowest = find_next_bit(mle->maybe_map, - O2NM_MAX_NODES, - lowest+1); - if (lowest < O2NM_MAX_NODES) { - mlog(0, "still blocked. waiting " - "on %u now\n", lowest); - goto next; + if (node == lowest) { + mlog(0, "expected master %u died" + " while this node was blocked " + "waiting on it!\n", node); + lowest = find_next_bit(mle->maybe_map, + O2NM_MAX_NODES, + lowest+1); + if (lowest < O2NM_MAX_NODES) { + mlog(0, "%s:%.*s:still " + "blocked. waiting on %u " + "now\n", dlm->name, + res->lockname.len, + res->lockname.name, + lowest); + } else { + /* mle is an MLE_BLOCK, but + * there is now nothing left to + * block on. we need to return + * all the way back out and try + * again with an MLE_MASTER. + * dlm_do_local_recovery_cleanup + * has already run, so the mle + * refcount is ok */ + mlog(0, "%s:%.*s: no " + "longer blocking. try to " + "master this here\n", + dlm->name, + res->lockname.len, + res->lockname.name); + mle->type = DLM_MLE_MASTER; + mle->u.res = res; + } } - - /* mle is an MLE_BLOCK, but there is now - * nothing left to block on. we need to return - * all the way back out and try again with - * an MLE_MASTER. dlm_do_local_recovery_cleanup - * has already run, so the mle refcount is ok */ - mlog(0, "no longer blocking. we can " - "try to master this here\n"); - mle->type = DLM_MLE_MASTER; - memset(mle->maybe_map, 0, - sizeof(mle->maybe_map)); - memset(mle->response_map, 0, - sizeof(mle->maybe_map)); - memcpy(mle->vote_map, mle->node_map, - sizeof(mle->node_map)); - mle->u.res = res; - set_bit(dlm->node_num, mle->maybe_map); - - ret = -EAGAIN; - goto next; } - clear_bit(node, mle->maybe_map); - if (node > dlm->node_num) - goto next; - - mlog(0, "dead node in map!\n"); - /* yuck. go back and re-contact all nodes - * in the vote_map, removing this node. */ - memset(mle->response_map, 0, - sizeof(mle->response_map)); + /* now blank out everything, as if we had never + * contacted anyone */ + memset(mle->maybe_map, 0, sizeof(mle->maybe_map)); + memset(mle->response_map, 0, sizeof(mle->response_map)); + /* reset the vote_map to the current node_map */ + memcpy(mle->vote_map, mle->node_map, + sizeof(mle->node_map)); + /* put myself into the maybe map */ + if (mle->type != DLM_MLE_BLOCK) + set_bit(dlm->node_num, mle->maybe_map); } ret = -EAGAIN; -next: node = dlm_bitmap_diff_iter_next(&bdi, &sc); } return ret; @@ -1316,7 +1387,7 @@ int dlm_master_request_handler(struct o2 struct dlm_master_request *request = (struct dlm_master_request *) msg->buf; struct dlm_master_list_entry *mle = NULL, *tmpmle = NULL; char *name; - unsigned int namelen; + unsigned int namelen, hash; int found, ret; int set_maybe; int dispatch_assert = 0; @@ -1331,6 +1402,7 @@ int dlm_master_request_handler(struct o2 name = request->name; namelen = request->namelen; + hash = dlm_lockid_hash(name, namelen); if (namelen > DLM_LOCKID_NAME_MAX) { response = DLM_IVBUFLEN; @@ -1339,7 +1411,7 @@ int dlm_master_request_handler(struct o2 way_up_top: spin_lock(&dlm->spinlock); - res = __dlm_lookup_lockres(dlm, name, namelen); + res = __dlm_lookup_lockres(dlm, name, namelen, hash); if (res) { spin_unlock(&dlm->spinlock); @@ -1459,21 +1531,18 @@ way_up_top: spin_unlock(&dlm->spinlock); mle = (struct dlm_master_list_entry *) - kmem_cache_alloc(dlm_mle_cache, GFP_KERNEL); + kmem_cache_alloc(dlm_mle_cache, GFP_NOFS); if (!mle) { response = DLM_MASTER_RESP_ERROR; mlog_errno(-ENOMEM); goto send_response; } - spin_lock(&dlm->spinlock); - dlm_init_mle(mle, DLM_MLE_BLOCK, dlm, NULL, - name, namelen); - spin_unlock(&dlm->spinlock); goto way_up_top; } // mlog(0, "this is second time thru, already allocated, " // "add the block.\n"); + dlm_init_mle(mle, DLM_MLE_BLOCK, dlm, NULL, name, namelen); set_bit(request->node_idx, mle->maybe_map); list_add(&mle->list, &dlm->master_list); response = DLM_MASTER_RESP_NO; @@ -1556,6 +1625,8 @@ again: dlm_node_iter_init(nodemap, &iter); while ((to = dlm_node_iter_next(&iter)) >= 0) { int r = 0; + struct dlm_master_list_entry *mle = NULL; + mlog(0, "sending assert master to %d (%.*s)\n", to, namelen, lockname); memset(&assert, 0, sizeof(assert)); @@ -1567,20 +1638,28 @@ again: tmpret = o2net_send_message(DLM_ASSERT_MASTER_MSG, dlm->key, &assert, sizeof(assert), to, &r); if (tmpret < 0) { - mlog(ML_ERROR, "assert_master returned %d!\n", tmpret); + mlog(0, "assert_master returned %d!\n", tmpret); if (!dlm_is_host_down(tmpret)) { - mlog(ML_ERROR, "unhandled error!\n"); + mlog(ML_ERROR, "unhandled error=%d!\n", tmpret); BUG(); } /* a node died. finish out the rest of the nodes. */ - mlog(ML_ERROR, "link to %d went down!\n", to); + mlog(0, "link to %d went down!\n", to); /* any nonzero status return will do */ ret = tmpret; } else if (r < 0) { /* ok, something horribly messed. kill thyself. */ mlog(ML_ERROR,"during assert master of %.*s to %u, " "got %d.\n", namelen, lockname, to, r); - dlm_dump_lock_resources(dlm); + spin_lock(&dlm->spinlock); + spin_lock(&dlm->master_lock); + if (dlm_find_mle(dlm, &mle, (char *)lockname, + namelen)) { + dlm_print_one_mle(mle); + __dlm_put_mle(mle); + } + spin_unlock(&dlm->master_lock); + spin_unlock(&dlm->spinlock); BUG(); } else if (r == EAGAIN) { mlog(0, "%.*s: node %u create mles on other " @@ -1612,7 +1691,7 @@ int dlm_assert_master_handler(struct o2n struct dlm_assert_master *assert = (struct dlm_assert_master *)msg->buf; struct dlm_lock_resource *res = NULL; char *name; - unsigned int namelen; + unsigned int namelen, hash; u32 flags; int master_request = 0; int ret = 0; @@ -1622,6 +1701,7 @@ int dlm_assert_master_handler(struct o2n name = assert->name; namelen = assert->namelen; + hash = dlm_lockid_hash(name, namelen); flags = be32_to_cpu(assert->flags); if (namelen > DLM_LOCKID_NAME_MAX) { @@ -1646,7 +1726,7 @@ int dlm_assert_master_handler(struct o2n if (bit >= O2NM_MAX_NODES) { /* not necessarily an error, though less likely. * could be master just re-asserting. */ - mlog(ML_ERROR, "no bits set in the maybe_map, but %u " + mlog(0, "no bits set in the maybe_map, but %u " "is asserting! (%.*s)\n", assert->node_idx, namelen, name); } else if (bit != assert->node_idx) { @@ -1658,19 +1738,36 @@ int dlm_assert_master_handler(struct o2n * number winning the mastery will respond * YES to mastery requests, but this node * had no way of knowing. let it pass. */ - mlog(ML_ERROR, "%u is the lowest node, " + mlog(0, "%u is the lowest node, " "%u is asserting. (%.*s) %u must " "have begun after %u won.\n", bit, assert->node_idx, namelen, name, bit, assert->node_idx); } } + if (mle->type == DLM_MLE_MIGRATION) { + if (flags & DLM_ASSERT_MASTER_MLE_CLEANUP) { + mlog(0, "%s:%.*s: got cleanup assert" + " from %u for migration\n", + dlm->name, namelen, name, + assert->node_idx); + } else if (!(flags & DLM_ASSERT_MASTER_FINISH_MIGRATION)) { + mlog(0, "%s:%.*s: got unrelated assert" + " from %u for migration, ignoring\n", + dlm->name, namelen, name, + assert->node_idx); + __dlm_put_mle(mle); + spin_unlock(&dlm->master_lock); + spin_unlock(&dlm->spinlock); + goto done; + } + } } spin_unlock(&dlm->master_lock); /* ok everything checks out with the MLE * now check to see if there is a lockres */ - res = __dlm_lookup_lockres(dlm, name, namelen); + res = __dlm_lookup_lockres(dlm, name, namelen, hash); if (res) { spin_lock(&res->spinlock); if (res->state & DLM_LOCK_RES_RECOVERING) { @@ -1679,7 +1776,8 @@ int dlm_assert_master_handler(struct o2n goto kill; } if (!mle) { - if (res->owner != assert->node_idx) { + if (res->owner != DLM_LOCK_RES_OWNER_UNKNOWN && + res->owner != assert->node_idx) { mlog(ML_ERROR, "assert_master from " "%u, but current owner is " "%u! (%.*s)\n", @@ -1732,6 +1830,7 @@ ok: if (mle) { int extra_ref = 0; int nn = -1; + int rr, err = 0; spin_lock(&mle->spinlock); if (mle->type == DLM_MLE_BLOCK || mle->type == DLM_MLE_MIGRATION) @@ -1751,27 +1850,64 @@ ok: wake_up(&mle->wq); spin_unlock(&mle->spinlock); - if (mle->type == DLM_MLE_MIGRATION && res) { - mlog(0, "finishing off migration of lockres %.*s, " - "from %u to %u\n", - res->lockname.len, res->lockname.name, - dlm->node_num, mle->new_master); + if (res) { spin_lock(&res->spinlock); - res->state &= ~DLM_LOCK_RES_MIGRATING; - dlm_change_lockres_owner(dlm, res, mle->new_master); - BUG_ON(res->state & DLM_LOCK_RES_DIRTY); + if (mle->type == DLM_MLE_MIGRATION) { + mlog(0, "finishing off migration of lockres %.*s, " + "from %u to %u\n", + res->lockname.len, res->lockname.name, + dlm->node_num, mle->new_master); + res->state &= ~DLM_LOCK_RES_MIGRATING; + dlm_change_lockres_owner(dlm, res, mle->new_master); + BUG_ON(res->state & DLM_LOCK_RES_DIRTY); + } else { + dlm_change_lockres_owner(dlm, res, mle->master); + } spin_unlock(&res->spinlock); } - /* master is known, detach if not already detached */ - dlm_mle_detach_hb_events(dlm, mle); - dlm_put_mle(mle); - + + /* master is known, detach if not already detached. + * ensures that only one assert_master call will happen + * on this mle. */ + spin_lock(&dlm->spinlock); + spin_lock(&dlm->master_lock); + + rr = atomic_read(&mle->mle_refs.refcount); + if (mle->inuse > 0) { + if (extra_ref && rr < 3) + err = 1; + else if (!extra_ref && rr < 2) + err = 1; + } else { + if (extra_ref && rr < 2) + err = 1; + else if (!extra_ref && rr < 1) + err = 1; + } + if (err) { + mlog(ML_ERROR, "%s:%.*s: got assert master from %u " + "that will mess up this node, refs=%d, extra=%d, " + "inuse=%d\n", dlm->name, namelen, name, + assert->node_idx, rr, extra_ref, mle->inuse); + dlm_print_one_mle(mle); + } + list_del_init(&mle->list); + __dlm_mle_detach_hb_events(dlm, mle); + __dlm_put_mle(mle); if (extra_ref) { /* the assert master message now balances the extra * ref given by the master / migration request message. * if this is the last put, it will be removed * from the list. */ - dlm_put_mle(mle); + __dlm_put_mle(mle); + } + spin_unlock(&dlm->master_lock); + spin_unlock(&dlm->spinlock); + } else if (res) { + if (res->owner != assert->node_idx) { + mlog(0, "assert_master from %u, but current " + "owner is %u (%.*s), no mle\n", assert->node_idx, + res->owner, namelen, name); } } @@ -1788,12 +1924,12 @@ done: kill: /* kill the caller! */ + mlog(ML_ERROR, "Bad message received from another node. Dumping state " + "and killing the other node now! This node is OK and can continue.\n"); + __dlm_print_one_lock_resource(res); spin_unlock(&res->spinlock); spin_unlock(&dlm->spinlock); dlm_lockres_put(res); - mlog(ML_ERROR, "Bad message received from another node. Dumping state " - "and killing the other node now! This node is OK and can continue.\n"); - dlm_dump_lock_resources(dlm); dlm_put(dlm); return -EINVAL; } @@ -1803,7 +1939,7 @@ int dlm_dispatch_assert_master(struct dl int ignore_higher, u8 request_from, u32 flags) { struct dlm_work_item *item; - item = kcalloc(1, sizeof(*item), GFP_KERNEL); + item = kcalloc(1, sizeof(*item), GFP_NOFS); if (!item) return -ENOMEM; @@ -1825,7 +1961,7 @@ int dlm_dispatch_assert_master(struct dl list_add_tail(&item->list, &dlm->work_list); spin_unlock(&dlm->work_lock); - schedule_work(&dlm->dispatched_work); + queue_work(dlm->dlm_worker, &dlm->dispatched_work); return 0; } @@ -1866,6 +2002,23 @@ static void dlm_assert_master_worker(str } } + /* + * If we're migrating this lock to someone else, we are no + * longer allowed to assert out own mastery. OTOH, we need to + * prevent migration from starting while we're still asserting + * our dominance. The reserved ast delays migration. + */ + spin_lock(&res->spinlock); + if (res->state & DLM_LOCK_RES_MIGRATING) { + mlog(0, "Someone asked us to assert mastery, but we're " + "in the middle of migration. Skipping assert, " + "the new master will handle that.\n"); + spin_unlock(&res->spinlock); + goto put; + } else + __dlm_lockres_reserve_ast(res); + spin_unlock(&res->spinlock); + /* this call now finishes out the nodemap * even if one or more nodes die */ mlog(0, "worker about to master %.*s here, this=%u\n", @@ -1875,9 +2028,14 @@ static void dlm_assert_master_worker(str nodemap, flags); if (ret < 0) { /* no need to restart, we are done */ - mlog_errno(ret); + if (!dlm_is_host_down(ret)) + mlog_errno(ret); } + /* Ok, we've asserted ourselves. Let's let migration start. */ + dlm_lockres_release_ast(dlm, res); + +put: dlm_lockres_put(res); mlog(0, "finished with dlm_assert_master_worker\n"); @@ -1916,6 +2074,7 @@ static int dlm_pre_master_reco_lockres(s BUG(); /* host is down, so answer for that node would be * DLM_LOCK_RES_OWNER_UNKNOWN. continue. */ + ret = 0; } if (master != DLM_LOCK_RES_OWNER_UNKNOWN) { @@ -2016,14 +2175,14 @@ int dlm_migrate_lockres(struct dlm_ctxt */ ret = -ENOMEM; - mres = (struct dlm_migratable_lockres *) __get_free_page(GFP_KERNEL); + mres = (struct dlm_migratable_lockres *) __get_free_page(GFP_NOFS); if (!mres) { mlog_errno(ret); goto leave; } mle = (struct dlm_master_list_entry *) kmem_cache_alloc(dlm_mle_cache, - GFP_KERNEL); + GFP_NOFS); if (!mle) { mlog_errno(ret); goto leave; @@ -2117,7 +2276,7 @@ fail: * take both dlm->spinlock and dlm->master_lock */ spin_lock(&dlm->spinlock); spin_lock(&dlm->master_lock); - dlm_get_mle(mle); + dlm_get_mle_inuse(mle); spin_unlock(&dlm->master_lock); spin_unlock(&dlm->spinlock); @@ -2134,7 +2293,10 @@ fail: /* migration failed, detach and clean up mle */ dlm_mle_detach_hb_events(dlm, mle); dlm_put_mle(mle); - dlm_put_mle(mle); + dlm_put_mle_inuse(mle); + spin_lock(&res->spinlock); + res->state &= ~DLM_LOCK_RES_MIGRATING; + spin_unlock(&res->spinlock); goto leave; } @@ -2164,8 +2326,8 @@ fail: /* avoid hang during shutdown when migrating lockres * to a node which also goes down */ if (dlm_is_node_dead(dlm, target)) { - mlog(0, "%s:%.*s: expected migration target %u " - "is no longer up. restarting.\n", + mlog(0, "%s:%.*s: expected migration " + "target %u is no longer up, restarting\n", dlm->name, res->lockname.len, res->lockname.name, target); ret = -ERESTARTSYS; @@ -2175,7 +2337,10 @@ fail: /* migration failed, detach and clean up mle */ dlm_mle_detach_hb_events(dlm, mle); dlm_put_mle(mle); - dlm_put_mle(mle); + dlm_put_mle_inuse(mle); + spin_lock(&res->spinlock); + res->state &= ~DLM_LOCK_RES_MIGRATING; + spin_unlock(&res->spinlock); goto leave; } /* TODO: if node died: stop, clean up, return error */ @@ -2191,7 +2356,7 @@ fail: /* master is known, detach if not already detached */ dlm_mle_detach_hb_events(dlm, mle); - dlm_put_mle(mle); + dlm_put_mle_inuse(mle); ret = 0; dlm_lockres_calc_usage(dlm, res); @@ -2462,7 +2627,7 @@ int dlm_migrate_request_handler(struct o struct dlm_migrate_request *migrate = (struct dlm_migrate_request *) msg->buf; struct dlm_master_list_entry *mle = NULL, *oldmle = NULL; const char *name; - unsigned int namelen; + unsigned int namelen, hash; int ret = 0; if (!dlm_grab(dlm)) @@ -2470,10 +2635,11 @@ int dlm_migrate_request_handler(struct o name = migrate->name; namelen = migrate->namelen; + hash = dlm_lockid_hash(name, namelen); /* preallocate.. if this fails, abort */ mle = (struct dlm_master_list_entry *) kmem_cache_alloc(dlm_mle_cache, - GFP_KERNEL); + GFP_NOFS); if (!mle) { ret = -ENOMEM; @@ -2482,7 +2648,7 @@ int dlm_migrate_request_handler(struct o /* check for pre-existing lock */ spin_lock(&dlm->spinlock); - res = __dlm_lookup_lockres(dlm, name, namelen); + res = __dlm_lookup_lockres(dlm, name, namelen, hash); spin_lock(&dlm->master_lock); if (res) { @@ -2580,6 +2746,7 @@ static int dlm_add_migration_mle(struct /* remove it from the list so that only one * mle will be found */ list_del_init(&tmp->list); + __dlm_mle_detach_hb_events(dlm, mle); } spin_unlock(&tmp->spinlock); } @@ -2601,6 +2768,7 @@ void dlm_clean_master_list(struct dlm_ct struct list_head *iter, *iter2; struct dlm_master_list_entry *mle; struct dlm_lock_resource *res; + unsigned int hash; mlog_entry("dlm=%s, dead node=%u\n", dlm->name, dead_node); top: @@ -2640,7 +2808,7 @@ top: * may result in the mle being unlinked and * freed, but there may still be a process * waiting in the dlmlock path which is fine. */ - mlog(ML_ERROR, "node %u was expected master\n", + mlog(0, "node %u was expected master\n", dead_node); atomic_set(&mle->woken, 1); spin_unlock(&mle->spinlock); @@ -2673,19 +2841,21 @@ top: /* remove from the list early. NOTE: unlinking * list_head while in list_for_each_safe */ + __dlm_mle_detach_hb_events(dlm, mle); spin_lock(&mle->spinlock); list_del_init(&mle->list); atomic_set(&mle->woken, 1); spin_unlock(&mle->spinlock); wake_up(&mle->wq); - mlog(0, "node %u died during migration from " - "%u to %u!\n", dead_node, + mlog(0, "%s: node %u died during migration from " + "%u to %u!\n", dlm->name, dead_node, mle->master, mle->new_master); /* if there is a lockres associated with this * mle, find it and set its owner to UNKNOWN */ + hash = dlm_lockid_hash(mle->u.name.name, mle->u.name.len); res = __dlm_lookup_lockres(dlm, mle->u.name.name, - mle->u.name.len); + mle->u.name.len, hash); if (res) { /* unfortunately if we hit this rare case, our * lock ordering is messed. we need to drop diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 805cbab..594745f 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -95,11 +95,14 @@ static void dlm_reco_unlock_ast(void *as static void dlm_request_all_locks_worker(struct dlm_work_item *item, void *data); static void dlm_mig_lockres_worker(struct dlm_work_item *item, void *data); +static int dlm_lockres_master_requery(struct dlm_ctxt *dlm, + struct dlm_lock_resource *res, + u8 *real_master); static u64 dlm_get_next_mig_cookie(void); -static spinlock_t dlm_reco_state_lock = SPIN_LOCK_UNLOCKED; -static spinlock_t dlm_mig_cookie_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(dlm_reco_state_lock); +static DEFINE_SPINLOCK(dlm_mig_cookie_lock); static u64 dlm_mig_cookie = 1; static u64 dlm_get_next_mig_cookie(void) @@ -115,12 +118,37 @@ static u64 dlm_get_next_mig_cookie(void) return c; } +static inline void dlm_set_reco_dead_node(struct dlm_ctxt *dlm, + u8 dead_node) +{ + assert_spin_locked(&dlm->spinlock); + if (dlm->reco.dead_node != dead_node) + mlog(0, "%s: changing dead_node from %u to %u\n", + dlm->name, dlm->reco.dead_node, dead_node); + dlm->reco.dead_node = dead_node; +} + +static inline void dlm_set_reco_master(struct dlm_ctxt *dlm, + u8 master) +{ + assert_spin_locked(&dlm->spinlock); + mlog(0, "%s: changing new_master from %u to %u\n", + dlm->name, dlm->reco.new_master, master); + dlm->reco.new_master = master; +} + +static inline void __dlm_reset_recovery(struct dlm_ctxt *dlm) +{ + assert_spin_locked(&dlm->spinlock); + clear_bit(dlm->reco.dead_node, dlm->recovery_map); + dlm_set_reco_dead_node(dlm, O2NM_INVALID_NODE_NUM); + dlm_set_reco_master(dlm, O2NM_INVALID_NODE_NUM); +} + static inline void dlm_reset_recovery(struct dlm_ctxt *dlm) { spin_lock(&dlm->spinlock); - clear_bit(dlm->reco.dead_node, dlm->recovery_map); - dlm->reco.dead_node = O2NM_INVALID_NODE_NUM; - dlm->reco.new_master = O2NM_INVALID_NODE_NUM; + __dlm_reset_recovery(dlm); spin_unlock(&dlm->spinlock); } @@ -132,12 +160,21 @@ void dlm_dispatch_work(void *data) struct list_head *iter, *iter2; struct dlm_work_item *item; dlm_workfunc_t *workfunc; + int tot=0; + + if (!dlm_joined(dlm)) + return; spin_lock(&dlm->work_lock); list_splice_init(&dlm->work_list, &tmp_list); spin_unlock(&dlm->work_lock); list_for_each_safe(iter, iter2, &tmp_list) { + tot++; + } + mlog(0, "%s: work thread has %d work items\n", dlm->name, tot); + + list_for_each_safe(iter, iter2, &tmp_list) { item = list_entry(iter, struct dlm_work_item, list); workfunc = item->func; list_del_init(&item->list); @@ -220,6 +257,52 @@ void dlm_complete_recovery_thread(struct * */ +static void dlm_print_reco_node_status(struct dlm_ctxt *dlm) +{ + struct dlm_reco_node_data *ndata; + struct dlm_lock_resource *res; + + mlog(ML_NOTICE, "%s(%d): recovery info, state=%s, dead=%u, master=%u\n", + dlm->name, dlm->dlm_reco_thread_task->pid, + dlm->reco.state & DLM_RECO_STATE_ACTIVE ? "ACTIVE" : "inactive", + dlm->reco.dead_node, dlm->reco.new_master); + + list_for_each_entry(ndata, &dlm->reco.node_data, list) { + char *st = "unknown"; + switch (ndata->state) { + case DLM_RECO_NODE_DATA_INIT: + st = "init"; + break; + case DLM_RECO_NODE_DATA_REQUESTING: + st = "requesting"; + break; + case DLM_RECO_NODE_DATA_DEAD: + st = "dead"; + break; + case DLM_RECO_NODE_DATA_RECEIVING: + st = "receiving"; + break; + case DLM_RECO_NODE_DATA_REQUESTED: + st = "requested"; + break; + case DLM_RECO_NODE_DATA_DONE: + st = "done"; + break; + case DLM_RECO_NODE_DATA_FINALIZE_SENT: + st = "finalize-sent"; + break; + default: + st = "bad"; + break; + } + mlog(ML_NOTICE, "%s: reco state, node %u, state=%s\n", + dlm->name, ndata->node_num, st); + } + list_for_each_entry(res, &dlm->reco.resources, recovering) { + mlog(ML_NOTICE, "%s: lockres %.*s on recovering list\n", + dlm->name, res->lockname.len, res->lockname.name); + } +} #define DLM_RECO_THREAD_TIMEOUT_MS (5 * 1000) @@ -267,11 +350,23 @@ int dlm_is_node_dead(struct dlm_ctxt *dl { int dead; spin_lock(&dlm->spinlock); - dead = test_bit(node, dlm->domain_map); + dead = !test_bit(node, dlm->domain_map); spin_unlock(&dlm->spinlock); return dead; } +/* returns true if node is no longer in the domain + * could be dead or just not joined */ +static int dlm_is_node_recovered(struct dlm_ctxt *dlm, u8 node) +{ + int recovered; + spin_lock(&dlm->spinlock); + recovered = !test_bit(node, dlm->recovery_map); + spin_unlock(&dlm->spinlock); + return recovered; +} + + int dlm_wait_for_node_death(struct dlm_ctxt *dlm, u8 node, int timeout) { if (timeout) { @@ -290,6 +385,24 @@ int dlm_wait_for_node_death(struct dlm_c return 0; } +int dlm_wait_for_node_recovery(struct dlm_ctxt *dlm, u8 node, int timeout) +{ + if (timeout) { + mlog(0, "%s: waiting %dms for notification of " + "recovery of node %u\n", dlm->name, timeout, node); + wait_event_timeout(dlm->dlm_reco_thread_wq, + dlm_is_node_recovered(dlm, node), + msecs_to_jiffies(timeout)); + } else { + mlog(0, "%s: waiting indefinitely for notification " + "of recovery of node %u\n", dlm->name, node); + wait_event(dlm->dlm_reco_thread_wq, + dlm_is_node_recovered(dlm, node)); + } + /* for now, return 0 */ + return 0; +} + /* callers of the top-level api calls (dlmlock/dlmunlock) should * block on the dlm->reco.event when recovery is in progress. * the dlm recovery thread will set this state when it begins @@ -308,6 +421,13 @@ static int dlm_in_recovery(struct dlm_ct void dlm_wait_for_recovery(struct dlm_ctxt *dlm) { + if (dlm_in_recovery(dlm)) { + mlog(0, "%s: reco thread %d in recovery: " + "state=%d, master=%u, dead=%u\n", + dlm->name, dlm->dlm_reco_thread_task->pid, + dlm->reco.state, dlm->reco.new_master, + dlm->reco.dead_node); + } wait_event(dlm->reco.event, !dlm_in_recovery(dlm)); } @@ -341,7 +461,7 @@ static int dlm_do_recovery(struct dlm_ct mlog(0, "new master %u died while recovering %u!\n", dlm->reco.new_master, dlm->reco.dead_node); /* unset the new_master, leave dead_node */ - dlm->reco.new_master = O2NM_INVALID_NODE_NUM; + dlm_set_reco_master(dlm, O2NM_INVALID_NODE_NUM); } /* select a target to recover */ @@ -350,14 +470,14 @@ static int dlm_do_recovery(struct dlm_ct bit = find_next_bit (dlm->recovery_map, O2NM_MAX_NODES+1, 0); if (bit >= O2NM_MAX_NODES || bit < 0) - dlm->reco.dead_node = O2NM_INVALID_NODE_NUM; + dlm_set_reco_dead_node(dlm, O2NM_INVALID_NODE_NUM); else - dlm->reco.dead_node = bit; + dlm_set_reco_dead_node(dlm, bit); } else if (!test_bit(dlm->reco.dead_node, dlm->recovery_map)) { /* BUG? */ mlog(ML_ERROR, "dead_node %u no longer in recovery map!\n", dlm->reco.dead_node); - dlm->reco.dead_node = O2NM_INVALID_NODE_NUM; + dlm_set_reco_dead_node(dlm, O2NM_INVALID_NODE_NUM); } if (dlm->reco.dead_node == O2NM_INVALID_NODE_NUM) { @@ -366,7 +486,8 @@ static int dlm_do_recovery(struct dlm_ct /* return to main thread loop and sleep. */ return 0; } - mlog(0, "recovery thread found node %u in the recovery map!\n", + mlog(0, "%s(%d):recovery thread found node %u in the recovery map!\n", + dlm->name, dlm->dlm_reco_thread_task->pid, dlm->reco.dead_node); spin_unlock(&dlm->spinlock); @@ -389,8 +510,8 @@ static int dlm_do_recovery(struct dlm_ct } mlog(0, "another node will master this recovery session.\n"); } - mlog(0, "dlm=%s, new_master=%u, this node=%u, dead_node=%u\n", - dlm->name, dlm->reco.new_master, + mlog(0, "dlm=%s (%d), new_master=%u, this node=%u, dead_node=%u\n", + dlm->name, dlm->dlm_reco_thread_task->pid, dlm->reco.new_master, dlm->node_num, dlm->reco.dead_node); /* it is safe to start everything back up here @@ -402,11 +523,13 @@ static int dlm_do_recovery(struct dlm_ct return 0; master_here: - mlog(0, "mastering recovery of %s:%u here(this=%u)!\n", + mlog(0, "(%d) mastering recovery of %s:%u here(this=%u)!\n", + dlm->dlm_reco_thread_task->pid, dlm->name, dlm->reco.dead_node, dlm->node_num); status = dlm_remaster_locks(dlm, dlm->reco.dead_node); if (status < 0) { + /* we should never hit this anymore */ mlog(ML_ERROR, "error %d remastering locks for node %u, " "retrying.\n", status, dlm->reco.dead_node); /* yield a bit to allow any final network messages @@ -433,9 +556,16 @@ static int dlm_remaster_locks(struct dlm int destroy = 0; int pass = 0; - status = dlm_init_recovery_area(dlm, dead_node); - if (status < 0) - goto leave; + do { + /* we have become recovery master. there is no escaping + * this, so just keep trying until we get it. */ + status = dlm_init_recovery_area(dlm, dead_node); + if (status < 0) { + mlog(ML_ERROR, "%s: failed to alloc recovery area, " + "retrying\n", dlm->name); + msleep(1000); + } + } while (status != 0); /* safe to access the node data list without a lock, since this * process is the only one to change the list */ @@ -452,16 +582,36 @@ static int dlm_remaster_locks(struct dlm continue; } - status = dlm_request_all_locks(dlm, ndata->node_num, dead_node); - if (status < 0) { - mlog_errno(status); - if (dlm_is_host_down(status)) - ndata->state = DLM_RECO_NODE_DATA_DEAD; - else { - destroy = 1; - goto leave; + do { + status = dlm_request_all_locks(dlm, ndata->node_num, + dead_node); + if (status < 0) { + mlog_errno(status); + if (dlm_is_host_down(status)) { + /* node died, ignore it for recovery */ + status = 0; + ndata->state = DLM_RECO_NODE_DATA_DEAD; + /* wait for the domain map to catch up + * with the network state. */ + wait_event_timeout(dlm->dlm_reco_thread_wq, + dlm_is_node_dead(dlm, + ndata->node_num), + msecs_to_jiffies(1000)); + mlog(0, "waited 1 sec for %u, " + "dead? %s\n", ndata->node_num, + dlm_is_node_dead(dlm, ndata->node_num) ? + "yes" : "no"); + } else { + /* -ENOMEM on the other node */ + mlog(0, "%s: node %u returned " + "%d during recovery, retrying " + "after a short wait\n", + dlm->name, ndata->node_num, + status); + msleep(100); + } } - } + } while (status != 0); switch (ndata->state) { case DLM_RECO_NODE_DATA_INIT: @@ -473,10 +623,9 @@ static int dlm_remaster_locks(struct dlm mlog(0, "node %u died after requesting " "recovery info for node %u\n", ndata->node_num, dead_node); - // start all over - destroy = 1; - status = -EAGAIN; - goto leave; + /* fine. don't need this node's info. + * continue without it. */ + break; case DLM_RECO_NODE_DATA_REQUESTING: ndata->state = DLM_RECO_NODE_DATA_REQUESTED; mlog(0, "now receiving recovery data from " @@ -520,35 +669,26 @@ static int dlm_remaster_locks(struct dlm BUG(); break; case DLM_RECO_NODE_DATA_DEAD: - mlog(ML_NOTICE, "node %u died after " + mlog(0, "node %u died after " "requesting recovery info for " "node %u\n", ndata->node_num, dead_node); - spin_unlock(&dlm_reco_state_lock); - // start all over - destroy = 1; - status = -EAGAIN; - /* instead of spinning like crazy here, - * wait for the domain map to catch up - * with the network state. otherwise this - * can be hit hundreds of times before - * the node is really seen as dead. */ - wait_event_timeout(dlm->dlm_reco_thread_wq, - dlm_is_node_dead(dlm, - ndata->node_num), - msecs_to_jiffies(1000)); - mlog(0, "waited 1 sec for %u, " - "dead? %s\n", ndata->node_num, - dlm_is_node_dead(dlm, ndata->node_num) ? - "yes" : "no"); - goto leave; + break; case DLM_RECO_NODE_DATA_RECEIVING: case DLM_RECO_NODE_DATA_REQUESTED: + mlog(0, "%s: node %u still in state %s\n", + dlm->name, ndata->node_num, + ndata->state==DLM_RECO_NODE_DATA_RECEIVING ? + "receiving" : "requested"); all_nodes_done = 0; break; case DLM_RECO_NODE_DATA_DONE: + mlog(0, "%s: node %u state is done\n", + dlm->name, ndata->node_num); break; case DLM_RECO_NODE_DATA_FINALIZE_SENT: + mlog(0, "%s: node %u state is finalize\n", + dlm->name, ndata->node_num); break; } } @@ -578,7 +718,7 @@ static int dlm_remaster_locks(struct dlm jiffies, dlm->reco.dead_node, dlm->node_num, dlm->reco.new_master); destroy = 1; - status = ret; + status = 0; /* rescan everything marked dirty along the way */ dlm_kick_thread(dlm, NULL); break; @@ -591,7 +731,6 @@ static int dlm_remaster_locks(struct dlm } -leave: if (destroy) dlm_destroy_recovery_area(dlm, dead_node); @@ -617,7 +756,7 @@ static int dlm_init_recovery_area(struct } BUG_ON(num == dead_node); - ndata = kcalloc(1, sizeof(*ndata), GFP_KERNEL); + ndata = kcalloc(1, sizeof(*ndata), GFP_NOFS); if (!ndata) { dlm_destroy_recovery_area(dlm, dead_node); return -ENOMEM; @@ -691,16 +830,25 @@ int dlm_request_all_locks_handler(struct if (!dlm_grab(dlm)) return -EINVAL; + if (lr->dead_node != dlm->reco.dead_node) { + mlog(ML_ERROR, "%s: node %u sent dead_node=%u, but local " + "dead_node is %u\n", dlm->name, lr->node_idx, + lr->dead_node, dlm->reco.dead_node); + dlm_print_reco_node_status(dlm); + /* this is a hack */ + dlm_put(dlm); + return -ENOMEM; + } BUG_ON(lr->dead_node != dlm->reco.dead_node); - item = kcalloc(1, sizeof(*item), GFP_KERNEL); + item = kcalloc(1, sizeof(*item), GFP_NOFS); if (!item) { dlm_put(dlm); return -ENOMEM; } /* this will get freed by dlm_request_all_locks_worker */ - buf = (char *) __get_free_page(GFP_KERNEL); + buf = (char *) __get_free_page(GFP_NOFS); if (!buf) { kfree(item); dlm_put(dlm); @@ -715,7 +863,7 @@ int dlm_request_all_locks_handler(struct spin_lock(&dlm->work_lock); list_add_tail(&item->list, &dlm->work_list); spin_unlock(&dlm->work_lock); - schedule_work(&dlm->dispatched_work); + queue_work(dlm->dlm_worker, &dlm->dispatched_work); dlm_put(dlm); return 0; @@ -730,32 +878,34 @@ static void dlm_request_all_locks_worker struct list_head *iter; int ret; u8 dead_node, reco_master; + int skip_all_done = 0; dlm = item->dlm; dead_node = item->u.ral.dead_node; reco_master = item->u.ral.reco_master; mres = (struct dlm_migratable_lockres *)data; + mlog(0, "%s: recovery worker started, dead=%u, master=%u\n", + dlm->name, dead_node, reco_master); + if (dead_node != dlm->reco.dead_node || reco_master != dlm->reco.new_master) { - /* show extra debug info if the recovery state is messed */ - mlog(ML_ERROR, "%s: bad reco state: reco(dead=%u, master=%u), " - "request(dead=%u, master=%u)\n", - dlm->name, dlm->reco.dead_node, dlm->reco.new_master, - dead_node, reco_master); - mlog(ML_ERROR, "%s: name=%.*s master=%u locks=%u/%u flags=%u " - "entry[0]={c=%u:%llu,l=%u,f=%u,t=%d,ct=%d,hb=%d,n=%u}\n", - dlm->name, mres->lockname_len, mres->lockname, mres->master, - mres->num_locks, mres->total_locks, mres->flags, - dlm_get_lock_cookie_node(mres->ml[0].cookie), - dlm_get_lock_cookie_seq(mres->ml[0].cookie), - mres->ml[0].list, mres->ml[0].flags, - mres->ml[0].type, mres->ml[0].convert_type, - mres->ml[0].highest_blocked, mres->ml[0].node); - BUG(); + /* worker could have been created before the recovery master + * died. if so, do not continue, but do not error. */ + if (dlm->reco.new_master == O2NM_INVALID_NODE_NUM) { + mlog(ML_NOTICE, "%s: will not send recovery state, " + "recovery master %u died, thread=(dead=%u,mas=%u)" + " current=(dead=%u,mas=%u)\n", dlm->name, + reco_master, dead_node, reco_master, + dlm->reco.dead_node, dlm->reco.new_master); + } else { + mlog(ML_NOTICE, "%s: reco state invalid: reco(dead=%u, " + "master=%u), request(dead=%u, master=%u)\n", + dlm->name, dlm->reco.dead_node, + dlm->reco.new_master, dead_node, reco_master); + } + goto leave; } - BUG_ON(dead_node != dlm->reco.dead_node); - BUG_ON(reco_master != dlm->reco.new_master); /* lock resources should have already been moved to the * dlm->reco.resources list. now move items from that list @@ -766,12 +916,20 @@ static void dlm_request_all_locks_worker dlm_move_reco_locks_to_list(dlm, &resources, dead_node); /* now we can begin blasting lockreses without the dlm lock */ + + /* any errors returned will be due to the new_master dying, + * the dlm_reco_thread should detect this */ list_for_each(iter, &resources) { res = list_entry (iter, struct dlm_lock_resource, recovering); ret = dlm_send_one_lockres(dlm, res, mres, reco_master, DLM_MRES_RECOVERY); - if (ret < 0) - mlog_errno(ret); + if (ret < 0) { + mlog(ML_ERROR, "%s: node %u went down while sending " + "recovery state for dead node %u, ret=%d\n", dlm->name, + reco_master, dead_node, ret); + skip_all_done = 1; + break; + } } /* move the resources back to the list */ @@ -779,10 +937,15 @@ static void dlm_request_all_locks_worker list_splice_init(&resources, &dlm->reco.resources); spin_unlock(&dlm->spinlock); - ret = dlm_send_all_done_msg(dlm, dead_node, reco_master); - if (ret < 0) - mlog_errno(ret); - + if (!skip_all_done) { + ret = dlm_send_all_done_msg(dlm, dead_node, reco_master); + if (ret < 0) { + mlog(ML_ERROR, "%s: node %u went down while sending " + "recovery all-done for dead node %u, ret=%d\n", + dlm->name, reco_master, dead_node, ret); + } + } +leave: free_page((unsigned long)data); } @@ -801,8 +964,14 @@ static int dlm_send_all_done_msg(struct ret = o2net_send_message(DLM_RECO_DATA_DONE_MSG, dlm->key, &done_msg, sizeof(done_msg), send_to, &tmpret); - /* negative status is ignored by the caller */ - if (ret >= 0) + if (ret < 0) { + if (!dlm_is_host_down(ret)) { + mlog_errno(ret); + mlog(ML_ERROR, "%s: unknown error sending data-done " + "to %u\n", dlm->name, send_to); + BUG(); + } + } else ret = tmpret; return ret; } @@ -822,7 +991,11 @@ int dlm_reco_data_done_handler(struct o2 mlog(0, "got DATA DONE: dead_node=%u, reco.dead_node=%u, " "node_idx=%u, this node=%u\n", done->dead_node, dlm->reco.dead_node, done->node_idx, dlm->node_num); - BUG_ON(done->dead_node != dlm->reco.dead_node); + + mlog_bug_on_msg((done->dead_node != dlm->reco.dead_node), + "Got DATA DONE: dead_node=%u, reco.dead_node=%u, " + "node_idx=%u, this node=%u\n", done->dead_node, + dlm->reco.dead_node, done->node_idx, dlm->node_num); spin_lock(&dlm_reco_state_lock); list_for_each(iter, &dlm->reco.node_data) { @@ -905,13 +1078,11 @@ static void dlm_move_reco_locks_to_list( mlog(0, "found lockres owned by dead node while " "doing recovery for node %u. sending it.\n", dead_node); - list_del_init(&res->recovering); - list_add_tail(&res->recovering, list); + list_move_tail(&res->recovering, list); } else if (res->owner == DLM_LOCK_RES_OWNER_UNKNOWN) { mlog(0, "found UNKNOWN owner while doing recovery " "for node %u. sending it.\n", dead_node); - list_del_init(&res->recovering); - list_add_tail(&res->recovering, list); + list_move_tail(&res->recovering, list); } } spin_unlock(&dlm->spinlock); @@ -1023,8 +1194,9 @@ static int dlm_add_lock_to_array(struct ml->type == LKM_PRMODE) { /* if it is already set, this had better be a PR * and it has to match */ - if (mres->lvb[0] && (ml->type == LKM_EXMODE || - memcmp(mres->lvb, lock->lksb->lvb, DLM_LVB_LEN))) { + if (!dlm_lvb_is_empty(mres->lvb) && + (ml->type == LKM_EXMODE || + memcmp(mres->lvb, lock->lksb->lvb, DLM_LVB_LEN))) { mlog(ML_ERROR, "mismatched lvbs!\n"); __dlm_print_one_lock_resource(lock->lockres); BUG(); @@ -1083,22 +1255,25 @@ int dlm_send_one_lockres(struct dlm_ctxt * we must send it immediately. */ ret = dlm_send_mig_lockres_msg(dlm, mres, send_to, res, total_locks); - if (ret < 0) { - // TODO - mlog(ML_ERROR, "dlm_send_mig_lockres_msg " - "returned %d, TODO\n", ret); - BUG(); - } + if (ret < 0) + goto error; } } /* flush any remaining locks */ ret = dlm_send_mig_lockres_msg(dlm, mres, send_to, res, total_locks); - if (ret < 0) { - // TODO - mlog(ML_ERROR, "dlm_send_mig_lockres_msg returned %d, " - "TODO\n", ret); + if (ret < 0) + goto error; + return ret; + +error: + mlog(ML_ERROR, "%s: dlm_send_mig_lockres_msg returned %d\n", + dlm->name, ret); + if (!dlm_is_host_down(ret)) BUG(); - } + mlog(0, "%s: node %u went down while sending %s " + "lockres %.*s\n", dlm->name, send_to, + flags & DLM_MRES_RECOVERY ? "recovery" : "migration", + res->lockname.len, res->lockname.name); return ret; } @@ -1146,8 +1321,8 @@ int dlm_mig_lockres_handler(struct o2net mlog(0, "all done flag. all lockres data received!\n"); ret = -ENOMEM; - buf = kmalloc(be16_to_cpu(msg->data_len), GFP_KERNEL); - item = kcalloc(1, sizeof(*item), GFP_KERNEL); + buf = kmalloc(be16_to_cpu(msg->data_len), GFP_NOFS); + item = kcalloc(1, sizeof(*item), GFP_NOFS); if (!buf || !item) goto leave; @@ -1238,7 +1413,7 @@ int dlm_mig_lockres_handler(struct o2net spin_lock(&dlm->work_lock); list_add_tail(&item->list, &dlm->work_list); spin_unlock(&dlm->work_lock); - schedule_work(&dlm->dispatched_work); + queue_work(dlm->dlm_worker, &dlm->dispatched_work); leave: dlm_put(dlm); @@ -1312,8 +1487,9 @@ leave: -int dlm_lockres_master_requery(struct dlm_ctxt *dlm, - struct dlm_lock_resource *res, u8 *real_master) +static int dlm_lockres_master_requery(struct dlm_ctxt *dlm, + struct dlm_lock_resource *res, + u8 *real_master) { struct dlm_node_iter iter; int nodenum; @@ -1406,6 +1582,7 @@ int dlm_master_requery_handler(struct o2 struct dlm_ctxt *dlm = data; struct dlm_master_requery *req = (struct dlm_master_requery *)msg->buf; struct dlm_lock_resource *res = NULL; + unsigned int hash; int master = DLM_LOCK_RES_OWNER_UNKNOWN; u32 flags = DLM_ASSERT_MASTER_REQUERY; @@ -1415,8 +1592,10 @@ int dlm_master_requery_handler(struct o2 return master; } + hash = dlm_lockid_hash(req->name, req->namelen); + spin_lock(&dlm->spinlock); - res = __dlm_lookup_lockres(dlm, req->name, req->namelen); + res = __dlm_lookup_lockres(dlm, req->name, req->namelen, hash); if (res) { spin_lock(&res->spinlock); master = res->owner; @@ -1483,7 +1662,7 @@ static int dlm_process_recovery_data(str struct dlm_lock *newlock = NULL; struct dlm_lockstatus *lksb = NULL; int ret = 0; - int i; + int i, bad; struct list_head *iter; struct dlm_lock *lock = NULL; @@ -1529,8 +1708,7 @@ static int dlm_process_recovery_data(str /* move the lock to its proper place */ /* do not alter lock refcount. switching lists. */ - list_del_init(&lock->list); - list_add_tail(&lock->list, queue); + list_move_tail(&lock->list, queue); spin_unlock(&res->spinlock); mlog(0, "just reordered a local lock!\n"); @@ -1553,28 +1731,48 @@ static int dlm_process_recovery_data(str } lksb->flags |= (ml->flags & (DLM_LKSB_PUT_LVB|DLM_LKSB_GET_LVB)); - - if (mres->lvb[0]) { + + if (ml->type == LKM_NLMODE) + goto skip_lvb; + + if (!dlm_lvb_is_empty(mres->lvb)) { if (lksb->flags & DLM_LKSB_PUT_LVB) { /* other node was trying to update * lvb when node died. recreate the * lksb with the updated lvb. */ memcpy(lksb->lvb, mres->lvb, DLM_LVB_LEN); + /* the lock resource lvb update must happen + * NOW, before the spinlock is dropped. + * we no longer wait for the AST to update + * the lvb. */ + memcpy(res->lvb, mres->lvb, DLM_LVB_LEN); } else { /* otherwise, the node is sending its * most recent valid lvb info */ BUG_ON(ml->type != LKM_EXMODE && ml->type != LKM_PRMODE); - if (res->lvb[0] && (ml->type == LKM_EXMODE || - memcmp(res->lvb, mres->lvb, DLM_LVB_LEN))) { - mlog(ML_ERROR, "received bad lvb!\n"); - __dlm_print_one_lock_resource(res); - BUG(); + if (!dlm_lvb_is_empty(res->lvb) && + (ml->type == LKM_EXMODE || + memcmp(res->lvb, mres->lvb, DLM_LVB_LEN))) { + int i; + mlog(ML_ERROR, "%s:%.*s: received bad " + "lvb! type=%d\n", dlm->name, + res->lockname.len, + res->lockname.name, ml->type); + printk("lockres lvb=["); + for (i=0; ilvb[i]); + printk("]\nmigrated lvb=["); + for (i=0; ilvb[i]); + printk("]\n"); + dlm_print_one_lock_resource(res); + BUG(); } memcpy(res->lvb, mres->lvb, DLM_LVB_LEN); } } - +skip_lvb: /* NOTE: * wrt lock queue ordering and recovery: @@ -1592,9 +1790,33 @@ static int dlm_process_recovery_data(str * relative to each other, but clearly *not* * preserved relative to locks from other nodes. */ + bad = 0; spin_lock(&res->spinlock); - dlm_lock_get(newlock); - list_add_tail(&newlock->list, queue); + list_for_each_entry(lock, queue, list) { + if (lock->ml.cookie == ml->cookie) { + u64 c = lock->ml.cookie; + mlog(ML_ERROR, "%s:%.*s: %u:%llu: lock already " + "exists on this lockres!\n", dlm->name, + res->lockname.len, res->lockname.name, + dlm_get_lock_cookie_node(c), + dlm_get_lock_cookie_seq(c)); + + mlog(ML_NOTICE, "sent lock: type=%d, conv=%d, " + "node=%u, cookie=%u:%llu, queue=%d\n", + ml->type, ml->convert_type, ml->node, + dlm_get_lock_cookie_node(ml->cookie), + dlm_get_lock_cookie_seq(ml->cookie), + ml->list); + + __dlm_print_one_lock_resource(res); + bad = 1; + break; + } + } + if (!bad) { + dlm_lock_get(newlock); + list_add_tail(&newlock->list, queue); + } spin_unlock(&res->spinlock); } mlog(0, "done running all the locks\n"); @@ -1618,8 +1840,14 @@ void dlm_move_lockres_to_recovery_list(s struct dlm_lock *lock; res->state |= DLM_LOCK_RES_RECOVERING; - if (!list_empty(&res->recovering)) + if (!list_empty(&res->recovering)) { + mlog(0, + "Recovering res %s:%.*s, is already on recovery list!\n", + dlm->name, res->lockname.len, res->lockname.name); list_del_init(&res->recovering); + } + /* We need to hold a reference while on the recovery list */ + dlm_lockres_get(res); list_add_tail(&res->recovering, &dlm->reco.resources); /* find any pending locks and put them back on proper list */ @@ -1708,9 +1936,11 @@ static void dlm_finish_local_lockres_rec spin_lock(&res->spinlock); dlm_change_lockres_owner(dlm, res, new_master); res->state &= ~DLM_LOCK_RES_RECOVERING; - __dlm_dirty_lockres(dlm, res); + if (!__dlm_lockres_unused(res)) + __dlm_dirty_lockres(dlm, res); spin_unlock(&res->spinlock); wake_up(&res->wq); + dlm_lockres_put(res); } } @@ -1719,7 +1949,7 @@ static void dlm_finish_local_lockres_rec * the RECOVERING state and set the owner * if necessary */ for (i = 0; i < DLM_HASH_BUCKETS; i++) { - bucket = &(dlm->lockres_hash[i]); + bucket = dlm_lockres_hash(dlm, i); hlist_for_each_entry(res, hash_iter, bucket, hash_node) { if (res->state & DLM_LOCK_RES_RECOVERING) { if (res->owner == dead_node) { @@ -1743,11 +1973,13 @@ static void dlm_finish_local_lockres_rec dlm->name, res->lockname.len, res->lockname.name, res->owner); list_del_init(&res->recovering); + dlm_lockres_put(res); } spin_lock(&res->spinlock); dlm_change_lockres_owner(dlm, res, new_master); res->state &= ~DLM_LOCK_RES_RECOVERING; - __dlm_dirty_lockres(dlm, res); + if (!__dlm_lockres_unused(res)) + __dlm_dirty_lockres(dlm, res); spin_unlock(&res->spinlock); wake_up(&res->wq); } @@ -1884,7 +2116,7 @@ static void dlm_do_local_recovery_cleanu * need to be fired as a result. */ for (i = 0; i < DLM_HASH_BUCKETS; i++) { - bucket = &(dlm->lockres_hash[i]); + bucket = dlm_lockres_hash(dlm, i); hlist_for_each_entry(res, iter, bucket, hash_node) { /* always prune any $RECOVERY entries for dead nodes, * otherwise hangs can occur during later recovery */ @@ -1924,6 +2156,20 @@ static void __dlm_hb_node_down(struct dl { assert_spin_locked(&dlm->spinlock); + if (dlm->reco.new_master == idx) { + mlog(0, "%s: recovery master %d just died\n", + dlm->name, idx); + if (dlm->reco.state & DLM_RECO_STATE_FINALIZE) { + /* finalize1 was reached, so it is safe to clear + * the new_master and dead_node. that recovery + * is complete. */ + mlog(0, "%s: dead master %d had reached " + "finalize1 state, clearing\n", dlm->name, idx); + dlm->reco.state &= ~DLM_RECO_STATE_FINALIZE; + __dlm_reset_recovery(dlm); + } + } + /* check to see if the node is already considered dead */ if (!test_bit(idx, dlm->live_nodes_map)) { mlog(0, "for domain %s, node %d is already dead. " @@ -2087,7 +2333,7 @@ again: /* set the new_master to this node */ spin_lock(&dlm->spinlock); - dlm->reco.new_master = dlm->node_num; + dlm_set_reco_master(dlm, dlm->node_num); spin_unlock(&dlm->spinlock); } @@ -2125,6 +2371,10 @@ again: mlog(0, "%s: reco master %u is ready to recover %u\n", dlm->name, dlm->reco.new_master, dlm->reco.dead_node); status = -EEXIST; + } else if (ret == DLM_RECOVERING) { + mlog(0, "dlm=%s dlmlock says master node died (this=%u)\n", + dlm->name, dlm->node_num); + goto again; } else { struct dlm_lock_resource *res; @@ -2156,7 +2406,7 @@ static int dlm_send_begin_reco_message(s mlog_entry("%u\n", dead_node); - mlog(0, "dead node is %u\n", dead_node); + mlog(0, "%s: dead node is %u\n", dlm->name, dead_node); spin_lock(&dlm->spinlock); dlm_node_iter_init(dlm->domain_map, &iter); @@ -2214,6 +2464,14 @@ retry: * another ENOMEM */ msleep(100); goto retry; + } else if (ret == EAGAIN) { + mlog(0, "%s: trying to start recovery of node " + "%u, but node %u is waiting for last recovery " + "to complete, backoff for a bit\n", dlm->name, + dead_node, nodenum); + /* TODO Look into replacing msleep with cond_resched() */ + msleep(100); + goto retry; } } @@ -2229,8 +2487,20 @@ int dlm_begin_reco_handler(struct o2net_ if (!dlm_grab(dlm)) return 0; - mlog(0, "node %u wants to recover node %u\n", - br->node_idx, br->dead_node); + spin_lock(&dlm->spinlock); + if (dlm->reco.state & DLM_RECO_STATE_FINALIZE) { + mlog(0, "%s: node %u wants to recover node %u (%u:%u) " + "but this node is in finalize state, waiting on finalize2\n", + dlm->name, br->node_idx, br->dead_node, + dlm->reco.dead_node, dlm->reco.new_master); + spin_unlock(&dlm->spinlock); + return EAGAIN; + } + spin_unlock(&dlm->spinlock); + + mlog(0, "%s: node %u wants to recover node %u (%u:%u)\n", + dlm->name, br->node_idx, br->dead_node, + dlm->reco.dead_node, dlm->reco.new_master); dlm_fire_domain_eviction_callbacks(dlm, br->dead_node); @@ -2252,8 +2522,8 @@ int dlm_begin_reco_handler(struct o2net_ "node %u changing it to %u\n", dlm->name, dlm->reco.dead_node, br->node_idx, br->dead_node); } - dlm->reco.new_master = br->node_idx; - dlm->reco.dead_node = br->dead_node; + dlm_set_reco_master(dlm, br->node_idx); + dlm_set_reco_dead_node(dlm, br->dead_node); if (!test_bit(br->dead_node, dlm->recovery_map)) { mlog(0, "recovery master %u sees %u as dead, but this " "node has not yet. marking %u as dead\n", @@ -2272,10 +2542,16 @@ int dlm_begin_reco_handler(struct o2net_ spin_unlock(&dlm->spinlock); dlm_kick_recovery_thread(dlm); + + mlog(0, "%s: recovery started by node %u, for %u (%u:%u)\n", + dlm->name, br->node_idx, br->dead_node, + dlm->reco.dead_node, dlm->reco.new_master); + dlm_put(dlm); return 0; } +#define DLM_FINALIZE_STAGE2 0x01 static int dlm_send_finalize_reco_message(struct dlm_ctxt *dlm) { int ret = 0; @@ -2283,25 +2559,31 @@ static int dlm_send_finalize_reco_messag struct dlm_node_iter iter; int nodenum; int status; + int stage = 1; - mlog(0, "finishing recovery for node %s:%u\n", - dlm->name, dlm->reco.dead_node); + mlog(0, "finishing recovery for node %s:%u, " + "stage %d\n", dlm->name, dlm->reco.dead_node, stage); spin_lock(&dlm->spinlock); dlm_node_iter_init(dlm->domain_map, &iter); spin_unlock(&dlm->spinlock); +stage2: memset(&fr, 0, sizeof(fr)); fr.node_idx = dlm->node_num; fr.dead_node = dlm->reco.dead_node; + if (stage == 2) + fr.flags |= DLM_FINALIZE_STAGE2; while ((nodenum = dlm_node_iter_next(&iter)) >= 0) { if (nodenum == dlm->node_num) continue; ret = o2net_send_message(DLM_FINALIZE_RECO_MSG, dlm->key, &fr, sizeof(fr), nodenum, &status); - if (ret >= 0) { + if (ret >= 0) ret = status; + if (ret < 0) { + mlog_errno(ret); if (dlm_is_host_down(ret)) { /* this has no effect on this recovery * session, so set the status to zero to @@ -2309,13 +2591,17 @@ static int dlm_send_finalize_reco_messag mlog(ML_ERROR, "node %u went down after this " "node finished recovery.\n", nodenum); ret = 0; + continue; } - } - if (ret < 0) { - mlog_errno(ret); break; } } + if (stage == 1) { + /* reset the node_iter back to the top and send finalize2 */ + iter.curnode = -1; + stage = 2; + goto stage2; + } return ret; } @@ -2324,14 +2610,19 @@ int dlm_finalize_reco_handler(struct o2n { struct dlm_ctxt *dlm = data; struct dlm_finalize_reco *fr = (struct dlm_finalize_reco *)msg->buf; + int stage = 1; /* ok to return 0, domain has gone away */ if (!dlm_grab(dlm)) return 0; - mlog(0, "node %u finalizing recovery of node %u\n", - fr->node_idx, fr->dead_node); + if (fr->flags & DLM_FINALIZE_STAGE2) + stage = 2; + mlog(0, "%s: node %u finalizing recovery stage%d of " + "node %u (%u:%u)\n", dlm->name, fr->node_idx, stage, + fr->dead_node, dlm->reco.dead_node, dlm->reco.new_master); + spin_lock(&dlm->spinlock); if (dlm->reco.new_master != fr->node_idx) { @@ -2347,13 +2638,41 @@ int dlm_finalize_reco_handler(struct o2n BUG(); } - dlm_finish_local_lockres_recovery(dlm, fr->dead_node, fr->node_idx); - - spin_unlock(&dlm->spinlock); + switch (stage) { + case 1: + dlm_finish_local_lockres_recovery(dlm, fr->dead_node, fr->node_idx); + if (dlm->reco.state & DLM_RECO_STATE_FINALIZE) { + mlog(ML_ERROR, "%s: received finalize1 from " + "new master %u for dead node %u, but " + "this node has already received it!\n", + dlm->name, fr->node_idx, fr->dead_node); + dlm_print_reco_node_status(dlm); + BUG(); + } + dlm->reco.state |= DLM_RECO_STATE_FINALIZE; + spin_unlock(&dlm->spinlock); + break; + case 2: + if (!(dlm->reco.state & DLM_RECO_STATE_FINALIZE)) { + mlog(ML_ERROR, "%s: received finalize2 from " + "new master %u for dead node %u, but " + "this node did not have finalize1!\n", + dlm->name, fr->node_idx, fr->dead_node); + dlm_print_reco_node_status(dlm); + BUG(); + } + dlm->reco.state &= ~DLM_RECO_STATE_FINALIZE; + spin_unlock(&dlm->spinlock); + dlm_reset_recovery(dlm); + dlm_kick_recovery_thread(dlm); + break; + default: + BUG(); + } - dlm_reset_recovery(dlm); + mlog(0, "%s: recovery done, reco master was %u, dead now %u, master now %u\n", + dlm->name, fr->node_idx, dlm->reco.dead_node, dlm->reco.new_master); - dlm_kick_recovery_thread(dlm); dlm_put(dlm); return 0; } diff --git a/fs/ocfs2/dlm/dlmthread.c b/fs/ocfs2/dlm/dlmthread.c index 5be9d14..0c822f3 100644 --- a/fs/ocfs2/dlm/dlmthread.c +++ b/fs/ocfs2/dlm/dlmthread.c @@ -39,6 +39,7 @@ #include #include #include #include +#include #include "cluster/heartbeat.h" @@ -53,6 +54,8 @@ #define MLOG_MASK_PREFIX (ML_DLM|ML_DLM_ #include "cluster/masklog.h" static int dlm_thread(void *data); +static void dlm_purge_lockres_now(struct dlm_ctxt *dlm, + struct dlm_lock_resource *lockres); static void dlm_flush_asts(struct dlm_ctxt *dlm); @@ -80,7 +83,7 @@ repeat: } -static int __dlm_lockres_unused(struct dlm_lock_resource *res) +int __dlm_lockres_unused(struct dlm_lock_resource *res) { if (list_empty(&res->granted) && list_empty(&res->converting) && @@ -103,6 +106,20 @@ void __dlm_lockres_calc_usage(struct dlm assert_spin_locked(&res->spinlock); if (__dlm_lockres_unused(res)){ + /* For now, just keep any resource we master */ + if (res->owner == dlm->node_num) + { + if (!list_empty(&res->purge)) { + mlog(0, "we master %s:%.*s, but it is on " + "the purge list. Removing\n", + dlm->name, res->lockname.len, + res->lockname.name); + list_del_init(&res->purge); + dlm->purge_count--; + } + return; + } + if (list_empty(&res->purge)) { mlog(0, "putting lockres %.*s from purge list\n", res->lockname.len, res->lockname.name); @@ -110,10 +127,23 @@ void __dlm_lockres_calc_usage(struct dlm res->last_used = jiffies; list_add_tail(&res->purge, &dlm->purge_list); dlm->purge_count++; + + /* if this node is not the owner, there is + * no way to keep track of who the owner could be. + * unhash it to avoid serious problems. */ + if (res->owner != dlm->node_num) { + mlog(0, "%s:%.*s: doing immediate " + "purge of lockres owned by %u\n", + dlm->name, res->lockname.len, + res->lockname.name, res->owner); + + dlm_purge_lockres_now(dlm, res); + } } } else if (!list_empty(&res->purge)) { - mlog(0, "removing lockres %.*s from purge list\n", - res->lockname.len, res->lockname.name); + mlog(0, "removing lockres %.*s from purge list, " + "owner=%u\n", res->lockname.len, res->lockname.name, + res->owner); list_del_init(&res->purge); dlm->purge_count--; @@ -165,6 +195,7 @@ again: } else if (ret < 0) { mlog(ML_NOTICE, "lockres %.*s: migrate failed, retrying\n", lockres->lockname.len, lockres->lockname.name); + msleep(100); goto again; } @@ -178,6 +209,24 @@ finish: __dlm_unhash_lockres(lockres); } +/* make an unused lockres go away immediately. + * as soon as the dlm spinlock is dropped, this lockres + * will not be found. kfree still happens on last put. */ +static void dlm_purge_lockres_now(struct dlm_ctxt *dlm, + struct dlm_lock_resource *lockres) +{ + assert_spin_locked(&dlm->spinlock); + assert_spin_locked(&lockres->spinlock); + + BUG_ON(!__dlm_lockres_unused(lockres)); + + if (!list_empty(&lockres->purge)) { + list_del_init(&lockres->purge); + dlm->purge_count--; + } + __dlm_unhash_lockres(lockres); +} + static void dlm_run_purge_list(struct dlm_ctxt *dlm, int purge_now) { @@ -318,8 +367,7 @@ converting: target->ml.type = target->ml.convert_type; target->ml.convert_type = LKM_IVMODE; - list_del_init(&target->list); - list_add_tail(&target->list, &res->granted); + list_move_tail(&target->list, &res->granted); BUG_ON(!target->lksb); target->lksb->status = DLM_NORMAL; @@ -380,8 +428,7 @@ blocked: target->ml.type, target->ml.node); // target->ml.type is already correct - list_del_init(&target->list); - list_add_tail(&target->list, &res->granted); + list_move_tail(&target->list, &res->granted); BUG_ON(!target->lksb); target->lksb->status = DLM_NORMAL; @@ -422,6 +469,8 @@ void __dlm_dirty_lockres(struct dlm_ctxt /* don't shuffle secondary queues */ if ((res->owner == dlm->node_num) && !(res->state & DLM_LOCK_RES_DIRTY)) { + /* ref for dirty_list */ + dlm_lockres_get(res); list_add_tail(&res->dirty, &dlm->dirty_list); res->state |= DLM_LOCK_RES_DIRTY; } @@ -606,6 +655,8 @@ static int dlm_thread(void *data) list_del_init(&res->dirty); spin_unlock(&res->spinlock); spin_unlock(&dlm->spinlock); + /* Drop dirty_list ref */ + dlm_lockres_put(res); /* lockres can be re-dirtied/re-added to the * dirty_list in this gap, but that is ok */ @@ -642,8 +693,9 @@ static int dlm_thread(void *data) * spinlock and do NOT have the dlm lock. * safe to reserve/queue asts and run the lists. */ - mlog(0, "calling dlm_shuffle_lists with dlm=%p, " - "res=%p\n", dlm, res); + mlog(0, "calling dlm_shuffle_lists with dlm=%s, " + "res=%.*s\n", dlm->name, + res->lockname.len, res->lockname.name); /* called while holding lockres lock */ dlm_shuffle_lists(dlm, res); @@ -657,6 +709,8 @@ in_progress: /* if the lock was in-progress, stick * it on the back of the list */ if (delay) { + /* ref for dirty_list */ + dlm_lockres_get(res); spin_lock(&res->spinlock); list_add_tail(&res->dirty, &dlm->dirty_list); res->state |= DLM_LOCK_RES_DIRTY; @@ -677,7 +731,7 @@ in_progress: /* yield and continue right away if there is more work to do */ if (!n) { - yield(); + cond_resched(); continue; } diff --git a/fs/ocfs2/dlm/dlmunlock.c b/fs/ocfs2/dlm/dlmunlock.c index 7b1a275..b0c3134 100644 --- a/fs/ocfs2/dlm/dlmunlock.c +++ b/fs/ocfs2/dlm/dlmunlock.c @@ -271,8 +271,7 @@ void dlm_commit_pending_unlock(struct dl void dlm_commit_pending_cancel(struct dlm_lock_resource *res, struct dlm_lock *lock) { - list_del_init(&lock->list); - list_add_tail(&lock->list, &res->granted); + list_move_tail(&lock->list, &res->granted); lock->ml.convert_type = LKM_IVMODE; } @@ -319,6 +318,16 @@ static enum dlm_status dlm_send_remote_u mlog_entry("%.*s\n", res->lockname.len, res->lockname.name); + if (owner == dlm->node_num) { + /* ended up trying to contact ourself. this means + * that the lockres had been remote but became local + * via a migration. just retry it, now as local */ + mlog(0, "%s:%.*s: this node became the master due to a " + "migration, re-evaluate now\n", dlm->name, + res->lockname.len, res->lockname.name); + return DLM_FORWARD; + } + memset(&unlock, 0, sizeof(unlock)); unlock.node_idx = dlm->node_num; unlock.flags = cpu_to_be32(flags); diff --git a/fs/ocfs2/dlm/userdlm.c b/fs/ocfs2/dlm/userdlm.c index 74ca4e5..e641b08 100644 --- a/fs/ocfs2/dlm/userdlm.c +++ b/fs/ocfs2/dlm/userdlm.c @@ -672,7 +672,7 @@ struct dlm_ctxt *user_dlm_register_conte u32 dlm_key; char *domain; - domain = kmalloc(name->len + 1, GFP_KERNEL); + domain = kmalloc(name->len + 1, GFP_NOFS); if (!domain) { mlog_errno(-ENOMEM); return ERR_PTR(-ENOMEM); diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 64cd528..762eb1f 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -242,7 +242,7 @@ static void ocfs2_build_lock_name(enum o mlog_exit_void(); } -static spinlock_t ocfs2_dlm_tracking_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(ocfs2_dlm_tracking_lock); static void ocfs2_add_lockres_tracking(struct ocfs2_lock_res *res, struct ocfs2_dlm_debug *dlm_debug) @@ -2071,8 +2071,7 @@ int ocfs2_dlm_init(struct ocfs2_super *o } /* launch vote thread */ - osb->vote_task = kthread_run(ocfs2_vote_thread, osb, "ocfs2vote-%d", - osb->osb_id); + osb->vote_task = kthread_run(ocfs2_vote_thread, osb, "ocfs2vote"); if (IS_ERR(osb->vote_task)) { status = PTR_ERR(osb->vote_task); osb->vote_task = NULL; diff --git a/fs/ocfs2/extent_map.c b/fs/ocfs2/extent_map.c index 1a5c690..fcd4475 100644 --- a/fs/ocfs2/extent_map.c +++ b/fs/ocfs2/extent_map.c @@ -298,7 +298,7 @@ static int ocfs2_extent_map_find_leaf(st ret = ocfs2_extent_map_insert(inode, rec, le16_to_cpu(el->l_tree_depth)); - if (ret) { + if (ret && (ret != -EEXIST)) { mlog_errno(ret); goto out_free; } @@ -427,6 +427,11 @@ static int ocfs2_extent_map_insert_entry /* * Simple rule: on any return code other than -EAGAIN, anything left * in the insert_context will be freed. + * + * Simple rule #2: A return code of -EEXIST from this function or + * its calls to ocfs2_extent_map_insert_entry() signifies that another + * thread beat us to the insert. It is not an actual error, but it + * tells the caller we have no more work to do. */ static int ocfs2_extent_map_try_insert(struct inode *inode, struct ocfs2_extent_rec *rec, @@ -448,22 +453,32 @@ static int ocfs2_extent_map_try_insert(s goto out_unlock; } + /* Since insert_entry failed, the map MUST have old_ent */ old_ent = ocfs2_extent_map_lookup(em, le32_to_cpu(rec->e_cpos), - le32_to_cpu(rec->e_clusters), NULL, - NULL); + le32_to_cpu(rec->e_clusters), + NULL, NULL); BUG_ON(!old_ent); - ret = -EEXIST; - if (old_ent->e_tree_depth < tree_depth) + if (old_ent->e_tree_depth < tree_depth) { + /* Another thread beat us to the lower tree_depth */ + ret = -EEXIST; goto out_unlock; + } if (old_ent->e_tree_depth == tree_depth) { + /* + * Another thread beat us to this tree_depth. + * Let's make sure we agree with that thread (the + * extent_rec should be identical). + */ if (!memcmp(rec, &old_ent->e_rec, sizeof(struct ocfs2_extent_rec))) ret = 0; + else + /* FIXME: Should this be ESRCH/EBADR??? */ + ret = -EEXIST; - /* FIXME: Should this be ESRCH/EBADR??? */ goto out_unlock; } @@ -599,7 +614,7 @@ static int ocfs2_extent_map_insert(struc tree_depth, &ctxt); } while (ret == -EAGAIN); - if (ret < 0) + if ((ret < 0) && (ret != -EEXIST)) mlog_errno(ret); if (ctxt.left_ent) diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 84c5079..35140f6 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -114,7 +114,7 @@ #define SET_INODE_JOURNAL(i) (OCFS2_I(i) extern kmem_cache_t *ocfs2_inode_cache; -extern struct address_space_operations ocfs2_aops; +extern const struct address_space_operations ocfs2_aops; struct buffer_head *ocfs2_bread(struct inode *inode, int block, int *err, int reada); diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index eebc3cf..f92bf1d 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -49,7 +49,7 @@ #include "sysfile.h" #include "buffer_head_io.h" -spinlock_t trans_inc_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(trans_inc_lock); static int ocfs2_force_read_journal(struct inode *inode); static int ocfs2_recover_node(struct ocfs2_super *osb, @@ -222,8 +222,7 @@ void ocfs2_handle_add_inode(struct ocfs2 BUG_ON(!list_empty(&OCFS2_I(inode)->ip_handle_list)); OCFS2_I(inode)->ip_handle = handle; - list_del(&(OCFS2_I(inode)->ip_handle_list)); - list_add_tail(&(OCFS2_I(inode)->ip_handle_list), &(handle->inode_list)); + list_move_tail(&(OCFS2_I(inode)->ip_handle_list), &(handle->inode_list)); } static void ocfs2_handle_unlock_inodes(struct ocfs2_journal_handle *handle) @@ -785,8 +784,7 @@ int ocfs2_journal_load(struct ocfs2_jour } /* Launch the commit thread */ - osb->commit_task = kthread_run(ocfs2_commit_thread, osb, "ocfs2cmt-%d", - osb->osb_id); + osb->commit_task = kthread_run(ocfs2_commit_thread, osb, "ocfs2cmt"); if (IS_ERR(osb->commit_task)) { status = PTR_ERR(osb->commit_task); osb->commit_task = NULL; @@ -1119,7 +1117,7 @@ void ocfs2_recovery_thread(struct ocfs2_ goto out; osb->recovery_thread_task = kthread_run(__ocfs2_recovery_thread, osb, - "ocfs2rec-%d", osb->osb_id); + "ocfs2rec"); if (IS_ERR(osb->recovery_thread_task)) { mlog_errno((int)PTR_ERR(osb->recovery_thread_task)); osb->recovery_thread_task = NULL; diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index 843cf9d..83934e3 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -46,12 +46,12 @@ static struct page *ocfs2_nopage(struct unsigned long address, int *type) { - struct inode *inode = area->vm_file->f_dentry->d_inode; struct page *page = NOPAGE_SIGBUS; sigset_t blocked, oldset; int ret; - mlog_entry("(inode %lu, address %lu)\n", inode->i_ino, address); + mlog_entry("(area=%p, address=%lu, type=%p)\n", area, address, + type); /* The best way to deal with signals in this path is * to block them upfront, rather than allowing the diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h index da10930..cd4a6f2 100644 --- a/fs/ocfs2/ocfs2.h +++ b/fs/ocfs2/ocfs2.h @@ -184,7 +184,6 @@ struct ocfs2_journal; struct ocfs2_journal_handle; struct ocfs2_super { - u32 osb_id; /* id used by the proc interface */ struct task_struct *commit_task; struct super_block *sb; struct inode *root_inode; @@ -222,13 +221,11 @@ struct ocfs2_super unsigned long s_mount_opt; u16 max_slots; - u16 num_nodes; s16 node_num; s16 slot_num; int s_sectsize_bits; int s_clustersize; int s_clustersize_bits; - struct proc_dir_entry *proc_sub_dir; /* points to /proc/fs/ocfs2/ */ atomic_t vol_state; struct mutex recovery_lock; @@ -294,7 +291,6 @@ struct ocfs2_super }; #define OCFS2_SB(sb) ((struct ocfs2_super *)(sb)->s_fs_info) -#define OCFS2_MAX_OSB_ID 65536 static inline int ocfs2_should_order_data(struct inode *inode) { diff --git a/fs/ocfs2/slot_map.c b/fs/ocfs2/slot_map.c index 8716279..aa6f5aa 100644 --- a/fs/ocfs2/slot_map.c +++ b/fs/ocfs2/slot_map.c @@ -264,7 +264,7 @@ int ocfs2_find_slot(struct ocfs2_super * osb->slot_num = slot; spin_unlock(&si->si_lock); - mlog(ML_NOTICE, "taking node slot %d\n", osb->slot_num); + mlog(0, "taking node slot %d\n", osb->slot_num); status = ocfs2_update_disk_slots(osb, si); if (status < 0) diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 949b3da..382706a 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -68,13 +68,6 @@ #include "vote.h" #include "buffer_head_io.h" -/* - * Globals - */ -static spinlock_t ocfs2_globals_lock = SPIN_LOCK_UNLOCKED; - -static u32 osb_id; /* Keeps track of next available OSB Id */ - static kmem_cache_t *ocfs2_inode_cachep = NULL; kmem_cache_t *ocfs2_lock_cache = NULL; @@ -100,7 +93,7 @@ static int ocfs2_initialize_mem_caches(v static void ocfs2_free_mem_caches(void); static void ocfs2_delete_osb(struct ocfs2_super *osb); -static int ocfs2_statfs(struct super_block *sb, struct kstatfs *buf); +static int ocfs2_statfs(struct dentry *dentry, struct kstatfs *buf); static int ocfs2_sync_fs(struct super_block *sb, int wait); @@ -642,10 +635,9 @@ static int ocfs2_fill_super(struct super ocfs2_complete_mount_recovery(osb); - printk("ocfs2: Mounting device (%u,%u) on (node %d, slot %d) with %s " - "data mode.\n", - MAJOR(sb->s_dev), MINOR(sb->s_dev), osb->node_num, - osb->slot_num, + printk(KERN_INFO "ocfs2: Mounting device (%s) on (node %d, slot %d) " + "with %s data mode.\n", + osb->dev_str, osb->node_num, osb->slot_num, osb->s_mount_opt & OCFS2_MOUNT_DATA_WRITEBACK ? "writeback" : "ordered"); @@ -672,12 +664,14 @@ read_super_error: return status; } -static struct super_block *ocfs2_get_sb(struct file_system_type *fs_type, - int flags, - const char *dev_name, - void *data) +static int ocfs2_get_sb(struct file_system_type *fs_type, + int flags, + const char *dev_name, + void *data, + struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, ocfs2_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, ocfs2_fill_super, + mnt); } static struct file_system_type ocfs2_fs_type = { @@ -798,10 +792,6 @@ static int __init ocfs2_init(void) goto leave; } - spin_lock(&ocfs2_globals_lock); - osb_id = 0; - spin_unlock(&ocfs2_globals_lock); - ocfs2_debugfs_root = debugfs_create_dir("ocfs2", NULL); if (!ocfs2_debugfs_root) { status = -EFAULT; @@ -855,7 +845,7 @@ static void ocfs2_put_super(struct super mlog_exit_void(); } -static int ocfs2_statfs(struct super_block *sb, struct kstatfs *buf) +static int ocfs2_statfs(struct dentry *dentry, struct kstatfs *buf) { struct ocfs2_super *osb; u32 numbits, freebits; @@ -864,9 +854,9 @@ static int ocfs2_statfs(struct super_blo struct buffer_head *bh = NULL; struct inode *inode = NULL; - mlog_entry("(%p, %p)\n", sb, buf); + mlog_entry("(%p, %p)\n", dentry->d_sb, buf); - osb = OCFS2_SB(sb); + osb = OCFS2_SB(dentry->d_sb); inode = ocfs2_get_system_file_inode(osb, GLOBAL_BITMAP_SYSTEM_INODE, @@ -889,7 +879,7 @@ static int ocfs2_statfs(struct super_blo freebits = numbits - le32_to_cpu(bm_lock->id1.bitmap1.i_used); buf->f_type = OCFS2_SUPER_MAGIC; - buf->f_bsize = sb->s_blocksize; + buf->f_bsize = dentry->d_sb->s_blocksize; buf->f_namelen = OCFS2_MAX_FILENAME_LEN; buf->f_blocks = ((sector_t) numbits) * (osb->s_clustersize >> osb->sb->s_blocksize_bits); @@ -1018,7 +1008,7 @@ static int ocfs2_fill_local_node_info(st goto bail; } - mlog(ML_NOTICE, "I am node %d\n", osb->node_num); + mlog(0, "I am node %d\n", osb->node_num); status = 0; bail: @@ -1189,8 +1179,8 @@ static void ocfs2_dismount_volume(struct atomic_set(&osb->vol_state, VOLUME_DISMOUNTED); - printk("ocfs2: Unmounting device (%u,%u) on (node %d)\n", - MAJOR(osb->sb->s_dev), MINOR(osb->sb->s_dev), osb->node_num); + printk(KERN_INFO "ocfs2: Unmounting device (%s) on (node %d)\n", + osb->dev_str, osb->node_num); ocfs2_delete_osb(osb); kfree(osb); @@ -1210,8 +1200,6 @@ static int ocfs2_setup_osb_uuid(struct o if (osb->uuid_str == NULL) return -ENOMEM; - memcpy(osb->uuid, uuid, OCFS2_VOL_UUID_LEN); - for (i = 0, ptr = osb->uuid_str; i < OCFS2_VOL_UUID_LEN; i++) { /* print with null */ ret = snprintf(ptr, 3, "%02X", uuid[i]); @@ -1309,13 +1297,6 @@ static int ocfs2_initialize_super(struct goto bail; } - osb->uuid = kmalloc(OCFS2_VOL_UUID_LEN, GFP_KERNEL); - if (!osb->uuid) { - mlog(ML_ERROR, "unable to alloc uuid\n"); - status = -ENOMEM; - goto bail; - } - di = (struct ocfs2_dinode *)bh->b_data; osb->max_slots = le16_to_cpu(di->id2.i_super.s_max_slots); @@ -1325,7 +1306,7 @@ static int ocfs2_initialize_super(struct status = -EINVAL; goto bail; } - mlog(ML_NOTICE, "max_slots for this device: %u\n", osb->max_slots); + mlog(0, "max_slots for this device: %u\n", osb->max_slots); init_waitqueue_head(&osb->osb_wipe_event); osb->osb_orphan_wipes = kcalloc(osb->max_slots, @@ -1416,7 +1397,7 @@ static int ocfs2_initialize_super(struct goto bail; } - memcpy(&uuid_net_key, &osb->uuid[i], sizeof(osb->net_key)); + memcpy(&uuid_net_key, di->id2.i_super.s_uuid, sizeof(uuid_net_key)); osb->net_key = le32_to_cpu(uuid_net_key); strncpy(osb->vol_label, di->id2.i_super.s_label, 63); @@ -1482,18 +1463,6 @@ static int ocfs2_initialize_super(struct goto bail; } - /* Link this osb onto the global linked list of all osb structures. */ - /* The Global Link List is mainted for the whole driver . */ - spin_lock(&ocfs2_globals_lock); - osb->osb_id = osb_id; - if (osb_id < OCFS2_MAX_OSB_ID) - osb_id++; - else { - mlog(ML_ERROR, "Too many volumes mounted\n"); - status = -ENOMEM; - } - spin_unlock(&ocfs2_globals_lock); - bail: mlog_exit(status); return status; diff --git a/fs/ocfs2/symlink.c b/fs/ocfs2/symlink.c index f6986bd..c0f68aa 100644 --- a/fs/ocfs2/symlink.c +++ b/fs/ocfs2/symlink.c @@ -64,8 +64,7 @@ static char *ocfs2_page_getlink(struct d { struct page * page; struct address_space *mapping = dentry->d_inode->i_mapping; - page = read_cache_page(mapping, 0, - (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, 0, NULL); if (IS_ERR(page)) goto sync_fail; wait_on_page_locked(page); @@ -155,7 +154,7 @@ static void *ocfs2_follow_link(struct de } status = vfs_follow_link(nd, link); - if (status) + if (status && status != -ENOENT) mlog_errno(status); bail: if (page) { diff --git a/fs/ocfs2/vote.c b/fs/ocfs2/vote.c index ee42765..cf70fe2 100644 --- a/fs/ocfs2/vote.c +++ b/fs/ocfs2/vote.c @@ -988,9 +988,7 @@ int ocfs2_request_mount_vote(struct ocfs } bail: - if (request) - kfree(request); - + kfree(request); return status; } @@ -1021,9 +1019,7 @@ int ocfs2_request_umount_vote(struct ocf } bail: - if (request) - kfree(request); - + kfree(request); return status; } diff --git a/fs/open.c b/fs/open.c index 317b7c7..303f06d 100644 --- a/fs/open.c +++ b/fs/open.c @@ -31,18 +31,18 @@ #include #include -int vfs_statfs(struct super_block *sb, struct kstatfs *buf) +int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) { int retval = -ENODEV; - if (sb) { + if (dentry) { retval = -ENOSYS; - if (sb->s_op->statfs) { + if (dentry->d_sb->s_op->statfs) { memset(buf, 0, sizeof(*buf)); - retval = security_sb_statfs(sb); + retval = security_sb_statfs(dentry); if (retval) return retval; - retval = sb->s_op->statfs(sb, buf); + retval = dentry->d_sb->s_op->statfs(dentry, buf); if (retval == 0 && buf->f_frsize == 0) buf->f_frsize = buf->f_bsize; } @@ -52,12 +52,12 @@ int vfs_statfs(struct super_block *sb, s EXPORT_SYMBOL(vfs_statfs); -static int vfs_statfs_native(struct super_block *sb, struct statfs *buf) +static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf) { struct kstatfs st; int retval; - retval = vfs_statfs(sb, &st); + retval = vfs_statfs(dentry, &st); if (retval) return retval; @@ -95,12 +95,12 @@ static int vfs_statfs_native(struct supe return 0; } -static int vfs_statfs64(struct super_block *sb, struct statfs64 *buf) +static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf) { struct kstatfs st; int retval; - retval = vfs_statfs(sb, &st); + retval = vfs_statfs(dentry, &st); if (retval) return retval; @@ -130,7 +130,7 @@ asmlinkage long sys_statfs(const char __ error = user_path_walk(path, &nd); if (!error) { struct statfs tmp; - error = vfs_statfs_native(nd.dentry->d_inode->i_sb, &tmp); + error = vfs_statfs_native(nd.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; path_release(&nd); @@ -149,7 +149,7 @@ asmlinkage long sys_statfs64(const char error = user_path_walk(path, &nd); if (!error) { struct statfs64 tmp; - error = vfs_statfs64(nd.dentry->d_inode->i_sb, &tmp); + error = vfs_statfs64(nd.dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; path_release(&nd); @@ -168,7 +168,7 @@ asmlinkage long sys_fstatfs(unsigned int file = fget(fd); if (!file) goto out; - error = vfs_statfs_native(file->f_dentry->d_inode->i_sb, &tmp); + error = vfs_statfs_native(file->f_dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; fput(file); @@ -189,7 +189,7 @@ asmlinkage long sys_fstatfs64(unsigned i file = fget(fd); if (!file) goto out; - error = vfs_statfs64(file->f_dentry->d_inode->i_sb, &tmp); + error = vfs_statfs64(file->f_dentry, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; fput(file); @@ -322,7 +322,7 @@ static long do_sys_ftruncate(unsigned in error = locks_verify_truncate(inode, file, length); if (!error) - error = do_truncate(dentry, length, 0, file); + error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); out_putf: fput(file); out: @@ -633,7 +633,7 @@ asmlinkage long sys_fchmod(unsigned int dentry = file->f_dentry; inode = dentry->d_inode; - audit_inode(NULL, inode, 0); + audit_inode(NULL, inode); err = -EROFS; if (IS_RDONLY(inode)) @@ -786,7 +786,7 @@ asmlinkage long sys_fchown(unsigned int if (file) { struct dentry * dentry; dentry = file->f_dentry; - audit_inode(NULL, dentry->d_inode, 0); + audit_inode(NULL, dentry->d_inode); error = chown_common(dentry, user, group); fput(file); } @@ -1152,7 +1152,7 @@ int filp_close(struct file *filp, fl_own } if (filp->f_op && filp->f_op->flush) - retval = filp->f_op->flush(filp); + retval = filp->f_op->flush(filp, id); dnotify_flush(filp, id); locks_remove_posix(filp, id); diff --git a/fs/openpromfs/inode.c b/fs/openpromfs/inode.c index 0f14276..93a56bd 100644 --- a/fs/openpromfs/inode.c +++ b/fs/openpromfs/inode.c @@ -1,5 +1,4 @@ -/* $Id: inode.c,v 1.15 2001/11/12 09:43:39 davem Exp $ - * openpromfs.c: /proc/openprom handling routines +/* inode.c: /proc/openprom handling routines * * Copyright (C) 1996-1999 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) @@ -12,756 +11,245 @@ #include #include #include #include -#include +#include #include #include +#include #include -#define ALIASES_NNODES 64 - -typedef struct { - u16 parent; - u16 next; - u16 child; - u16 first_prop; - u32 node; -} openpromfs_node; - -typedef struct { -#define OPP_STRING 0x10 -#define OPP_STRINGLIST 0x20 -#define OPP_BINARY 0x40 -#define OPP_HEXSTRING 0x80 -#define OPP_DIRTY 0x01 -#define OPP_QUOTED 0x02 -#define OPP_NOTQUOTED 0x04 -#define OPP_ASCIIZ 0x08 - u32 flag; - u32 alloclen; - u32 len; - char *value; - char name[8]; -} openprom_property; - -static openpromfs_node *nodes; -static int alloced; -static u16 last_node; -static u16 first_prop; -static u16 options = 0xffff; -static u16 aliases = 0xffff; -static int aliases_nodes; -static char *alias_names [ALIASES_NNODES]; - -#define OPENPROM_ROOT_INO 16 -#define OPENPROM_FIRST_INO OPENPROM_ROOT_INO -#define NODE(ino) nodes[ino - OPENPROM_FIRST_INO] -#define NODE2INO(node) (node + OPENPROM_FIRST_INO) -#define NODEP2INO(no) (no + OPENPROM_FIRST_INO + last_node) - -static int openpromfs_create (struct inode *, struct dentry *, int, struct nameidata *); -static int openpromfs_readdir(struct file *, void *, filldir_t); -static struct dentry *openpromfs_lookup(struct inode *, struct dentry *dentry, struct nameidata *nd); -static int openpromfs_unlink (struct inode *, struct dentry *dentry); +static DEFINE_MUTEX(op_mutex); + +#define OPENPROM_ROOT_INO 0 + +enum op_inode_type { + op_inode_node, + op_inode_prop, +}; + +union op_inode_data { + struct device_node *node; + struct property *prop; +}; -static ssize_t nodenum_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +struct op_inode_info { + struct inode vfs_inode; + enum op_inode_type type; + union op_inode_data u; +}; + +static inline struct op_inode_info *OP_I(struct inode *inode) { - struct inode *inode = file->f_dentry->d_inode; - char buffer[10]; - - if (count < 0 || !inode->u.generic_ip) - return -EINVAL; - sprintf (buffer, "%8.8x\n", (u32)(long)(inode->u.generic_ip)); - if (file->f_pos >= 9) - return 0; - if (count > 9 - file->f_pos) - count = 9 - file->f_pos; - if (copy_to_user(buf, buffer + file->f_pos, count)) - return -EFAULT; - *ppos += count; - return count; + return container_of(inode, struct op_inode_info, vfs_inode); } -static ssize_t property_read(struct file *filp, char __user *buf, - size_t count, loff_t *ppos) +static int is_string(unsigned char *p, int len) { - struct inode *inode = filp->f_dentry->d_inode; - int i, j, k; - u32 node; - char *p, *s; - u32 *q; - openprom_property *op; - char buffer[64]; - - if (!filp->private_data) { - node = nodes[(u16)((long)inode->u.generic_ip)].node; - i = ((u32)(long)inode->u.generic_ip) >> 16; - if ((u16)((long)inode->u.generic_ip) == aliases) { - if (i >= aliases_nodes) - p = NULL; - else - p = alias_names [i]; - } else - for (p = prom_firstprop (node, buffer); - i && p && *p; - p = prom_nextprop (node, p, buffer), i--) - /* nothing */ ; - if (!p || !*p) - return -EIO; - i = prom_getproplen (node, p); - if (i < 0) { - if ((u16)((long)inode->u.generic_ip) == aliases) - i = 0; - else - return -EIO; - } - k = i; - if (i < 64) i = 64; - filp->private_data = kmalloc (sizeof (openprom_property) - + (j = strlen (p)) + 2 * i, - GFP_KERNEL); - if (!filp->private_data) - return -ENOMEM; - op = (openprom_property *)filp->private_data; - op->flag = 0; - op->alloclen = 2 * i; - strcpy (op->name, p); - op->value = (char *)(((unsigned long)(op->name + j + 4)) & ~3); - op->len = k; - if (k && prom_getproperty (node, p, op->value, i) < 0) - return -EIO; - op->value [k] = 0; - if (k) { - for (s = NULL, p = op->value; p < op->value + k; p++) { - if ((*p >= ' ' && *p <= '~') || *p == '\n') { - op->flag |= OPP_STRING; - s = p; - continue; - } - if (p > op->value && !*p && s == p - 1) { - if (p < op->value + k - 1) - op->flag |= OPP_STRINGLIST; - else - op->flag |= OPP_ASCIIZ; - continue; - } - if (k == 1 && !*p) { - op->flag |= (OPP_STRING|OPP_ASCIIZ); - break; - } - op->flag &= ~(OPP_STRING|OPP_STRINGLIST); - if (k & 3) - op->flag |= OPP_HEXSTRING; - else - op->flag |= OPP_BINARY; - break; - } - if (op->flag & OPP_STRINGLIST) - op->flag &= ~(OPP_STRING); - if (op->flag & OPP_ASCIIZ) - op->len--; - } - } else - op = (openprom_property *)filp->private_data; - if (!count || !(op->len || (op->flag & OPP_ASCIIZ))) - return 0; - if (*ppos >= 0xffffff || count >= 0xffffff) - return -EINVAL; - if (op->flag & OPP_STRINGLIST) { - for (k = 0, p = op->value; p < op->value + op->len; p++) - if (!*p) - k++; - i = op->len + 4 * k + 3; - } else if (op->flag & OPP_STRING) { - i = op->len + 3; - } else if (op->flag & OPP_BINARY) { - i = (op->len * 9) >> 2; - } else { - i = (op->len << 1) + 1; - } - k = *ppos; - if (k >= i) return 0; - if (count > i - k) count = i - k; - if (op->flag & OPP_STRING) { - if (!k) { - if (put_user('\'', buf)) - return -EFAULT; - k++; - count--; - } + int i; - if (k + count >= i - 2) - j = i - 2 - k; - else - j = count; - - if (j >= 0) { - if (copy_to_user(buf + k - *ppos, - op->value + k - 1, j)) - return -EFAULT; - count -= j; - k += j; - } + for (i = 0; i < len; i++) { + unsigned char val = p[i]; - if (count) { - if (put_user('\'', &buf [k++ - *ppos])) - return -EFAULT; - } - if (count > 1) { - if (put_user('\n', &buf [k++ - *ppos])) - return -EFAULT; - } - } else if (op->flag & OPP_STRINGLIST) { - char *tmp; - - tmp = kmalloc (i, GFP_KERNEL); - if (!tmp) - return -ENOMEM; - - s = tmp; - *s++ = '\''; - for (p = op->value; p < op->value + op->len; p++) { - if (!*p) { - strcpy(s, "' + '"); - s += 5; - continue; - } - *s++ = *p; - } - strcpy(s, "'\n"); - - if (copy_to_user(buf, tmp + k, count)) - return -EFAULT; - - kfree(tmp); - k += count; - - } else if (op->flag & OPP_BINARY) { - char buffer[10]; - u32 *first, *last; - int first_off, last_cnt; - - first = ((u32 *)op->value) + k / 9; - first_off = k % 9; - last = ((u32 *)op->value) + (k + count - 1) / 9; - last_cnt = (k + count) % 9; - if (!last_cnt) last_cnt = 9; - - if (first == last) { - sprintf (buffer, "%08x.", *first); - if (copy_to_user(buf, buffer + first_off, - last_cnt - first_off)) - return -EFAULT; - buf += last_cnt - first_off; - } else { - for (q = first; q <= last; q++) { - sprintf (buffer, "%08x.", *q); - if (q == first) { - if (copy_to_user(buf, buffer + first_off, - 9 - first_off)) - return -EFAULT; - buf += 9 - first_off; - } else if (q == last) { - if (copy_to_user(buf, buffer, last_cnt)) - return -EFAULT; - buf += last_cnt; - } else { - if (copy_to_user(buf, buffer, 9)) - return -EFAULT; - buf += 9; - } - } - } + if ((i && !val) || + (val >= ' ' && val <= '~')) + continue; - if (last == (u32 *)(op->value + op->len - 4) && last_cnt == 9) { - if (put_user('\n', (buf - 1))) - return -EFAULT; - } + return 0; + } - k += count; + return 1; +} - } else if (op->flag & OPP_HEXSTRING) { - char buffer[3]; +static int property_show(struct seq_file *f, void *v) +{ + struct property *prop = f->private; + void *pval; + int len; - if ((k < i - 1) && (k & 1)) { - sprintf (buffer, "%02x", - (unsigned char) *(op->value + (k >> 1)) & 0xff); - if (put_user(buffer[1], &buf[k++ - *ppos])) - return -EFAULT; - count--; - } + len = prop->length; + pval = prop->value; - for (; (count > 1) && (k < i - 1); k += 2) { - sprintf (buffer, "%02x", - (unsigned char) *(op->value + (k >> 1)) & 0xff); - if (copy_to_user(buf + k - *ppos, buffer, 2)) - return -EFAULT; - count -= 2; - } + if (is_string(pval, len)) { + while (len > 0) { + int n = strlen(pval); - if (count && (k < i - 1)) { - sprintf (buffer, "%02x", - (unsigned char) *(op->value + (k >> 1)) & 0xff); - if (put_user(buffer[0], &buf[k++ - *ppos])) - return -EFAULT; - count--; - } + seq_printf(f, "%s", (char *) pval); - if (count) { - if (put_user('\n', &buf [k++ - *ppos])) - return -EFAULT; - } - } - count = k - *ppos; - *ppos = k; - return count; -} + /* Skip over the NULL byte too. */ + pval += n + 1; + len -= n + 1; -static ssize_t property_write(struct file *filp, const char __user *buf, - size_t count, loff_t *ppos) -{ - int i, j, k; - char *p; - u32 *q; - void *b; - openprom_property *op; - - if (*ppos >= 0xffffff || count >= 0xffffff) - return -EINVAL; - if (!filp->private_data) { - i = property_read (filp, NULL, 0, NULL); - if (i) - return i; - } - k = *ppos; - op = (openprom_property *)filp->private_data; - if (!(op->flag & OPP_STRING)) { - u32 *first, *last; - int first_off, last_cnt; - u32 mask, mask2; - char tmp [9]; - int forcelen = 0; - - j = k % 9; - for (i = 0; i < count; i++, j++) { - if (j == 9) j = 0; - if (!j) { - char ctmp; - if (get_user(ctmp, &buf[i])) - return -EFAULT; - if (ctmp != '.') { - if (ctmp != '\n') { - if (op->flag & OPP_BINARY) - return -EINVAL; - else - goto write_try_string; - } else { - count = i + 1; - forcelen = 1; - break; - } - } - } else { - char ctmp; - if (get_user(ctmp, &buf[i])) - return -EFAULT; - if (ctmp < '0' || - (ctmp > '9' && ctmp < 'A') || - (ctmp > 'F' && ctmp < 'a') || - ctmp > 'f') { - if (op->flag & OPP_BINARY) - return -EINVAL; - else - goto write_try_string; - } - } - } - op->flag |= OPP_BINARY; - tmp [8] = 0; - i = ((count + k + 8) / 9) << 2; - if (op->alloclen <= i) { - b = kmalloc (sizeof (openprom_property) + 2 * i, - GFP_KERNEL); - if (!b) - return -ENOMEM; - memcpy (b, filp->private_data, - sizeof (openprom_property) - + strlen (op->name) + op->alloclen); - memset (((char *)b) + sizeof (openprom_property) - + strlen (op->name) + op->alloclen, - 0, 2 * i - op->alloclen); - op = (openprom_property *)b; - op->alloclen = 2*i; - b = filp->private_data; - filp->private_data = (void *)op; - kfree (b); + if (len > 0) + seq_printf(f, " + "); } - first = ((u32 *)op->value) + (k / 9); - first_off = k % 9; - last = (u32 *)(op->value + i); - last_cnt = (k + count) % 9; - if (first + 1 == last) { - memset (tmp, '0', 8); - if (copy_from_user(tmp + first_off, buf, - (count + first_off > 8) ? - 8 - first_off : count)) - return -EFAULT; - mask = 0xffffffff; - mask2 = 0xffffffff; - for (j = 0; j < first_off; j++) - mask >>= 1; - for (j = 8 - count - first_off; j > 0; j--) - mask2 <<= 1; - mask &= mask2; - if (mask) { - *first &= ~mask; - *first |= simple_strtoul (tmp, NULL, 16); - op->flag |= OPP_DIRTY; + } else { + if (len & 3) { + while (len) { + len--; + if (len) + seq_printf(f, "%02x.", + *(unsigned char *) pval); + else + seq_printf(f, "%02x", + *(unsigned char *) pval); + pval++; } } else { - op->flag |= OPP_DIRTY; - for (q = first; q < last; q++) { - if (q == first) { - if (first_off < 8) { - memset (tmp, '0', 8); - if (copy_from_user(tmp + first_off, - buf, - 8 - first_off)) - return -EFAULT; - mask = 0xffffffff; - for (j = 0; j < first_off; j++) - mask >>= 1; - *q &= ~mask; - *q |= simple_strtoul (tmp,NULL,16); - } - buf += 9; - } else if ((q == last - 1) && last_cnt - && (last_cnt < 8)) { - memset (tmp, '0', 8); - if (copy_from_user(tmp, buf, last_cnt)) - return -EFAULT; - mask = 0xffffffff; - for (j = 0; j < 8 - last_cnt; j++) - mask <<= 1; - *q &= ~mask; - *q |= simple_strtoul (tmp, NULL, 16); - buf += last_cnt; - } else { - char tchars[17]; /* XXX yuck... */ - - if (copy_from_user(tchars, buf, 16)) - return -EFAULT; - *q = simple_strtoul (tchars, NULL, 16); - buf += 9; - } - } - } - if (!forcelen) { - if (op->len < i) - op->len = i; - } else - op->len = i; - *ppos += count; - } -write_try_string: - if (!(op->flag & OPP_BINARY)) { - if (!(op->flag & (OPP_QUOTED | OPP_NOTQUOTED))) { - char ctmp; - - /* No way, if somebody starts writing from the middle, - * we don't know whether he uses quotes around or not - */ - if (k > 0) - return -EINVAL; - if (get_user(ctmp, buf)) - return -EFAULT; - if (ctmp == '\'') { - op->flag |= OPP_QUOTED; - buf++; - count--; - (*ppos)++; - if (!count) { - op->flag |= OPP_STRING; - return 1; - } - } else - op->flag |= OPP_NOTQUOTED; - } - op->flag |= OPP_STRING; - if (op->alloclen <= count + *ppos) { - b = kmalloc (sizeof (openprom_property) - + 2 * (count + *ppos), GFP_KERNEL); - if (!b) - return -ENOMEM; - memcpy (b, filp->private_data, - sizeof (openprom_property) - + strlen (op->name) + op->alloclen); - memset (((char *)b) + sizeof (openprom_property) - + strlen (op->name) + op->alloclen, - 0, 2*(count - *ppos) - op->alloclen); - op = (openprom_property *)b; - op->alloclen = 2*(count + *ppos); - b = filp->private_data; - filp->private_data = (void *)op; - kfree (b); - } - p = op->value + *ppos - ((op->flag & OPP_QUOTED) ? 1 : 0); - if (copy_from_user(p, buf, count)) - return -EFAULT; - op->flag |= OPP_DIRTY; - for (i = 0; i < count; i++, p++) - if (*p == '\n') { - *p = 0; - break; + while (len >= 4) { + len -= 4; + + if (len) + seq_printf(f, "%08x.", + *(unsigned int *) pval); + else + seq_printf(f, "%08x", + *(unsigned int *) pval); + pval += 4; } - if (i < count) { - op->len = p - op->value; - *ppos += i + 1; - if ((p > op->value) && (op->flag & OPP_QUOTED) - && (*(p - 1) == '\'')) - op->len--; - } else { - if (p - op->value > op->len) - op->len = p - op->value; - *ppos += count; } } - return *ppos - k; + seq_printf(f, "\n"); + + return 0; } -int property_release (struct inode *inode, struct file *filp) +static void *property_start(struct seq_file *f, loff_t *pos) { - openprom_property *op = (openprom_property *)filp->private_data; - int error; - u32 node; - - if (!op) - return 0; - lock_kernel(); - node = nodes[(u16)((long)inode->u.generic_ip)].node; - if ((u16)((long)inode->u.generic_ip) == aliases) { - if ((op->flag & OPP_DIRTY) && (op->flag & OPP_STRING)) { - char *p = op->name; - int i = (op->value - op->name) - strlen (op->name) - 1; - op->value [op->len] = 0; - *(op->value - 1) = ' '; - if (i) { - for (p = op->value - i - 2; p >= op->name; p--) - p[i] = *p; - p = op->name + i; - } - memcpy (p - 8, "nvalias ", 8); - prom_feval (p - 8); - } - } else if (op->flag & OPP_DIRTY) { - if (op->flag & OPP_STRING) { - op->value [op->len] = 0; - error = prom_setprop (node, op->name, - op->value, op->len + 1); - if (error <= 0) - printk (KERN_WARNING "openpromfs: " - "Couldn't write property %s\n", - op->name); - } else if ((op->flag & OPP_BINARY) || !op->len) { - error = prom_setprop (node, op->name, - op->value, op->len); - if (error <= 0) - printk (KERN_WARNING "openpromfs: " - "Couldn't write property %s\n", - op->name); - } else { - printk (KERN_WARNING "openpromfs: " - "Unknown property type of %s\n", - op->name); - } + if (*pos == 0) + return pos; + return NULL; +} + +static void *property_next(struct seq_file *f, void *v, loff_t *pos) +{ + (*pos)++; + return NULL; +} + +static void property_stop(struct seq_file *f, void *v) +{ + /* Nothing to do */ +} + +static struct seq_operations property_op = { + .start = property_start, + .next = property_next, + .stop = property_stop, + .show = property_show +}; + +static int property_open(struct inode *inode, struct file *file) +{ + struct op_inode_info *oi = OP_I(inode); + int ret; + + BUG_ON(oi->type != op_inode_prop); + + ret = seq_open(file, &property_op); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = oi->u.prop; } - unlock_kernel(); - kfree (filp->private_data); - return 0; + return ret; } static const struct file_operations openpromfs_prop_ops = { - .read = property_read, - .write = property_write, - .release = property_release, + .open = property_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, }; -static const struct file_operations openpromfs_nodenum_ops = { - .read = nodenum_read, -}; +static int openpromfs_readdir(struct file *, void *, filldir_t); static const struct file_operations openprom_operations = { .read = generic_read_dir, .readdir = openpromfs_readdir, }; -static struct inode_operations openprom_alias_inode_operations = { - .create = openpromfs_create, - .lookup = openpromfs_lookup, - .unlink = openpromfs_unlink, -}; +static struct dentry *openpromfs_lookup(struct inode *, struct dentry *, struct nameidata *); static struct inode_operations openprom_inode_operations = { .lookup = openpromfs_lookup, }; -static int lookup_children(u16 n, const char * name, int len) -{ - int ret; - u16 node; - for (; n != 0xffff; n = nodes[n].next) { - node = nodes[n].child; - if (node != 0xffff) { - char buffer[128]; - int i; - char *p; - - while (node != 0xffff) { - if (prom_getname (nodes[node].node, - buffer, 128) >= 0) { - i = strlen (buffer); - if ((len == i) - && !strncmp (buffer, name, len)) - return NODE2INO(node); - p = strchr (buffer, '@'); - if (p && (len == p - buffer) - && !strncmp (buffer, name, len)) - return NODE2INO(node); - } - node = nodes[node].next; - } - } else - continue; - ret = lookup_children (nodes[n].child, name, len); - if (ret) return ret; - } - return 0; -} - -static struct dentry *openpromfs_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) +static struct dentry *openpromfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { - int ino = 0; -#define OPFSL_DIR 0 -#define OPFSL_PROPERTY 1 -#define OPFSL_NODENUM 2 - int type = 0; - char buffer[128]; - char *p; + struct op_inode_info *ent_oi, *oi = OP_I(dir); + struct device_node *dp, *child; + struct property *prop; + enum op_inode_type ent_type; + union op_inode_data ent_data; const char *name; - u32 n; - u16 dirnode; - unsigned int len; - int i; struct inode *inode; - char buffer2[64]; + unsigned int ino; + int len; - inode = NULL; + BUG_ON(oi->type != op_inode_node); + + dp = oi->u.node; + name = dentry->d_name.name; len = dentry->d_name.len; - lock_kernel(); - if (name [0] == '.' && len == 5 && !strncmp (name + 1, "node", 4)) { - ino = NODEP2INO(NODE(dir->i_ino).first_prop); - type = OPFSL_NODENUM; - } - if (!ino) { - u16 node = NODE(dir->i_ino).child; - while (node != 0xffff) { - if (prom_getname (nodes[node].node, buffer, 128) >= 0) { - i = strlen (buffer); - if (len == i && !strncmp (buffer, name, len)) { - ino = NODE2INO(node); - type = OPFSL_DIR; - break; - } - p = strchr (buffer, '@'); - if (p && (len == p - buffer) - && !strncmp (buffer, name, len)) { - ino = NODE2INO(node); - type = OPFSL_DIR; - break; - } - } - node = nodes[node].next; - } - } - n = NODE(dir->i_ino).node; - dirnode = dir->i_ino - OPENPROM_FIRST_INO; - if (!ino) { - int j = NODEP2INO(NODE(dir->i_ino).first_prop); - if (dirnode != aliases) { - for (p = prom_firstprop (n, buffer2); - p && *p; - p = prom_nextprop (n, p, buffer2)) { - j++; - if ((len == strlen (p)) - && !strncmp (p, name, len)) { - ino = j; - type = OPFSL_PROPERTY; - break; - } - } - } else { - int k; - for (k = 0; k < aliases_nodes; k++) { - j++; - if (alias_names [k] - && (len == strlen (alias_names [k])) - && !strncmp (alias_names [k], name, len)) { - ino = j; - type = OPFSL_PROPERTY; - break; - } - } + + mutex_lock(&op_mutex); + + child = dp->child; + while (child) { + int n = strlen(child->path_component_name); + + if (len == n && + !strncmp(child->path_component_name, name, len)) { + ent_type = op_inode_node; + ent_data.node = child; + ino = child->unique_id; + goto found; } + child = child->sibling; } - if (!ino) { - ino = lookup_children (NODE(dir->i_ino).child, name, len); - if (ino) - type = OPFSL_DIR; - else { - unlock_kernel(); - return ERR_PTR(-ENOENT); + + prop = dp->properties; + while (prop) { + int n = strlen(prop->name); + + if (len == n && !strncmp(prop->name, name, len)) { + ent_type = op_inode_prop; + ent_data.prop = prop; + ino = prop->unique_id; + goto found; } + + prop = prop->next; } - inode = iget (dir->i_sb, ino); - unlock_kernel(); + + mutex_unlock(&op_mutex); + return ERR_PTR(-ENOENT); + +found: + inode = iget(dir->i_sb, ino); + mutex_unlock(&op_mutex); if (!inode) return ERR_PTR(-EINVAL); - switch (type) { - case OPFSL_DIR: + ent_oi = OP_I(inode); + ent_oi->type = ent_type; + ent_oi->u = ent_data; + + switch (ent_type) { + case op_inode_node: inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; - if (ino == OPENPROM_FIRST_INO + aliases) { - inode->i_mode |= S_IWUSR; - inode->i_op = &openprom_alias_inode_operations; - } else - inode->i_op = &openprom_inode_operations; + inode->i_op = &openprom_inode_operations; inode->i_fop = &openprom_operations; inode->i_nlink = 2; break; - case OPFSL_NODENUM: - inode->i_mode = S_IFREG | S_IRUGO; - inode->i_fop = &openpromfs_nodenum_ops; - inode->i_nlink = 1; - inode->u.generic_ip = (void *)(long)(n); - break; - case OPFSL_PROPERTY: - if ((dirnode == options) && (len == 17) - && !strncmp (name, "security-password", 17)) + case op_inode_prop: + if (!strcmp(dp->name, "options") && (len == 17) && + !strncmp (name, "security-password", 17)) inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; - else { + else inode->i_mode = S_IFREG | S_IRUGO; - if (dirnode == options || dirnode == aliases) { - if (len != 4 || strncmp (name, "name", 4)) - inode->i_mode |= S_IWUSR; - } - } inode->i_fop = &openpromfs_prop_ops; inode->i_nlink = 1; - if (inode->i_size < 0) - inode->i_size = 0; - inode->u.generic_ip = (void *)(long)(((u16)dirnode) | - (((u16)(ino - NODEP2INO(NODE(dir->i_ino).first_prop) - 1)) << 16)); + inode->i_size = ent_oi->u.prop->length; break; } @@ -775,237 +263,89 @@ #define OPFSL_NODENUM 2 static int openpromfs_readdir(struct file * filp, void * dirent, filldir_t filldir) { struct inode *inode = filp->f_dentry->d_inode; + struct op_inode_info *oi = OP_I(inode); + struct device_node *dp = oi->u.node; + struct device_node *child; + struct property *prop; unsigned int ino; - u32 n; - int i, j; - char buffer[128]; - u16 node; - char *p; - char buffer2[64]; - - lock_kernel(); + int i; + + mutex_lock(&op_mutex); ino = inode->i_ino; i = filp->f_pos; switch (i) { case 0: - if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) goto out; + if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) + goto out; i++; filp->f_pos++; /* fall thru */ case 1: - if (filldir(dirent, "..", 2, i, - (NODE(ino).parent == 0xffff) ? - OPENPROM_ROOT_INO : NODE2INO(NODE(ino).parent), DT_DIR) < 0) + if (filldir(dirent, "..", 2, i, + (dp->parent == NULL ? + OPENPROM_ROOT_INO : + dp->parent->unique_id), DT_DIR) < 0) goto out; i++; filp->f_pos++; /* fall thru */ default: i -= 2; - node = NODE(ino).child; - while (i && node != 0xffff) { - node = nodes[node].next; + + /* First, the children nodes as directories. */ + child = dp->child; + while (i && child) { + child = child->sibling; i--; } - while (node != 0xffff) { - if (prom_getname (nodes[node].node, buffer, 128) < 0) - goto out; - if (filldir(dirent, buffer, strlen(buffer), - filp->f_pos, NODE2INO(node), DT_DIR) < 0) + while (child) { + if (filldir(dirent, + child->path_component_name, + strlen(child->path_component_name), + filp->f_pos, child->unique_id, DT_DIR) < 0) goto out; + filp->f_pos++; - node = nodes[node].next; + child = child->sibling; } - j = NODEP2INO(NODE(ino).first_prop); - if (!i) { - if (filldir(dirent, ".node", 5, filp->f_pos, j, DT_REG) < 0) + + /* Next, the properties as files. */ + prop = dp->properties; + while (i && prop) { + prop = prop->next; + i--; + } + while (prop) { + if (filldir(dirent, prop->name, strlen(prop->name), + filp->f_pos, prop->unique_id, DT_REG) < 0) goto out; + filp->f_pos++; - } else - i--; - n = NODE(ino).node; - if (ino == OPENPROM_FIRST_INO + aliases) { - for (j++; i < aliases_nodes; i++, j++) { - if (alias_names [i]) { - if (filldir (dirent, alias_names [i], - strlen (alias_names [i]), - filp->f_pos, j, DT_REG) < 0) goto out; - filp->f_pos++; - } - } - } else { - for (p = prom_firstprop (n, buffer2); - p && *p; - p = prom_nextprop (n, p, buffer2)) { - j++; - if (i) i--; - else { - if (filldir(dirent, p, strlen(p), - filp->f_pos, j, DT_REG) < 0) - goto out; - filp->f_pos++; - } - } + prop = prop->next; } } out: - unlock_kernel(); - return 0; -} - -static int openpromfs_create (struct inode *dir, struct dentry *dentry, int mode, - struct nameidata *nd) -{ - char *p; - struct inode *inode; - - if (!dir) - return -ENOENT; - if (dentry->d_name.len > 256) - return -EINVAL; - p = kmalloc (dentry->d_name.len + 1, GFP_KERNEL); - if (!p) - return -ENOMEM; - strncpy (p, dentry->d_name.name, dentry->d_name.len); - p [dentry->d_name.len] = 0; - lock_kernel(); - if (aliases_nodes == ALIASES_NNODES) { - kfree(p); - unlock_kernel(); - return -EIO; - } - alias_names [aliases_nodes++] = p; - inode = iget (dir->i_sb, - NODEP2INO(NODE(dir->i_ino).first_prop) + aliases_nodes); - if (!inode) { - unlock_kernel(); - return -EINVAL; - } - inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; - inode->i_fop = &openpromfs_prop_ops; - inode->i_nlink = 1; - if (inode->i_size < 0) inode->i_size = 0; - inode->u.generic_ip = (void *)(long)(((u16)aliases) | - (((u16)(aliases_nodes - 1)) << 16)); - unlock_kernel(); - d_instantiate(dentry, inode); + mutex_unlock(&op_mutex); return 0; } -static int openpromfs_unlink (struct inode *dir, struct dentry *dentry) -{ - unsigned int len; - char *p; - const char *name; - int i; - - name = dentry->d_name.name; - len = dentry->d_name.len; - lock_kernel(); - for (i = 0; i < aliases_nodes; i++) - if ((strlen (alias_names [i]) == len) - && !strncmp (name, alias_names[i], len)) { - char buffer[512]; - - p = alias_names [i]; - alias_names [i] = NULL; - kfree (p); - strcpy (buffer, "nvunalias "); - memcpy (buffer + 10, name, len); - buffer [10 + len] = 0; - prom_feval (buffer); - } - unlock_kernel(); - return 0; -} +static kmem_cache_t *op_inode_cachep; -/* {{{ init section */ -static int __init check_space (u16 n) +static struct inode *openprom_alloc_inode(struct super_block *sb) { - unsigned long pages; + struct op_inode_info *oi; - if ((1 << alloced) * PAGE_SIZE < (n + 2) * sizeof(openpromfs_node)) { - pages = __get_free_pages (GFP_KERNEL, alloced + 1); - if (!pages) - return -1; + oi = kmem_cache_alloc(op_inode_cachep, SLAB_KERNEL); + if (!oi) + return NULL; - if (nodes) { - memcpy ((char *)pages, (char *)nodes, - (1 << alloced) * PAGE_SIZE); - free_pages ((unsigned long)nodes, alloced); - } - alloced++; - nodes = (openpromfs_node *)pages; - } - return 0; + return &oi->vfs_inode; } -static u16 __init get_nodes (u16 parent, u32 node) +static void openprom_destroy_inode(struct inode *inode) { - char *p; - u16 n = last_node++, i; - char buffer[64]; - - if (check_space (n) < 0) - return 0xffff; - nodes[n].parent = parent; - nodes[n].node = node; - nodes[n].next = 0xffff; - nodes[n].child = 0xffff; - nodes[n].first_prop = first_prop++; - if (!parent) { - char buffer[8]; - int j; - - if ((j = prom_getproperty (node, "name", buffer, 8)) >= 0) { - buffer[j] = 0; - if (!strcmp (buffer, "options")) - options = n; - else if (!strcmp (buffer, "aliases")) - aliases = n; - } - } - if (n != aliases) - for (p = prom_firstprop (node, buffer); - p && p != (char *)-1 && *p; - p = prom_nextprop (node, p, buffer)) - first_prop++; - else { - char *q; - for (p = prom_firstprop (node, buffer); - p && p != (char *)-1 && *p; - p = prom_nextprop (node, p, buffer)) { - if (aliases_nodes == ALIASES_NNODES) - break; - for (i = 0; i < aliases_nodes; i++) - if (!strcmp (p, alias_names [i])) - break; - if (i < aliases_nodes) - continue; - q = kmalloc (strlen (p) + 1, GFP_KERNEL); - if (!q) - return 0xffff; - strcpy (q, p); - alias_names [aliases_nodes++] = q; - } - first_prop += ALIASES_NNODES; - } - node = prom_getchild (node); - if (node) { - parent = get_nodes (n, node); - if (parent == 0xffff) - return 0xffff; - nodes[n].child = parent; - while ((node = prom_getsibling (node)) != 0) { - i = get_nodes (n, node); - if (i == 0xffff) - return 0xffff; - nodes[parent].next = i; - parent = i; - } - } - return n; + kmem_cache_free(op_inode_cachep, OP_I(inode)); } static void openprom_read_inode(struct inode * inode) @@ -1025,6 +365,8 @@ static int openprom_remount(struct super } static struct super_operations openprom_sops = { + .alloc_inode = openprom_alloc_inode, + .destroy_inode = openprom_destroy_inode, .read_inode = openprom_read_inode, .statfs = simple_statfs, .remount_fs = openprom_remount, @@ -1032,7 +374,8 @@ static struct super_operations openprom_ static int openprom_fill_super(struct super_block *s, void *data, int silent) { - struct inode * root_inode; + struct inode *root_inode; + struct op_inode_info *oi; s->s_flags |= MS_NOATIME; s->s_blocksize = 1024; @@ -1043,6 +386,11 @@ static int openprom_fill_super(struct su root_inode = iget(s, OPENPROM_ROOT_INO); if (!root_inode) goto out_no_root; + + oi = OP_I(root_inode); + oi->type = op_inode_node; + oi->u.node = of_find_node_by_path("/"); + s->s_root = d_alloc_root(root_inode); if (!s->s_root) goto out_no_root; @@ -1054,10 +402,10 @@ out_no_root: return -ENOMEM; } -static struct super_block *openprom_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int openprom_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, openprom_fill_super); + return get_sb_single(fs_type, flags, data, openprom_fill_super, mnt); } static struct file_system_type openprom_fs_type = { @@ -1067,29 +415,39 @@ static struct file_system_type openprom_ .kill_sb = kill_anon_super, }; +static void op_inode_init_once(void *data, kmem_cache_t * cachep, unsigned long flags) +{ + struct op_inode_info *oi = (struct op_inode_info *) data; + + if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == + SLAB_CTOR_CONSTRUCTOR) + inode_init_once(&oi->vfs_inode); +} + static int __init init_openprom_fs(void) { - nodes = (openpromfs_node *)__get_free_pages(GFP_KERNEL, 0); - if (!nodes) { - printk (KERN_WARNING "openpromfs: can't get free page\n"); - return -EIO; - } - if (get_nodes (0xffff, prom_root_node) == 0xffff) { - printk (KERN_WARNING "openpromfs: couldn't setup tree\n"); - return -EIO; - } - nodes[last_node].first_prop = first_prop; - return register_filesystem(&openprom_fs_type); + int err; + + op_inode_cachep = kmem_cache_create("op_inode_cache", + sizeof(struct op_inode_info), + 0, + (SLAB_RECLAIM_ACCOUNT | + SLAB_MEM_SPREAD), + op_inode_init_once, NULL); + if (!op_inode_cachep) + return -ENOMEM; + + err = register_filesystem(&openprom_fs_type); + if (err) + kmem_cache_destroy(op_inode_cachep); + + return err; } static void __exit exit_openprom_fs(void) { - int i; unregister_filesystem(&openprom_fs_type); - free_pages ((unsigned long)nodes, alloced); - for (i = 0; i < aliases_nodes; i++) - kfree (alias_names [i]); - nodes = NULL; + kmem_cache_destroy(op_inode_cachep); } module_init(init_openprom_fs) diff --git a/fs/partitions/Makefile b/fs/partitions/Makefile index 42c7d38..d713ce6 100644 --- a/fs/partitions/Makefile +++ b/fs/partitions/Makefile @@ -4,7 +4,6 @@ # obj-y := check.o -obj-$(CONFIG_DEVFS_FS) += devfs.o obj-$(CONFIG_ACORN_PARTITION) += acorn.o obj-$(CONFIG_AMIGA_PARTITION) += amiga.o obj-$(CONFIG_ATARI_PARTITION) += atari.o diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index c050857..1bc9f37 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -12,7 +12,6 @@ * every single manufacturer of SCSI and IDE cards created their own * method. */ -#include #include #include diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 7ef1f09..8396340 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -18,10 +18,8 @@ #include #include #include #include -#include #include "check.h" -#include "devfs.h" #include "acorn.h" #include "amiga.h" @@ -161,18 +159,11 @@ check_partition(struct gendisk *hd, stru if (!state) return NULL; -#ifdef CONFIG_DEVFS_FS - if (hd->devfs_name[0] != '\0') { - printk(KERN_INFO " /dev/%s:", hd->devfs_name); + disk_name(hd, 0, state->name); + printk(KERN_INFO " %s:", state->name); + if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); - } -#endif - else { - disk_name(hd, 0, state->name); - printk(KERN_INFO " %s:", state->name); - if (isdigit(state->name[strlen(state->name)-1])) - sprintf(state->name, "p"); - } + state->limit = hd->minors; i = res = 0; while (!res && check_part[i]) { @@ -328,7 +319,7 @@ void delete_partition(struct gendisk *di p->nr_sects = 0; p->ios[0] = p->ios[1] = 0; p->sectors[0] = p->sectors[1] = 0; - devfs_remove("%s/part%d", disk->devfs_name, part); + sysfs_remove_link(&p->kobj, "subsystem"); if (p->holder_dir) kobject_unregister(p->holder_dir); kobject_uevent(&p->kobj, KOBJ_REMOVE); @@ -349,10 +340,6 @@ void add_partition(struct gendisk *disk, p->nr_sects = len; p->partno = part; - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor + part), - S_IFBLK|S_IRUSR|S_IWUSR, - "%s/part%d", disk->devfs_name, part); - if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1])) snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part); else @@ -363,6 +350,7 @@ void add_partition(struct gendisk *disk, kobject_add(&p->kobj); if (!disk->part_uevent_suppress) kobject_uevent(&p->kobj, KOBJ_ADD); + sysfs_create_link(&p->kobj, &block_subsys.kset.kobj, "subsystem"); partition_sysfs_add_subdir(p); disk->part[part-1] = p; } @@ -398,6 +386,7 @@ static void disk_sysfs_symlinks(struct g kfree(disk_name); } } + sysfs_create_link(&disk->kobj, &block_subsys.kset.kobj, "subsystem"); } /* Not exported, helper to add_disk(). */ @@ -420,14 +409,8 @@ void register_disk(struct gendisk *disk) disk_sysfs_add_subdirs(disk); /* No minors to use for partitions */ - if (disk->minors == 1) { - if (disk->devfs_name[0] != '\0') - devfs_add_disk(disk); + if (disk->minors == 1) goto exit; - } - - /* always add handle for the whole disk */ - devfs_add_partitioned(disk); /* No such device (e.g., media were just removed) */ if (!get_capacity(disk)) @@ -481,6 +464,10 @@ int rescan_partitions(struct gendisk *di sector_t from = state->parts[p].from; if (!size) continue; + if (from + size > get_capacity(disk)) { + printk(" %s: p%d exceeds device capacity\n", + disk->disk_name, p); + } add_partition(disk, p, from, size); #ifdef CONFIG_BLK_DEV_MD if (state->parts[p].flags) @@ -496,8 +483,8 @@ unsigned char *read_dev_sector(struct bl struct address_space *mapping = bdev->bd_inode->i_mapping; struct page *page; - page = read_cache_page(mapping, (pgoff_t)(n >> (PAGE_CACHE_SHIFT-9)), - (filler_t *)mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, (pgoff_t)(n >> (PAGE_CACHE_SHIFT-9)), + NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); if (!PageUptodate(page)) @@ -531,8 +518,6 @@ void del_gendisk(struct gendisk *disk) disk_stat_set_all(disk, 0); disk->stamp = 0; - devfs_remove_disk(disk); - kobject_uevent(&disk->kobj, KOBJ_REMOVE); if (disk->holder_dir) kobject_unregister(disk->holder_dir); @@ -548,5 +533,6 @@ void del_gendisk(struct gendisk *disk) put_device(disk->driverfs_dev); disk->driverfs_dev = NULL; } + sysfs_remove_link(&disk->kobj, "subsystem"); kobject_del(&disk->kobj); } diff --git a/fs/partitions/devfs.c b/fs/partitions/devfs.c deleted file mode 100644 index 3f0a780..0000000 --- a/fs/partitions/devfs.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This tries to keep block devices away from devfs as much as possible. - */ -#include -#include -#include -#include -#include -#include - - -struct unique_numspace { - u32 num_free; /* Num free in bits */ - u32 length; /* Array length in bytes */ - unsigned long *bits; - struct semaphore mutex; -}; - -static DEFINE_MUTEX(numspace_mutex); - -static int expand_numspace(struct unique_numspace *s) -{ - u32 length; - void *bits; - - if (s->length < 16) - length = 16; - else - length = s->length << 1; - - bits = vmalloc(length); - if (!bits) - return -ENOMEM; - if (s->bits) { - memcpy(bits, s->bits, s->length); - vfree(s->bits); - } - - s->num_free = (length - s->length) << 3; - s->bits = bits; - memset(bits + s->length, 0, length - s->length); - s->length = length; - - return 0; -} - -static int alloc_unique_number(struct unique_numspace *s) -{ - int rval = 0; - - mutex_lock(&numspace_mutex); - if (s->num_free < 1) - rval = expand_numspace(s); - if (!rval) { - rval = find_first_zero_bit(s->bits, s->length << 3); - --s->num_free; - __set_bit(rval, s->bits); - } - mutex_unlock(&numspace_mutex); - - return rval; -} - -static void dealloc_unique_number(struct unique_numspace *s, int number) -{ - int old_val; - - if (number >= 0) { - mutex_lock(&numspace_mutex); - old_val = __test_and_clear_bit(number, s->bits); - if (old_val) - ++s->num_free; - mutex_unlock(&numspace_mutex); - } -} - -static struct unique_numspace disc_numspace; -static struct unique_numspace cdrom_numspace; - -void devfs_add_partitioned(struct gendisk *disk) -{ - char dirname[64], symlink[16]; - - devfs_mk_dir(disk->devfs_name); - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), - S_IFBLK|S_IRUSR|S_IWUSR, - "%s/disc", disk->devfs_name); - - disk->number = alloc_unique_number(&disc_numspace); - - sprintf(symlink, "discs/disc%d", disk->number); - sprintf(dirname, "../%s", disk->devfs_name); - devfs_mk_symlink(symlink, dirname); - -} - -void devfs_add_disk(struct gendisk *disk) -{ - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), - (disk->flags & GENHD_FL_CD) ? - S_IFBLK|S_IRUGO|S_IWUGO : - S_IFBLK|S_IRUSR|S_IWUSR, - "%s", disk->devfs_name); - - if (disk->flags & GENHD_FL_CD) { - char dirname[64], symlink[16]; - - disk->number = alloc_unique_number(&cdrom_numspace); - - sprintf(symlink, "cdroms/cdrom%d", disk->number); - sprintf(dirname, "../%s", disk->devfs_name); - devfs_mk_symlink(symlink, dirname); - } -} - -void devfs_remove_disk(struct gendisk *disk) -{ - if (disk->minors != 1) { - devfs_remove("discs/disc%d", disk->number); - dealloc_unique_number(&disc_numspace, disk->number); - devfs_remove("%s/disc", disk->devfs_name); - } - if (disk->flags & GENHD_FL_CD) { - devfs_remove("cdroms/cdrom%d", disk->number); - dealloc_unique_number(&cdrom_numspace, disk->number); - } - devfs_remove(disk->devfs_name); -} - - diff --git a/fs/partitions/devfs.h b/fs/partitions/devfs.h deleted file mode 100644 index 176118b..0000000 --- a/fs/partitions/devfs.h +++ /dev/null @@ -1,10 +0,0 @@ - -#ifdef CONFIG_DEVFS_FS -void devfs_add_disk(struct gendisk *dev); -void devfs_add_partitioned(struct gendisk *dev); -void devfs_remove_disk(struct gendisk *dev); -#else -# define devfs_add_disk(disk) do { } while (0) -# define devfs_add_partitioned(disk) do { } while (0) -# define devfs_remove_disk(disk) do { } while (0) -#endif diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index 0f5b017..6373028 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -91,7 +91,6 @@ * - Code works, detects all the partitions. * ************************************************************/ -#include #include #include "check.h" #include "efi.h" diff --git a/fs/partitions/efi.h b/fs/partitions/efi.h index c44fb05..2cc89d0 100644 --- a/fs/partitions/efi.h +++ b/fs/partitions/efi.h @@ -26,7 +26,6 @@ #ifndef FS_PART_EFI_H_INCLUDED #define FS_PART_EFI_H_INCLUDED #include -#include #include #include #include diff --git a/fs/partitions/ibm.c b/fs/partitions/ibm.c index 830c55d..d352a73 100644 --- a/fs/partitions/ibm.c +++ b/fs/partitions/ibm.c @@ -6,7 +6,6 @@ * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 */ -#include #include #include #include diff --git a/fs/partitions/mac.c b/fs/partitions/mac.c index 813292f..c087100 100644 --- a/fs/partitions/mac.c +++ b/fs/partitions/mac.c @@ -6,7 +6,6 @@ * Re-organised Feb 1998 Russell King */ -#include #include #include "check.h" #include "mac.h" diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c index 9935d25..8f12587 100644 --- a/fs/partitions/msdos.c +++ b/fs/partitions/msdos.c @@ -19,7 +19,6 @@ * Re-organised Feb 1998 Russell King */ -#include #include "check.h" #include "msdos.h" diff --git a/fs/pipe.c b/fs/pipe.c index 5acd895..2035257 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -979,12 +979,11 @@ no_files: * any operations on the root directory. However, we need a non-trivial * d_name - pipe: will go nicely and kill the special-casing in procfs. */ - -static struct super_block * -pipefs_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) +static int pipefs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) { - return get_sb_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC); + return get_sb_pseudo(fs_type, "pipe:", NULL, PIPEFS_MAGIC, mnt); } static struct file_system_type pipe_fs_type = { diff --git a/fs/pnode.c b/fs/pnode.c index 37b568e..da42ee6 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -53,8 +53,7 @@ static int do_make_slave(struct vfsmount if (master) { list_for_each_entry(slave_mnt, &mnt->mnt_slave_list, mnt_slave) slave_mnt->mnt_master = master; - list_del(&mnt->mnt_slave); - list_add(&mnt->mnt_slave, &master->mnt_slave_list); + list_move(&mnt->mnt_slave, &master->mnt_slave_list); list_splice(&mnt->mnt_slave_list, master->mnt_slave_list.prev); INIT_LIST_HEAD(&mnt->mnt_slave_list); } else { @@ -283,10 +282,8 @@ static void __propagate_umount(struct vf * umount the child only if the child has no * other children */ - if (child && list_empty(&child->mnt_mounts)) { - list_del(&child->mnt_hash); - list_add_tail(&child->mnt_hash, &mnt->mnt_hash); - } + if (child && list_empty(&child->mnt_mounts)) + list_move_tail(&child->mnt_hash, &mnt->mnt_hash); } } diff --git a/fs/proc/array.c b/fs/proc/array.c index 7a76ad5..7495d3e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -52,7 +52,6 @@ * : base.c too. */ -#include #include #include #include diff --git a/fs/proc/base.c b/fs/proc/base.c index 6cc77dc..243a94a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -49,7 +49,6 @@ #include -#include #include #include #include @@ -74,6 +73,16 @@ #include #include #include "internal.h" +/* NOTE: + * Implementing inode permission operations in /proc is almost + * certainly an error. Permission checks need to happen during + * each system call not at open time. The reason is that most of + * what we wish to check for permissions in /proc varies at runtime. + * + * The classic example of a problem is opening file descriptors + * in /proc for a task before it execs a suid executable. + */ + /* * For hysterical raisins we keep the same inumbers as in the old procfs. * Feel free to change the macro below - just keep the range distinct from @@ -121,6 +130,8 @@ #ifdef CONFIG_SECURITY PROC_TGID_ATTR_PREV, PROC_TGID_ATTR_EXEC, PROC_TGID_ATTR_FSCREATE, + PROC_TGID_ATTR_KEYCREATE, + PROC_TGID_ATTR_SOCKCREATE, #endif #ifdef CONFIG_AUDITSYSCALL PROC_TGID_LOGINUID, @@ -162,6 +173,8 @@ #ifdef CONFIG_SECURITY PROC_TID_ATTR_PREV, PROC_TID_ATTR_EXEC, PROC_TID_ATTR_FSCREATE, + PROC_TID_ATTR_KEYCREATE, + PROC_TID_ATTR_SOCKCREATE, #endif #ifdef CONFIG_AUDITSYSCALL PROC_TID_LOGINUID, @@ -173,6 +186,9 @@ #endif PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */ }; +/* Worst case buffer size needed for holding an integer. */ +#define PROC_NUMBUF 10 + struct pid_entry { int type; int len; @@ -275,6 +291,8 @@ static struct pid_entry tgid_attr_stuff[ E(PROC_TGID_ATTR_PREV, "prev", S_IFREG|S_IRUGO), E(PROC_TGID_ATTR_EXEC, "exec", S_IFREG|S_IRUGO|S_IWUGO), E(PROC_TGID_ATTR_FSCREATE, "fscreate", S_IFREG|S_IRUGO|S_IWUGO), + E(PROC_TGID_ATTR_KEYCREATE, "keycreate", S_IFREG|S_IRUGO|S_IWUGO), + E(PROC_TGID_ATTR_SOCKCREATE, "sockcreate", S_IFREG|S_IRUGO|S_IWUGO), {0,0,NULL,0} }; static struct pid_entry tid_attr_stuff[] = { @@ -282,6 +300,8 @@ static struct pid_entry tid_attr_stuff[] E(PROC_TID_ATTR_PREV, "prev", S_IFREG|S_IRUGO), E(PROC_TID_ATTR_EXEC, "exec", S_IFREG|S_IRUGO|S_IWUGO), E(PROC_TID_ATTR_FSCREATE, "fscreate", S_IFREG|S_IRUGO|S_IWUGO), + E(PROC_TID_ATTR_KEYCREATE, "keycreate", S_IFREG|S_IRUGO|S_IWUGO), + E(PROC_TID_ATTR_SOCKCREATE, "sockcreate", S_IFREG|S_IRUGO|S_IWUGO), {0,0,NULL,0} }; #endif @@ -290,12 +310,15 @@ #undef E static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) { - struct task_struct *task = proc_task(inode); - struct files_struct *files; + struct task_struct *task = get_proc_task(inode); + struct files_struct *files = NULL; struct file *file; - int fd = proc_type(inode) - PROC_TID_FD_DIR; + int fd = proc_fd(inode); - files = get_files_struct(task); + if (task) { + files = get_files_struct(task); + put_task_struct(task); + } if (files) { /* * We are not taking a ref to the file structure, so we must @@ -327,29 +350,33 @@ static struct fs_struct *get_fs_struct(s return fs; } -static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) +static int get_nr_threads(struct task_struct *tsk) { - struct fs_struct *fs = get_fs_struct(proc_task(inode)); - int result = -ENOENT; - if (fs) { - read_lock(&fs->lock); - *mnt = mntget(fs->pwdmnt); - *dentry = dget(fs->pwd); - read_unlock(&fs->lock); - result = 0; - put_fs_struct(fs); + /* Must be called with the rcu_read_lock held */ + unsigned long flags; + int count = 0; + + if (lock_task_sighand(tsk, &flags)) { + count = atomic_read(&tsk->signal->count); + unlock_task_sighand(tsk, &flags); } - return result; + return count; } -static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) +static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) { - struct fs_struct *fs = get_fs_struct(proc_task(inode)); + struct task_struct *task = get_proc_task(inode); + struct fs_struct *fs = NULL; int result = -ENOENT; + + if (task) { + fs = get_fs_struct(task); + put_task_struct(task); + } if (fs) { read_lock(&fs->lock); - *mnt = mntget(fs->rootmnt); - *dentry = dget(fs->root); + *mnt = mntget(fs->pwdmnt); + *dentry = dget(fs->pwd); read_unlock(&fs->lock); result = 0; put_fs_struct(fs); @@ -357,42 +384,16 @@ static int proc_root_link(struct inode * return result; } - -/* Same as proc_root_link, but this addionally tries to get fs from other - * threads in the group */ -static int proc_task_root_link(struct inode *inode, struct dentry **dentry, - struct vfsmount **mnt) +static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) { - struct fs_struct *fs; + struct task_struct *task = get_proc_task(inode); + struct fs_struct *fs = NULL; int result = -ENOENT; - struct task_struct *leader = proc_task(inode); - task_lock(leader); - fs = leader->fs; - if (fs) { - atomic_inc(&fs->count); - task_unlock(leader); - } else { - /* Try to get fs from other threads */ - task_unlock(leader); - read_lock(&tasklist_lock); - if (pid_alive(leader)) { - struct task_struct *task = leader; - - while ((task = next_thread(task)) != leader) { - task_lock(task); - fs = task->fs; - if (fs) { - atomic_inc(&fs->count); - task_unlock(task); - break; - } - task_unlock(task); - } - } - read_unlock(&tasklist_lock); + if (task) { + fs = get_fs_struct(task); + put_task_struct(task); } - if (fs) { read_lock(&fs->lock); *mnt = mntget(fs->rootmnt); @@ -404,7 +405,6 @@ static int proc_task_root_link(struct in return result; } - #define MAY_PTRACE(task) \ (task == current || \ (task->parent == current && \ @@ -535,142 +535,22 @@ static int proc_oom_score(struct task_st /************************************************************************/ /* permission checks */ - -/* If the process being read is separated by chroot from the reading process, - * don't let the reader access the threads. - * - * note: this does dput(root) and mntput(vfsmnt) on exit. - */ -static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt) +static int proc_fd_access_allowed(struct inode *inode) { - struct dentry *de, *base; - struct vfsmount *our_vfsmnt, *mnt; - int res = 0; - - read_lock(¤t->fs->lock); - our_vfsmnt = mntget(current->fs->rootmnt); - base = dget(current->fs->root); - read_unlock(¤t->fs->lock); - - spin_lock(&vfsmount_lock); - de = root; - mnt = vfsmnt; - - while (mnt != our_vfsmnt) { - if (mnt == mnt->mnt_parent) - goto out; - de = mnt->mnt_mountpoint; - mnt = mnt->mnt_parent; - } - - if (!is_subdir(de, base)) - goto out; - spin_unlock(&vfsmount_lock); - -exit: - dput(base); - mntput(our_vfsmnt); - dput(root); - mntput(vfsmnt); - return res; -out: - spin_unlock(&vfsmount_lock); - res = -EACCES; - goto exit; -} - -static int proc_check_root(struct inode *inode) -{ - struct dentry *root; - struct vfsmount *vfsmnt; - - if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */ - return -ENOENT; - return proc_check_chroot(root, vfsmnt); -} - -static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) -{ - if (generic_permission(inode, mask, NULL) != 0) - return -EACCES; - return proc_check_root(inode); -} - -static int proc_task_permission(struct inode *inode, int mask, struct nameidata *nd) -{ - struct dentry *root; - struct vfsmount *vfsmnt; - - if (generic_permission(inode, mask, NULL) != 0) - return -EACCES; - - if (proc_task_root_link(inode, &root, &vfsmnt)) - return -ENOENT; - - return proc_check_chroot(root, vfsmnt); -} - -extern struct seq_operations proc_pid_maps_op; -static int maps_open(struct inode *inode, struct file *file) -{ - struct task_struct *task = proc_task(inode); - int ret = seq_open(file, &proc_pid_maps_op); - if (!ret) { - struct seq_file *m = file->private_data; - m->private = task; - } - return ret; -} - -static struct file_operations proc_maps_operations = { - .open = maps_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -#ifdef CONFIG_NUMA -extern struct seq_operations proc_pid_numa_maps_op; -static int numa_maps_open(struct inode *inode, struct file *file) -{ - struct task_struct *task = proc_task(inode); - int ret = seq_open(file, &proc_pid_numa_maps_op); - if (!ret) { - struct seq_file *m = file->private_data; - m->private = task; - } - return ret; -} - -static struct file_operations proc_numa_maps_operations = { - .open = numa_maps_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif - -#ifdef CONFIG_MMU -extern struct seq_operations proc_pid_smaps_op; -static int smaps_open(struct inode *inode, struct file *file) -{ - struct task_struct *task = proc_task(inode); - int ret = seq_open(file, &proc_pid_smaps_op); - if (!ret) { - struct seq_file *m = file->private_data; - m->private = task; + struct task_struct *task; + int allowed = 0; + /* Allow access to a task's file descriptors if it is us or we + * may use ptrace attach to the process and find out that + * information. + */ + task = get_proc_task(inode); + if (task) { + allowed = ptrace_may_attach(task); + put_task_struct(task); } - return ret; + return allowed; } -static struct file_operations proc_smaps_operations = { - .open = smaps_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif - extern struct seq_operations mounts_op; struct proc_mounts { struct seq_file m; @@ -679,16 +559,19 @@ struct proc_mounts { static int mounts_open(struct inode *inode, struct file *file) { - struct task_struct *task = proc_task(inode); - struct namespace *namespace; + struct task_struct *task = get_proc_task(inode); + struct namespace *namespace = NULL; struct proc_mounts *p; int ret = -EINVAL; - task_lock(task); - namespace = task->namespace; - if (namespace) - get_namespace(namespace); - task_unlock(task); + if (task) { + task_lock(task); + namespace = task->namespace; + if (namespace) + get_namespace(namespace); + task_unlock(task); + put_task_struct(task); + } if (namespace) { ret = -ENOMEM; @@ -745,17 +628,21 @@ static struct file_operations proc_mount extern struct seq_operations mountstats_op; static int mountstats_open(struct inode *inode, struct file *file) { - struct task_struct *task = proc_task(inode); int ret = seq_open(file, &mountstats_op); if (!ret) { struct seq_file *m = file->private_data; - struct namespace *namespace; - task_lock(task); - namespace = task->namespace; - if (namespace) - get_namespace(namespace); - task_unlock(task); + struct namespace *namespace = NULL; + struct task_struct *task = get_proc_task(inode); + + if (task) { + task_lock(task); + namespace = task->namespace; + if (namespace) + get_namespace(namespace); + task_unlock(task); + put_task_struct(task); + } if (namespace) m->private = namespace; @@ -782,18 +669,27 @@ static ssize_t proc_info_read(struct fil struct inode * inode = file->f_dentry->d_inode; unsigned long page; ssize_t length; - struct task_struct *task = proc_task(inode); + struct task_struct *task = get_proc_task(inode); + + length = -ESRCH; + if (!task) + goto out_no_task; if (count > PROC_BLOCK_SIZE) count = PROC_BLOCK_SIZE; + + length = -ENOMEM; if (!(page = __get_free_page(GFP_KERNEL))) - return -ENOMEM; + goto out; length = PROC_I(inode)->op.proc_read(task, (char*)page); if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); free_page(page); +out: + put_task_struct(task); +out_no_task: return length; } @@ -810,12 +706,15 @@ static int mem_open(struct inode* inode, static ssize_t mem_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { - struct task_struct *task = proc_task(file->f_dentry->d_inode); + struct task_struct *task = get_proc_task(file->f_dentry->d_inode); char *page; unsigned long src = *ppos; int ret = -ESRCH; struct mm_struct *mm; + if (!task) + goto out_no_task; + if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) goto out; @@ -865,6 +764,8 @@ out_put: out_free: free_page((unsigned long) page); out: + put_task_struct(task); +out_no_task: return ret; } @@ -877,15 +778,20 @@ static ssize_t mem_write(struct file * f { int copied = 0; char *page; - struct task_struct *task = proc_task(file->f_dentry->d_inode); + struct task_struct *task = get_proc_task(file->f_dentry->d_inode); unsigned long dst = *ppos; + copied = -ESRCH; + if (!task) + goto out_no_task; + if (!MAY_PTRACE(task) || !ptrace_may_attach(task)) - return -ESRCH; + goto out; + copied = -ENOMEM; page = (char *)__get_free_page(GFP_USER); if (!page) - return -ENOMEM; + goto out; while (count > 0) { int this_len, retval; @@ -908,6 +814,9 @@ static ssize_t mem_write(struct file * f } *ppos = dst; free_page((unsigned long) page); +out: + put_task_struct(task); +out_no_task: return copied; } #endif @@ -938,13 +847,18 @@ static struct file_operations proc_mem_o static ssize_t oom_adjust_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct task_struct *task = proc_task(file->f_dentry->d_inode); - char buffer[8]; + struct task_struct *task = get_proc_task(file->f_dentry->d_inode); + char buffer[PROC_NUMBUF]; size_t len; - int oom_adjust = task->oomkilladj; + int oom_adjust; loff_t __ppos = *ppos; - len = sprintf(buffer, "%i\n", oom_adjust); + if (!task) + return -ESRCH; + oom_adjust = task->oomkilladj; + put_task_struct(task); + + len = snprintf(buffer, sizeof(buffer), "%i\n", oom_adjust); if (__ppos >= len) return 0; if (count > len-__ppos) @@ -958,15 +872,15 @@ static ssize_t oom_adjust_read(struct fi static ssize_t oom_adjust_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct task_struct *task = proc_task(file->f_dentry->d_inode); - char buffer[8], *end; + struct task_struct *task; + char buffer[PROC_NUMBUF], *end; int oom_adjust; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; - memset(buffer, 0, 8); - if (count > 6) - count = 6; + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; if (copy_from_user(buffer, buf, count)) return -EFAULT; oom_adjust = simple_strtol(buffer, &end, 0); @@ -974,7 +888,11 @@ static ssize_t oom_adjust_write(struct f return -EINVAL; if (*end == '\n') end++; + task = get_proc_task(file->f_dentry->d_inode); + if (!task) + return -ESRCH; task->oomkilladj = oom_adjust; + put_task_struct(task); if (end - buffer == 0) return -EIO; return end - buffer; @@ -985,22 +903,21 @@ static struct file_operations proc_oom_a .write = oom_adjust_write, }; -static struct inode_operations proc_mem_inode_operations = { - .permission = proc_permission, -}; - #ifdef CONFIG_AUDITSYSCALL #define TMPBUFLEN 21 static ssize_t proc_loginuid_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { struct inode * inode = file->f_dentry->d_inode; - struct task_struct *task = proc_task(inode); + struct task_struct *task = get_proc_task(inode); ssize_t length; char tmpbuf[TMPBUFLEN]; + if (!task) + return -ESRCH; length = scnprintf(tmpbuf, TMPBUFLEN, "%u", audit_get_loginuid(task->audit_context)); + put_task_struct(task); return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); } @@ -1010,17 +927,16 @@ static ssize_t proc_loginuid_write(struc struct inode * inode = file->f_dentry->d_inode; char *page, *tmp; ssize_t length; - struct task_struct *task = proc_task(inode); uid_t loginuid; if (!capable(CAP_AUDIT_CONTROL)) return -EPERM; - if (current != task) + if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) return -EPERM; - if (count > PAGE_SIZE) - count = PAGE_SIZE; + if (count >= PAGE_SIZE) + count = PAGE_SIZE - 1; if (*ppos != 0) { /* No partial writes. */ @@ -1033,13 +949,14 @@ static ssize_t proc_loginuid_write(struc if (copy_from_user(page, buf, count)) goto out_free_page; + page[count] = '\0'; loginuid = simple_strtoul(page, &tmp, 10); if (tmp == page) { length = -EINVAL; goto out_free_page; } - length = audit_set_loginuid(task, loginuid); + length = audit_set_loginuid(current, loginuid); if (likely(length == 0)) length = count; @@ -1058,13 +975,16 @@ #ifdef CONFIG_SECCOMP static ssize_t seccomp_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct task_struct *tsk = proc_task(file->f_dentry->d_inode); + struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode); char __buf[20]; loff_t __ppos = *ppos; size_t len; + if (!tsk) + return -ESRCH; /* no need to print the trailing zero, so use only len */ len = sprintf(__buf, "%u\n", tsk->seccomp.mode); + put_task_struct(tsk); if (__ppos >= len) return 0; if (count > len - __ppos) @@ -1078,29 +998,43 @@ static ssize_t seccomp_read(struct file static ssize_t seccomp_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct task_struct *tsk = proc_task(file->f_dentry->d_inode); + struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode); char __buf[20], *end; unsigned int seccomp_mode; + ssize_t result; + + result = -ESRCH; + if (!tsk) + goto out_no_task; /* can set it only once to be even more secure */ + result = -EPERM; if (unlikely(tsk->seccomp.mode)) - return -EPERM; + goto out; + result = -EFAULT; memset(__buf, 0, sizeof(__buf)); count = min(count, sizeof(__buf) - 1); if (copy_from_user(__buf, buf, count)) - return -EFAULT; + goto out; + seccomp_mode = simple_strtoul(__buf, &end, 0); if (*end == '\n') end++; + result = -EINVAL; if (seccomp_mode && seccomp_mode <= NR_SECCOMP_MODES) { tsk->seccomp.mode = seccomp_mode; set_tsk_thread_flag(tsk, TIF_SECCOMP); } else - return -EINVAL; + goto out; + result = -EIO; if (unlikely(!(end - __buf))) - return -EIO; - return end - __buf; + goto out; + result = end - __buf; +out: + put_task_struct(tsk); +out_no_task: + return result; } static struct file_operations proc_seccomp_operations = { @@ -1117,10 +1051,8 @@ static void *proc_pid_follow_link(struct /* We don't need a base pointer in the /proc filesystem */ path_release(nd); - if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE)) - goto out; - error = proc_check_root(inode); - if (error) + /* Are we allowed to snoop on the tasks file descriptors? */ + if (!proc_fd_access_allowed(inode)) goto out; error = PROC_I(inode)->op.proc_get_link(inode, &nd->dentry, &nd->mnt); @@ -1162,12 +1094,8 @@ static int proc_pid_readlink(struct dent struct dentry *de; struct vfsmount *mnt = NULL; - lock_kernel(); - - if (current->fsuid != inode->i_uid && !capable(CAP_DAC_OVERRIDE)) - goto out; - error = proc_check_root(inode); - if (error) + /* Are we allowed to snoop on the tasks file descriptors? */ + if (!proc_fd_access_allowed(inode)) goto out; error = PROC_I(inode)->op.proc_get_link(inode, &de, &mnt); @@ -1178,7 +1106,6 @@ static int proc_pid_readlink(struct dent dput(de); mntput(mnt); out: - unlock_kernel(); return error; } @@ -1187,21 +1114,20 @@ static struct inode_operations proc_pid_ .follow_link = proc_pid_follow_link }; -#define NUMBUF 10 - static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) { - struct inode *inode = filp->f_dentry->d_inode; - struct task_struct *p = proc_task(inode); + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + struct task_struct *p = get_proc_task(inode); unsigned int fd, tid, ino; int retval; - char buf[NUMBUF]; + char buf[PROC_NUMBUF]; struct files_struct * files; struct fdtable *fdt; retval = -ENOENT; - if (!pid_alive(p)) - goto out; + if (!p) + goto out_no_task; retval = 0; tid = p->pid; @@ -1212,7 +1138,7 @@ static int proc_readfd(struct file * fil goto out; filp->f_pos++; case 1: - ino = fake_ino(tid, PROC_TID_INO); + ino = parent_ino(dentry); if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) goto out; filp->f_pos++; @@ -1231,7 +1157,7 @@ static int proc_readfd(struct file * fil continue; rcu_read_unlock(); - j = NUMBUF; + j = PROC_NUMBUF; i = fd; do { j--; @@ -1240,7 +1166,7 @@ static int proc_readfd(struct file * fil } while (i); ino = fake_ino(tid, PROC_TID_FD_DIR + fd); - if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) { + if (filldir(dirent, buf+j, PROC_NUMBUF-j, fd+2, ino, DT_LNK) < 0) { rcu_read_lock(); break; } @@ -1250,6 +1176,8 @@ static int proc_readfd(struct file * fil put_files_struct(files); } out: + put_task_struct(p); +out_no_task: return retval; } @@ -1261,16 +1189,18 @@ static int proc_pident_readdir(struct fi int pid; struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; + struct task_struct *task = get_proc_task(inode); struct pid_entry *p; ino_t ino; int ret; ret = -ENOENT; - if (!pid_alive(proc_task(inode))) + if (!task) goto out; ret = 0; - pid = proc_task(inode)->pid; + pid = task->pid; + put_task_struct(task); i = filp->f_pos; switch (i) { case 0: @@ -1353,22 +1283,19 @@ static struct inode *proc_pid_make_inode /* Common stuff */ ei = PROC_I(inode); - ei->task = NULL; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; inode->i_ino = fake_ino(task->pid, ino); - if (!pid_alive(task)) - goto out_unlock; - /* * grab the reference to task. */ - get_task_struct(task); - ei->task = task; - ei->type = ino; + ei->pid = get_pid(task->pids[PIDTYPE_PID].pid); + if (!ei->pid) + goto out_unlock; + inode->i_uid = 0; inode->i_gid = 0; - if (ino == PROC_TGID_INO || ino == PROC_TID_INO || task_dumpable(task)) { + if (task_dumpable(task)) { inode->i_uid = task->euid; inode->i_gid = task->egid; } @@ -1378,7 +1305,6 @@ out: return inode; out_unlock: - ei->pde = NULL; iput(inode); return NULL; } @@ -1392,13 +1318,21 @@ out_unlock: * * Rewrite the inode's ownerships here because the owning task may have * performed a setuid(), etc. + * + * Before the /proc/pid/status file was created the only way to read + * the effective uid of a /process was to stat /proc/pid. Reading + * /proc/pid/status is slow enough that procps and other packages + * kept stating /proc/pid. To keep the rules in /proc simple I have + * made this apply to all per process world readable and executable + * directories. */ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; - struct task_struct *task = proc_task(inode); - if (pid_alive(task)) { - if (proc_type(inode) == PROC_TGID_INO || proc_type(inode) == PROC_TID_INO || task_dumpable(task)) { + struct task_struct *task = get_proc_task(inode); + if (task) { + if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || + task_dumpable(task)) { inode->i_uid = task->euid; inode->i_gid = task->egid; } else { @@ -1406,59 +1340,75 @@ static int pid_revalidate(struct dentry inode->i_gid = 0; } security_task_to_inode(task, inode); + put_task_struct(task); return 1; } d_drop(dentry); return 0; } +static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + struct task_struct *task; + generic_fillattr(inode, stat); + + rcu_read_lock(); + stat->uid = 0; + stat->gid = 0; + task = pid_task(proc_pid(inode), PIDTYPE_PID); + if (task) { + if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || + task_dumpable(task)) { + stat->uid = task->euid; + stat->gid = task->egid; + } + } + rcu_read_unlock(); + return 0; +} + static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; - struct task_struct *task = proc_task(inode); - int fd = proc_type(inode) - PROC_TID_FD_DIR; + struct task_struct *task = get_proc_task(inode); + int fd = proc_fd(inode); struct files_struct *files; - files = get_files_struct(task); - if (files) { - rcu_read_lock(); - if (fcheck_files(files, fd)) { + if (task) { + files = get_files_struct(task); + if (files) { + rcu_read_lock(); + if (fcheck_files(files, fd)) { + rcu_read_unlock(); + put_files_struct(files); + if (task_dumpable(task)) { + inode->i_uid = task->euid; + inode->i_gid = task->egid; + } else { + inode->i_uid = 0; + inode->i_gid = 0; + } + security_task_to_inode(task, inode); + put_task_struct(task); + return 1; + } rcu_read_unlock(); put_files_struct(files); - if (task_dumpable(task)) { - inode->i_uid = task->euid; - inode->i_gid = task->egid; - } else { - inode->i_uid = 0; - inode->i_gid = 0; - } - security_task_to_inode(task, inode); - return 1; } - rcu_read_unlock(); - put_files_struct(files); + put_task_struct(task); } d_drop(dentry); return 0; } -static void pid_base_iput(struct dentry *dentry, struct inode *inode) -{ - struct task_struct *task = proc_task(inode); - spin_lock(&task->proc_lock); - if (task->proc_dentry == dentry) - task->proc_dentry = NULL; - spin_unlock(&task->proc_lock); - iput(inode); -} - static int pid_delete_dentry(struct dentry * dentry) { /* Is the task we represent dead? * If so, then don't put the dentry on the lru list, * kill it immediately. */ - return !pid_alive(proc_task(dentry->d_inode)); + return !proc_pid(dentry->d_inode)->tasks[PIDTYPE_PID].first; } static struct dentry_operations tid_fd_dentry_operations = @@ -1473,13 +1423,6 @@ static struct dentry_operations pid_dent .d_delete = pid_delete_dentry, }; -static struct dentry_operations pid_base_dentry_operations = -{ - .d_revalidate = pid_revalidate, - .d_iput = pid_base_iput, - .d_delete = pid_delete_dentry, -}; - /* Lookups */ static unsigned name_to_int(struct dentry *dentry) @@ -1507,22 +1450,24 @@ out: /* SMP-safe */ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd) { - struct task_struct *task = proc_task(dir); + struct task_struct *task = get_proc_task(dir); unsigned fd = name_to_int(dentry); + struct dentry *result = ERR_PTR(-ENOENT); struct file * file; struct files_struct * files; struct inode *inode; struct proc_inode *ei; + if (!task) + goto out_no_task; if (fd == ~0U) goto out; - if (!pid_alive(task)) - goto out; inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_FD_DIR+fd); if (!inode) goto out; ei = PROC_I(inode); + ei->fd = fd; files = get_files_struct(task); if (!files) goto out_unlock; @@ -1547,19 +1492,25 @@ static struct dentry *proc_lookupfd(stru ei->op.proc_get_link = proc_fd_link; dentry->d_op = &tid_fd_dentry_operations; d_add(dentry, inode); - return NULL; + /* Close the race of the process dying before we return the dentry */ + if (tid_fd_revalidate(dentry, NULL)) + result = NULL; +out: + put_task_struct(task); +out_no_task: + return result; out_unlock2: spin_unlock(&files->file_lock); put_files_struct(files); out_unlock: iput(inode); -out: - return ERR_PTR(-ENOENT); + goto out; } static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir); static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd); +static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); static struct file_operations proc_fd_operations = { .read = generic_read_dir, @@ -1576,12 +1527,11 @@ static struct file_operations proc_task_ */ static struct inode_operations proc_fd_inode_operations = { .lookup = proc_lookupfd, - .permission = proc_permission, }; static struct inode_operations proc_task_inode_operations = { .lookup = proc_task_lookup, - .permission = proc_task_permission, + .getattr = proc_task_getattr, }; #ifdef CONFIG_SECURITY @@ -1591,12 +1541,17 @@ static ssize_t proc_pid_attr_read(struct struct inode * inode = file->f_dentry->d_inode; unsigned long page; ssize_t length; - struct task_struct *task = proc_task(inode); + struct task_struct *task = get_proc_task(inode); + + length = -ESRCH; + if (!task) + goto out_no_task; if (count > PAGE_SIZE) count = PAGE_SIZE; + length = -ENOMEM; if (!(page = __get_free_page(GFP_KERNEL))) - return -ENOMEM; + goto out; length = security_getprocattr(task, (char*)file->f_dentry->d_name.name, @@ -1604,6 +1559,9 @@ static ssize_t proc_pid_attr_read(struct if (length >= 0) length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); free_page(page); +out: + put_task_struct(task); +out_no_task: return length; } @@ -1613,26 +1571,36 @@ static ssize_t proc_pid_attr_write(struc struct inode * inode = file->f_dentry->d_inode; char *page; ssize_t length; - struct task_struct *task = proc_task(inode); + struct task_struct *task = get_proc_task(inode); + length = -ESRCH; + if (!task) + goto out_no_task; if (count > PAGE_SIZE) count = PAGE_SIZE; - if (*ppos != 0) { - /* No partial writes. */ - return -EINVAL; - } + + /* No partial writes. */ + length = -EINVAL; + if (*ppos != 0) + goto out; + + length = -ENOMEM; page = (char*)__get_free_page(GFP_USER); if (!page) - return -ENOMEM; + goto out; + length = -EFAULT; if (copy_from_user(page, buf, count)) - goto out; + goto out_free; length = security_setprocattr(task, (char*)file->f_dentry->d_name.name, (void*)page, count); -out: +out_free: free_page((unsigned long) page); +out: + put_task_struct(task); +out_no_task: return length; } @@ -1647,24 +1615,22 @@ static struct file_operations proc_tgid_ static struct inode_operations proc_tgid_attr_inode_operations; #endif -static int get_tid_list(int index, unsigned int *tids, struct inode *dir); - /* SMP-safe */ static struct dentry *proc_pident_lookup(struct inode *dir, struct dentry *dentry, struct pid_entry *ents) { struct inode *inode; - int error; - struct task_struct *task = proc_task(dir); + struct dentry *error; + struct task_struct *task = get_proc_task(dir); struct pid_entry *p; struct proc_inode *ei; - error = -ENOENT; + error = ERR_PTR(-ENOENT); inode = NULL; - if (!pid_alive(task)) - goto out; + if (!task) + goto out_no_task; for (p = ents; p->name; p++) { if (p->len != dentry->d_name.len) @@ -1675,7 +1641,7 @@ static struct dentry *proc_pident_lookup if (!p->name) goto out; - error = -EINVAL; + error = ERR_PTR(-EINVAL); inode = proc_pid_make_inode(dir->i_sb, task, p->type); if (!inode) goto out; @@ -1688,7 +1654,7 @@ static struct dentry *proc_pident_lookup */ switch(p->type) { case PROC_TGID_TASK: - inode->i_nlink = 2 + get_tid_list(2, NULL, dir); + inode->i_nlink = 2; inode->i_op = &proc_task_inode_operations; inode->i_fop = &proc_task_operations; break; @@ -1758,7 +1724,6 @@ #ifdef CONFIG_NUMA #endif case PROC_TID_MEM: case PROC_TGID_MEM: - inode->i_op = &proc_mem_inode_operations; inode->i_fop = &proc_mem_operations; break; #ifdef CONFIG_SECCOMP @@ -1800,6 +1765,10 @@ #ifdef CONFIG_SECURITY case PROC_TGID_ATTR_EXEC: case PROC_TID_ATTR_FSCREATE: case PROC_TGID_ATTR_FSCREATE: + case PROC_TID_ATTR_KEYCREATE: + case PROC_TGID_ATTR_KEYCREATE: + case PROC_TID_ATTR_SOCKCREATE: + case PROC_TGID_ATTR_SOCKCREATE: inode->i_fop = &proc_pid_attr_operations; break; #endif @@ -1841,14 +1810,18 @@ #endif default: printk("procfs: impossible type (%d)",p->type); iput(inode); - return ERR_PTR(-EINVAL); + error = ERR_PTR(-EINVAL); + goto out; } dentry->d_op = &pid_dentry_operations; d_add(dentry, inode); - return NULL; - + /* Close the race of the process dying before we return the dentry */ + if (pid_revalidate(dentry, NULL)) + error = NULL; out: - return ERR_PTR(error); + put_task_struct(task); +out_no_task: + return error; } static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ @@ -1871,10 +1844,12 @@ static struct file_operations proc_tid_b static struct inode_operations proc_tgid_base_inode_operations = { .lookup = proc_tgid_base_lookup, + .getattr = pid_getattr, }; static struct inode_operations proc_tid_base_inode_operations = { .lookup = proc_tid_base_lookup, + .getattr = pid_getattr, }; #ifdef CONFIG_SECURITY @@ -1916,10 +1891,12 @@ static struct dentry *proc_tid_attr_look static struct inode_operations proc_tgid_attr_inode_operations = { .lookup = proc_tgid_attr_lookup, + .getattr = pid_getattr, }; static struct inode_operations proc_tid_attr_inode_operations = { .lookup = proc_tid_attr_lookup, + .getattr = pid_getattr, }; #endif @@ -1929,14 +1906,14 @@ #endif static int proc_self_readlink(struct dentry *dentry, char __user *buffer, int buflen) { - char tmp[30]; + char tmp[PROC_NUMBUF]; sprintf(tmp, "%d", current->tgid); return vfs_readlink(dentry,buffer,buflen,tmp); } static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd) { - char tmp[30]; + char tmp[PROC_NUMBUF]; sprintf(tmp, "%d", current->tgid); return ERR_PTR(vfs_follow_link(nd,tmp)); } @@ -1947,67 +1924,80 @@ static struct inode_operations proc_self }; /** - * proc_pid_unhash - Unhash /proc/@pid entry from the dcache. - * @p: task that should be flushed. + * proc_flush_task - Remove dcache entries for @task from the /proc dcache. + * + * @task: task that should be flushed. + * + * Looks in the dcache for + * /proc/@pid + * /proc/@tgid/task/@pid + * if either directory is present flushes it and all of it'ts children + * from the dcache. * - * Drops the /proc/@pid dcache entry from the hash chains. + * It is safe and reasonable to cache /proc entries for a task until + * that task exits. After that they just clog up the dcache with + * useless entries, possibly causing useful dcache entries to be + * flushed instead. This routine is proved to flush those useless + * dcache entries at process exit time. * - * Dropping /proc/@pid entries and detach_pid must be synchroneous, - * otherwise e.g. /proc/@pid/exe might point to the wrong executable, - * if the pid value is immediately reused. This is enforced by - * - caller must acquire spin_lock(p->proc_lock) - * - must be called before detach_pid() - * - proc_pid_lookup acquires proc_lock, and checks that - * the target is not dead by looking at the attach count - * of PIDTYPE_PID. + * NOTE: This routine is just an optimization so it does not guarantee + * that no dcache entries will exist at process exit time it + * just makes it very unlikely that any will persist. */ - -struct dentry *proc_pid_unhash(struct task_struct *p) +void proc_flush_task(struct task_struct *task) { - struct dentry *proc_dentry; + struct dentry *dentry, *leader, *dir; + char buf[PROC_NUMBUF]; + struct qstr name; + + name.name = buf; + name.len = snprintf(buf, sizeof(buf), "%d", task->pid); + dentry = d_hash_and_lookup(proc_mnt->mnt_root, &name); + if (dentry) { + shrink_dcache_parent(dentry); + d_drop(dentry); + dput(dentry); + } - proc_dentry = p->proc_dentry; - if (proc_dentry != NULL) { + if (thread_group_leader(task)) + goto out; - spin_lock(&dcache_lock); - spin_lock(&proc_dentry->d_lock); - if (!d_unhashed(proc_dentry)) { - dget_locked(proc_dentry); - __d_drop(proc_dentry); - spin_unlock(&proc_dentry->d_lock); - } else { - spin_unlock(&proc_dentry->d_lock); - proc_dentry = NULL; - } - spin_unlock(&dcache_lock); - } - return proc_dentry; -} + name.name = buf; + name.len = snprintf(buf, sizeof(buf), "%d", task->tgid); + leader = d_hash_and_lookup(proc_mnt->mnt_root, &name); + if (!leader) + goto out; -/** - * proc_pid_flush - recover memory used by stale /proc/@pid/x entries - * @proc_dentry: directoy to prune. - * - * Shrink the /proc directory that was used by the just killed thread. - */ - -void proc_pid_flush(struct dentry *proc_dentry) -{ - might_sleep(); - if(proc_dentry != NULL) { - shrink_dcache_parent(proc_dentry); - dput(proc_dentry); + name.name = "task"; + name.len = strlen(name.name); + dir = d_hash_and_lookup(leader, &name); + if (!dir) + goto out_put_leader; + + name.name = buf; + name.len = snprintf(buf, sizeof(buf), "%d", task->pid); + dentry = d_hash_and_lookup(dir, &name); + if (dentry) { + shrink_dcache_parent(dentry); + d_drop(dentry); + dput(dentry); } + + dput(dir); +out_put_leader: + dput(leader); +out: + return; } /* SMP-safe */ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) { + struct dentry *result = ERR_PTR(-ENOENT); struct task_struct *task; struct inode *inode; struct proc_inode *ei; unsigned tgid; - int died; if (dentry->d_name.len == 4 && !memcmp(dentry->d_name.name,"self",4)) { inode = new_inode(dir->i_sb); @@ -2028,21 +2018,18 @@ struct dentry *proc_pid_lookup(struct in if (tgid == ~0U) goto out; - read_lock(&tasklist_lock); + rcu_read_lock(); task = find_task_by_pid(tgid); if (task) get_task_struct(task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); if (!task) goto out; inode = proc_pid_make_inode(dir->i_sb, task, PROC_TGID_INO); + if (!inode) + goto out_put_task; - - if (!inode) { - put_task_struct(task); - goto out; - } inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; inode->i_op = &proc_tgid_base_inode_operations; inode->i_fop = &proc_tgid_base_operations; @@ -2053,45 +2040,40 @@ #else inode->i_nlink = 4; #endif - dentry->d_op = &pid_base_dentry_operations; + dentry->d_op = &pid_dentry_operations; - died = 0; d_add(dentry, inode); - spin_lock(&task->proc_lock); - task->proc_dentry = dentry; - if (!pid_alive(task)) { - dentry = proc_pid_unhash(task); - died = 1; - } - spin_unlock(&task->proc_lock); + /* Close the race of the process dying before we return the dentry */ + if (pid_revalidate(dentry, NULL)) + result = NULL; +out_put_task: put_task_struct(task); - if (died) { - proc_pid_flush(dentry); - goto out; - } - return NULL; out: - return ERR_PTR(-ENOENT); + return result; } /* SMP-safe */ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) { + struct dentry *result = ERR_PTR(-ENOENT); struct task_struct *task; - struct task_struct *leader = proc_task(dir); + struct task_struct *leader = get_proc_task(dir); struct inode *inode; unsigned tid; + if (!leader) + goto out_no_task; + tid = name_to_int(dentry); if (tid == ~0U) goto out; - read_lock(&tasklist_lock); + rcu_read_lock(); task = find_task_by_pid(tid); if (task) get_task_struct(task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); if (!task) goto out; if (leader->tgid != task->tgid) @@ -2112,101 +2094,95 @@ #else inode->i_nlink = 3; #endif - dentry->d_op = &pid_base_dentry_operations; + dentry->d_op = &pid_dentry_operations; d_add(dentry, inode); + /* Close the race of the process dying before we return the dentry */ + if (pid_revalidate(dentry, NULL)) + result = NULL; - put_task_struct(task); - return NULL; out_drop_task: put_task_struct(task); out: - return ERR_PTR(-ENOENT); + put_task_struct(leader); +out_no_task: + return result; } -#define PROC_NUMBUF 10 -#define PROC_MAXPIDS 20 - /* - * Get a few tgid's to return for filldir - we need to hold the - * tasklist lock while doing this, and we must release it before - * we actually do the filldir itself, so we use a temp buffer.. + * Find the first tgid to return to user space. + * + * Usually this is just whatever follows &init_task, but if the users + * buffer was too small to hold the full list or there was a seek into + * the middle of the directory we have more work to do. + * + * In the case of a short read we start with find_task_by_pid. + * + * In the case of a seek we start with &init_task and walk nr + * threads past it. */ -static int get_tgid_list(int index, unsigned long version, unsigned int *tgids) -{ - struct task_struct *p; - int nr_tgids = 0; - - index--; - read_lock(&tasklist_lock); - p = NULL; - if (version) { - p = find_task_by_pid(version); - if (p && !thread_group_leader(p)) - p = NULL; +static struct task_struct *first_tgid(int tgid, unsigned int nr) +{ + struct task_struct *pos; + rcu_read_lock(); + if (tgid && nr) { + pos = find_task_by_pid(tgid); + if (pos && thread_group_leader(pos)) + goto found; } + /* If nr exceeds the number of processes get out quickly */ + pos = NULL; + if (nr && nr >= nr_processes()) + goto done; - if (p) - index = 0; - else - p = next_task(&init_task); - - for ( ; p != &init_task; p = next_task(p)) { - int tgid = p->pid; - if (!pid_alive(p)) - continue; - if (--index >= 0) - continue; - tgids[nr_tgids] = tgid; - nr_tgids++; - if (nr_tgids >= PROC_MAXPIDS) - break; + /* If we haven't found our starting place yet start with + * the init_task and walk nr tasks forward. + */ + for (pos = next_task(&init_task); nr > 0; --nr) { + pos = next_task(pos); + if (pos == &init_task) { + pos = NULL; + goto done; + } } - read_unlock(&tasklist_lock); - return nr_tgids; +found: + get_task_struct(pos); +done: + rcu_read_unlock(); + return pos; } /* - * Get a few tid's to return for filldir - we need to hold the - * tasklist lock while doing this, and we must release it before - * we actually do the filldir itself, so we use a temp buffer.. + * Find the next task in the task list. + * Return NULL if we loop or there is any error. + * + * The reference to the input task_struct is released. */ -static int get_tid_list(int index, unsigned int *tids, struct inode *dir) -{ - struct task_struct *leader_task = proc_task(dir); - struct task_struct *task = leader_task; - int nr_tids = 0; - - index -= 2; - read_lock(&tasklist_lock); - /* - * The starting point task (leader_task) might be an already - * unlinked task, which cannot be used to access the task-list - * via next_thread(). - */ - if (pid_alive(task)) do { - int tid = task->pid; - - if (--index >= 0) - continue; - if (tids != NULL) - tids[nr_tids] = tid; - nr_tids++; - if (nr_tids >= PROC_MAXPIDS) - break; - } while ((task = next_thread(task)) != leader_task); - read_unlock(&tasklist_lock); - return nr_tids; +static struct task_struct *next_tgid(struct task_struct *start) +{ + struct task_struct *pos; + rcu_read_lock(); + pos = start; + if (pid_alive(start)) + pos = next_task(start); + if (pid_alive(pos) && (pos != &init_task)) { + get_task_struct(pos); + goto done; + } + pos = NULL; +done: + rcu_read_unlock(); + put_task_struct(start); + return pos; } /* for the /proc/ directory itself, after non-process stuff has been done */ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) { - unsigned int tgid_array[PROC_MAXPIDS]; char buf[PROC_NUMBUF]; unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY; - unsigned int nr_tgids, i; - int next_tgid; + struct task_struct *task; + int tgid; if (!nr) { ino_t ino = fake_ino(0,PROC_TGID_INO); @@ -2215,63 +2191,116 @@ int proc_pid_readdir(struct file * filp, filp->f_pos++; nr++; } + nr -= 1; /* f_version caches the tgid value that the last readdir call couldn't * return. lseek aka telldir automagically resets f_version to 0. */ - next_tgid = filp->f_version; + tgid = filp->f_version; filp->f_version = 0; - for (;;) { - nr_tgids = get_tgid_list(nr, next_tgid, tgid_array); - if (!nr_tgids) { - /* no more entries ! */ + for (task = first_tgid(tgid, nr); + task; + task = next_tgid(task), filp->f_pos++) { + int len; + ino_t ino; + tgid = task->pid; + len = snprintf(buf, sizeof(buf), "%d", tgid); + ino = fake_ino(tgid, PROC_TGID_INO); + if (filldir(dirent, buf, len, filp->f_pos, ino, DT_DIR) < 0) { + /* returning this tgid failed, save it as the first + * pid for the next readir call */ + filp->f_version = tgid; + put_task_struct(task); break; } - next_tgid = 0; + } + return 0; +} - /* do not use the last found pid, reserve it for next_tgid */ - if (nr_tgids == PROC_MAXPIDS) { - nr_tgids--; - next_tgid = tgid_array[nr_tgids]; - } +/* + * Find the first tid of a thread group to return to user space. + * + * Usually this is just the thread group leader, but if the users + * buffer was too small or there was a seek into the middle of the + * directory we have more work todo. + * + * In the case of a short read we start with find_task_by_pid. + * + * In the case of a seek we start with the leader and walk nr + * threads past it. + */ +static struct task_struct *first_tid(struct task_struct *leader, + int tid, int nr) +{ + struct task_struct *pos; - for (i=0;i 0)) { + pos = find_task_by_pid(tid); + if (pos && (pos->group_leader == leader)) + goto found; + } - do - buf[--j] = '0' + (tgid % 10); - while ((tgid /= 10) != 0); + /* If nr exceeds the number of threads there is nothing todo */ + pos = NULL; + if (nr && nr >= get_nr_threads(leader)) + goto out; - if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) { - /* returning this tgid failed, save it as the first - * pid for the next readir call */ - filp->f_version = tgid_array[i]; - goto out; - } - filp->f_pos++; - nr++; + /* If we haven't found our starting place yet start + * with the leader and walk nr threads forward. + */ + for (pos = leader; nr > 0; --nr) { + pos = next_thread(pos); + if (pos == leader) { + pos = NULL; + goto out; } } +found: + get_task_struct(pos); out: - return 0; + rcu_read_unlock(); + return pos; +} + +/* + * Find the next thread in the thread list. + * Return NULL if there is an error or no next thread. + * + * The reference to the input task_struct is released. + */ +static struct task_struct *next_tid(struct task_struct *start) +{ + struct task_struct *pos = NULL; + rcu_read_lock(); + if (pid_alive(start)) { + pos = next_thread(start); + if (thread_group_leader(pos)) + pos = NULL; + else + get_task_struct(pos); + } + rcu_read_unlock(); + put_task_struct(start); + return pos; } /* for the /proc/TGID/task/ directories */ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir) { - unsigned int tid_array[PROC_MAXPIDS]; char buf[PROC_NUMBUF]; - unsigned int nr_tids, i; struct dentry *dentry = filp->f_dentry; struct inode *inode = dentry->d_inode; + struct task_struct *leader = get_proc_task(inode); + struct task_struct *task; int retval = -ENOENT; ino_t ino; + int tid; unsigned long pos = filp->f_pos; /* avoiding "long long" filp->f_pos */ - if (!pid_alive(proc_task(inode))) - goto out; + if (!leader) + goto out_no_task; retval = 0; switch (pos) { @@ -2289,24 +2318,45 @@ static int proc_task_readdir(struct file /* fall through */ } - nr_tids = get_tid_list(pos, tid_array, inode); - inode->i_nlink = pos + nr_tids; - - for (i = 0; i < nr_tids; i++) { - unsigned long j = PROC_NUMBUF; - int tid = tid_array[i]; - - ino = fake_ino(tid,PROC_TID_INO); - - do - buf[--j] = '0' + (tid % 10); - while ((tid /= 10) != 0); - - if (filldir(dirent, buf+j, PROC_NUMBUF-j, pos, ino, DT_DIR) < 0) + /* f_version caches the tgid value that the last readdir call couldn't + * return. lseek aka telldir automagically resets f_version to 0. + */ + tid = filp->f_version; + filp->f_version = 0; + for (task = first_tid(leader, tid, pos - 2); + task; + task = next_tid(task), pos++) { + int len; + tid = task->pid; + len = snprintf(buf, sizeof(buf), "%d", tid); + ino = fake_ino(tid, PROC_TID_INO); + if (filldir(dirent, buf, len, pos, ino, DT_DIR < 0)) { + /* returning this tgid failed, save it as the first + * pid for the next readir call */ + filp->f_version = tid; + put_task_struct(task); break; - pos++; + } } out: filp->f_pos = pos; + put_task_struct(leader); +out_no_task: return retval; } + +static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + struct task_struct *p = get_proc_task(inode); + generic_fillattr(inode, stat); + + if (p) { + rcu_read_lock(); + stat->nlink += get_nr_threads(p); + rcu_read_unlock(); + put_task_struct(p); + } + + return 0; +} diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 722b9c4..6dcef08 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -58,14 +58,11 @@ static void de_put(struct proc_dir_entry static void proc_delete_inode(struct inode *inode) { struct proc_dir_entry *de; - struct task_struct *tsk; truncate_inode_pages(&inode->i_data, 0); - /* Let go of any associated process */ - tsk = PROC_I(inode)->task; - if (tsk) - put_task_struct(tsk); + /* Stop tracking associated processes */ + put_pid(PROC_I(inode)->pid); /* Let go of any associated proc directory entry */ de = PROC_I(inode)->pde; @@ -94,8 +91,8 @@ static struct inode *proc_alloc_inode(st ei = (struct proc_inode *)kmem_cache_alloc(proc_inode_cachep, SLAB_KERNEL); if (!ei) return NULL; - ei->task = NULL; - ei->type = 0; + ei->pid = NULL; + ei->fd = 0; ei->op.proc_get_link = NULL; ei->pde = NULL; inode = &ei->vfs_inode; diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 0502f17..146a434 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -37,16 +37,30 @@ extern int proc_tgid_stat(struct task_st extern int proc_pid_status(struct task_struct *, char *); extern int proc_pid_statm(struct task_struct *, char *); +extern struct file_operations proc_maps_operations; +extern struct file_operations proc_numa_maps_operations; +extern struct file_operations proc_smaps_operations; + +extern struct file_operations proc_maps_operations; +extern struct file_operations proc_numa_maps_operations; +extern struct file_operations proc_smaps_operations; + + void free_proc_entry(struct proc_dir_entry *de); int proc_init_inodecache(void); -static inline struct task_struct *proc_task(struct inode *inode) +static inline struct pid *proc_pid(struct inode *inode) +{ + return PROC_I(inode)->pid; +} + +static inline struct task_struct *get_proc_task(struct inode *inode) { - return PROC_I(inode)->task; + return get_pid_task(proc_pid(inode), PIDTYPE_PID); } -static inline int proc_type(struct inode *inode) +static inline int proc_fd(struct inode *inode) { - return PROC_I(inode)->type; + return PROC_I(inode)->fd; } diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c index 17f6e8f..036d14d 100644 --- a/fs/proc/kcore.c +++ b/fs/proc/kcore.c @@ -9,7 +9,6 @@ * Safe accesses to vmalloc/direct-mapped discontiguous areas, Kanoj Sarcar */ -#include #include #include #include diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 5c10ea1..9f2cfc3 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -26,7 +26,6 @@ #include #include #include #include -#include #include #include #include @@ -120,7 +119,6 @@ static int meminfo_read_proc(char *page, { struct sysinfo i; int len; - struct page_state ps; unsigned long inactive; unsigned long active; unsigned long free; @@ -129,7 +127,6 @@ static int meminfo_read_proc(char *page, struct vmalloc_info vmi; long cached; - get_page_state(&ps); get_zone_counts(&active, &inactive, &free); /* @@ -142,7 +139,8 @@ #define K(x) ((x) << (PAGE_SHIFT - 10)) allowed = ((totalram_pages - hugetlb_total_pages()) * sysctl_overcommit_ratio / 100) + total_swap_pages; - cached = get_page_cache_size() - total_swapcache_pages - i.bufferram; + cached = global_page_state(NR_FILE_PAGES) - + total_swapcache_pages - i.bufferram; if (cached < 0) cached = 0; @@ -167,11 +165,14 @@ #define K(x) ((x) << (PAGE_SHIFT - 10)) "SwapFree: %8lu kB\n" "Dirty: %8lu kB\n" "Writeback: %8lu kB\n" + "AnonPages: %8lu kB\n" "Mapped: %8lu kB\n" "Slab: %8lu kB\n" + "PageTables: %8lu kB\n" + "NFS Unstable: %8lu kB\n" + "Bounce: %8lu kB\n" "CommitLimit: %8lu kB\n" "Committed_AS: %8lu kB\n" - "PageTables: %8lu kB\n" "VmallocTotal: %8lu kB\n" "VmallocUsed: %8lu kB\n" "VmallocChunk: %8lu kB\n", @@ -188,13 +189,16 @@ #define K(x) ((x) << (PAGE_SHIFT - 10)) K(i.freeram-i.freehigh), K(i.totalswap), K(i.freeswap), - K(ps.nr_dirty), - K(ps.nr_writeback), - K(ps.nr_mapped), - K(ps.nr_slab), + K(global_page_state(NR_FILE_DIRTY)), + K(global_page_state(NR_WRITEBACK)), + K(global_page_state(NR_ANON_PAGES)), + K(global_page_state(NR_FILE_MAPPED)), + K(global_page_state(NR_SLAB)), + K(global_page_state(NR_PAGETABLE)), + K(global_page_state(NR_UNSTABLE_NFS)), + K(global_page_state(NR_BOUNCE)), K(allowed), K(committed), - K(ps.nr_page_table_pages), (unsigned long)VMALLOC_TOTAL >> 10, vmi.used >> 10, vmi.largest_chunk >> 10 diff --git a/fs/proc/root.c b/fs/proc/root.c index c3fd361..8901c65 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -12,7 +12,6 @@ #include #include #include #include -#include #include #include #include @@ -26,10 +25,10 @@ #ifdef CONFIG_SYSCTL struct proc_dir_entry *proc_sys_root; #endif -static struct super_block *proc_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int proc_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, proc_fill_super); + return get_sb_single(fs_type, flags, data, proc_fill_super, mnt); } static struct file_system_type proc_fs_type = { diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 91b7c15..0a163a4 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -75,9 +75,13 @@ int proc_exe_link(struct inode *inode, s { struct vm_area_struct * vma; int result = -ENOENT; - struct task_struct *task = proc_task(inode); - struct mm_struct * mm = get_task_mm(task); + struct task_struct *task = get_proc_task(inode); + struct mm_struct * mm = NULL; + if (task) { + mm = get_task_mm(task); + put_task_struct(task); + } if (!mm) goto out; down_read(&mm->mmap_sem); @@ -118,9 +122,15 @@ struct mem_size_stats unsigned long private_dirty; }; +__attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma) +{ + return NULL; +} + static int show_map_internal(struct seq_file *m, void *v, struct mem_size_stats *mss) { - struct task_struct *task = m->private; + struct proc_maps_private *priv = m->private; + struct task_struct *task = priv->task; struct vm_area_struct *vma = v; struct mm_struct *mm = vma->vm_mm; struct file *file = vma->vm_file; @@ -153,22 +163,23 @@ static int show_map_internal(struct seq_ pad_len_spaces(m, len); seq_path(m, file->f_vfsmnt, file->f_dentry, "\n"); } else { - if (mm) { - if (vma->vm_start <= mm->start_brk && + const char *name = arch_vma_name(vma); + if (!name) { + if (mm) { + if (vma->vm_start <= mm->start_brk && vma->vm_end >= mm->brk) { - pad_len_spaces(m, len); - seq_puts(m, "[heap]"); - } else { - if (vma->vm_start <= mm->start_stack && - vma->vm_end >= mm->start_stack) { - - pad_len_spaces(m, len); - seq_puts(m, "[stack]"); + name = "[heap]"; + } else if (vma->vm_start <= mm->start_stack && + vma->vm_end >= mm->start_stack) { + name = "[stack]"; } + } else { + name = "[vdso]"; } - } else { + } + if (name) { pad_len_spaces(m, len); - seq_puts(m, "[vdso]"); + seq_puts(m, name); } } seq_putc(m, '\n'); @@ -295,12 +306,16 @@ static int show_smap(struct seq_file *m, static void *m_start(struct seq_file *m, loff_t *pos) { - struct task_struct *task = m->private; + struct proc_maps_private *priv = m->private; unsigned long last_addr = m->version; struct mm_struct *mm; - struct vm_area_struct *vma, *tail_vma; + struct vm_area_struct *vma, *tail_vma = NULL; loff_t l = *pos; + /* Clear the per syscall fields in priv */ + priv->task = NULL; + priv->tail_vma = NULL; + /* * We remember last_addr rather than next_addr to hit with * mmap_cache most of the time. We have zero last_addr at @@ -311,11 +326,15 @@ static void *m_start(struct seq_file *m, if (last_addr == -1UL) return NULL; - mm = get_task_mm(task); + priv->task = get_pid_task(priv->pid, PIDTYPE_PID); + if (!priv->task) + return NULL; + + mm = get_task_mm(priv->task); if (!mm) return NULL; - tail_vma = get_gate_vma(task); + priv->tail_vma = tail_vma = get_gate_vma(priv->task); down_read(&mm->mmap_sem); /* Start with last addr hint */ @@ -350,11 +369,9 @@ out: return tail_vma; } -static void m_stop(struct seq_file *m, void *v) +static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma) { - struct task_struct *task = m->private; - struct vm_area_struct *vma = v; - if (vma && vma != get_gate_vma(task)) { + if (vma && vma != priv->tail_vma) { struct mm_struct *mm = vma->vm_mm; up_read(&mm->mmap_sem); mmput(mm); @@ -363,38 +380,103 @@ static void m_stop(struct seq_file *m, v static void *m_next(struct seq_file *m, void *v, loff_t *pos) { - struct task_struct *task = m->private; + struct proc_maps_private *priv = m->private; struct vm_area_struct *vma = v; - struct vm_area_struct *tail_vma = get_gate_vma(task); + struct vm_area_struct *tail_vma = priv->tail_vma; (*pos)++; if (vma && (vma != tail_vma) && vma->vm_next) return vma->vm_next; - m_stop(m, v); + vma_stop(priv, vma); return (vma != tail_vma)? tail_vma: NULL; } -struct seq_operations proc_pid_maps_op = { +static void m_stop(struct seq_file *m, void *v) +{ + struct proc_maps_private *priv = m->private; + struct vm_area_struct *vma = v; + + vma_stop(priv, vma); + if (priv->task) + put_task_struct(priv->task); +} + +static struct seq_operations proc_pid_maps_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = show_map }; -struct seq_operations proc_pid_smaps_op = { +static struct seq_operations proc_pid_smaps_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = show_smap }; +static int do_maps_open(struct inode *inode, struct file *file, + struct seq_operations *ops) +{ + struct proc_maps_private *priv; + int ret = -ENOMEM; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv) { + priv->pid = proc_pid(inode); + ret = seq_open(file, ops); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = priv; + } else { + kfree(priv); + } + } + return ret; +} + +static int maps_open(struct inode *inode, struct file *file) +{ + return do_maps_open(inode, file, &proc_pid_maps_op); +} + +struct file_operations proc_maps_operations = { + .open = maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + #ifdef CONFIG_NUMA extern int show_numa_map(struct seq_file *m, void *v); -struct seq_operations proc_pid_numa_maps_op = { +static struct seq_operations proc_pid_numa_maps_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = show_numa_map }; + +static int numa_maps_open(struct inode *inode, struct file *file) +{ + return do_maps_open(inode, file, &proc_pid_numa_maps_op); +} + +struct file_operations proc_numa_maps_operations = { + .open = numa_maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; #endif + +static int smaps_open(struct inode *inode, struct file *file) +{ + return do_maps_open(inode, file, &proc_pid_smaps_op); +} + +struct file_operations proc_smaps_operations = { + .open = smaps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 8f68827..4616ed5 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -107,7 +107,7 @@ int proc_exe_link(struct inode *inode, s { struct vm_list_struct *vml; struct vm_area_struct *vma; - struct task_struct *task = proc_task(inode); + struct task_struct *task = get_proc_task(inode); struct mm_struct *mm = get_task_mm(task); int result = -ENOENT; @@ -156,9 +156,28 @@ static void *m_next(struct seq_file *m, { return NULL; } -struct seq_operations proc_pid_maps_op = { +static struct seq_operations proc_pid_maps_op = { .start = m_start, .next = m_next, .stop = m_stop, .show = show_map }; + +static int maps_open(struct inode *inode, struct file *file) +{ + int ret; + ret = seq_open(file, &proc_pid_maps_op); + if (!ret) { + struct seq_file *m = file->private_data; + m->private = NULL; + } + return ret; +} + +struct file_operations proc_maps_operations = { + .open = maps_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 20d4b22..d960507 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -7,7 +7,6 @@ * */ -#include #include #include #include diff --git a/fs/qnx4/bitmap.c b/fs/qnx4/bitmap.c index 46efbf5..8425cf6 100644 --- a/fs/qnx4/bitmap.c +++ b/fs/qnx4/bitmap.c @@ -13,7 +13,6 @@ * 28-06-1998 by Frank Denis : qnx4_free_inode (to be fixed) . */ -#include #include #include #include diff --git a/fs/qnx4/dir.c b/fs/qnx4/dir.c index 9031948..0d7103f 100644 --- a/fs/qnx4/dir.c +++ b/fs/qnx4/dir.c @@ -11,7 +11,6 @@ * 20-06-1998 by Frank Denis : Linux 2.1.99+ & dcache support. */ -#include #include #include #include diff --git a/fs/qnx4/fsync.c b/fs/qnx4/fsync.c index df5bc75..aa3b195 100644 --- a/fs/qnx4/fsync.c +++ b/fs/qnx4/fsync.c @@ -10,7 +10,6 @@ * 24-03-1998 by Richard Frowijn : first release. */ -#include #include #include #include diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 2ecd46f..5a90349 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -12,7 +12,6 @@ * 30-06-1998 by Frank Denis : first step to write inodes. */ -#include #include #include #include @@ -128,7 +127,7 @@ static struct inode *qnx4_alloc_inode(st static void qnx4_destroy_inode(struct inode *inode); static void qnx4_read_inode(struct inode *); static int qnx4_remount(struct super_block *sb, int *flags, char *data); -static int qnx4_statfs(struct super_block *, struct kstatfs *); +static int qnx4_statfs(struct dentry *, struct kstatfs *); static struct super_operations qnx4_sops = { @@ -282,8 +281,10 @@ unsigned long qnx4_block_map( struct ino return block; } -static int qnx4_statfs(struct super_block *sb, struct kstatfs *buf) +static int qnx4_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; + lock_kernel(); buf->f_type = sb->s_magic; @@ -448,7 +449,7 @@ static sector_t qnx4_bmap(struct address { return generic_block_bmap(mapping,block,qnx4_get_block); } -static struct address_space_operations qnx4_aops = { +static const struct address_space_operations qnx4_aops = { .readpage = qnx4_readpage, .writepage = qnx4_writepage, .sync_page = block_sync_page, @@ -561,10 +562,11 @@ static void destroy_inodecache(void) "qnx4_inode_cache: not all structures were freed\n"); } -static struct super_block *qnx4_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int qnx4_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, qnx4_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, qnx4_fill_super, + mnt); } static struct file_system_type qnx4_fs_type = { diff --git a/fs/qnx4/namei.c b/fs/qnx4/namei.c index 4af4951..c3d83f6 100644 --- a/fs/qnx4/namei.c +++ b/fs/qnx4/namei.c @@ -12,7 +12,6 @@ * 04-07-1998 by Frank Denis : first step for rmdir/unlink. */ -#include #include #include #include diff --git a/fs/qnx4/truncate.c b/fs/qnx4/truncate.c index 86563ec..6437c1c 100644 --- a/fs/qnx4/truncate.c +++ b/fs/qnx4/truncate.c @@ -10,7 +10,6 @@ * 30-06-1998 by Frank DENIS : ugly filler. */ -#include #include #include #include diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c index 00a933e..86f14ca 100644 --- a/fs/ramfs/file-mmu.c +++ b/fs/ramfs/file-mmu.c @@ -26,7 +26,7 @@ #include -struct address_space_operations ramfs_aops = { +const struct address_space_operations ramfs_aops = { .readpage = simple_readpage, .prepare_write = simple_prepare_write, .commit_write = simple_commit_write diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index f443a84..99fffc9 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -27,7 +27,7 @@ #include "internal.h" static int ramfs_nommu_setattr(struct dentry *, struct iattr *); -struct address_space_operations ramfs_aops = { +const struct address_space_operations ramfs_aops = { .readpage = simple_readpage, .prepare_write = simple_prepare_write, .commit_write = simple_commit_write diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c index 14bd224..b967733 100644 --- a/fs/ramfs/inode.c +++ b/fs/ramfs/inode.c @@ -185,16 +185,17 @@ static int ramfs_fill_super(struct super return 0; } -struct super_block *ramfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +int ramfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, ramfs_fill_super); + return get_sb_nodev(fs_type, flags, data, ramfs_fill_super, mnt); } -static struct super_block *rootfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int rootfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super); + return get_sb_nodev(fs_type, flags|MS_NOUSER, data, ramfs_fill_super, + mnt); } static struct file_system_type ramfs_fs_type = { diff --git a/fs/ramfs/internal.h b/fs/ramfs/internal.h index 3132376..c2bb58e 100644 --- a/fs/ramfs/internal.h +++ b/fs/ramfs/internal.h @@ -10,6 +10,6 @@ */ -extern struct address_space_operations ramfs_aops; +extern const struct address_space_operations ramfs_aops; extern const struct file_operations ramfs_file_operations; extern struct inode_operations ramfs_file_inode_operations; diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c index 909f71e..4a7dbde 100644 --- a/fs/reiserfs/bitmap.c +++ b/fs/reiserfs/bitmap.c @@ -3,7 +3,6 @@ */ /* Reiserfs block (de)allocator, bitmap-based. */ -#include #include #include #include diff --git a/fs/reiserfs/dir.c b/fs/reiserfs/dir.c index 973c819..9aabcc0 100644 --- a/fs/reiserfs/dir.c +++ b/fs/reiserfs/dir.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include diff --git a/fs/reiserfs/do_balan.c b/fs/reiserfs/do_balan.c index b2264ba..fba304e 100644 --- a/fs/reiserfs/do_balan.c +++ b/fs/reiserfs/do_balan.c @@ -15,7 +15,6 @@ ** **/ -#include #include #include #include diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index cf6e1cf..752cea1 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -1560,12 +1560,6 @@ static ssize_t reiserfs_file_write(struc return res; } -static ssize_t reiserfs_aio_write(struct kiocb *iocb, const char __user * buf, - size_t count, loff_t pos) -{ - return generic_file_aio_write(iocb, buf, count, pos); -} - const struct file_operations reiserfs_file_operations = { .read = generic_file_read, .write = reiserfs_file_write, @@ -1575,7 +1569,7 @@ const struct file_operations reiserfs_fi .fsync = reiserfs_sync_file, .sendfile = generic_file_sendfile, .aio_read = generic_file_aio_read, - .aio_write = reiserfs_aio_write, + .aio_write = generic_file_aio_write, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, }; diff --git a/fs/reiserfs/fix_node.c b/fs/reiserfs/fix_node.c index 5600d3d..6d0e554 100644 --- a/fs/reiserfs/fix_node.c +++ b/fs/reiserfs/fix_node.c @@ -34,7 +34,6 @@ ** **/ -#include #include #include #include diff --git a/fs/reiserfs/ibalance.c b/fs/reiserfs/ibalance.c index 6c5a726..de391a8 100644 --- a/fs/reiserfs/ibalance.c +++ b/fs/reiserfs/ibalance.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 9857e50..12dfdcf 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include @@ -2933,6 +2932,11 @@ int reiserfs_setattr(struct dentry *dent } if (error) goto out; + /* + * file size is changed, ctime and mtime are + * to be updated + */ + attr->ia_valid |= (ATTR_MTIME | ATTR_CTIME); } } @@ -2996,7 +3000,7 @@ int reiserfs_setattr(struct dentry *dent return error; } -struct address_space_operations reiserfs_address_space_operations = { +const struct address_space_operations reiserfs_address_space_operations = { .writepage = reiserfs_writepage, .readpage = reiserfs_readpage, .readpages = reiserfs_readpages, diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 1b73529..9b3672d 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -34,7 +34,6 @@ ** from within kupdate, it will ignore the immediate flag */ -#include #include #include @@ -834,8 +833,7 @@ static int write_ordered_buffers(spinloc get_bh(bh); if (test_set_buffer_locked(bh)) { if (!buffer_dirty(bh)) { - list_del_init(&jh->list); - list_add(&jh->list, &tmp); + list_move(&jh->list, &tmp); goto loop_next; } spin_unlock(lock); @@ -855,8 +853,7 @@ static int write_ordered_buffers(spinloc ret = -EIO; } if (buffer_dirty(bh)) { - list_del_init(&jh->list); - list_add(&jh->list, &tmp); + list_move(&jh->list, &tmp); add_to_chunk(&chunk, bh, lock, write_ordered_chunk); } else { reiserfs_free_jh(bh); diff --git a/fs/reiserfs/lbalance.c b/fs/reiserfs/lbalance.c index 2533c1f..281f806 100644 --- a/fs/reiserfs/lbalance.c +++ b/fs/reiserfs/lbalance.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index 284f785..c61710e 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -11,7 +11,6 @@ * NO WARRANTY */ -#include #include #include #include diff --git a/fs/reiserfs/objectid.c b/fs/reiserfs/objectid.c index f62590a..65feba4 100644 --- a/fs/reiserfs/objectid.c +++ b/fs/reiserfs/objectid.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include diff --git a/fs/reiserfs/prints.c b/fs/reiserfs/prints.c index 27bd3a1..bc808a9 100644 --- a/fs/reiserfs/prints.c +++ b/fs/reiserfs/prints.c @@ -2,7 +2,6 @@ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README */ -#include #include #include #include diff --git a/fs/reiserfs/procfs.c b/fs/reiserfs/procfs.c index 731688e..5d8a8cf 100644 --- a/fs/reiserfs/procfs.c +++ b/fs/reiserfs/procfs.c @@ -10,7 +10,6 @@ /* $Id: procfs.c,v 1.1.8.2 2001/07/15 17:08:42 god Exp $ */ -#include #include #include #include diff --git a/fs/reiserfs/stree.c b/fs/reiserfs/stree.c index d2b25e1..8b9b131 100644 --- a/fs/reiserfs/stree.c +++ b/fs/reiserfs/stree.c @@ -49,7 +49,6 @@ * reiserfs_insert_item */ -#include #include #include #include diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index cae2abb..5567328 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -11,7 +11,6 @@ * NO WARRANTY */ -#include #include #include #include @@ -60,7 +59,7 @@ static int is_any_reiserfs_magic_string( } static int reiserfs_remount(struct super_block *s, int *flags, char *data); -static int reiserfs_statfs(struct super_block *s, struct kstatfs *buf); +static int reiserfs_statfs(struct dentry *dentry, struct kstatfs *buf); static int reiserfs_sync_fs(struct super_block *s, int wait) { @@ -1938,15 +1937,15 @@ #endif return errval; } -static int reiserfs_statfs(struct super_block *s, struct kstatfs *buf) +static int reiserfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct reiserfs_super_block *rs = SB_DISK_SUPER_BLOCK(s); + struct reiserfs_super_block *rs = SB_DISK_SUPER_BLOCK(dentry->d_sb); buf->f_namelen = (REISERFS_MAX_NAME(s->s_blocksize)); buf->f_bfree = sb_free_blocks(rs); buf->f_bavail = buf->f_bfree; buf->f_blocks = sb_block_count(rs) - sb_bmap_nr(rs) - 1; - buf->f_bsize = s->s_blocksize; + buf->f_bsize = dentry->d_sb->s_blocksize; /* changed to accommodate gcc folks. */ buf->f_type = REISERFS_SUPER_MAGIC; return 0; @@ -2204,7 +2203,7 @@ static ssize_t reiserfs_quota_write(stru size_t towrite = len; struct buffer_head tmp_bh, *bh; - mutex_lock(&inode->i_mutex); + mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); while (towrite > 0) { tocopy = sb->s_blocksize - offset < towrite ? sb->s_blocksize - offset : towrite; @@ -2249,11 +2248,12 @@ static ssize_t reiserfs_quota_write(stru #endif -static struct super_block *get_super_block(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int get_super_block(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, reiserfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, reiserfs_fill_super, + mnt); } static int __init init_reiserfs_fs(void) diff --git a/fs/reiserfs/tail_conversion.c b/fs/reiserfs/tail_conversion.c index 196e971..36f108f 100644 --- a/fs/reiserfs/tail_conversion.c +++ b/fs/reiserfs/tail_conversion.c @@ -2,7 +2,6 @@ * Copyright 1999 Hans Reiser, see reiserfs/README for licensing and copyright details */ -#include #include #include #include diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c index ffb79c4..39fedaa 100644 --- a/fs/reiserfs/xattr.c +++ b/fs/reiserfs/xattr.c @@ -452,8 +452,7 @@ static struct page *reiserfs_get_page(st /* We can deadlock if we try to free dentries, and an unlink/rmdir has just occured - GFP_NOFS avoids this */ mapping_set_gfp_mask(mapping, GFP_NOFS); - page = read_cache_page(mapping, n, - (filler_t *) mapping->a_ops->readpage, NULL); + page = read_mapping_page(mapping, n, NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); kmap(page); diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c index 9b9eda7..22eed61 100644 --- a/fs/romfs/inode.c +++ b/fs/romfs/inode.c @@ -179,12 +179,12 @@ outnobh: /* That's simple too. */ static int -romfs_statfs(struct super_block *sb, struct kstatfs *buf) +romfs_statfs(struct dentry *dentry, struct kstatfs *buf) { buf->f_type = ROMFS_MAGIC; buf->f_bsize = ROMBSIZE; buf->f_bfree = buf->f_bavail = buf->f_ffree; - buf->f_blocks = (romfs_maxsize(sb)+ROMBSIZE-1)>>ROMBSBITS; + buf->f_blocks = (romfs_maxsize(dentry->d_sb)+ROMBSIZE-1)>>ROMBSBITS; buf->f_namelen = ROMFS_MAXFN; return 0; } @@ -459,7 +459,7 @@ err_out: /* Mapping from our types to the kernel */ -static struct address_space_operations romfs_aops = { +static const struct address_space_operations romfs_aops = { .readpage = romfs_readpage }; @@ -607,10 +607,11 @@ static struct super_operations romfs_ops .remount_fs = romfs_remount, }; -static struct super_block *romfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int romfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, romfs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, romfs_fill_super, + mnt); } static struct file_system_type romfs_fs_type = { diff --git a/fs/select.c b/fs/select.c index a8109ba..33b72ba 100644 --- a/fs/select.c +++ b/fs/select.c @@ -546,37 +546,38 @@ struct poll_list { #define POLLFD_PER_PAGE ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd)) -static void do_pollfd(unsigned int num, struct pollfd * fdpage, - poll_table ** pwait, int *count) +/* + * Fish for pollable events on the pollfd->fd file descriptor. We're only + * interested in events matching the pollfd->events mask, and the result + * matching that mask is both recorded in pollfd->revents and returned. The + * pwait poll_table will be used by the fd-provided poll handler for waiting, + * if non-NULL. + */ +static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait) { - int i; - - for (i = 0; i < num; i++) { - int fd; - unsigned int mask; - struct pollfd *fdp; - - mask = 0; - fdp = fdpage+i; - fd = fdp->fd; - if (fd >= 0) { - int fput_needed; - struct file * file = fget_light(fd, &fput_needed); - mask = POLLNVAL; - if (file != NULL) { - mask = DEFAULT_POLLMASK; - if (file->f_op && file->f_op->poll) - mask = file->f_op->poll(file, *pwait); - mask &= fdp->events | POLLERR | POLLHUP; - fput_light(file, fput_needed); - } - if (mask) { - *pwait = NULL; - (*count)++; - } + unsigned int mask; + int fd; + + mask = 0; + fd = pollfd->fd; + if (fd >= 0) { + int fput_needed; + struct file * file; + + file = fget_light(fd, &fput_needed); + mask = POLLNVAL; + if (file != NULL) { + mask = DEFAULT_POLLMASK; + if (file->f_op && file->f_op->poll) + mask = file->f_op->poll(file, pwait); + /* Mask out unneeded events. */ + mask &= pollfd->events | POLLERR | POLLHUP; + fput_light(file, fput_needed); } - fdp->revents = mask; } + pollfd->revents = mask; + + return mask; } static int do_poll(unsigned int nfds, struct poll_list *list, @@ -594,11 +595,29 @@ static int do_poll(unsigned int nfds, s long __timeout; set_current_state(TASK_INTERRUPTIBLE); - walk = list; - while(walk != NULL) { - do_pollfd( walk->len, walk->entries, &pt, &count); - walk = walk->next; + for (walk = list; walk != NULL; walk = walk->next) { + struct pollfd * pfd, * pfd_end; + + pfd = walk->entries; + pfd_end = pfd + walk->len; + for (; pfd != pfd_end; pfd++) { + /* + * Fish for events. If we found one, record it + * and kill the poll_table, so we don't + * needlessly register any other waiters after + * this. They'll get immediately deregistered + * when we break out and return. + */ + if (do_pollfd(pfd, pt)) { + count++; + pt = NULL; + } + } } + /* + * All waiters have already been registered, so don't provide + * a poll_table to them on the next loop iteration. + */ pt = NULL; if (count || !*timeout || signal_pending(current)) break; @@ -727,9 +746,9 @@ out_fds: asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds, long timeout_msecs) { - s64 timeout_jiffies = 0; + s64 timeout_jiffies; - if (timeout_msecs) { + if (timeout_msecs > 0) { #if HZ > 1000 /* We can only overflow if HZ > 1000 */ if (timeout_msecs / 1000 > (s64)0x7fffffffffffffffULL / (s64)HZ) @@ -737,6 +756,9 @@ #if HZ > 1000 else #endif timeout_jiffies = msecs_to_jiffies(timeout_msecs); + } else { + /* Infinite (< 0) or no (0) timeout */ + timeout_jiffies = timeout_msecs; } return do_sys_poll(ufds, nfds, &timeout_jiffies); diff --git a/fs/smbfs/file.c b/fs/smbfs/file.c index ed9a24d..dae6704 100644 --- a/fs/smbfs/file.c +++ b/fs/smbfs/file.c @@ -306,7 +306,7 @@ static int smb_commit_write(struct file return status; } -struct address_space_operations smb_file_aops = { +const struct address_space_operations smb_file_aops = { .readpage = smb_readpage, .writepage = smb_writepage, .prepare_write = smb_prepare_write, diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index fdeabc0..a1ed657 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -7,7 +7,6 @@ * Please add a note about your changes to smbfs in the ChangeLog file. */ -#include #include #include #include @@ -48,7 +47,7 @@ #define SMB_TTL_DEFAULT 1000 static void smb_delete_inode(struct inode *); static void smb_put_super(struct super_block *); -static int smb_statfs(struct super_block *, struct kstatfs *); +static int smb_statfs(struct dentry *, struct kstatfs *); static int smb_show_options(struct seq_file *, struct vfsmount *); static kmem_cache_t *smb_inode_cachep; @@ -641,13 +640,13 @@ out_no_server: } static int -smb_statfs(struct super_block *sb, struct kstatfs *buf) +smb_statfs(struct dentry *dentry, struct kstatfs *buf) { int result; lock_kernel(); - result = smb_proc_dskattr(sb, buf); + result = smb_proc_dskattr(dentry, buf); unlock_kernel(); @@ -782,10 +781,10 @@ out: return error; } -static struct super_block *smb_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int smb_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, smb_fill_super); + return get_sb_nodev(fs_type, flags, data, smb_fill_super, mnt); } static struct file_system_type smb_fs_type = { diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c index b1b878b..c349505 100644 --- a/fs/smbfs/proc.c +++ b/fs/smbfs/proc.c @@ -3226,9 +3226,9 @@ smb_proc_settime(struct dentry *dentry, } int -smb_proc_dskattr(struct super_block *sb, struct kstatfs *attr) +smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr) { - struct smb_sb_info *server = SMB_SB(sb); + struct smb_sb_info *server = SMB_SB(dentry->d_sb); int result; char *p; long unit; diff --git a/fs/smbfs/proto.h b/fs/smbfs/proto.h index 4766459..34fb462 100644 --- a/fs/smbfs/proto.h +++ b/fs/smbfs/proto.h @@ -29,7 +29,7 @@ extern int smb_proc_getattr(struct dentr extern int smb_proc_setattr(struct dentry *dir, struct smb_fattr *fattr); extern int smb_proc_setattr_unix(struct dentry *d, struct iattr *attr, unsigned int major, unsigned int minor); extern int smb_proc_settime(struct dentry *dentry, struct smb_fattr *fattr); -extern int smb_proc_dskattr(struct super_block *sb, struct kstatfs *attr); +extern int smb_proc_dskattr(struct dentry *dentry, struct kstatfs *attr); extern int smb_proc_read_link(struct smb_sb_info *server, struct dentry *d, char *buffer, int len); extern int smb_proc_symlink(struct smb_sb_info *server, struct dentry *d, const char *oldpath); extern int smb_proc_link(struct smb_sb_info *server, struct dentry *dentry, struct dentry *new_dentry); @@ -63,7 +63,7 @@ extern int smb_revalidate_inode(struct d extern int smb_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); extern int smb_notify_change(struct dentry *dentry, struct iattr *attr); /* file.c */ -extern struct address_space_operations smb_file_aops; +extern const struct address_space_operations smb_file_aops; extern const struct file_operations smb_file_operations; extern struct inode_operations smb_file_inode_operations; /* ioctl.c */ diff --git a/fs/smbfs/request.c b/fs/smbfs/request.c index c71dd27..c8e9619 100644 --- a/fs/smbfs/request.c +++ b/fs/smbfs/request.c @@ -400,8 +400,7 @@ static int smb_request_send_req(struct s if (!(req->rq_flags & SMB_REQ_TRANSMITTED)) goto out; - list_del_init(&req->rq_queue); - list_add_tail(&req->rq_queue, &server->recvq); + list_move_tail(&req->rq_queue, &server->recvq); result = 1; out: return result; @@ -435,8 +434,7 @@ int smb_request_send_server(struct smb_s result = smb_request_send_req(req); if (result < 0) { server->conn_error = result; - list_del_init(&req->rq_queue); - list_add(&req->rq_queue, &server->xmitq); + list_move(&req->rq_queue, &server->xmitq); result = -EIO; goto out; } diff --git a/fs/smbfs/smbiod.c b/fs/smbfs/smbiod.c index 481a97a..e675404 100644 --- a/fs/smbfs/smbiod.c +++ b/fs/smbfs/smbiod.c @@ -5,7 +5,6 @@ * Copyright (C) 2001, Urban Widmark */ -#include #include #include @@ -20,6 +19,7 @@ #include #include #include #include +#include #include #include @@ -40,7 +40,7 @@ enum smbiod_state { }; static enum smbiod_state smbiod_state = SMBIOD_DEAD; -static pid_t smbiod_pid; +static struct task_struct *smbiod_thread; static DECLARE_WAIT_QUEUE_HEAD(smbiod_wait); static LIST_HEAD(smb_servers); static DEFINE_SPINLOCK(servers_lock); @@ -67,20 +67,29 @@ void smbiod_wake_up(void) */ static int smbiod_start(void) { - pid_t pid; + struct task_struct *tsk; + int err = 0; + if (smbiod_state != SMBIOD_DEAD) return 0; smbiod_state = SMBIOD_STARTING; __module_get(THIS_MODULE); spin_unlock(&servers_lock); - pid = kernel_thread(smbiod, NULL, 0); - if (pid < 0) + tsk = kthread_run(smbiod, NULL, "smbiod"); + if (IS_ERR(tsk)) { + err = PTR_ERR(tsk); module_put(THIS_MODULE); + } spin_lock(&servers_lock); - smbiod_state = pid < 0 ? SMBIOD_DEAD : SMBIOD_RUNNING; - smbiod_pid = pid; - return pid; + if (err < 0) { + smbiod_state = SMBIOD_DEAD; + smbiod_thread = NULL; + } else { + smbiod_state = SMBIOD_RUNNING; + smbiod_thread = tsk; + } + return err; } /* @@ -183,8 +192,7 @@ #if 0 if (req->rq_flags & SMB_REQ_RETRY) { /* must move the request to the xmitq */ VERBOSE("retrying request %p on recvq\n", req); - list_del(&req->rq_queue); - list_add(&req->rq_queue, &server->xmitq); + list_move(&req->rq_queue, &server->xmitq); continue; } #endif @@ -290,8 +298,6 @@ out: */ static int smbiod(void *unused) { - daemonize("smbiod"); - allow_signal(SIGKILL); VERBOSE("SMB Kernel thread starting (%d) ...\n", current->pid); diff --git a/fs/splice.c b/fs/splice.c index a285fd7..05fd278 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -55,31 +55,43 @@ static int page_cache_pipe_buf_steal(str struct pipe_buffer *buf) { struct page *page = buf->page; - struct address_space *mapping = page_mapping(page); + struct address_space *mapping; lock_page(page); - WARN_ON(!PageUptodate(page)); + mapping = page_mapping(page); + if (mapping) { + WARN_ON(!PageUptodate(page)); - /* - * At least for ext2 with nobh option, we need to wait on writeback - * completing on this page, since we'll remove it from the pagecache. - * Otherwise truncate wont wait on the page, allowing the disk - * blocks to be reused by someone else before we actually wrote our - * data to them. fs corruption ensues. - */ - wait_on_page_writeback(page); + /* + * At least for ext2 with nobh option, we need to wait on + * writeback completing on this page, since we'll remove it + * from the pagecache. Otherwise truncate wont wait on the + * page, allowing the disk blocks to be reused by someone else + * before we actually wrote our data to them. fs corruption + * ensues. + */ + wait_on_page_writeback(page); - if (PagePrivate(page)) - try_to_release_page(page, mapping_gfp_mask(mapping)); + if (PagePrivate(page)) + try_to_release_page(page, mapping_gfp_mask(mapping)); - if (!remove_mapping(mapping, page)) { - unlock_page(page); - return 1; + /* + * If we succeeded in removing the mapping, set LRU flag + * and return good. + */ + if (remove_mapping(mapping, page)) { + buf->flags |= PIPE_BUF_FLAG_LRU; + return 0; + } } - buf->flags |= PIPE_BUF_FLAG_LRU; - return 0; + /* + * Raced with truncate or failed to remove page from current + * address space, unlock and return failure. + */ + unlock_page(page); + return 1; } static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, diff --git a/fs/stat.c b/fs/stat.c index 0f282fa..3a44dcf 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include #include #include #include diff --git a/fs/super.c b/fs/super.c index a66f66b..6d4e817 100644 --- a/fs/super.c +++ b/fs/super.c @@ -20,7 +20,6 @@ * Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000 */ -#include #include #include #include @@ -54,7 +53,7 @@ DEFINE_SPINLOCK(sb_lock); * Allocates and initializes a new &struct super_block. alloc_super() * returns a pointer new superblock or %NULL if allocation had failed. */ -static struct super_block *alloc_super(void) +static struct super_block *alloc_super(struct file_system_type *type) { struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER); static struct super_operations default_op; @@ -73,6 +72,13 @@ static struct super_block *alloc_super(v INIT_LIST_HEAD(&s->s_inodes); init_rwsem(&s->s_umount); mutex_init(&s->s_lock); + lockdep_set_class(&s->s_umount, &type->s_umount_key); + /* + * The locking rules for s_lock are up to the + * filesystem. For example ext3fs has different + * lock ordering than usbfs: + */ + lockdep_set_class(&s->s_lock, &type->s_lock_key); down_write(&s->s_umount); s->s_count = S_BIAS; atomic_set(&s->s_active, 1); @@ -231,7 +237,7 @@ void generic_shutdown_super(struct super if (root) { sb->s_root = NULL; shrink_dcache_parent(root); - shrink_dcache_anon(&sb->s_anon); + shrink_dcache_sb(sb); dput(root); fsync_super(sb); lock_super(sb); @@ -296,7 +302,7 @@ retry: } if (!s) { spin_unlock(&sb_lock); - s = alloc_super(); + s = alloc_super(type); if (!s) return ERR_PTR(-ENOMEM); goto retry; @@ -486,7 +492,7 @@ asmlinkage long sys_ustat(unsigned dev, s = user_get_super(new_decode_dev(dev)); if (s == NULL) goto out; - err = vfs_statfs(s, &sbuf); + err = vfs_statfs(s->s_root, &sbuf); drop_super(s); if (err) goto out; @@ -676,9 +682,10 @@ static void bdev_uevent(struct block_dev } } -struct super_block *get_sb_bdev(struct file_system_type *fs_type, +int get_sb_bdev(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, - int (*fill_super)(struct super_block *, void *, int)) + int (*fill_super)(struct super_block *, void *, int), + struct vfsmount *mnt) { struct block_device *bdev; struct super_block *s; @@ -686,7 +693,7 @@ struct super_block *get_sb_bdev(struct f bdev = open_bdev_excl(dev_name, flags, fs_type); if (IS_ERR(bdev)) - return (struct super_block *)bdev; + return PTR_ERR(bdev); /* * once the super is inserted into the list by sget, s_umount @@ -697,15 +704,17 @@ struct super_block *get_sb_bdev(struct f s = sget(fs_type, test_bdev_super, set_bdev_super, bdev); mutex_unlock(&bdev->bd_mount_mutex); if (IS_ERR(s)) - goto out; + goto error_s; if (s->s_root) { if ((flags ^ s->s_flags) & MS_RDONLY) { up_write(&s->s_umount); deactivate_super(s); - s = ERR_PTR(-EBUSY); + error = -EBUSY; + goto error_bdev; } - goto out; + + close_bdev_excl(bdev); } else { char b[BDEVNAME_SIZE]; @@ -716,18 +725,21 @@ struct super_block *get_sb_bdev(struct f if (error) { up_write(&s->s_umount); deactivate_super(s); - s = ERR_PTR(error); - } else { - s->s_flags |= MS_ACTIVE; - bdev_uevent(bdev, KOBJ_MOUNT); + goto error; } + + s->s_flags |= MS_ACTIVE; + bdev_uevent(bdev, KOBJ_MOUNT); } - return s; + return simple_set_mnt(mnt, s); -out: +error_s: + error = PTR_ERR(s); +error_bdev: close_bdev_excl(bdev); - return s; +error: + return error; } EXPORT_SYMBOL(get_sb_bdev); @@ -744,15 +756,16 @@ void kill_block_super(struct super_block EXPORT_SYMBOL(kill_block_super); -struct super_block *get_sb_nodev(struct file_system_type *fs_type, +int get_sb_nodev(struct file_system_type *fs_type, int flags, void *data, - int (*fill_super)(struct super_block *, void *, int)) + int (*fill_super)(struct super_block *, void *, int), + struct vfsmount *mnt) { int error; struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL); if (IS_ERR(s)) - return s; + return PTR_ERR(s); s->s_flags = flags; @@ -760,10 +773,10 @@ struct super_block *get_sb_nodev(struct if (error) { up_write(&s->s_umount); deactivate_super(s); - return ERR_PTR(error); + return error; } s->s_flags |= MS_ACTIVE; - return s; + return simple_set_mnt(mnt, s); } EXPORT_SYMBOL(get_sb_nodev); @@ -773,94 +786,100 @@ static int compare_single(struct super_b return 1; } -struct super_block *get_sb_single(struct file_system_type *fs_type, +int get_sb_single(struct file_system_type *fs_type, int flags, void *data, - int (*fill_super)(struct super_block *, void *, int)) + int (*fill_super)(struct super_block *, void *, int), + struct vfsmount *mnt) { struct super_block *s; int error; s = sget(fs_type, compare_single, set_anon_super, NULL); if (IS_ERR(s)) - return s; + return PTR_ERR(s); if (!s->s_root) { s->s_flags = flags; error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); if (error) { up_write(&s->s_umount); deactivate_super(s); - return ERR_PTR(error); + return error; } s->s_flags |= MS_ACTIVE; } do_remount_sb(s, flags, data, 0); - return s; + return simple_set_mnt(mnt, s); } EXPORT_SYMBOL(get_sb_single); struct vfsmount * -do_kern_mount(const char *fstype, int flags, const char *name, void *data) +vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data) { - struct file_system_type *type = get_fs_type(fstype); - struct super_block *sb = ERR_PTR(-ENOMEM); struct vfsmount *mnt; - int error; char *secdata = NULL; + int error; if (!type) return ERR_PTR(-ENODEV); + error = -ENOMEM; mnt = alloc_vfsmnt(name); if (!mnt) goto out; if (data) { secdata = alloc_secdata(); - if (!secdata) { - sb = ERR_PTR(-ENOMEM); + if (!secdata) goto out_mnt; - } error = security_sb_copy_data(type, data, secdata); - if (error) { - sb = ERR_PTR(error); + if (error) goto out_free_secdata; - } } - sb = type->get_sb(type, flags, name, data); - if (IS_ERR(sb)) + error = type->get_sb(type, flags, name, data, mnt); + if (error < 0) goto out_free_secdata; - error = security_sb_kern_mount(sb, secdata); + + error = security_sb_kern_mount(mnt->mnt_sb, secdata); if (error) goto out_sb; - mnt->mnt_sb = sb; - mnt->mnt_root = dget(sb->s_root); - mnt->mnt_mountpoint = sb->s_root; + + mnt->mnt_mountpoint = mnt->mnt_root; mnt->mnt_parent = mnt; - up_write(&sb->s_umount); + up_write(&mnt->mnt_sb->s_umount); free_secdata(secdata); - put_filesystem(type); return mnt; out_sb: - up_write(&sb->s_umount); - deactivate_super(sb); - sb = ERR_PTR(error); + dput(mnt->mnt_root); + up_write(&mnt->mnt_sb->s_umount); + deactivate_super(mnt->mnt_sb); out_free_secdata: free_secdata(secdata); out_mnt: free_vfsmnt(mnt); out: - put_filesystem(type); - return (struct vfsmount *)sb; + return ERR_PTR(error); } -EXPORT_SYMBOL_GPL(do_kern_mount); +EXPORT_SYMBOL_GPL(vfs_kern_mount); + +struct vfsmount * +do_kern_mount(const char *fstype, int flags, const char *name, void *data) +{ + struct file_system_type *type = get_fs_type(fstype); + struct vfsmount *mnt; + if (!type) + return ERR_PTR(-ENODEV); + mnt = vfs_kern_mount(type, flags, name, data); + put_filesystem(type); + return mnt; +} struct vfsmount *kern_mount(struct file_system_type *type) { - return do_kern_mount(type->name, 0, type->name, NULL); + return vfs_kern_mount(type, 0, type->name, NULL); } EXPORT_SYMBOL(kern_mount); diff --git a/fs/sync.c b/fs/sync.c index aab5ffe..955aef0 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -100,7 +100,7 @@ asmlinkage long sys_sync_file_range(int } if (nbytes == 0) - endbyte = -1; + endbyte = LLONG_MAX; else endbyte--; /* inclusive */ diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 610b5bd..61c4243 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -430,10 +430,9 @@ static int sysfs_readdir(struct file * f i++; /* fallthrough */ default: - if (filp->f_pos == 2) { - list_del(q); - list_add(q, &parent_sd->s_children); - } + if (filp->f_pos == 2) + list_move(q, &parent_sd->s_children); + for (p=q->next; p!= &parent_sd->s_children; p=p->next) { struct sysfs_dirent *next; const char * name; @@ -455,8 +454,7 @@ static int sysfs_readdir(struct file * f dt_type(next)) < 0) return 0; - list_del(q); - list_add(q, p); + list_move(q, p); p = q; filp->f_pos++; } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index f0b347b..5e0e31c 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -16,7 +16,7 @@ #include "sysfs.h" extern struct super_block * sysfs_sb; -static struct address_space_operations sysfs_aops = { +static const struct address_space_operations sysfs_aops = { .readpage = simple_readpage, .prepare_write = simple_prepare_write, .commit_write = simple_commit_write diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index f1117e8..40190c4 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -66,10 +66,10 @@ static int sysfs_fill_super(struct super return 0; } -static struct super_block *sysfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int sysfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, sysfs_fill_super); + return get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt); } static struct file_system_type sysfs_fs_type = { diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index d707434..f2bef96 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -53,8 +53,7 @@ static int dir_commit_chunk(struct page static struct page * dir_get_page(struct inode *dir, unsigned long n) { struct address_space *mapping = dir->i_mapping; - struct page *page = read_cache_page(mapping, n, - (filler_t*)mapping->a_ops->readpage, NULL); + struct page *page = read_mapping_page(mapping, n, NULL); if (!IS_ERR(page)) { wait_on_page_locked(page); kmap(page); diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 3ff89cc..58b2d22 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -85,8 +85,9 @@ static void sysv_put_super(struct super_ kfree(sbi); } -static int sysv_statfs(struct super_block *sb, struct kstatfs *buf) +static int sysv_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; struct sysv_sb_info *sbi = SYSV_SB(sb); buf->f_type = sb->s_magic; diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c index 86f5f8d..f2bcccd 100644 --- a/fs/sysv/itree.c +++ b/fs/sysv/itree.c @@ -465,7 +465,7 @@ static sector_t sysv_bmap(struct address { return generic_block_bmap(mapping,block,get_block); } -struct address_space_operations sysv_aops = { +const struct address_space_operations sysv_aops = { .readpage = sysv_readpage, .writepage = sysv_writepage, .sync_page = block_sync_page, diff --git a/fs/sysv/super.c b/fs/sysv/super.c index e92b991..876639b 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -506,16 +506,17 @@ failed: /* Every kernel module contains stuff like this. */ -static struct super_block *sysv_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int sysv_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, sysv_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, sysv_fill_super, + mnt); } -static struct super_block *v7_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int v7_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, v7_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, v7_fill_super, mnt); } static struct file_system_type sysv_fs_type = { diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index 393a480..9dcc821 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -161,7 +161,7 @@ extern struct inode_operations sysv_dir_ extern struct inode_operations sysv_fast_symlink_inode_operations; extern const struct file_operations sysv_file_operations; extern const struct file_operations sysv_dir_operations; -extern struct address_space_operations sysv_aops; +extern const struct address_space_operations sysv_aops; extern struct super_operations sysv_sops; extern struct dentry_operations sysv_dentry_operations; diff --git a/fs/udf/file.c b/fs/udf/file.c index e34b00e..a59e5f3 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -95,7 +95,7 @@ static int udf_adinicb_commit_write(stru return 0; } -struct address_space_operations udf_adinicb_aops = { +const struct address_space_operations udf_adinicb_aops = { .readpage = udf_adinicb_readpage, .writepage = udf_adinicb_writepage, .sync_page = block_sync_page, diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 2983afd..605f511 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -132,7 +132,7 @@ static sector_t udf_bmap(struct address_ return generic_block_bmap(mapping,block,udf_get_block); } -struct address_space_operations udf_aops = { +const struct address_space_operations udf_aops = { .readpage = udf_readpage, .writepage = udf_writepage, .sync_page = block_sync_page, diff --git a/fs/udf/super.c b/fs/udf/super.c index e45789f..4df822c 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -40,7 +40,6 @@ #include "udfdecl.h" -#include #include #include #include @@ -91,13 +90,13 @@ static void udf_load_partdesc(struct sup static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); -static int udf_statfs(struct super_block *, struct kstatfs *); +static int udf_statfs(struct dentry *, struct kstatfs *); /* UDF filesystem type */ -static struct super_block *udf_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int udf_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, udf_fill_super, mnt); } static struct file_system_type udf_fstype = { @@ -1779,8 +1778,10 @@ #endif * Written, tested, and released. */ static int -udf_statfs(struct super_block *sb, struct kstatfs *buf) +udf_statfs(struct dentry *dentry, struct kstatfs *buf) { + struct super_block *sb = dentry->d_sb; + buf->f_type = UDF_SUPER_MAGIC; buf->f_bsize = sb->s_blocksize; buf->f_blocks = UDF_SB_PARTLEN(sb, UDF_SB_PARTITION(sb)); diff --git a/fs/udf/symlink.c b/fs/udf/symlink.c index 674bb40..ba068a7 100644 --- a/fs/udf/symlink.c +++ b/fs/udf/symlink.c @@ -113,6 +113,6 @@ out: /* * symlinks can't do much... */ -struct address_space_operations udf_symlink_aops = { +const struct address_space_operations udf_symlink_aops = { .readpage = udf_symlink_filler, }; diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 023e19b..1033b7c 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -6,7 +6,6 @@ #include "ecma_167.h" #include "osta_udf.h" #include -#include #include #include #include @@ -47,9 +46,9 @@ extern struct inode_operations udf_dir_i extern const struct file_operations udf_dir_operations; extern struct inode_operations udf_file_inode_operations; extern const struct file_operations udf_file_operations; -extern struct address_space_operations udf_aops; -extern struct address_space_operations udf_adinicb_aops; -extern struct address_space_operations udf_symlink_aops; +extern const struct address_space_operations udf_aops; +extern const struct address_space_operations udf_adinicb_aops; +extern const struct address_space_operations udf_symlink_aops; struct udf_fileident_bh { diff --git a/fs/ufs/balloc.c b/fs/ufs/balloc.c index 3ada9dc..b01804b 100644 --- a/fs/ufs/balloc.c +++ b/fs/ufs/balloc.c @@ -21,14 +21,6 @@ #include #include "swab.h" #include "util.h" -#undef UFS_BALLOC_DEBUG - -#ifdef UFS_BALLOC_DEBUG -#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - static unsigned ufs_add_fragments (struct inode *, unsigned, unsigned, unsigned, int *); static unsigned ufs_alloc_fragments (struct inode *, unsigned, unsigned, unsigned, int *); static unsigned ufs_alloccg_block (struct inode *, struct ufs_cg_private_info *, unsigned, int *); @@ -39,7 +31,8 @@ static void ufs_clusteracct(struct super /* * Free 'count' fragments from fragment number 'fragment' */ -void ufs_free_fragments (struct inode * inode, unsigned fragment, unsigned count) { +void ufs_free_fragments(struct inode *inode, unsigned fragment, unsigned count) +{ struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; @@ -51,7 +44,7 @@ void ufs_free_fragments (struct inode * uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first(uspi); - UFSD(("ENTER, fragment %u, count %u\n", fragment, count)) + UFSD("ENTER, fragment %u, count %u\n", fragment, count); if (ufs_fragnum(fragment) + count > uspi->s_fpg) ufs_error (sb, "ufs_free_fragments", "internal error"); @@ -68,7 +61,7 @@ void ufs_free_fragments (struct inode * ucpi = ufs_load_cylinder (sb, cgno); if (!ucpi) goto failed; - ucg = ubh_get_ucg (UCPI_UBH); + ucg = ubh_get_ucg (UCPI_UBH(ucpi)); if (!ufs_cg_chkmagic(sb, ucg)) { ufs_panic (sb, "ufs_free_fragments", "internal error, bad magic number on cg %u", cgno); goto failed; @@ -76,11 +69,11 @@ void ufs_free_fragments (struct inode * end_bit = bit + count; bbase = ufs_blknum (bit); - blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase); + blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase); ufs_fragacct (sb, blkmap, ucg->cg_frsum, -1); for (i = bit; i < end_bit; i++) { - if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, i)) - ubh_setbit (UCPI_UBH, ucpi->c_freeoff, i); + if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, i)) + ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, i); else ufs_error (sb, "ufs_free_fragments", "bit already cleared for fragment %u", i); @@ -90,51 +83,52 @@ void ufs_free_fragments (struct inode * fs32_add(sb, &ucg->cg_cs.cs_nffree, count); - fs32_add(sb, &usb1->fs_cstotal.cs_nffree, count); + uspi->cs_total.cs_nffree += count; fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count); - blkmap = ubh_blkmap (UCPI_UBH, ucpi->c_freeoff, bbase); + blkmap = ubh_blkmap (UCPI_UBH(ucpi), ucpi->c_freeoff, bbase); ufs_fragacct(sb, blkmap, ucg->cg_frsum, 1); /* * Trying to reassemble free fragments into block */ blkno = ufs_fragstoblks (bbase); - if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) { + if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { fs32_sub(sb, &ucg->cg_cs.cs_nffree, uspi->s_fpb); - fs32_sub(sb, &usb1->fs_cstotal.cs_nffree, uspi->s_fpb); + uspi->cs_total.cs_nffree -= uspi->s_fpb; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, uspi->s_fpb); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, 1); fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1); - fs32_add(sb, &usb1->fs_cstotal.cs_nbfree, 1); + uspi->cs_total.cs_nbfree++; fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1); cylno = ufs_cbtocylno (bbase); fs16_add(sb, &ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(bbase)), 1); fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1); } - ubh_mark_buffer_dirty (USPI_UBH); - ubh_mark_buffer_dirty (UCPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); if (sb->s_flags & MS_SYNCHRONOUS) { - ubh_ll_rw_block (SWRITE, 1, (struct ufs_buffer_head **)&ucpi); - ubh_wait_on_buffer (UCPI_UBH); + ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi)); + ubh_wait_on_buffer (UCPI_UBH(ucpi)); } sb->s_dirt = 1; unlock_super (sb); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return; failed: unlock_super (sb); - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return; } /* * Free 'count' fragments from fragment number 'fragment' (free whole blocks) */ -void ufs_free_blocks (struct inode * inode, unsigned fragment, unsigned count) { +void ufs_free_blocks(struct inode *inode, unsigned fragment, unsigned count) +{ struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; @@ -146,7 +140,7 @@ void ufs_free_blocks (struct inode * ino uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first(uspi); - UFSD(("ENTER, fragment %u, count %u\n", fragment, count)) + UFSD("ENTER, fragment %u, count %u\n", fragment, count); if ((fragment & uspi->s_fpbmask) || (count & uspi->s_fpbmask)) { ufs_error (sb, "ufs_free_blocks", "internal error, " @@ -162,7 +156,7 @@ do_more: bit = ufs_dtogd (fragment); if (cgno >= uspi->s_ncg) { ufs_panic (sb, "ufs_free_blocks", "freeing blocks are outside device"); - goto failed; + goto failed_unlock; } end_bit = bit + count; if (end_bit > uspi->s_fpg) { @@ -173,36 +167,36 @@ do_more: ucpi = ufs_load_cylinder (sb, cgno); if (!ucpi) - goto failed; - ucg = ubh_get_ucg (UCPI_UBH); + goto failed_unlock; + ucg = ubh_get_ucg (UCPI_UBH(ucpi)); if (!ufs_cg_chkmagic(sb, ucg)) { ufs_panic (sb, "ufs_free_blocks", "internal error, bad magic number on cg %u", cgno); - goto failed; + goto failed_unlock; } for (i = bit; i < end_bit; i += uspi->s_fpb) { blkno = ufs_fragstoblks(i); - if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, blkno)) { + if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno)) { ufs_error(sb, "ufs_free_blocks", "freeing free fragment"); } - ubh_setblock(UCPI_UBH, ucpi->c_freeoff, blkno); + ubh_setblock(UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, 1); DQUOT_FREE_BLOCK(inode, uspi->s_fpb); fs32_add(sb, &ucg->cg_cs.cs_nbfree, 1); - fs32_add(sb, &usb1->fs_cstotal.cs_nbfree, 1); + uspi->cs_total.cs_nbfree++; fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nbfree, 1); cylno = ufs_cbtocylno(i); fs16_add(sb, &ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(i)), 1); fs32_add(sb, &ubh_cg_blktot(ucpi, cylno), 1); } - ubh_mark_buffer_dirty (USPI_UBH); - ubh_mark_buffer_dirty (UCPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); if (sb->s_flags & MS_SYNCHRONOUS) { - ubh_ll_rw_block (SWRITE, 1, (struct ufs_buffer_head **)&ucpi); - ubh_wait_on_buffer (UCPI_UBH); + ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi)); + ubh_wait_on_buffer (UCPI_UBH(ucpi)); } if (overflow) { @@ -213,38 +207,83 @@ do_more: sb->s_dirt = 1; unlock_super (sb); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return; -failed: +failed_unlock: unlock_super (sb); - UFSD(("EXIT (FAILED)\n")) +failed: + UFSD("EXIT (FAILED)\n"); return; } +/* + * Modify inode page cache in such way: + * have - blocks with b_blocknr equal to oldb...oldb+count-1 + * get - blocks with b_blocknr equal to newb...newb+count-1 + * also we suppose that oldb...oldb+count-1 blocks + * situated at the end of file. + * + * We can come here from ufs_writepage or ufs_prepare_write, + * locked_page is argument of these functions, so we already lock it. + */ +static void ufs_change_blocknr(struct inode *inode, unsigned int baseblk, + unsigned int count, unsigned int oldb, + unsigned int newb, struct page *locked_page) +{ + unsigned int blk_per_page = 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits); + struct address_space *mapping = inode->i_mapping; + pgoff_t index, cur_index = locked_page->index; + unsigned int i, j; + struct page *page; + struct buffer_head *head, *bh; + + UFSD("ENTER, ino %lu, count %u, oldb %u, newb %u\n", + inode->i_ino, count, oldb, newb); + + BUG_ON(!PageLocked(locked_page)); + + for (i = 0; i < count; i += blk_per_page) { + index = (baseblk+i) >> (PAGE_CACHE_SHIFT - inode->i_blkbits); + + if (likely(cur_index != index)) { + page = ufs_get_locked_page(mapping, index); + if (IS_ERR(page)) + continue; + } else + page = locked_page; + + j = i; + head = page_buffers(page); + bh = head; + do { + if (likely(bh->b_blocknr == j + oldb && j < count)) { + unmap_underlying_metadata(bh->b_bdev, + bh->b_blocknr); + bh->b_blocknr = newb + j++; + mark_buffer_dirty(bh); + } + bh = bh->b_this_page; + } while (bh != head); -#define NULLIFY_FRAGMENTS \ - for (i = oldcount; i < newcount; i++) { \ - bh = sb_getblk(sb, result + i); \ - memset (bh->b_data, 0, sb->s_blocksize); \ - set_buffer_uptodate(bh); \ - mark_buffer_dirty (bh); \ - if (IS_SYNC(inode)) \ - sync_dirty_buffer(bh); \ - brelse (bh); \ - } + set_page_dirty(page); + + if (likely(cur_index != index)) + ufs_put_locked_page(page); + } + UFSD("EXIT\n"); +} -unsigned ufs_new_fragments (struct inode * inode, __fs32 * p, unsigned fragment, - unsigned goal, unsigned count, int * err ) +unsigned ufs_new_fragments(struct inode * inode, __fs32 * p, unsigned fragment, + unsigned goal, unsigned count, int * err, struct page *locked_page) { struct super_block * sb; struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; - struct buffer_head * bh; - unsigned cgno, oldcount, newcount, tmp, request, i, result; + unsigned cgno, oldcount, newcount, tmp, request, result; - UFSD(("ENTER, ino %lu, fragment %u, goal %u, count %u\n", inode->i_ino, fragment, goal, count)) + UFSD("ENTER, ino %lu, fragment %u, goal %u, count %u\n", inode->i_ino, fragment, goal, count); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -273,14 +312,14 @@ unsigned ufs_new_fragments (struct inode return (unsigned)-1; } if (fragment < UFS_I(inode)->i_lastfrag) { - UFSD(("EXIT (ALREADY ALLOCATED)\n")) + UFSD("EXIT (ALREADY ALLOCATED)\n"); unlock_super (sb); return 0; } } else { if (tmp) { - UFSD(("EXIT (ALREADY ALLOCATED)\n")) + UFSD("EXIT (ALREADY ALLOCATED)\n"); unlock_super(sb); return 0; } @@ -289,9 +328,9 @@ unsigned ufs_new_fragments (struct inode /* * There is not enough space for user on the device */ - if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(usb1, UFS_MINFREE) <= 0) { + if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(uspi, UFS_MINFREE) <= 0) { unlock_super (sb); - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return 0; } @@ -310,12 +349,10 @@ unsigned ufs_new_fragments (struct inode if (result) { *p = cpu_to_fs32(sb, result); *err = 0; - inode->i_blocks += count << uspi->s_nspfshift; UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); - NULLIFY_FRAGMENTS } unlock_super(sb); - UFSD(("EXIT, result %u\n", result)) + UFSD("EXIT, result %u\n", result); return result; } @@ -325,11 +362,9 @@ unsigned ufs_new_fragments (struct inode result = ufs_add_fragments (inode, tmp, oldcount, newcount, err); if (result) { *err = 0; - inode->i_blocks += count << uspi->s_nspfshift; UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); - NULLIFY_FRAGMENTS unlock_super(sb); - UFSD(("EXIT, result %u\n", result)) + UFSD("EXIT, result %u\n", result); return result; } @@ -339,8 +374,8 @@ unsigned ufs_new_fragments (struct inode switch (fs32_to_cpu(sb, usb1->fs_optim)) { case UFS_OPTSPACE: request = newcount; - if (uspi->s_minfree < 5 || fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree) - > uspi->s_dsize * uspi->s_minfree / (2 * 100) ) + if (uspi->s_minfree < 5 || uspi->cs_total.cs_nffree + > uspi->s_dsize * uspi->s_minfree / (2 * 100)) break; usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME); break; @@ -349,7 +384,7 @@ unsigned ufs_new_fragments (struct inode case UFS_OPTTIME: request = uspi->s_fpb; - if (fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree) < uspi->s_dsize * + if (uspi->cs_total.cs_nffree < uspi->s_dsize * (uspi->s_minfree - 2) / 100) break; usb1->fs_optim = cpu_to_fs32(sb, UFS_OPTTIME); @@ -357,39 +392,22 @@ unsigned ufs_new_fragments (struct inode } result = ufs_alloc_fragments (inode, cgno, goal, request, err); if (result) { - for (i = 0; i < oldcount; i++) { - bh = sb_bread(sb, tmp + i); - if(bh) - { - clear_buffer_dirty(bh); - bh->b_blocknr = result + i; - mark_buffer_dirty (bh); - if (IS_SYNC(inode)) - sync_dirty_buffer(bh); - brelse (bh); - } - else - { - printk(KERN_ERR "ufs_new_fragments: bread fail\n"); - unlock_super(sb); - return 0; - } - } + ufs_change_blocknr(inode, fragment - oldcount, oldcount, tmp, + result, locked_page); + *p = cpu_to_fs32(sb, result); *err = 0; - inode->i_blocks += count << uspi->s_nspfshift; UFS_I(inode)->i_lastfrag = max_t(u32, UFS_I(inode)->i_lastfrag, fragment + count); - NULLIFY_FRAGMENTS unlock_super(sb); if (newcount < request) ufs_free_fragments (inode, result + newcount, request - newcount); ufs_free_fragments (inode, tmp, oldcount); - UFSD(("EXIT, result %u\n", result)) + UFSD("EXIT, result %u\n", result); return result; } unlock_super(sb); - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return 0; } @@ -404,7 +422,7 @@ ufs_add_fragments (struct inode * inode, struct ufs_cylinder_group * ucg; unsigned cgno, fragno, fragoff, count, fragsize, i; - UFSD(("ENTER, fragment %u, oldcount %u, newcount %u\n", fragment, oldcount, newcount)) + UFSD("ENTER, fragment %u, oldcount %u, newcount %u\n", fragment, oldcount, newcount); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -419,7 +437,7 @@ ufs_add_fragments (struct inode * inode, ucpi = ufs_load_cylinder (sb, cgno); if (!ucpi) return 0; - ucg = ubh_get_ucg (UCPI_UBH); + ucg = ubh_get_ucg (UCPI_UBH(ucpi)); if (!ufs_cg_chkmagic(sb, ucg)) { ufs_panic (sb, "ufs_add_fragments", "internal error, bad magic number on cg %u", cgno); @@ -429,14 +447,14 @@ ufs_add_fragments (struct inode * inode, fragno = ufs_dtogd (fragment); fragoff = ufs_fragnum (fragno); for (i = oldcount; i < newcount; i++) - if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, fragno + i)) + if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i)) return 0; /* * Block can be extended */ ucg->cg_time = cpu_to_fs32(sb, get_seconds()); for (i = newcount; i < (uspi->s_fpb - fragoff); i++) - if (ubh_isclr (UCPI_UBH, ucpi->c_freeoff, fragno + i)) + if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i)) break; fragsize = i - oldcount; if (!fs32_to_cpu(sb, ucg->cg_frsum[fragsize])) @@ -446,7 +464,7 @@ ufs_add_fragments (struct inode * inode, if (fragsize != count) fs32_add(sb, &ucg->cg_frsum[fragsize - count], 1); for (i = oldcount; i < newcount; i++) - ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, fragno + i); + ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, fragno + i); if(DQUOT_ALLOC_BLOCK(inode, count)) { *err = -EDQUOT; return 0; @@ -454,17 +472,17 @@ ufs_add_fragments (struct inode * inode, fs32_sub(sb, &ucg->cg_cs.cs_nffree, count); fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count); - fs32_sub(sb, &usb1->fs_cstotal.cs_nffree, count); + uspi->cs_total.cs_nffree -= count; - ubh_mark_buffer_dirty (USPI_UBH); - ubh_mark_buffer_dirty (UCPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); if (sb->s_flags & MS_SYNCHRONOUS) { - ubh_ll_rw_block (SWRITE, 1, (struct ufs_buffer_head **)&ucpi); - ubh_wait_on_buffer (UCPI_UBH); + ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi)); + ubh_wait_on_buffer (UCPI_UBH(ucpi)); } sb->s_dirt = 1; - UFSD(("EXIT, fragment %u\n", fragment)) + UFSD("EXIT, fragment %u\n", fragment); return fragment; } @@ -487,7 +505,7 @@ static unsigned ufs_alloc_fragments (str struct ufs_cylinder_group * ucg; unsigned oldcg, i, j, k, result, allocsize; - UFSD(("ENTER, ino %lu, cgno %u, goal %u, count %u\n", inode->i_ino, cgno, goal, count)) + UFSD("ENTER, ino %lu, cgno %u, goal %u, count %u\n", inode->i_ino, cgno, goal, count); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -521,14 +539,14 @@ static unsigned ufs_alloc_fragments (str UFS_TEST_FREE_SPACE_CG } - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return 0; cg_found: ucpi = ufs_load_cylinder (sb, cgno); if (!ucpi) return 0; - ucg = ubh_get_ucg (UCPI_UBH); + ucg = ubh_get_ucg (UCPI_UBH(ucpi)); if (!ufs_cg_chkmagic(sb, ucg)) ufs_panic (sb, "ufs_alloc_fragments", "internal error, bad magic number on cg %u", cgno); @@ -551,12 +569,12 @@ cg_found: return 0; goal = ufs_dtogd (result); for (i = count; i < uspi->s_fpb; i++) - ubh_setbit (UCPI_UBH, ucpi->c_freeoff, goal + i); + ubh_setbit (UCPI_UBH(ucpi), ucpi->c_freeoff, goal + i); i = uspi->s_fpb - count; DQUOT_FREE_BLOCK(inode, i); fs32_add(sb, &ucg->cg_cs.cs_nffree, i); - fs32_add(sb, &usb1->fs_cstotal.cs_nffree, i); + uspi->cs_total.cs_nffree += i; fs32_add(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, i); fs32_add(sb, &ucg->cg_frsum[i], 1); goto succed; @@ -570,10 +588,10 @@ cg_found: return 0; } for (i = 0; i < count; i++) - ubh_clrbit (UCPI_UBH, ucpi->c_freeoff, result + i); + ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_freeoff, result + i); fs32_sub(sb, &ucg->cg_cs.cs_nffree, count); - fs32_sub(sb, &usb1->fs_cstotal.cs_nffree, count); + uspi->cs_total.cs_nffree -= count; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cgno).cs_nffree, count); fs32_sub(sb, &ucg->cg_frsum[allocsize], 1); @@ -581,16 +599,16 @@ cg_found: fs32_add(sb, &ucg->cg_frsum[allocsize - count], 1); succed: - ubh_mark_buffer_dirty (USPI_UBH); - ubh_mark_buffer_dirty (UCPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); if (sb->s_flags & MS_SYNCHRONOUS) { - ubh_ll_rw_block (SWRITE, 1, (struct ufs_buffer_head **)&ucpi); - ubh_wait_on_buffer (UCPI_UBH); + ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi)); + ubh_wait_on_buffer (UCPI_UBH(ucpi)); } sb->s_dirt = 1; result += cgno * uspi->s_fpg; - UFSD(("EXIT3, result %u\n", result)) + UFSD("EXIT3, result %u\n", result); return result; } @@ -603,12 +621,12 @@ static unsigned ufs_alloccg_block (struc struct ufs_cylinder_group * ucg; unsigned result, cylno, blkno; - UFSD(("ENTER, goal %u\n", goal)) + UFSD("ENTER, goal %u\n", goal); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first(uspi); - ucg = ubh_get_ucg(UCPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH(ucpi)); if (goal == 0) { goal = ucpi->c_rotor; @@ -620,7 +638,7 @@ static unsigned ufs_alloccg_block (struc /* * If the requested block is available, use it. */ - if (ubh_isblockset(UCPI_UBH, ucpi->c_freeoff, ufs_fragstoblks(goal))) { + if (ubh_isblockset(UCPI_UBH(ucpi), ucpi->c_freeoff, ufs_fragstoblks(goal))) { result = goal; goto gotit; } @@ -632,7 +650,7 @@ norot: ucpi->c_rotor = result; gotit: blkno = ufs_fragstoblks(result); - ubh_clrblock (UCPI_UBH, ucpi->c_freeoff, blkno); + ubh_clrblock (UCPI_UBH(ucpi), ucpi->c_freeoff, blkno); if ((UFS_SB(sb)->s_flags & UFS_CG_MASK) == UFS_CG_44BSD) ufs_clusteracct (sb, ucpi, blkno, -1); if(DQUOT_ALLOC_BLOCK(inode, uspi->s_fpb)) { @@ -641,31 +659,76 @@ gotit: } fs32_sub(sb, &ucg->cg_cs.cs_nbfree, 1); - fs32_sub(sb, &usb1->fs_cstotal.cs_nbfree, 1); + uspi->cs_total.cs_nbfree--; fs32_sub(sb, &UFS_SB(sb)->fs_cs(ucpi->c_cgx).cs_nbfree, 1); cylno = ufs_cbtocylno(result); fs16_sub(sb, &ubh_cg_blks(ucpi, cylno, ufs_cbtorpos(result)), 1); fs32_sub(sb, &ubh_cg_blktot(ucpi, cylno), 1); - UFSD(("EXIT, result %u\n", result)) + UFSD("EXIT, result %u\n", result); return result; } -static unsigned ufs_bitmap_search (struct super_block * sb, - struct ufs_cg_private_info * ucpi, unsigned goal, unsigned count) +static unsigned ubh_scanc(struct ufs_sb_private_info *uspi, + struct ufs_buffer_head *ubh, + unsigned begin, unsigned size, + unsigned char *table, unsigned char mask) { - struct ufs_sb_private_info * uspi; - struct ufs_super_block_first * usb1; - struct ufs_cylinder_group * ucg; - unsigned start, length, location, result; - unsigned possition, fragsize, blockmap, mask; - - UFSD(("ENTER, cg %u, goal %u, count %u\n", ucpi->c_cgx, goal, count)) + unsigned rest, offset; + unsigned char *cp; + + + offset = begin & ~uspi->s_fmask; + begin >>= uspi->s_fshift; + for (;;) { + if ((offset + size) < uspi->s_fsize) + rest = size; + else + rest = uspi->s_fsize - offset; + size -= rest; + cp = ubh->bh[begin]->b_data + offset; + while ((table[*cp++] & mask) == 0 && --rest) + ; + if (rest || !size) + break; + begin++; + offset = 0; + } + return (size + rest); +} + +/* + * Find a block of the specified size in the specified cylinder group. + * @sp: pointer to super block + * @ucpi: pointer to cylinder group info + * @goal: near which block we want find new one + * @count: specified size + */ +static unsigned ufs_bitmap_search(struct super_block *sb, + struct ufs_cg_private_info *ucpi, + unsigned goal, unsigned count) +{ + /* + * Bit patterns for identifying fragments in the block map + * used as ((map & mask_arr) == want_arr) + */ + static const int mask_arr[9] = { + 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff + }; + static const int want_arr[9] = { + 0x0, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, 0x1fe + }; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; + struct ufs_super_block_first *usb1; + struct ufs_cylinder_group *ucg; + unsigned start, length, loc, result; + unsigned pos, want, blockmap, mask, end; + + UFSD("ENTER, cg %u, goal %u, count %u\n", ucpi->c_cgx, goal, count); - uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first (uspi); - ucg = ubh_get_ucg(UCPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH(ucpi)); if (goal) start = ufs_dtogd(goal) >> 3; @@ -673,53 +736,50 @@ static unsigned ufs_bitmap_search (struc start = ucpi->c_frotor >> 3; length = ((uspi->s_fpg + 7) >> 3) - start; - location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff + start, length, + loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff + start, length, (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other, 1 << (count - 1 + (uspi->s_fpb & 7))); - if (location == 0) { + if (loc == 0) { length = start + 1; - location = ubh_scanc(UCPI_UBH, ucpi->c_freeoff, length, - (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : ufs_fragtable_other, - 1 << (count - 1 + (uspi->s_fpb & 7))); - if (location == 0) { - ufs_error (sb, "ufs_bitmap_search", - "bitmap corrupted on cg %u, start %u, length %u, count %u, freeoff %u\n", - ucpi->c_cgx, start, length, count, ucpi->c_freeoff); + loc = ubh_scanc(uspi, UCPI_UBH(ucpi), ucpi->c_freeoff, length, + (uspi->s_fpb == 8) ? ufs_fragtable_8fpb : + ufs_fragtable_other, + 1 << (count - 1 + (uspi->s_fpb & 7))); + if (loc == 0) { + ufs_error(sb, "ufs_bitmap_search", + "bitmap corrupted on cg %u, start %u," + " length %u, count %u, freeoff %u\n", + ucpi->c_cgx, start, length, count, + ucpi->c_freeoff); return (unsigned)-1; } start = 0; } - result = (start + length - location) << 3; + result = (start + length - loc) << 3; ucpi->c_frotor = result; /* * found the byte in the map */ - blockmap = ubh_blkmap(UCPI_UBH, ucpi->c_freeoff, result); - fragsize = 0; - for (possition = 0, mask = 1; possition < 8; possition++, mask <<= 1) { - if (blockmap & mask) { - if (!(possition & uspi->s_fpbmask)) - fragsize = 1; - else - fragsize++; - } - else { - if (fragsize == count) { - result += possition - count; - UFSD(("EXIT, result %u\n", result)) - return result; - } - fragsize = 0; - } - } - if (fragsize == count) { - result += possition - count; - UFSD(("EXIT, result %u\n", result)) - return result; - } - ufs_error (sb, "ufs_bitmap_search", "block not in map on cg %u\n", ucpi->c_cgx); - UFSD(("EXIT (FAILED)\n")) + + for (end = result + 8; result < end; result += uspi->s_fpb) { + blockmap = ubh_blkmap(UCPI_UBH(ucpi), ucpi->c_freeoff, result); + blockmap <<= 1; + mask = mask_arr[count]; + want = want_arr[count]; + for (pos = 0; pos <= uspi->s_fpb - count; pos++) { + if ((blockmap & mask) == want) { + UFSD("EXIT, result %u\n", result); + return result + pos; + } + mask <<= 1; + want <<= 1; + } + } + + ufs_error(sb, "ufs_bitmap_search", "block not in map on cg %u\n", + ucpi->c_cgx); + UFSD("EXIT (FAILED)\n"); return (unsigned)-1; } @@ -734,9 +794,9 @@ static void ufs_clusteracct(struct super return; if (cnt > 0) - ubh_setbit(UCPI_UBH, ucpi->c_clusteroff, blkno); + ubh_setbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno); else - ubh_clrbit(UCPI_UBH, ucpi->c_clusteroff, blkno); + ubh_clrbit(UCPI_UBH(ucpi), ucpi->c_clusteroff, blkno); /* * Find the size of the cluster going forward. @@ -745,7 +805,7 @@ static void ufs_clusteracct(struct super end = start + uspi->s_contigsumsize; if ( end >= ucpi->c_nclusterblks) end = ucpi->c_nclusterblks; - i = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_clusteroff, end, start); + i = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, end, start); if (i > end) i = end; forw = i - start; @@ -757,7 +817,7 @@ static void ufs_clusteracct(struct super end = start - uspi->s_contigsumsize; if (end < 0 ) end = -1; - i = ubh_find_last_zero_bit (UCPI_UBH, ucpi->c_clusteroff, start, end); + i = ubh_find_last_zero_bit (UCPI_UBH(ucpi), ucpi->c_clusteroff, start, end); if ( i < end) i = end; back = start - i; @@ -769,11 +829,11 @@ static void ufs_clusteracct(struct super i = back + forw + 1; if (i > uspi->s_contigsumsize) i = uspi->s_contigsumsize; - fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (i << 2)), cnt); + fs32_add(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (i << 2)), cnt); if (back > 0) - fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (back << 2)), cnt); + fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (back << 2)), cnt); if (forw > 0) - fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH, ucpi->c_clustersumoff + (forw << 2)), cnt); + fs32_sub(sb, (__fs32*)ubh_get_addr(UCPI_UBH(ucpi), ucpi->c_clustersumoff + (forw << 2)), cnt); } diff --git a/fs/ufs/cylinder.c b/fs/ufs/cylinder.c index 14abb8b..09c39e5 100644 --- a/fs/ufs/cylinder.c +++ b/fs/ufs/cylinder.c @@ -20,15 +20,6 @@ #include #include "swab.h" #include "util.h" -#undef UFS_CYLINDER_DEBUG - -#ifdef UFS_CYLINDER_DEBUG -#define UFSD(x) printk("(%s, %d), %s:", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - - /* * Read cylinder group into cache. The memory space for ufs_cg_private_info * structure is already allocated during ufs_read_super. @@ -42,19 +33,19 @@ static void ufs_read_cylinder (struct su struct ufs_cylinder_group * ucg; unsigned i, j; - UFSD(("ENTER, cgno %u, bitmap_nr %u\n", cgno, bitmap_nr)) + UFSD("ENTER, cgno %u, bitmap_nr %u\n", cgno, bitmap_nr); uspi = sbi->s_uspi; ucpi = sbi->s_ucpi[bitmap_nr]; ucg = (struct ufs_cylinder_group *)sbi->s_ucg[cgno]->b_data; - UCPI_UBH->fragment = ufs_cgcmin(cgno); - UCPI_UBH->count = uspi->s_cgsize >> sb->s_blocksize_bits; + UCPI_UBH(ucpi)->fragment = ufs_cgcmin(cgno); + UCPI_UBH(ucpi)->count = uspi->s_cgsize >> sb->s_blocksize_bits; /* * We have already the first fragment of cylinder group block in buffer */ - UCPI_UBH->bh[0] = sbi->s_ucg[cgno]; - for (i = 1; i < UCPI_UBH->count; i++) - if (!(UCPI_UBH->bh[i] = sb_bread(sb, UCPI_UBH->fragment + i))) + UCPI_UBH(ucpi)->bh[0] = sbi->s_ucg[cgno]; + for (i = 1; i < UCPI_UBH(ucpi)->count; i++) + if (!(UCPI_UBH(ucpi)->bh[i] = sb_bread(sb, UCPI_UBH(ucpi)->fragment + i))) goto failed; sbi->s_cgno[bitmap_nr] = cgno; @@ -73,7 +64,7 @@ static void ufs_read_cylinder (struct su ucpi->c_clustersumoff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clustersumoff); ucpi->c_clusteroff = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_clusteroff); ucpi->c_nclusterblks = fs32_to_cpu(sb, ucg->cg_u.cg_44.cg_nclusterblks); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return; failed: @@ -95,15 +86,15 @@ void ufs_put_cylinder (struct super_bloc struct ufs_cylinder_group * ucg; unsigned i; - UFSD(("ENTER, bitmap_nr %u\n", bitmap_nr)) + UFSD("ENTER, bitmap_nr %u\n", bitmap_nr); uspi = sbi->s_uspi; if (sbi->s_cgno[bitmap_nr] == UFS_CGNO_EMPTY) { - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return; } ucpi = sbi->s_ucpi[bitmap_nr]; - ucg = ubh_get_ucg(UCPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH(ucpi)); if (uspi->s_ncg > UFS_MAX_GROUP_LOADED && bitmap_nr >= sbi->s_cg_loaded) { ufs_panic (sb, "ufs_put_cylinder", "internal error"); @@ -116,13 +107,13 @@ void ufs_put_cylinder (struct super_bloc ucg->cg_rotor = cpu_to_fs32(sb, ucpi->c_rotor); ucg->cg_frotor = cpu_to_fs32(sb, ucpi->c_frotor); ucg->cg_irotor = cpu_to_fs32(sb, ucpi->c_irotor); - ubh_mark_buffer_dirty (UCPI_UBH); - for (i = 1; i < UCPI_UBH->count; i++) { - brelse (UCPI_UBH->bh[i]); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); + for (i = 1; i < UCPI_UBH(ucpi)->count; i++) { + brelse (UCPI_UBH(ucpi)->bh[i]); } sbi->s_cgno[bitmap_nr] = UFS_CGNO_EMPTY; - UFSD(("EXIT\n")) + UFSD("EXIT\n"); } /* @@ -139,7 +130,7 @@ struct ufs_cg_private_info * ufs_load_cy struct ufs_cg_private_info * ucpi; unsigned cg, i, j; - UFSD(("ENTER, cgno %u\n", cgno)) + UFSD("ENTER, cgno %u\n", cgno); uspi = sbi->s_uspi; if (cgno >= uspi->s_ncg) { @@ -150,7 +141,7 @@ struct ufs_cg_private_info * ufs_load_cy * Cylinder group number cg it in cache and it was last used */ if (sbi->s_cgno[0] == cgno) { - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return sbi->s_ucpi[0]; } /* @@ -160,16 +151,16 @@ struct ufs_cg_private_info * ufs_load_cy if (sbi->s_cgno[cgno] != UFS_CGNO_EMPTY) { if (sbi->s_cgno[cgno] != cgno) { ufs_panic (sb, "ufs_load_cylinder", "internal error, wrong number of cg in cache"); - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return NULL; } else { - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return sbi->s_ucpi[cgno]; } } else { ufs_read_cylinder (sb, cgno, cgno); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return sbi->s_ucpi[cgno]; } } @@ -204,6 +195,6 @@ struct ufs_cg_private_info * ufs_load_cy sbi->s_ucpi[0] = ucpi; ufs_read_cylinder (sb, cgno, 0); } - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return sbi->s_ucpi[0]; } diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index 1a56120..7f0a0aa 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -11,31 +11,20 @@ * 4.4BSD (FreeBSD) support added on February 1st 1998 by * Niels Kristian Bech Jensen partially based * on code by Martin von Loewis . + * + * Migration to usage of "page cache" on May 2006 by + * Evgeniy Dushistov based on ext2 code base. */ #include #include #include #include -#include #include #include "swab.h" #include "util.h" -#undef UFS_DIR_DEBUG - -#ifdef UFS_DIR_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - -static int -ufs_check_dir_entry (const char *, struct inode *, struct ufs_dir_entry *, - struct buffer_head *, unsigned long); - - /* * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure. * @@ -51,495 +40,541 @@ static inline int ufs_match(struct super return !memcmp(name, de->d_name, len); } -/* - * This is blatantly stolen from ext2fs - */ -static int -ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) +static int ufs_commit_chunk(struct page *page, unsigned from, unsigned to) { - struct inode *inode = filp->f_dentry->d_inode; - int error = 0; - unsigned long offset, lblk; - int i, stored; - struct buffer_head * bh; - struct ufs_dir_entry * de; - struct super_block * sb; - int de_reclen; - unsigned flags; - u64 blk= 0L; - - lock_kernel(); - - sb = inode->i_sb; - flags = UFS_SB(sb)->s_flags; - - UFSD(("ENTER, ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos)) - - stored = 0; - bh = NULL; - offset = filp->f_pos & (sb->s_blocksize - 1); - - while (!error && !stored && filp->f_pos < inode->i_size) { - lblk = (filp->f_pos) >> sb->s_blocksize_bits; - blk = ufs_frag_map(inode, lblk); - if (!blk || !(bh = sb_bread(sb, blk))) { - /* XXX - error - skip to the next block */ - printk("ufs_readdir: " - "dir inode %lu has a hole at offset %lu\n", - inode->i_ino, (unsigned long int)filp->f_pos); - filp->f_pos += sb->s_blocksize - offset; - continue; - } - -revalidate: - /* If the dir block has changed since the last call to - * readdir(2), then we might be pointing to an invalid - * dirent right now. Scan from the start of the block - * to make sure. */ - if (filp->f_version != inode->i_version) { - for (i = 0; i < sb->s_blocksize && i < offset; ) { - de = (struct ufs_dir_entry *)(bh->b_data + i); - /* It's too expensive to do a full - * dirent test each time round this - * loop, but we do have to test at - * least that it is non-zero. A - * failure will be detected in the - * dirent test below. */ - de_reclen = fs16_to_cpu(sb, de->d_reclen); - if (de_reclen < 1) - break; - i += de_reclen; - } - offset = i; - filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) - | offset; - filp->f_version = inode->i_version; - } + struct inode *dir = page->mapping->host; + int err = 0; + dir->i_version++; + page->mapping->a_ops->commit_write(NULL, page, from, to); + if (IS_DIRSYNC(dir)) + err = write_one_page(page, 1); + else + unlock_page(page); + return err; +} - while (!error && filp->f_pos < inode->i_size - && offset < sb->s_blocksize) { - de = (struct ufs_dir_entry *) (bh->b_data + offset); - /* XXX - put in a real ufs_check_dir_entry() */ - if ((de->d_reclen == 0) || (ufs_get_de_namlen(sb, de) == 0)) { - filp->f_pos = (filp->f_pos & - (sb->s_blocksize - 1)) + - sb->s_blocksize; - brelse(bh); - unlock_kernel(); - return stored; - } - if (!ufs_check_dir_entry ("ufs_readdir", inode, de, - bh, offset)) { - /* On error, skip the f_pos to the - next block. */ - filp->f_pos = (filp->f_pos | - (sb->s_blocksize - 1)) + - 1; - brelse (bh); - unlock_kernel(); - return stored; - } - offset += fs16_to_cpu(sb, de->d_reclen); - if (de->d_ino) { - /* We might block in the next section - * if the data destination is - * currently swapped out. So, use a - * version stamp to detect whether or - * not the directory has been modified - * during the copy operation. */ - unsigned long version = filp->f_version; - unsigned char d_type = DT_UNKNOWN; +static inline void ufs_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); +} - UFSD(("filldir(%s,%u)\n", de->d_name, - fs32_to_cpu(sb, de->d_ino))) - UFSD(("namlen %u\n", ufs_get_de_namlen(sb, de))) +static inline unsigned long ufs_dir_pages(struct inode *inode) +{ + return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; +} - if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) - d_type = de->d_u.d_44.d_type; - error = filldir(dirent, de->d_name, - ufs_get_de_namlen(sb, de), filp->f_pos, - fs32_to_cpu(sb, de->d_ino), d_type); - if (error) - break; - if (version != filp->f_version) - goto revalidate; - stored ++; - } - filp->f_pos += fs16_to_cpu(sb, de->d_reclen); - } - offset = 0; - brelse (bh); +ino_t ufs_inode_by_name(struct inode *dir, struct dentry *dentry) +{ + ino_t res = 0; + struct ufs_dir_entry *de; + struct page *page; + + de = ufs_find_entry(dir, dentry, &page); + if (de) { + res = fs32_to_cpu(dir->i_sb, de->d_ino); + ufs_put_page(page); } - unlock_kernel(); - return 0; + return res; } -/* - * define how far ahead to read directories while searching them. - */ -#define NAMEI_RA_CHUNKS 2 -#define NAMEI_RA_BLOCKS 4 -#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) -/* - * ufs_find_entry() - * - * finds an entry in the specified directory with the wanted name. It - * returns the cache buffer in which the entry was found, and the entry - * itself (as a parameter - res_bh). It does NOT read the inode of the - * entry - you'll have to do that yourself if you want to. - */ -struct ufs_dir_entry * ufs_find_entry (struct dentry *dentry, - struct buffer_head ** res_bh) +/* Releases the page */ +void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, + struct page *page, struct inode *inode) { - struct super_block * sb; - struct buffer_head * bh_use[NAMEI_RA_SIZE]; - struct buffer_head * bh_read[NAMEI_RA_SIZE]; - unsigned long offset; - int block, toread, i, err; - struct inode *dir = dentry->d_parent->d_inode; - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; + unsigned from = (char *) de - (char *) page_address(page); + unsigned to = from + fs16_to_cpu(dir->i_sb, de->d_reclen); + int err; - UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen)) - - *res_bh = NULL; - - sb = dir->i_sb; - - if (namelen > UFS_MAXNAMLEN) - return NULL; + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + BUG_ON(err); + de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); + ufs_set_de_type(dir->i_sb, de, inode->i_mode); + err = ufs_commit_chunk(page, from, to); + ufs_put_page(page); + dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(dir); +} - memset (bh_use, 0, sizeof (bh_use)); - toread = 0; - for (block = 0; block < NAMEI_RA_SIZE; ++block) { - struct buffer_head * bh; - if ((block << sb->s_blocksize_bits) >= dir->i_size) - break; - bh = ufs_getfrag (dir, block, 0, &err); - bh_use[block] = bh; - if (bh && !buffer_uptodate(bh)) - bh_read[toread++] = bh; +static void ufs_check_page(struct page *page) +{ + struct inode *dir = page->mapping->host; + struct super_block *sb = dir->i_sb; + char *kaddr = page_address(page); + unsigned offs, rec_len; + unsigned limit = PAGE_CACHE_SIZE; + struct ufs_dir_entry *p; + char *error; + + if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { + limit = dir->i_size & ~PAGE_CACHE_MASK; + if (limit & (UFS_SECTOR_SIZE - 1)) + goto Ebadsize; + if (!limit) + goto out; } + for (offs = 0; offs <= limit - UFS_DIR_REC_LEN(1); offs += rec_len) { + p = (struct ufs_dir_entry *)(kaddr + offs); + rec_len = fs16_to_cpu(sb, p->d_reclen); + + if (rec_len < UFS_DIR_REC_LEN(1)) + goto Eshort; + if (rec_len & 3) + goto Ealign; + if (rec_len < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, p))) + goto Enamelen; + if (((offs + rec_len - 1) ^ offs) & ~(UFS_SECTOR_SIZE-1)) + goto Espan; + if (fs32_to_cpu(sb, p->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg * + UFS_SB(sb)->s_uspi->s_ncg)) + goto Einumber; + } + if (offs != limit) + goto Eend; +out: + SetPageChecked(page); + return; + + /* Too bad, we had an error */ + +Ebadsize: + ufs_error(sb, "ufs_check_page", + "size of directory #%lu is not a multiple of chunk size", + dir->i_ino + ); + goto fail; +Eshort: + error = "rec_len is smaller than minimal"; + goto bad_entry; +Ealign: + error = "unaligned directory entry"; + goto bad_entry; +Enamelen: + error = "rec_len is too small for name_len"; + goto bad_entry; +Espan: + error = "directory entry across blocks"; + goto bad_entry; +Einumber: + error = "inode out of bounds"; +bad_entry: + ufs_error (sb, "ufs_check_page", "bad entry in directory #%lu: %s - " + "offset=%lu, rec_len=%d, name_len=%d", + dir->i_ino, error, (page->index<i_ino, (page->index<i_size; block++) { - struct buffer_head * bh; - struct ufs_dir_entry * de; - char * dlimit; - - if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { - ll_rw_block (READ, toread, bh_read); - toread = 0; - } - bh = bh_use[block % NAMEI_RA_SIZE]; - if (!bh) { - ufs_error (sb, "ufs_find_entry", - "directory #%lu contains a hole at offset %lu", - dir->i_ino, offset); - offset += sb->s_blocksize; - continue; - } - wait_on_buffer (bh); - if (!buffer_uptodate(bh)) { - /* - * read error: all bets are off - */ - break; - } - - de = (struct ufs_dir_entry *) bh->b_data; - dlimit = bh->b_data + sb->s_blocksize; - while ((char *) de < dlimit && offset < dir->i_size) { - /* this code is executed quadratically often */ - /* do minimal checking by hand */ - int de_len; - - if ((char *) de + namelen <= dlimit && - ufs_match(sb, namelen, name, de)) { - /* found a match - - just to be sure, do a full check */ - if (!ufs_check_dir_entry("ufs_find_entry", - dir, de, bh, offset)) - goto failed; - for (i = 0; i < NAMEI_RA_SIZE; ++i) { - if (bh_use[i] != bh) - brelse (bh_use[i]); - } - *res_bh = bh; - return de; - } - /* prevent looping on a bad block */ - de_len = fs16_to_cpu(sb, de->d_reclen); - if (de_len <= 0) - goto failed; - offset += de_len; - de = (struct ufs_dir_entry *) ((char *) de + de_len); - } - - brelse (bh); - if (((block + NAMEI_RA_SIZE) << sb->s_blocksize_bits ) >= - dir->i_size) - bh = NULL; - else - bh = ufs_getfrag (dir, block + NAMEI_RA_SIZE, 0, &err); - bh_use[block % NAMEI_RA_SIZE] = bh; - if (bh && !buffer_uptodate(bh)) - bh_read[toread++] = bh; +static struct page *ufs_get_page(struct inode *dir, unsigned long n) +{ + struct address_space *mapping = dir->i_mapping; + struct page *page = read_cache_page(mapping, n, + (filler_t*)mapping->a_ops->readpage, NULL); + if (!IS_ERR(page)) { + wait_on_page_locked(page); + kmap(page); + if (!PageUptodate(page)) + goto fail; + if (!PageChecked(page)) + ufs_check_page(page); + if (PageError(page)) + goto fail; } + return page; -failed: - for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]); - UFSD(("EXIT\n")) - return NULL; +fail: + ufs_put_page(page); + return ERR_PTR(-EIO); } -static int -ufs_check_dir_entry (const char *function, struct inode *dir, - struct ufs_dir_entry *de, struct buffer_head *bh, - unsigned long offset) +/* + * Return the offset into page `page_nr' of the last valid + * byte in that page, plus one. + */ +static unsigned +ufs_last_byte(struct inode *inode, unsigned long page_nr) { - struct super_block *sb = dir->i_sb; - const char *error_msg = NULL; - int rlen = fs16_to_cpu(sb, de->d_reclen); - - if (rlen < UFS_DIR_REC_LEN(1)) - error_msg = "reclen is smaller than minimal"; - else if (rlen % 4 != 0) - error_msg = "reclen % 4 != 0"; - else if (rlen < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))) - error_msg = "reclen is too small for namlen"; - else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) - error_msg = "directory entry across blocks"; - else if (fs32_to_cpu(sb, de->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg * - UFS_SB(sb)->s_uspi->s_ncg)) - error_msg = "inode out of bounds"; - - if (error_msg != NULL) - ufs_error (sb, function, "bad entry in directory #%lu, size %Lu: %s - " - "offset=%lu, inode=%lu, reclen=%d, namlen=%d", - dir->i_ino, dir->i_size, error_msg, offset, - (unsigned long)fs32_to_cpu(sb, de->d_ino), - rlen, ufs_get_de_namlen(sb, de)); - - return (error_msg == NULL ? 1 : 0); + unsigned last_byte = inode->i_size; + + last_byte -= page_nr << PAGE_CACHE_SHIFT; + if (last_byte > PAGE_CACHE_SIZE) + last_byte = PAGE_CACHE_SIZE; + return last_byte; } -struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct buffer_head **p) +static inline struct ufs_dir_entry * +ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p) { - int err; - struct buffer_head *bh = ufs_bread (dir, 0, 0, &err); - struct ufs_dir_entry *res = NULL; - - if (bh) { - res = (struct ufs_dir_entry *) bh->b_data; - res = (struct ufs_dir_entry *)((char *)res + - fs16_to_cpu(dir->i_sb, res->d_reclen)); - } - *p = bh; - return res; + return (struct ufs_dir_entry *)((char *)p + + fs16_to_cpu(sb, p->d_reclen)); } -ino_t ufs_inode_by_name(struct inode * dir, struct dentry *dentry) + +struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p) { - ino_t res = 0; - struct ufs_dir_entry * de; - struct buffer_head *bh; + struct page *page = ufs_get_page(dir, 0); + struct ufs_dir_entry *de = NULL; - de = ufs_find_entry (dentry, &bh); - if (de) { - res = fs32_to_cpu(dir->i_sb, de->d_ino); - brelse(bh); + if (!IS_ERR(page)) { + de = ufs_next_entry(dir->i_sb, + (struct ufs_dir_entry *)page_address(page)); + *p = page; } - return res; + return de; } -void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, - struct buffer_head *bh, struct inode *inode) +/* + * ufs_find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the page in which the entry was found, and the entry itself + * (as a parameter - res_dir). Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. + */ +struct ufs_dir_entry *ufs_find_entry(struct inode *dir, struct dentry *dentry, + struct page **res_page) { - dir->i_version++; - de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); - mark_buffer_dirty(bh); - if (IS_DIRSYNC(dir)) - sync_dirty_buffer(bh); - brelse (bh); + struct super_block *sb = dir->i_sb; + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; + unsigned reclen = UFS_DIR_REC_LEN(namelen); + unsigned long start, n; + unsigned long npages = ufs_dir_pages(dir); + struct page *page = NULL; + struct ufs_inode_info *ui = UFS_I(dir); + struct ufs_dir_entry *de; + + UFSD("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen); + + if (npages == 0 || namelen > UFS_MAXNAMLEN) + goto out; + + /* OFFSET_CACHE */ + *res_page = NULL; + + start = ui->i_dir_start_lookup; + + if (start >= npages) + start = 0; + n = start; + do { + char *kaddr; + page = ufs_get_page(dir, n); + if (!IS_ERR(page)) { + kaddr = page_address(page); + de = (struct ufs_dir_entry *) kaddr; + kaddr += ufs_last_byte(dir, n) - reclen; + while ((char *) de <= kaddr) { + if (de->d_reclen == 0) { + ufs_error(dir->i_sb, __FUNCTION__, + "zero-length directory entry"); + ufs_put_page(page); + goto out; + } + if (ufs_match(sb, namelen, name, de)) + goto found; + de = ufs_next_entry(sb, de); + } + ufs_put_page(page); + } + if (++n >= npages) + n = 0; + } while (n != start); +out: + return NULL; + +found: + *res_page = page; + ui->i_dir_start_lookup = n; + return de; } /* - * ufs_add_entry() - * - * adds a file entry to the specified directory, using the same - * semantics as ufs_find_entry(). It returns NULL if it failed. + * Parent is locked. */ int ufs_add_link(struct dentry *dentry, struct inode *inode) { - struct super_block * sb; - struct ufs_sb_private_info * uspi; - unsigned long offset; - unsigned fragoff; - unsigned short rec_len; - struct buffer_head * bh; - struct ufs_dir_entry * de, * de1; struct inode *dir = dentry->d_parent->d_inode; const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; + struct super_block *sb = dir->i_sb; + unsigned reclen = UFS_DIR_REC_LEN(namelen); + unsigned short rec_len, name_len; + struct page *page = NULL; + struct ufs_dir_entry *de; + unsigned long npages = ufs_dir_pages(dir); + unsigned long n; + char *kaddr; + unsigned from, to; int err; - UFSD(("ENTER, name %s, namelen %u\n", name, namelen)) - - sb = dir->i_sb; - uspi = UFS_SB(sb)->s_uspi; - - if (!namelen) - return -EINVAL; - bh = ufs_bread (dir, 0, 0, &err); - if (!bh) - return err; - rec_len = UFS_DIR_REC_LEN(namelen); - offset = 0; - de = (struct ufs_dir_entry *) bh->b_data; - while (1) { - if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) { - fragoff = offset & ~uspi->s_fmask; - if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE) - ufs_error (sb, "ufs_add_entry", "internal error" - " fragoff %u", fragoff); - if (!fragoff) { - brelse (bh); - bh = ufs_bread (dir, offset >> sb->s_blocksize_bits, 1, &err); - if (!bh) - return err; - } - if (dir->i_size <= offset) { - if (dir->i_size == 0) { - brelse(bh); - return -ENOENT; - } - de = (struct ufs_dir_entry *) (bh->b_data + fragoff); - de->d_ino = 0; + UFSD("ENTER, name %s, namelen %u\n", name, namelen); + + /* + * We take care of directory expansion in the same loop. + * This code plays outside i_size, so it locks the page + * to protect that region. + */ + for (n = 0; n <= npages; n++) { + char *dir_end; + + page = ufs_get_page(dir, n); + err = PTR_ERR(page); + if (IS_ERR(page)) + goto out; + lock_page(page); + kaddr = page_address(page); + dir_end = kaddr + ufs_last_byte(dir, n); + de = (struct ufs_dir_entry *)kaddr; + kaddr += PAGE_CACHE_SIZE - reclen; + while ((char *)de <= kaddr) { + if ((char *)de == dir_end) { + /* We hit i_size */ + name_len = 0; + rec_len = UFS_SECTOR_SIZE; de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE); - ufs_set_de_namlen(sb, de, 0); - dir->i_size = offset + UFS_SECTOR_SIZE; - mark_inode_dirty(dir); - } else { - de = (struct ufs_dir_entry *) bh->b_data; + de->d_ino = 0; + goto got_it; } + if (de->d_reclen == 0) { + ufs_error(dir->i_sb, __FUNCTION__, + "zero-length directory entry"); + err = -EIO; + goto out_unlock; + } + err = -EEXIST; + if (ufs_match(sb, namelen, name, de)) + goto out_unlock; + name_len = UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)); + rec_len = fs16_to_cpu(sb, de->d_reclen); + if (!de->d_ino && rec_len >= reclen) + goto got_it; + if (rec_len >= name_len + reclen) + goto got_it; + de = (struct ufs_dir_entry *) ((char *) de + rec_len); } - if (!ufs_check_dir_entry ("ufs_add_entry", dir, de, bh, offset)) { - brelse (bh); - return -ENOENT; - } - if (ufs_match(sb, namelen, name, de)) { - brelse (bh); - return -EEXIST; - } - if (de->d_ino == 0 && fs16_to_cpu(sb, de->d_reclen) >= rec_len) - break; - - if (fs16_to_cpu(sb, de->d_reclen) >= - UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)) + rec_len) - break; - offset += fs16_to_cpu(sb, de->d_reclen); - de = (struct ufs_dir_entry *) ((char *) de + fs16_to_cpu(sb, de->d_reclen)); + unlock_page(page); + ufs_put_page(page); } - + BUG(); + return -EINVAL; + +got_it: + from = (char*)de - (char*)page_address(page); + to = from + rec_len; + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + goto out_unlock; if (de->d_ino) { - de1 = (struct ufs_dir_entry *) ((char *) de + - UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))); - de1->d_reclen = - cpu_to_fs16(sb, fs16_to_cpu(sb, de->d_reclen) - - UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))); - de->d_reclen = - cpu_to_fs16(sb, UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))); + struct ufs_dir_entry *de1 = + (struct ufs_dir_entry *) ((char *) de + name_len); + de1->d_reclen = cpu_to_fs16(sb, rec_len - name_len); + de->d_reclen = cpu_to_fs16(sb, name_len); + de = de1; } - de->d_ino = 0; + ufs_set_de_namlen(sb, de, namelen); - memcpy (de->d_name, name, namelen + 1); + memcpy(de->d_name, name, namelen + 1); de->d_ino = cpu_to_fs32(sb, inode->i_ino); ufs_set_de_type(sb, de, inode->i_mode); - mark_buffer_dirty(bh); - if (IS_DIRSYNC(dir)) - sync_dirty_buffer(bh); - brelse (bh); + + err = ufs_commit_chunk(page, from, to); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; - dir->i_version++; + mark_inode_dirty(dir); + /* OFFSET_CACHE */ +out_put: + ufs_put_page(page); +out: + return err; +out_unlock: + unlock_page(page); + goto out_put; +} - UFSD(("EXIT\n")) +static inline unsigned +ufs_validate_entry(struct super_block *sb, char *base, + unsigned offset, unsigned mask) +{ + struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset); + struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask)); + while ((char*)p < (char*)de) { + if (p->d_reclen == 0) + break; + p = ufs_next_entry(sb, p); + } + return (char *)p - base; +} + + +/* + * This is blatantly stolen from ext2fs + */ +static int +ufs_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + loff_t pos = filp->f_pos; + struct inode *inode = filp->f_dentry->d_inode; + struct super_block *sb = inode->i_sb; + unsigned int offset = pos & ~PAGE_CACHE_MASK; + unsigned long n = pos >> PAGE_CACHE_SHIFT; + unsigned long npages = ufs_dir_pages(inode); + unsigned chunk_mask = ~(UFS_SECTOR_SIZE - 1); + int need_revalidate = filp->f_version != inode->i_version; + unsigned flags = UFS_SB(sb)->s_flags; + + UFSD("BEGIN\n"); + + if (pos > inode->i_size - UFS_DIR_REC_LEN(1)) + return 0; + + for ( ; n < npages; n++, offset = 0) { + char *kaddr, *limit; + struct ufs_dir_entry *de; + + struct page *page = ufs_get_page(inode, n); + + if (IS_ERR(page)) { + ufs_error(sb, __FUNCTION__, + "bad page in #%lu", + inode->i_ino); + filp->f_pos += PAGE_CACHE_SIZE - offset; + return -EIO; + } + kaddr = page_address(page); + if (unlikely(need_revalidate)) { + if (offset) { + offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask); + filp->f_pos = (n<f_version = inode->i_version; + need_revalidate = 0; + } + de = (struct ufs_dir_entry *)(kaddr+offset); + limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1); + for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) { + if (de->d_reclen == 0) { + ufs_error(sb, __FUNCTION__, + "zero-length directory entry"); + ufs_put_page(page); + return -EIO; + } + if (de->d_ino) { + int over; + unsigned char d_type = DT_UNKNOWN; + + offset = (char *)de - kaddr; + + UFSD("filldir(%s,%u)\n", de->d_name, + fs32_to_cpu(sb, de->d_ino)); + UFSD("namlen %u\n", ufs_get_de_namlen(sb, de)); + + if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) + d_type = de->d_u.d_44.d_type; + + over = filldir(dirent, de->d_name, + ufs_get_de_namlen(sb, de), + (n<d_ino), d_type); + if (over) { + ufs_put_page(page); + return 0; + } + } + filp->f_pos += fs16_to_cpu(sb, de->d_reclen); + } + ufs_put_page(page); + } return 0; } + /* * ufs_delete_entry deletes a directory entry by merging it with the * previous entry. */ -int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir, - struct buffer_head * bh ) - +int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, + struct page * page) { - struct super_block * sb; - struct ufs_dir_entry * de, * pde; - unsigned i; - - UFSD(("ENTER\n")) + struct super_block *sb = inode->i_sb; + struct address_space *mapping = page->mapping; + char *kaddr = page_address(page); + unsigned from = ((char*)dir - kaddr) & ~(UFS_SECTOR_SIZE - 1); + unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen); + struct ufs_dir_entry *pde = NULL; + struct ufs_dir_entry *de = (struct ufs_dir_entry *) (kaddr + from); + int err; - sb = inode->i_sb; - i = 0; - pde = NULL; - de = (struct ufs_dir_entry *) bh->b_data; - - UFSD(("ino %u, reclen %u, namlen %u, name %s\n", - fs32_to_cpu(sb, de->d_ino), - fs16_to_cpu(sb, de->d_reclen), - ufs_get_de_namlen(sb, de), de->d_name)) - - while (i < bh->b_size) { - if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) { - brelse(bh); - return -EIO; - } - if (de == dir) { - if (pde) - fs16_add(sb, &pde->d_reclen, - fs16_to_cpu(sb, dir->d_reclen)); - dir->d_ino = 0; - inode->i_version++; - inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); - mark_buffer_dirty(bh); - if (IS_DIRSYNC(inode)) - sync_dirty_buffer(bh); - brelse(bh); - UFSD(("EXIT\n")) - return 0; + UFSD("ENTER\n"); + + UFSD("ino %u, reclen %u, namlen %u, name %s\n", + fs32_to_cpu(sb, de->d_ino), + fs16_to_cpu(sb, de->d_reclen), + ufs_get_de_namlen(sb, de), de->d_name); + + while ((char*)de < (char*)dir) { + if (de->d_reclen == 0) { + ufs_error(inode->i_sb, __FUNCTION__, + "zero-length directory entry"); + err = -EIO; + goto out; } - i += fs16_to_cpu(sb, de->d_reclen); - if (i == UFS_SECTOR_SIZE) pde = NULL; - else pde = de; - de = (struct ufs_dir_entry *) - ((char *) de + fs16_to_cpu(sb, de->d_reclen)); - if (i == UFS_SECTOR_SIZE && de->d_reclen == 0) - break; + pde = de; + de = ufs_next_entry(sb, de); } - UFSD(("EXIT\n")) - brelse(bh); - return -ENOENT; + if (pde) + from = (char*)pde - (char*)page_address(page); + lock_page(page); + err = mapping->a_ops->prepare_write(NULL, page, from, to); + BUG_ON(err); + if (pde) + pde->d_reclen = cpu_to_fs16(sb, to-from); + dir->d_ino = 0; + err = ufs_commit_chunk(page, from, to); + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); +out: + ufs_put_page(page); + UFSD("EXIT\n"); + return err; } int ufs_make_empty(struct inode * inode, struct inode *dir) { struct super_block * sb = dir->i_sb; - struct buffer_head * dir_block; + struct address_space *mapping = inode->i_mapping; + struct page *page = grab_cache_page(mapping, 0); struct ufs_dir_entry * de; + char *base; int err; - dir_block = ufs_bread (inode, 0, 1, &err); - if (!dir_block) - return err; + if (!page) + return -ENOMEM; + kmap(page); + err = mapping->a_ops->prepare_write(NULL, page, 0, UFS_SECTOR_SIZE); + if (err) { + unlock_page(page); + goto fail; + } + + + base = (char*)page_address(page); + memset(base, 0, PAGE_CACHE_SIZE); + + de = (struct ufs_dir_entry *) base; - inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE; - de = (struct ufs_dir_entry *) dir_block->b_data; de->d_ino = cpu_to_fs32(sb, inode->i_ino); ufs_set_de_type(sb, de, inode->i_mode); ufs_set_de_namlen(sb, de, 1); @@ -552,72 +587,65 @@ int ufs_make_empty(struct inode * inode, de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1)); ufs_set_de_namlen(sb, de, 2); strcpy (de->d_name, ".."); - mark_buffer_dirty(dir_block); - brelse (dir_block); - mark_inode_dirty(inode); - return 0; + + err = ufs_commit_chunk(page, 0, UFS_SECTOR_SIZE); +fail: + kunmap(page); + page_cache_release(page); + return err; } /* * routine to check that the specified directory is empty (for rmdir) */ -int ufs_empty_dir (struct inode * inode) +int ufs_empty_dir(struct inode * inode) { - struct super_block * sb; - unsigned long offset; - struct buffer_head * bh; - struct ufs_dir_entry * de, * de1; - int err; - - sb = inode->i_sb; - - if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) || - !(bh = ufs_bread (inode, 0, 0, &err))) { - ufs_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no data block", - inode->i_ino); - return 1; - } - de = (struct ufs_dir_entry *) bh->b_data; - de1 = (struct ufs_dir_entry *) - ((char *)de + fs16_to_cpu(sb, de->d_reclen)); - if (fs32_to_cpu(sb, de->d_ino) != inode->i_ino || de1->d_ino == 0 || - strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) { - ufs_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no `.' or `..'", - inode->i_ino); - return 1; - } - offset = fs16_to_cpu(sb, de->d_reclen) + fs16_to_cpu(sb, de1->d_reclen); - de = (struct ufs_dir_entry *) - ((char *)de1 + fs16_to_cpu(sb, de1->d_reclen)); - while (offset < inode->i_size ) { - if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { - brelse (bh); - bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err); - if (!bh) { - ufs_error (sb, "empty_dir", - "directory #%lu contains a hole at offset %lu", - inode->i_ino, offset); - offset += sb->s_blocksize; - continue; + struct super_block *sb = inode->i_sb; + struct page *page = NULL; + unsigned long i, npages = ufs_dir_pages(inode); + + for (i = 0; i < npages; i++) { + char *kaddr; + struct ufs_dir_entry *de; + page = ufs_get_page(inode, i); + + if (IS_ERR(page)) + continue; + + kaddr = page_address(page); + de = (struct ufs_dir_entry *)kaddr; + kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1); + + while ((char *)de <= kaddr) { + if (de->d_reclen == 0) { + ufs_error(inode->i_sb, __FUNCTION__, + "zero-length directory entry: " + "kaddr=%p, de=%p\n", kaddr, de); + goto not_empty; } - de = (struct ufs_dir_entry *) bh->b_data; - } - if (!ufs_check_dir_entry ("empty_dir", inode, de, bh, offset)) { - brelse (bh); - return 1; - } - if (de->d_ino) { - brelse (bh); - return 0; + if (de->d_ino) { + u16 namelen=ufs_get_de_namlen(sb, de); + /* check for . and .. */ + if (de->d_name[0] != '.') + goto not_empty; + if (namelen > 2) + goto not_empty; + if (namelen < 2) { + if (inode->i_ino != + fs32_to_cpu(sb, de->d_ino)) + goto not_empty; + } else if (de->d_name[1] != '.') + goto not_empty; + } + de = ufs_next_entry(sb, de); } - offset += fs16_to_cpu(sb, de->d_reclen); - de = (struct ufs_dir_entry *) - ((char *)de + fs16_to_cpu(sb, de->d_reclen)); + ufs_put_page(page); } - brelse (bh); return 1; + +not_empty: + ufs_put_page(page); + return 0; } const struct file_operations ufs_dir_operations = { diff --git a/fs/ufs/file.c b/fs/ufs/file.c index 312fd3f..a9c6e5f 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -25,6 +25,26 @@ #include #include +#include /* for sync_mapping_buffers() */ + +static int ufs_sync_file(struct file *file, struct dentry *dentry, int datasync) +{ + struct inode *inode = dentry->d_inode; + int err; + int ret; + + ret = sync_mapping_buffers(inode->i_mapping); + if (!(inode->i_state & I_DIRTY)) + return ret; + if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) + return ret; + + err = ufs_sync_inode(inode); + if (ret == 0) + ret = err; + return ret; +} + /* * We have mostly NULL's here: the current defaults are ok for @@ -37,9 +57,6 @@ const struct file_operations ufs_file_op .write = generic_file_write, .mmap = generic_file_mmap, .open = generic_file_open, + .fsync = ufs_sync_file, .sendfile = generic_file_sendfile, }; - -struct inode_operations ufs_file_inode_operations = { - .truncate = ufs_truncate, -}; diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c index c7a47ed..9501dcd 100644 --- a/fs/ufs/ialloc.c +++ b/fs/ufs/ialloc.c @@ -34,14 +34,6 @@ #include #include "swab.h" #include "util.h" -#undef UFS_IALLOC_DEBUG - -#ifdef UFS_IALLOC_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - /* * NOTE! When we get the inode, we're the only people * that have access to it, and as such there are no @@ -68,7 +60,7 @@ void ufs_free_inode (struct inode * inod int is_directory; unsigned ino, cg, bit; - UFSD(("ENTER, ino %lu\n", inode->i_ino)) + UFSD("ENTER, ino %lu\n", inode->i_ino); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -91,7 +83,7 @@ void ufs_free_inode (struct inode * inod unlock_super (sb); return; } - ucg = ubh_get_ucg(UCPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH(ucpi)); if (!ufs_cg_chkmagic(sb, ucg)) ufs_panic (sb, "ufs_free_fragments", "internal error, bad cg magic number"); @@ -104,33 +96,33 @@ void ufs_free_inode (struct inode * inod clear_inode (inode); - if (ubh_isclr (UCPI_UBH, ucpi->c_iusedoff, bit)) + if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit)) ufs_error(sb, "ufs_free_inode", "bit already cleared for inode %u", ino); else { - ubh_clrbit (UCPI_UBH, ucpi->c_iusedoff, bit); + ubh_clrbit (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit); if (ino < ucpi->c_irotor) ucpi->c_irotor = ino; fs32_add(sb, &ucg->cg_cs.cs_nifree, 1); - fs32_add(sb, &usb1->fs_cstotal.cs_nifree, 1); + uspi->cs_total.cs_nifree++; fs32_add(sb, &UFS_SB(sb)->fs_cs(cg).cs_nifree, 1); if (is_directory) { fs32_sub(sb, &ucg->cg_cs.cs_ndir, 1); - fs32_sub(sb, &usb1->fs_cstotal.cs_ndir, 1); + uspi->cs_total.cs_ndir--; fs32_sub(sb, &UFS_SB(sb)->fs_cs(cg).cs_ndir, 1); } } - ubh_mark_buffer_dirty (USPI_UBH); - ubh_mark_buffer_dirty (UCPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); if (sb->s_flags & MS_SYNCHRONOUS) { - ubh_ll_rw_block (SWRITE, 1, (struct ufs_buffer_head **) &ucpi); - ubh_wait_on_buffer (UCPI_UBH); + ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi)); + ubh_wait_on_buffer (UCPI_UBH(ucpi)); } sb->s_dirt = 1; unlock_super (sb); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); } /* @@ -155,7 +147,7 @@ struct inode * ufs_new_inode(struct inod unsigned cg, bit, i, j, start; struct ufs_inode_info *ufsi; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); /* Cannot create files in a deleted directory */ if (!dir || !dir->i_nlink) @@ -213,43 +205,43 @@ cg_found: ucpi = ufs_load_cylinder (sb, cg); if (!ucpi) goto failed; - ucg = ubh_get_ucg(UCPI_UBH); + ucg = ubh_get_ucg(UCPI_UBH(ucpi)); if (!ufs_cg_chkmagic(sb, ucg)) ufs_panic (sb, "ufs_new_inode", "internal error, bad cg magic number"); start = ucpi->c_irotor; - bit = ubh_find_next_zero_bit (UCPI_UBH, ucpi->c_iusedoff, uspi->s_ipg, start); + bit = ubh_find_next_zero_bit (UCPI_UBH(ucpi), ucpi->c_iusedoff, uspi->s_ipg, start); if (!(bit < uspi->s_ipg)) { - bit = ubh_find_first_zero_bit (UCPI_UBH, ucpi->c_iusedoff, start); + bit = ubh_find_first_zero_bit (UCPI_UBH(ucpi), ucpi->c_iusedoff, start); if (!(bit < start)) { ufs_error (sb, "ufs_new_inode", "cylinder group %u corrupted - error in inode bitmap\n", cg); goto failed; } } - UFSD(("start = %u, bit = %u, ipg = %u\n", start, bit, uspi->s_ipg)) - if (ubh_isclr (UCPI_UBH, ucpi->c_iusedoff, bit)) - ubh_setbit (UCPI_UBH, ucpi->c_iusedoff, bit); + UFSD("start = %u, bit = %u, ipg = %u\n", start, bit, uspi->s_ipg); + if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit)) + ubh_setbit (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit); else { ufs_panic (sb, "ufs_new_inode", "internal error"); goto failed; } fs32_sub(sb, &ucg->cg_cs.cs_nifree, 1); - fs32_sub(sb, &usb1->fs_cstotal.cs_nifree, 1); + uspi->cs_total.cs_nifree--; fs32_sub(sb, &sbi->fs_cs(cg).cs_nifree, 1); if (S_ISDIR(mode)) { fs32_add(sb, &ucg->cg_cs.cs_ndir, 1); - fs32_add(sb, &usb1->fs_cstotal.cs_ndir, 1); + uspi->cs_total.cs_ndir++; fs32_add(sb, &sbi->fs_cs(cg).cs_ndir, 1); } - ubh_mark_buffer_dirty (USPI_UBH); - ubh_mark_buffer_dirty (UCPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); + ubh_mark_buffer_dirty (UCPI_UBH(ucpi)); if (sb->s_flags & MS_SYNCHRONOUS) { - ubh_ll_rw_block (SWRITE, 1, (struct ufs_buffer_head **) &ucpi); - ubh_wait_on_buffer (UCPI_UBH); + ubh_ll_rw_block(SWRITE, UCPI_UBH(ucpi)); + ubh_wait_on_buffer (UCPI_UBH(ucpi)); } sb->s_dirt = 1; @@ -272,6 +264,7 @@ cg_found: ufsi->i_shadow = 0; ufsi->i_osync = 0; ufsi->i_oeftflag = 0; + ufsi->i_dir_start_lookup = 0; memset(&ufsi->i_u1, 0, sizeof(ufsi->i_u1)); insert_inode_hash(inode); @@ -287,14 +280,14 @@ cg_found: return ERR_PTR(-EDQUOT); } - UFSD(("allocating inode %lu\n", inode->i_ino)) - UFSD(("EXIT\n")) + UFSD("allocating inode %lu\n", inode->i_ino); + UFSD("EXIT\n"); return inode; failed: unlock_super (sb); make_bad_inode(inode); iput (inode); - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return ERR_PTR(-ENOSPC); } diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 3c3f62c..e7c8615 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -41,14 +41,7 @@ #include #include "swab.h" #include "util.h" -#undef UFS_INODE_DEBUG -#undef UFS_INODE_DEBUG_MORE - -#ifdef UFS_INODE_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif +static u64 ufs_frag_map(struct inode *inode, sector_t frag); static int ufs_block_to_path(struct inode *inode, sector_t i_block, sector_t offsets[4]) { @@ -61,7 +54,7 @@ static int ufs_block_to_path(struct inod int n = 0; - UFSD(("ptrs=uspi->s_apb = %d,double_blocks=%ld \n",ptrs,double_blocks)); + UFSD("ptrs=uspi->s_apb = %d,double_blocks=%ld \n",ptrs,double_blocks); if (i_block < 0) { ufs_warning(inode->i_sb, "ufs_block_to_path", "block < 0"); } else if (i_block < direct_blocks) { @@ -89,7 +82,7 @@ static int ufs_block_to_path(struct inod * the begining of the filesystem. */ -u64 ufs_frag_map(struct inode *inode, sector_t frag) +static u64 ufs_frag_map(struct inode *inode, sector_t frag) { struct ufs_inode_info *ufsi = UFS_I(inode); struct super_block *sb = inode->i_sb; @@ -104,8 +97,10 @@ u64 ufs_frag_map(struct inode *inode, s unsigned flags = UFS_SB(sb)->s_flags; u64 temp = 0L; - UFSD((": frag = %llu depth = %d\n", (unsigned long long)frag, depth)); - UFSD((": uspi->s_fpbshift = %d ,uspi->s_apbmask = %x, mask=%llx\n",uspi->s_fpbshift,uspi->s_apbmask,mask)); + UFSD(": frag = %llu depth = %d\n", (unsigned long long)frag, depth); + UFSD(": uspi->s_fpbshift = %d ,uspi->s_apbmask = %x, mask=%llx\n", + uspi->s_fpbshift, uspi->s_apbmask, + (unsigned long long)mask); if (depth == 0) return 0; @@ -161,26 +156,64 @@ out: return ret; } -static struct buffer_head * ufs_inode_getfrag (struct inode *inode, - unsigned int fragment, unsigned int new_fragment, - unsigned int required, int *err, int metadata, long *phys, int *new) +static void ufs_clear_frag(struct inode *inode, struct buffer_head *bh) +{ + lock_buffer(bh); + memset(bh->b_data, 0, inode->i_sb->s_blocksize); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + unlock_buffer(bh); + if (IS_SYNC(inode)) + sync_dirty_buffer(bh); +} + +static struct buffer_head * +ufs_clear_frags(struct inode *inode, sector_t beg, + unsigned int n) +{ + struct buffer_head *res, *bh; + sector_t end = beg + n; + + res = sb_getblk(inode->i_sb, beg); + ufs_clear_frag(inode, res); + for (++beg; beg < end; ++beg) { + bh = sb_getblk(inode->i_sb, beg); + ufs_clear_frag(inode, bh); + brelse(bh); + } + return res; +} + +/** + * ufs_inode_getfrag() - allocate new fragment(s) + * @inode - pointer to inode + * @fragment - number of `fragment' which hold pointer + * to new allocated fragment(s) + * @new_fragment - number of new allocated fragment(s) + * @required - how many fragment(s) we require + * @err - we set it if something wrong + * @phys - pointer to where we save physical number of new allocated fragments, + * NULL if we allocate not data(indirect blocks for example). + * @new - we set it if we allocate new block + * @locked_page - for ufs_new_fragments() + */ +static struct buffer_head * +ufs_inode_getfrag(struct inode *inode, unsigned int fragment, + sector_t new_fragment, unsigned int required, int *err, + long *phys, int *new, struct page *locked_page) { struct ufs_inode_info *ufsi = UFS_I(inode); - struct super_block * sb; - struct ufs_sb_private_info * uspi; + struct super_block *sb = inode->i_sb; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; struct buffer_head * result; unsigned block, blockoff, lastfrag, lastblock, lastblockoff; unsigned tmp, goal; __fs32 * p, * p2; - unsigned flags = 0; - UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u, required %u\n", - inode->i_ino, fragment, new_fragment, required)) + UFSD("ENTER, ino %lu, fragment %u, new_fragment %llu, required %u, " + "metadata %d\n", inode->i_ino, fragment, + (unsigned long long)new_fragment, required, !phys); - sb = inode->i_sb; - uspi = UFS_SB(sb)->s_uspi; - - flags = UFS_SB(sb)->s_flags; /* TODO : to be done for write support if ( (flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) goto ufs2; @@ -195,16 +228,16 @@ repeat: tmp = fs32_to_cpu(sb, *p); lastfrag = ufsi->i_lastfrag; if (tmp && fragment < lastfrag) { - if (metadata) { + if (!phys) { result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff); if (tmp == fs32_to_cpu(sb, *p)) { - UFSD(("EXIT, result %u\n", tmp + blockoff)) + UFSD("EXIT, result %u\n", tmp + blockoff); return result; } brelse (result); goto repeat; } else { - *phys = tmp; + *phys = tmp + blockoff; return NULL; } } @@ -221,7 +254,8 @@ repeat: if (lastblockoff) { p2 = ufsi->i_u1.i_data + lastblock; tmp = ufs_new_fragments (inode, p2, lastfrag, - fs32_to_cpu(sb, *p2), uspi->s_fpb - lastblockoff, err); + fs32_to_cpu(sb, *p2), uspi->s_fpb - lastblockoff, + err, locked_page); if (!tmp) { if (lastfrag != ufsi->i_lastfrag) goto repeat; @@ -233,14 +267,16 @@ repeat: } goal = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock]) + uspi->s_fpb; tmp = ufs_new_fragments (inode, p, fragment - blockoff, - goal, required + blockoff, err); + goal, required + blockoff, + err, locked_page); } /* * We will extend last allocated block */ else if (lastblock == block) { - tmp = ufs_new_fragments (inode, p, fragment - (blockoff - lastblockoff), - fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff), err); + tmp = ufs_new_fragments(inode, p, fragment - (blockoff - lastblockoff), + fs32_to_cpu(sb, *p), required + (blockoff - lastblockoff), + err, locked_page); } /* * We will allocate new block before last allocated block @@ -248,8 +284,8 @@ repeat: else /* (lastblock > block) */ { if (lastblock && (tmp = fs32_to_cpu(sb, ufsi->i_u1.i_data[lastblock-1]))) goal = tmp + uspi->s_fpb; - tmp = ufs_new_fragments (inode, p, fragment - blockoff, - goal, uspi->s_fpb, err); + tmp = ufs_new_fragments(inode, p, fragment - blockoff, + goal, uspi->s_fpb, err, locked_page); } if (!tmp) { if ((!blockoff && *p) || @@ -259,14 +295,10 @@ repeat: return NULL; } - /* The nullification of framgents done in ufs/balloc.c is - * something I don't have the stomache to move into here right - * now. -DaveM - */ - if (metadata) { - result = sb_getblk(inode->i_sb, tmp + blockoff); + if (!phys) { + result = ufs_clear_frags(inode, tmp + blockoff, required); } else { - *phys = tmp; + *phys = tmp + blockoff; result = NULL; *err = 0; *new = 1; @@ -276,7 +308,7 @@ repeat: if (IS_SYNC(inode)) ufs_sync_inode (inode); mark_inode_dirty(inode); - UFSD(("EXIT, result %u\n", tmp + blockoff)) + UFSD("EXIT, result %u\n", tmp + blockoff); return result; /* This part : To be implemented .... @@ -295,22 +327,35 @@ repeat2: */ } -static struct buffer_head * ufs_block_getfrag (struct inode *inode, - struct buffer_head *bh, unsigned int fragment, unsigned int new_fragment, - unsigned int blocksize, int * err, int metadata, long *phys, int *new) +/** + * ufs_inode_getblock() - allocate new block + * @inode - pointer to inode + * @bh - pointer to block which hold "pointer" to new allocated block + * @fragment - number of `fragment' which hold pointer + * to new allocated block + * @new_fragment - number of new allocated fragment + * (block will hold this fragment and also uspi->s_fpb-1) + * @err - see ufs_inode_getfrag() + * @phys - see ufs_inode_getfrag() + * @new - see ufs_inode_getfrag() + * @locked_page - see ufs_inode_getfrag() + */ +static struct buffer_head * +ufs_inode_getblock(struct inode *inode, struct buffer_head *bh, + unsigned int fragment, sector_t new_fragment, int *err, + long *phys, int *new, struct page *locked_page) { - struct super_block * sb; - struct ufs_sb_private_info * uspi; + struct super_block *sb = inode->i_sb; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; struct buffer_head * result; unsigned tmp, goal, block, blockoff; __fs32 * p; - sb = inode->i_sb; - uspi = UFS_SB(sb)->s_uspi; block = ufs_fragstoblks (fragment); blockoff = ufs_fragnum (fragment); - UFSD(("ENTER, ino %lu, fragment %u, new_fragment %u\n", inode->i_ino, fragment, new_fragment)) + UFSD("ENTER, ino %lu, fragment %u, new_fragment %llu, metadata %d\n", + inode->i_ino, fragment, (unsigned long long)new_fragment, !phys); result = NULL; if (!bh) @@ -326,14 +371,14 @@ static struct buffer_head * ufs_block_ge repeat: tmp = fs32_to_cpu(sb, *p); if (tmp) { - if (metadata) { + if (!phys) { result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff); if (tmp == fs32_to_cpu(sb, *p)) goto out; brelse (result); goto repeat; } else { - *phys = tmp; + *phys = tmp + blockoff; goto out; } } @@ -342,21 +387,19 @@ repeat: goal = tmp + uspi->s_fpb; else goal = bh->b_blocknr + uspi->s_fpb; - tmp = ufs_new_fragments (inode, p, ufs_blknum(new_fragment), goal, uspi->s_fpb, err); + tmp = ufs_new_fragments(inode, p, ufs_blknum(new_fragment), goal, + uspi->s_fpb, err, locked_page); if (!tmp) { if (fs32_to_cpu(sb, *p)) goto repeat; goto out; } - /* The nullification of framgents done in ufs/balloc.c is - * something I don't have the stomache to move into here right - * now. -DaveM - */ - if (metadata) { - result = sb_getblk(sb, tmp + blockoff); + + if (!phys) { + result = ufs_clear_frags(inode, tmp + blockoff, uspi->s_fpb); } else { - *phys = tmp; + *phys = tmp + blockoff; *new = 1; } @@ -365,18 +408,19 @@ repeat: sync_dirty_buffer(bh); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); - UFSD(("result %u\n", tmp + blockoff)); + UFSD("result %u\n", tmp + blockoff); out: brelse (bh); - UFSD(("EXIT\n")); + UFSD("EXIT\n"); return result; } -/* - * This function gets the block which contains the fragment. +/** + * ufs_getfrag_bloc() - `get_block_t' function, interface between UFS and + * readpage, writepage and so on */ -int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create) +int ufs_getfrag_block(struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create) { struct super_block * sb = inode->i_sb; struct ufs_sb_private_info * uspi = UFS_SB(sb)->s_uspi; @@ -387,7 +431,7 @@ int ufs_getfrag_block (struct inode *ino if (!create) { phys64 = ufs_frag_map(inode, fragment); - UFSD(("phys64 = %llu \n",phys64)); + UFSD("phys64 = %llu\n", (unsigned long long)phys64); if (phys64) map_bh(bh_result, sb, phys64); return 0; @@ -402,7 +446,7 @@ int ufs_getfrag_block (struct inode *ino lock_kernel(); - UFSD(("ENTER, ino %lu, fragment %llu\n", inode->i_ino, (unsigned long long)fragment)) + UFSD("ENTER, ino %lu, fragment %llu\n", inode->i_ino, (unsigned long long)fragment); if (fragment < 0) goto abort_negative; if (fragment > @@ -418,15 +462,15 @@ int ufs_getfrag_block (struct inode *ino * it much more readable: */ #define GET_INODE_DATABLOCK(x) \ - ufs_inode_getfrag(inode, x, fragment, 1, &err, 0, &phys, &new) + ufs_inode_getfrag(inode, x, fragment, 1, &err, &phys, &new, bh_result->b_page) #define GET_INODE_PTR(x) \ - ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, 1, NULL, NULL) + ufs_inode_getfrag(inode, x, fragment, uspi->s_fpb, &err, NULL, NULL, bh_result->b_page) #define GET_INDIRECT_DATABLOCK(x) \ - ufs_block_getfrag(inode, bh, x, fragment, sb->s_blocksize, \ - &err, 0, &phys, &new); + ufs_inode_getblock(inode, bh, x, fragment, \ + &err, &phys, &new, bh_result->b_page); #define GET_INDIRECT_PTR(x) \ - ufs_block_getfrag(inode, bh, x, fragment, sb->s_blocksize, \ - &err, 1, NULL, NULL); + ufs_inode_getblock(inode, bh, x, fragment, \ + &err, NULL, NULL, bh_result->b_page); if (ptr < UFS_NDIR_FRAGMENT) { bh = GET_INODE_DATABLOCK(ptr); @@ -474,8 +518,9 @@ abort_too_big: goto abort; } -struct buffer_head *ufs_getfrag(struct inode *inode, unsigned int fragment, - int create, int *err) +static struct buffer_head *ufs_getfrag(struct inode *inode, + unsigned int fragment, + int create, int *err) { struct buffer_head dummy; int error; @@ -502,7 +547,7 @@ struct buffer_head * ufs_bread (struct i { struct buffer_head * bh; - UFSD(("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment)) + UFSD("ENTER, ino %lu, fragment %u\n", inode->i_ino, fragment); bh = ufs_getfrag (inode, fragment, create, err); if (!bh || buffer_uptodate(bh)) return bh; @@ -531,7 +576,7 @@ static sector_t ufs_bmap(struct address_ { return generic_block_bmap(mapping,block,ufs_getfrag_block); } -struct address_space_operations ufs_aops = { +const struct address_space_operations ufs_aops = { .readpage = ufs_readpage, .writepage = ufs_writepage, .sync_page = block_sync_page, @@ -540,39 +585,34 @@ struct address_space_operations ufs_aops .bmap = ufs_bmap }; -void ufs_read_inode (struct inode * inode) +static void ufs_set_inode_ops(struct inode *inode) +{ + if (S_ISREG(inode->i_mode)) { + inode->i_op = &ufs_file_inode_operations; + inode->i_fop = &ufs_file_operations; + inode->i_mapping->a_ops = &ufs_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &ufs_dir_inode_operations; + inode->i_fop = &ufs_dir_operations; + inode->i_mapping->a_ops = &ufs_aops; + } else if (S_ISLNK(inode->i_mode)) { + if (!inode->i_blocks) + inode->i_op = &ufs_fast_symlink_inode_operations; + else { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ufs_aops; + } + } else + init_special_inode(inode, inode->i_mode, + ufs_get_inode_dev(inode->i_sb, UFS_I(inode))); +} + +static void ufs1_read_inode(struct inode *inode, struct ufs_inode *ufs_inode) { struct ufs_inode_info *ufsi = UFS_I(inode); - struct super_block * sb; - struct ufs_sb_private_info * uspi; - struct ufs_inode * ufs_inode; - struct ufs2_inode *ufs2_inode; - struct buffer_head * bh; + struct super_block *sb = inode->i_sb; mode_t mode; unsigned i; - unsigned flags; - - UFSD(("ENTER, ino %lu\n", inode->i_ino)) - - sb = inode->i_sb; - uspi = UFS_SB(sb)->s_uspi; - flags = UFS_SB(sb)->s_flags; - - if (inode->i_ino < UFS_ROOTINO || - inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) { - ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino); - goto bad_inode; - } - - bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino)); - if (!bh) { - ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino); - goto bad_inode; - } - if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) - goto ufs2_inode; - - ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino)); /* * Copy data to the in-core inode. @@ -596,56 +636,29 @@ void ufs_read_inode (struct inode * inod inode->i_atime.tv_nsec = 0; inode->i_ctime.tv_nsec = 0; inode->i_blocks = fs32_to_cpu(sb, ufs_inode->ui_blocks); - inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */ - inode->i_version++; ufsi->i_flags = fs32_to_cpu(sb, ufs_inode->ui_flags); ufsi->i_gen = fs32_to_cpu(sb, ufs_inode->ui_gen); ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow); ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag); - ufsi->i_lastfrag = (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift; + if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) ufsi->i_u1.i_data[i] = ufs_inode->ui_u2.ui_addr.ui_db[i]; - } - else { + } else { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) ufsi->i_u1.i_symlink[i] = ufs_inode->ui_u2.ui_symlink[i]; } - ufsi->i_osync = 0; - - if (S_ISREG(inode->i_mode)) { - inode->i_op = &ufs_file_inode_operations; - inode->i_fop = &ufs_file_operations; - inode->i_mapping->a_ops = &ufs_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &ufs_dir_inode_operations; - inode->i_fop = &ufs_dir_operations; - } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) - inode->i_op = &ufs_fast_symlink_inode_operations; - else { - inode->i_op = &page_symlink_inode_operations; - inode->i_mapping->a_ops = &ufs_aops; - } - } else - init_special_inode(inode, inode->i_mode, - ufs_get_inode_dev(sb, ufsi)); - - brelse (bh); - - UFSD(("EXIT\n")) - return; - -bad_inode: - make_bad_inode(inode); - return; - -ufs2_inode : - UFSD(("Reading ufs2 inode, ino %lu\n", inode->i_ino)) +} - ufs2_inode = (struct ufs2_inode *)(bh->b_data + sizeof(struct ufs2_inode) * ufs_inotofsbo(inode->i_ino)); +static void ufs2_read_inode(struct inode *inode, struct ufs2_inode *ufs2_inode) +{ + struct ufs_inode_info *ufsi = UFS_I(inode); + struct super_block *sb = inode->i_sb; + mode_t mode; + unsigned i; + UFSD("Reading ufs2 inode, ino %lu\n", inode->i_ino); /* * Copy data to the in-core inode. */ @@ -668,50 +681,75 @@ ufs2_inode : inode->i_atime.tv_nsec = 0; inode->i_ctime.tv_nsec = 0; inode->i_blocks = fs64_to_cpu(sb, ufs2_inode->ui_blocks); - inode->i_blksize = PAGE_SIZE; /*This is the optimal IO size(for stat)*/ - - inode->i_version++; ufsi->i_flags = fs32_to_cpu(sb, ufs2_inode->ui_flags); ufsi->i_gen = fs32_to_cpu(sb, ufs2_inode->ui_gen); /* ufsi->i_shadow = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_shadow); ufsi->i_oeftflag = fs32_to_cpu(sb, ufs_inode->ui_u3.ui_sun.ui_oeftflag); */ - ufsi->i_lastfrag= (inode->i_size + uspi->s_fsize- 1) >> uspi->s_fshift; if (S_ISCHR(mode) || S_ISBLK(mode) || inode->i_blocks) { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR); i++) ufsi->i_u1.u2_i_data[i] = ufs2_inode->ui_u2.ui_addr.ui_db[i]; - } - else { + } else { for (i = 0; i < (UFS_NDADDR + UFS_NINDIR) * 4; i++) ufsi->i_u1.i_symlink[i] = ufs2_inode->ui_u2.ui_symlink[i]; } +} + +void ufs_read_inode(struct inode * inode) +{ + struct ufs_inode_info *ufsi = UFS_I(inode); + struct super_block * sb; + struct ufs_sb_private_info * uspi; + struct buffer_head * bh; + + UFSD("ENTER, ino %lu\n", inode->i_ino); + + sb = inode->i_sb; + uspi = UFS_SB(sb)->s_uspi; + + if (inode->i_ino < UFS_ROOTINO || + inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) { + ufs_warning(sb, "ufs_read_inode", "bad inode number (%lu)\n", + inode->i_ino); + goto bad_inode; + } + + bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino)); + if (!bh) { + ufs_warning(sb, "ufs_read_inode", "unable to read inode %lu\n", + inode->i_ino); + goto bad_inode; + } + if ((UFS_SB(sb)->s_flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + struct ufs2_inode *ufs2_inode = (struct ufs2_inode *)bh->b_data; + + ufs2_read_inode(inode, + ufs2_inode + ufs_inotofsbo(inode->i_ino)); + } else { + struct ufs_inode *ufs_inode = (struct ufs_inode *)bh->b_data; + + ufs1_read_inode(inode, ufs_inode + ufs_inotofsbo(inode->i_ino)); + } + + inode->i_blksize = PAGE_SIZE;/*This is the optimal IO size (for stat)*/ + inode->i_version++; + ufsi->i_lastfrag = + (inode->i_size + uspi->s_fsize - 1) >> uspi->s_fshift; + ufsi->i_dir_start_lookup = 0; ufsi->i_osync = 0; - if (S_ISREG(inode->i_mode)) { - inode->i_op = &ufs_file_inode_operations; - inode->i_fop = &ufs_file_operations; - inode->i_mapping->a_ops = &ufs_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &ufs_dir_inode_operations; - inode->i_fop = &ufs_dir_operations; - } else if (S_ISLNK(inode->i_mode)) { - if (!inode->i_blocks) - inode->i_op = &ufs_fast_symlink_inode_operations; - else { - inode->i_op = &page_symlink_inode_operations; - inode->i_mapping->a_ops = &ufs_aops; - } - } else /* TODO : here ...*/ - init_special_inode(inode, inode->i_mode, - ufs_get_inode_dev(sb, ufsi)); + ufs_set_inode_ops(inode); brelse(bh); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return; + +bad_inode: + make_bad_inode(inode); } static int ufs_update_inode(struct inode * inode, int do_sync) @@ -724,7 +762,7 @@ static int ufs_update_inode(struct inode unsigned i; unsigned flags; - UFSD(("ENTER, ino %lu\n", inode->i_ino)) + UFSD("ENTER, ino %lu\n", inode->i_ino); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -785,7 +823,7 @@ static int ufs_update_inode(struct inode sync_dirty_buffer(bh); brelse (bh); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return 0; } @@ -805,14 +843,17 @@ int ufs_sync_inode (struct inode *inode) void ufs_delete_inode (struct inode * inode) { + loff_t old_i_size; + truncate_inode_pages(&inode->i_data, 0); /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ lock_kernel(); mark_inode_dirty(inode); ufs_update_inode(inode, IS_SYNC(inode)); + old_i_size = inode->i_size; inode->i_size = 0; - if (inode->i_blocks) - ufs_truncate (inode); + if (inode->i_blocks && ufs_truncate(inode, old_i_size)) + ufs_warning(inode->i_sb, __FUNCTION__, "ufs_truncate failed\n"); ufs_free_inode (inode); unlock_kernel(); } diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 8d5f98a..abd5f23 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -1,6 +1,9 @@ /* * linux/fs/ufs/namei.c * + * Migration to usage of "page cache" on May 2006 by + * Evgeniy Dushistov based on ext2 code base. + * * Copyright (C) 1998 * Daniel Pirkl * Charles University, Faculty of Mathematics and Physics @@ -28,21 +31,9 @@ #include #include #include #include -#include #include "swab.h" /* will go away - see comment in mknod() */ #include "util.h" -/* -#undef UFS_NAMEI_DEBUG -*/ -#define UFS_NAMEI_DEBUG - -#ifdef UFS_NAMEI_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - static inline int ufs_add_nondir(struct dentry *dentry, struct inode *inode) { int err = ufs_add_link(dentry, inode); @@ -88,8 +79,13 @@ static struct dentry *ufs_lookup(struct static int ufs_create (struct inode * dir, struct dentry * dentry, int mode, struct nameidata *nd) { - struct inode * inode = ufs_new_inode(dir, mode); - int err = PTR_ERR(inode); + struct inode *inode; + int err; + + UFSD("BEGIN\n"); + inode = ufs_new_inode(dir, mode); + err = PTR_ERR(inode); + if (!IS_ERR(inode)) { inode->i_op = &ufs_file_inode_operations; inode->i_fop = &ufs_file_operations; @@ -99,6 +95,7 @@ static int ufs_create (struct inode * di err = ufs_add_nondir(dentry, inode); unlock_kernel(); } + UFSD("END: err=%d\n", err); return err; } @@ -205,6 +202,7 @@ static int ufs_mkdir(struct inode * dir, inode->i_op = &ufs_dir_inode_operations; inode->i_fop = &ufs_dir_operations; + inode->i_mapping->a_ops = &ufs_aops; inode_inc_link_count(inode); @@ -231,19 +229,18 @@ out_dir: goto out; } -static int ufs_unlink(struct inode * dir, struct dentry *dentry) +static int ufs_unlink(struct inode *dir, struct dentry *dentry) { struct inode * inode = dentry->d_inode; - struct buffer_head * bh; - struct ufs_dir_entry * de; + struct ufs_dir_entry *de; + struct page *page; int err = -ENOENT; - lock_kernel(); - de = ufs_find_entry (dentry, &bh); + de = ufs_find_entry(dir, dentry, &page); if (!de) goto out; - err = ufs_delete_entry (dir, de, bh); + err = ufs_delete_entry(dir, de, page); if (err) goto out; @@ -251,7 +248,6 @@ static int ufs_unlink(struct inode * dir inode_dec_link_count(inode); err = 0; out: - unlock_kernel(); return err; } @@ -273,42 +269,42 @@ static int ufs_rmdir (struct inode * dir return err; } -static int ufs_rename (struct inode * old_dir, struct dentry * old_dentry, - struct inode * new_dir, struct dentry * new_dentry ) +static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; - struct buffer_head *dir_bh = NULL; - struct ufs_dir_entry *dir_de = NULL; - struct buffer_head *old_bh; + struct page *dir_page = NULL; + struct ufs_dir_entry * dir_de = NULL; + struct page *old_page; struct ufs_dir_entry *old_de; int err = -ENOENT; - lock_kernel(); - old_de = ufs_find_entry (old_dentry, &old_bh); + old_de = ufs_find_entry(old_dir, old_dentry, &old_page); if (!old_de) goto out; if (S_ISDIR(old_inode->i_mode)) { err = -EIO; - dir_de = ufs_dotdot(old_inode, &dir_bh); + dir_de = ufs_dotdot(old_inode, &dir_page); if (!dir_de) goto out_old; } if (new_inode) { - struct buffer_head *new_bh; + struct page *new_page; struct ufs_dir_entry *new_de; err = -ENOTEMPTY; - if (dir_de && !ufs_empty_dir (new_inode)) + if (dir_de && !ufs_empty_dir(new_inode)) goto out_dir; + err = -ENOENT; - new_de = ufs_find_entry (new_dentry, &new_bh); + new_de = ufs_find_entry(new_dir, new_dentry, &new_page); if (!new_de) goto out_dir; inode_inc_link_count(old_inode); - ufs_set_link(new_dir, new_de, new_bh, old_inode); + ufs_set_link(new_dir, new_de, new_page, old_inode); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) new_inode->i_nlink--; @@ -329,24 +325,32 @@ static int ufs_rename (struct inode * ol inode_inc_link_count(new_dir); } - ufs_delete_entry (old_dir, old_de, old_bh); + /* + * Like most other Unix systems, set the ctime for inodes on a + * rename. + * inode_dec_link_count() will mark the inode dirty. + */ + old_inode->i_ctime = CURRENT_TIME_SEC; + ufs_delete_entry(old_dir, old_de, old_page); inode_dec_link_count(old_inode); if (dir_de) { - ufs_set_link(old_inode, dir_de, dir_bh, new_dir); + ufs_set_link(old_inode, dir_de, dir_page, new_dir); inode_dec_link_count(old_dir); } - unlock_kernel(); return 0; + out_dir: - if (dir_de) - brelse(dir_bh); + if (dir_de) { + kunmap(dir_page); + page_cache_release(dir_page); + } out_old: - brelse (old_bh); + kunmap(old_page); + page_cache_release(old_page); out: - unlock_kernel(); return err; } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index db98a4c..992ee0b 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -64,7 +64,6 @@ */ -#include #include #include @@ -90,95 +89,84 @@ #include #include "swab.h" #include "util.h" -#undef UFS_SUPER_DEBUG -#undef UFS_SUPER_DEBUG_MORE - - -#undef UFS_SUPER_DEBUG_MORE -#ifdef UFS_SUPER_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - -#ifdef UFS_SUPER_DEBUG_MORE +#ifdef CONFIG_UFS_DEBUG /* * Print contents of ufs_super_block, useful for debugging */ -void ufs_print_super_stuff(struct super_block *sb, - struct ufs_super_block_first * usb1, - struct ufs_super_block_second * usb2, - struct ufs_super_block_third * usb3) +static void ufs_print_super_stuff(struct super_block *sb, unsigned flags, + struct ufs_super_block_first *usb1, + struct ufs_super_block_second *usb2, + struct ufs_super_block_third *usb3) { printk("ufs_print_super_stuff\n"); - printk("size of usb: %u\n", sizeof(struct ufs_super_block)); - printk(" magic: 0x%x\n", fs32_to_cpu(sb, usb3->fs_magic)); - printk(" sblkno: %u\n", fs32_to_cpu(sb, usb1->fs_sblkno)); - printk(" cblkno: %u\n", fs32_to_cpu(sb, usb1->fs_cblkno)); - printk(" iblkno: %u\n", fs32_to_cpu(sb, usb1->fs_iblkno)); - printk(" dblkno: %u\n", fs32_to_cpu(sb, usb1->fs_dblkno)); - printk(" cgoffset: %u\n", fs32_to_cpu(sb, usb1->fs_cgoffset)); - printk(" ~cgmask: 0x%x\n", ~fs32_to_cpu(sb, usb1->fs_cgmask)); - printk(" size: %u\n", fs32_to_cpu(sb, usb1->fs_size)); - printk(" dsize: %u\n", fs32_to_cpu(sb, usb1->fs_dsize)); - printk(" ncg: %u\n", fs32_to_cpu(sb, usb1->fs_ncg)); - printk(" bsize: %u\n", fs32_to_cpu(sb, usb1->fs_bsize)); - printk(" fsize: %u\n", fs32_to_cpu(sb, usb1->fs_fsize)); - printk(" frag: %u\n", fs32_to_cpu(sb, usb1->fs_frag)); - printk(" fragshift: %u\n", fs32_to_cpu(sb, usb1->fs_fragshift)); - printk(" ~fmask: %u\n", ~fs32_to_cpu(sb, usb1->fs_fmask)); - printk(" fshift: %u\n", fs32_to_cpu(sb, usb1->fs_fshift)); - printk(" sbsize: %u\n", fs32_to_cpu(sb, usb1->fs_sbsize)); - printk(" spc: %u\n", fs32_to_cpu(sb, usb1->fs_spc)); - printk(" cpg: %u\n", fs32_to_cpu(sb, usb1->fs_cpg)); - printk(" ipg: %u\n", fs32_to_cpu(sb, usb1->fs_ipg)); - printk(" fpg: %u\n", fs32_to_cpu(sb, usb1->fs_fpg)); - printk(" csaddr: %u\n", fs32_to_cpu(sb, usb1->fs_csaddr)); - printk(" cssize: %u\n", fs32_to_cpu(sb, usb1->fs_cssize)); - printk(" cgsize: %u\n", fs32_to_cpu(sb, usb1->fs_cgsize)); - printk(" fstodb: %u\n", fs32_to_cpu(sb, usb1->fs_fsbtodb)); - printk(" contigsumsize: %d\n", fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_contigsumsize)); - printk(" postblformat: %u\n", fs32_to_cpu(sb, usb3->fs_postblformat)); - printk(" nrpos: %u\n", fs32_to_cpu(sb, usb3->fs_nrpos)); - printk(" ndir %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_ndir)); - printk(" nifree %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree)); - printk(" nbfree %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree)); - printk(" nffree %u\n", fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree)); - printk("\n"); -} - -/* - * Print contents of ufs2 ufs_super_block, useful for debugging - */ -void ufs2_print_super_stuff( - struct super_block *sb, - struct ufs_super_block *usb) -{ - printk("ufs_print_super_stuff\n"); - printk("size of usb: %u\n", sizeof(struct ufs_super_block)); - printk(" magic: 0x%x\n", fs32_to_cpu(sb, usb->fs_magic)); - printk(" fs_size: %u\n",fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_size)); - printk(" fs_dsize: %u\n",fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_dsize)); - printk(" bsize: %u\n", fs32_to_cpu(usb, usb->fs_bsize)); - printk(" fsize: %u\n", fs32_to_cpu(usb, usb->fs_fsize)); - printk(" fs_volname: %s\n", usb->fs_u11.fs_u2.fs_volname); - printk(" fs_fsmnt: %s\n", usb->fs_u11.fs_u2.fs_fsmnt); - printk(" fs_sblockloc: %u\n",fs64_to_cpu(sb, - usb->fs_u11.fs_u2.fs_sblockloc)); - printk(" cs_ndir(No of dirs): %u\n",fs64_to_cpu(sb, - usb->fs_u11.fs_u2.fs_cstotal.cs_ndir)); - printk(" cs_nbfree(No of free blocks): %u\n",fs64_to_cpu(sb, - usb->fs_u11.fs_u2.fs_cstotal.cs_nbfree)); + printk(" magic: 0x%x\n", fs32_to_cpu(sb, usb3->fs_magic)); + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + printk(" fs_size: %llu\n", (unsigned long long) + fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_size)); + printk(" fs_dsize: %llu\n", (unsigned long long) + fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_dsize)); + printk(" bsize: %u\n", + fs32_to_cpu(sb, usb1->fs_bsize)); + printk(" fsize: %u\n", + fs32_to_cpu(sb, usb1->fs_fsize)); + printk(" fs_volname: %s\n", usb2->fs_un.fs_u2.fs_volname); + printk(" fs_sblockloc: %llu\n", (unsigned long long) + fs64_to_cpu(sb, usb2->fs_un.fs_u2.fs_sblockloc)); + printk(" cs_ndir(No of dirs): %llu\n", (unsigned long long) + fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_ndir)); + printk(" cs_nbfree(No of free blocks): %llu\n", + (unsigned long long) + fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_nbfree)); + } else { + printk(" sblkno: %u\n", fs32_to_cpu(sb, usb1->fs_sblkno)); + printk(" cblkno: %u\n", fs32_to_cpu(sb, usb1->fs_cblkno)); + printk(" iblkno: %u\n", fs32_to_cpu(sb, usb1->fs_iblkno)); + printk(" dblkno: %u\n", fs32_to_cpu(sb, usb1->fs_dblkno)); + printk(" cgoffset: %u\n", + fs32_to_cpu(sb, usb1->fs_cgoffset)); + printk(" ~cgmask: 0x%x\n", + ~fs32_to_cpu(sb, usb1->fs_cgmask)); + printk(" size: %u\n", fs32_to_cpu(sb, usb1->fs_size)); + printk(" dsize: %u\n", fs32_to_cpu(sb, usb1->fs_dsize)); + printk(" ncg: %u\n", fs32_to_cpu(sb, usb1->fs_ncg)); + printk(" bsize: %u\n", fs32_to_cpu(sb, usb1->fs_bsize)); + printk(" fsize: %u\n", fs32_to_cpu(sb, usb1->fs_fsize)); + printk(" frag: %u\n", fs32_to_cpu(sb, usb1->fs_frag)); + printk(" fragshift: %u\n", + fs32_to_cpu(sb, usb1->fs_fragshift)); + printk(" ~fmask: %u\n", ~fs32_to_cpu(sb, usb1->fs_fmask)); + printk(" fshift: %u\n", fs32_to_cpu(sb, usb1->fs_fshift)); + printk(" sbsize: %u\n", fs32_to_cpu(sb, usb1->fs_sbsize)); + printk(" spc: %u\n", fs32_to_cpu(sb, usb1->fs_spc)); + printk(" cpg: %u\n", fs32_to_cpu(sb, usb1->fs_cpg)); + printk(" ipg: %u\n", fs32_to_cpu(sb, usb1->fs_ipg)); + printk(" fpg: %u\n", fs32_to_cpu(sb, usb1->fs_fpg)); + printk(" csaddr: %u\n", fs32_to_cpu(sb, usb1->fs_csaddr)); + printk(" cssize: %u\n", fs32_to_cpu(sb, usb1->fs_cssize)); + printk(" cgsize: %u\n", fs32_to_cpu(sb, usb1->fs_cgsize)); + printk(" fstodb: %u\n", + fs32_to_cpu(sb, usb1->fs_fsbtodb)); + printk(" nrpos: %u\n", fs32_to_cpu(sb, usb3->fs_nrpos)); + printk(" ndir %u\n", + fs32_to_cpu(sb, usb1->fs_cstotal.cs_ndir)); + printk(" nifree %u\n", + fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree)); + printk(" nbfree %u\n", + fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree)); + printk(" nffree %u\n", + fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree)); + } printk("\n"); } /* * Print contents of ufs_cylinder_group, useful for debugging */ -void ufs_print_cylinder_stuff(struct super_block *sb, struct ufs_cylinder_group *cg) +static void ufs_print_cylinder_stuff(struct super_block *sb, + struct ufs_cylinder_group *cg) { printk("\nufs_print_cylinder_stuff\n"); - printk("size of ucg: %u\n", sizeof(struct ufs_cylinder_group)); + printk("size of ucg: %zu\n", sizeof(struct ufs_cylinder_group)); printk(" magic: %x\n", fs32_to_cpu(sb, cg->cg_magic)); printk(" time: %u\n", fs32_to_cpu(sb, cg->cg_time)); printk(" cgx: %u\n", fs32_to_cpu(sb, cg->cg_cgx)); @@ -202,12 +190,18 @@ void ufs_print_cylinder_stuff(struct sup printk(" iuseoff: %u\n", fs32_to_cpu(sb, cg->cg_iusedoff)); printk(" freeoff: %u\n", fs32_to_cpu(sb, cg->cg_freeoff)); printk(" nextfreeoff: %u\n", fs32_to_cpu(sb, cg->cg_nextfreeoff)); - printk(" clustersumoff %u\n", fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clustersumoff)); - printk(" clusteroff %u\n", fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clusteroff)); - printk(" nclusterblks %u\n", fs32_to_cpu(sb, cg->cg_u.cg_44.cg_nclusterblks)); + printk(" clustersumoff %u\n", + fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clustersumoff)); + printk(" clusteroff %u\n", + fs32_to_cpu(sb, cg->cg_u.cg_44.cg_clusteroff)); + printk(" nclusterblks %u\n", + fs32_to_cpu(sb, cg->cg_u.cg_44.cg_nclusterblks)); printk("\n"); } -#endif /* UFS_SUPER_DEBUG_MORE */ +#else +# define ufs_print_super_stuff(sb, flags, usb1, usb2, usb3) /**/ +# define ufs_print_cylinder_stuff(sb, cg) /**/ +#endif /* CONFIG_UFS_DEBUG */ static struct super_operations ufs_super_ops; @@ -225,7 +219,7 @@ void ufs_error (struct super_block * sb, if (!(sb->s_flags & MS_RDONLY)) { usb1->fs_clean = UFS_FSBAD; - ubh_mark_buffer_dirty(USPI_UBH); + ubh_mark_buffer_dirty(USPI_UBH(uspi)); sb->s_dirt = 1; sb->s_flags |= MS_RDONLY; } @@ -257,7 +251,7 @@ void ufs_panic (struct super_block * sb, if (!(sb->s_flags & MS_RDONLY)) { usb1->fs_clean = UFS_FSBAD; - ubh_mark_buffer_dirty(USPI_UBH); + ubh_mark_buffer_dirty(USPI_UBH(uspi)); sb->s_dirt = 1; } va_start (args, fmt); @@ -309,7 +303,7 @@ static int ufs_parse_options (char * opt { char * p; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); if (!options) return 1; @@ -386,27 +380,57 @@ static int ufs_parse_options (char * opt } /* + * Diffrent types of UFS hold fs_cstotal in different + * places, and use diffrent data structure for it. + * To make things simplier we just copy fs_cstotal to ufs_sb_private_info + */ +static void ufs_setup_cstotal(struct super_block *sb) +{ + struct ufs_sb_info *sbi = UFS_SB(sb); + struct ufs_sb_private_info *uspi = sbi->s_uspi; + struct ufs_super_block_first *usb1; + struct ufs_super_block_second *usb2; + struct ufs_super_block_third *usb3; + unsigned mtype = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE; + + UFSD("ENTER, mtype=%u\n", mtype); + usb1 = ubh_get_usb_first(uspi); + usb2 = ubh_get_usb_second(uspi); + usb3 = ubh_get_usb_third(uspi); + + if ((mtype == UFS_MOUNT_UFSTYPE_44BSD && + (usb1->fs_flags & UFS_FLAGS_UPDATED)) || + mtype == UFS_MOUNT_UFSTYPE_UFS2) { + /*we have statistic in different place, then usual*/ + uspi->cs_total.cs_ndir = fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_ndir); + uspi->cs_total.cs_nbfree = fs64_to_cpu(sb, usb2->fs_un.fs_u2.cs_nbfree); + uspi->cs_total.cs_nifree = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.cs_nifree); + uspi->cs_total.cs_nffree = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.cs_nffree); + } else { + uspi->cs_total.cs_ndir = fs32_to_cpu(sb, usb1->fs_cstotal.cs_ndir); + uspi->cs_total.cs_nbfree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree); + uspi->cs_total.cs_nifree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree); + uspi->cs_total.cs_nffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree); + } + UFSD("EXIT\n"); +} + +/* * Read on-disk structures associated with cylinder groups */ -static int ufs_read_cylinder_structures (struct super_block *sb) +static int ufs_read_cylinder_structures(struct super_block *sb) { - struct ufs_sb_info * sbi = UFS_SB(sb); - struct ufs_sb_private_info * uspi; - struct ufs_super_block *usb; + struct ufs_sb_info *sbi = UFS_SB(sb); + struct ufs_sb_private_info *uspi = sbi->s_uspi; + unsigned flags = sbi->s_flags; struct ufs_buffer_head * ubh; unsigned char * base, * space; unsigned size, blks, i; - unsigned flags = 0; - - UFSD(("ENTER\n")) - - uspi = sbi->s_uspi; + struct ufs_super_block_third *usb3; - usb = (struct ufs_super_block *) - ((struct ufs_buffer_head *)uspi)->bh[0]->b_data; + UFSD("ENTER\n"); - flags = UFS_SB(sb)->s_flags; - + usb3 = ubh_get_usb_third(uspi); /* * Read cs structures from (usually) first data block * on the device. @@ -424,7 +448,7 @@ static int ufs_read_cylinder_structures if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) ubh = ubh_bread(sb, - fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_csaddr) + i, size); + fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_csaddr) + i, size); else ubh = ubh_bread(sb, uspi->s_csaddr + i, size); @@ -451,14 +475,13 @@ static int ufs_read_cylinder_structures sbi->s_cgno[i] = UFS_CGNO_EMPTY; } for (i = 0; i < uspi->s_ncg; i++) { - UFSD(("read cg %u\n", i)) + UFSD("read cg %u\n", i); if (!(sbi->s_ucg[i] = sb_bread(sb, ufs_cgcmin(i)))) goto failed; if (!ufs_cg_chkmagic (sb, (struct ufs_cylinder_group *) sbi->s_ucg[i]->b_data)) goto failed; -#ifdef UFS_SUPER_DEBUG_MORE + ufs_print_cylinder_stuff(sb, (struct ufs_cylinder_group *) sbi->s_ucg[i]->b_data); -#endif } for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) { if (!(sbi->s_ucpi[i] = kmalloc (sizeof(struct ufs_cg_private_info), GFP_KERNEL))) @@ -466,7 +489,7 @@ #endif sbi->s_cgno[i] = UFS_CGNO_EMPTY; } sbi->s_cg_loaded = 0; - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return 1; failed: @@ -479,26 +502,69 @@ failed: for (i = 0; i < UFS_MAX_GROUP_LOADED; i++) kfree (sbi->s_ucpi[i]); } - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return 0; } /* - * Put on-disk structures associated with cylinder groups and - * write them back to disk + * Sync our internal copy of fs_cstotal with disk */ -static void ufs_put_cylinder_structures (struct super_block *sb) +static void ufs_put_cstotal(struct super_block *sb) { - struct ufs_sb_info * sbi = UFS_SB(sb); - struct ufs_sb_private_info * uspi; + unsigned mtype = UFS_SB(sb)->s_mount_opt & UFS_MOUNT_UFSTYPE; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; + struct ufs_super_block_first *usb1; + struct ufs_super_block_second *usb2; + struct ufs_super_block_third *usb3; + + UFSD("ENTER\n"); + usb1 = ubh_get_usb_first(uspi); + usb2 = ubh_get_usb_second(uspi); + usb3 = ubh_get_usb_third(uspi); + + if ((mtype == UFS_MOUNT_UFSTYPE_44BSD && + (usb1->fs_flags & UFS_FLAGS_UPDATED)) || + mtype == UFS_MOUNT_UFSTYPE_UFS2) { + /*we have statistic in different place, then usual*/ + usb2->fs_un.fs_u2.cs_ndir = + cpu_to_fs64(sb, uspi->cs_total.cs_ndir); + usb2->fs_un.fs_u2.cs_nbfree = + cpu_to_fs64(sb, uspi->cs_total.cs_nbfree); + usb3->fs_un1.fs_u2.cs_nifree = + cpu_to_fs64(sb, uspi->cs_total.cs_nifree); + usb3->fs_un1.fs_u2.cs_nffree = + cpu_to_fs64(sb, uspi->cs_total.cs_nffree); + } else { + usb1->fs_cstotal.cs_ndir = + cpu_to_fs32(sb, uspi->cs_total.cs_ndir); + usb1->fs_cstotal.cs_nbfree = + cpu_to_fs32(sb, uspi->cs_total.cs_nbfree); + usb1->fs_cstotal.cs_nifree = + cpu_to_fs32(sb, uspi->cs_total.cs_nifree); + usb1->fs_cstotal.cs_nffree = + cpu_to_fs32(sb, uspi->cs_total.cs_nffree); + } + ubh_mark_buffer_dirty(USPI_UBH(uspi)); + UFSD("EXIT\n"); +} + +/** + * ufs_put_super_internal() - put on-disk intrenal structures + * @sb: pointer to super_block structure + * Put on-disk structures associated with cylinder groups + * and write them back to disk, also update cs_total on disk + */ +static void ufs_put_super_internal(struct super_block *sb) +{ + struct ufs_sb_info *sbi = UFS_SB(sb); + struct ufs_sb_private_info *uspi = sbi->s_uspi; struct ufs_buffer_head * ubh; unsigned char * base, * space; unsigned blks, size, i; - - UFSD(("ENTER\n")) - - uspi = sbi->s_uspi; + + UFSD("ENTER\n"); + ufs_put_cstotal(sb); size = uspi->s_cssize; blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift; base = space = (char*) sbi->s_csp; @@ -523,7 +589,7 @@ static void ufs_put_cylinder_structures brelse (sbi->s_ucg[i]); kfree (sbi->s_ucg); kfree (base); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); } static int ufs_fill_super(struct super_block *sb, void *data, int silent) @@ -533,7 +599,6 @@ static int ufs_fill_super(struct super_b struct ufs_super_block_first * usb1; struct ufs_super_block_second * usb2; struct ufs_super_block_third * usb3; - struct ufs_super_block *usb; struct ufs_buffer_head * ubh; struct inode *inode; unsigned block_size, super_block_size; @@ -544,7 +609,7 @@ static int ufs_fill_super(struct super_b ubh = NULL; flags = 0; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sbi = kmalloc(sizeof(struct ufs_sb_info), GFP_KERNEL); if (!sbi) @@ -552,7 +617,7 @@ static int ufs_fill_super(struct super_b sb->s_fs_info = sbi; memset(sbi, 0, sizeof(struct ufs_sb_info)); - UFSD(("flag %u\n", (int)(sb->s_flags & MS_RDONLY))) + UFSD("flag %u\n", (int)(sb->s_flags & MS_RDONLY)); #ifndef CONFIG_UFS_FS_WRITE if (!(sb->s_flags & MS_RDONLY)) { @@ -593,7 +658,7 @@ #endif the rules */ switch (sbi->s_mount_opt & UFS_MOUNT_UFSTYPE) { case UFS_MOUNT_UFSTYPE_44BSD: - UFSD(("ufstype=44bsd\n")) + UFSD("ufstype=44bsd\n"); uspi->s_fsize = block_size = 512; uspi->s_fmask = ~(512 - 1); uspi->s_fshift = 9; @@ -602,7 +667,7 @@ #endif flags |= UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD; break; case UFS_MOUNT_UFSTYPE_UFS2: - UFSD(("ufstype=ufs2\n")); + UFSD("ufstype=ufs2\n"); super_block_offset=SBLOCK_UFS2; uspi->s_fsize = block_size = 512; uspi->s_fmask = ~(512 - 1); @@ -617,7 +682,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_SUN: - UFSD(("ufstype=sun\n")) + UFSD("ufstype=sun\n"); uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -628,7 +693,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_SUNx86: - UFSD(("ufstype=sunx86\n")) + UFSD("ufstype=sunx86\n"); uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -639,7 +704,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_OLD: - UFSD(("ufstype=old\n")) + UFSD("ufstype=old\n"); uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -654,7 +719,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_NEXTSTEP: - UFSD(("ufstype=nextstep\n")) + UFSD("ufstype=nextstep\n"); uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -669,7 +734,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_NEXTSTEP_CD: - UFSD(("ufstype=nextstep-cd\n")) + UFSD("ufstype=nextstep-cd\n"); uspi->s_fsize = block_size = 2048; uspi->s_fmask = ~(2048 - 1); uspi->s_fshift = 11; @@ -684,7 +749,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_OPENSTEP: - UFSD(("ufstype=openstep\n")) + UFSD("ufstype=openstep\n"); uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -699,7 +764,7 @@ #endif break; case UFS_MOUNT_UFSTYPE_HP: - UFSD(("ufstype=hp\n")) + UFSD("ufstype=hp\n"); uspi->s_fsize = block_size = 1024; uspi->s_fmask = ~(1024 - 1); uspi->s_fshift = 10; @@ -737,8 +802,6 @@ again: usb1 = ubh_get_usb_first(uspi); usb2 = ubh_get_usb_second(uspi); usb3 = ubh_get_usb_third(uspi); - usb = (struct ufs_super_block *) - ((struct ufs_buffer_head *)uspi)->bh[0]->b_data ; /* * Check ufs magic number @@ -820,16 +883,12 @@ magic_found: ubh = NULL; block_size = uspi->s_fsize; super_block_size = uspi->s_sbsize; - UFSD(("another value of block_size or super_block_size %u, %u\n", block_size, super_block_size)) + UFSD("another value of block_size or super_block_size %u, %u\n", block_size, super_block_size); goto again; } -#ifdef UFS_SUPER_DEBUG_MORE - if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) - ufs2_print_super_stuff(sb,usb); - else - ufs_print_super_stuff(sb, usb1, usb2, usb3); -#endif + + ufs_print_super_stuff(sb, flags, usb1, usb2, usb3); /* * Check, if file system was correctly unmounted. @@ -842,13 +901,13 @@ #endif (ufs_get_fs_state(sb, usb1, usb3) == (UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time))))) { switch(usb1->fs_clean) { case UFS_FSCLEAN: - UFSD(("fs is clean\n")) + UFSD("fs is clean\n"); break; case UFS_FSSTABLE: - UFSD(("fs is stable\n")) + UFSD("fs is stable\n"); break; case UFS_FSOSF1: - UFSD(("fs is DEC OSF/1\n")) + UFSD("fs is DEC OSF/1\n"); break; case UFS_FSACTIVE: printk("ufs_read_super: fs is active\n"); @@ -863,8 +922,7 @@ #endif sb->s_flags |= MS_RDONLY; break; } - } - else { + } else { printk("ufs_read_super: fs needs fsck\n"); sb->s_flags |= MS_RDONLY; } @@ -884,10 +942,9 @@ #endif uspi->s_cgmask = fs32_to_cpu(sb, usb1->fs_cgmask); if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { - uspi->s_u2_size = fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_size); - uspi->s_u2_dsize = fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_dsize); - } - else { + uspi->s_u2_size = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_size); + uspi->s_u2_dsize = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_dsize); + } else { uspi->s_size = fs32_to_cpu(sb, usb1->fs_size); uspi->s_dsize = fs32_to_cpu(sb, usb1->fs_dsize); } @@ -901,8 +958,8 @@ #endif uspi->s_fmask = fs32_to_cpu(sb, usb1->fs_fmask); uspi->s_bshift = fs32_to_cpu(sb, usb1->fs_bshift); uspi->s_fshift = fs32_to_cpu(sb, usb1->fs_fshift); - UFSD(("uspi->s_bshift = %d,uspi->s_fshift = %d", uspi->s_bshift, - uspi->s_fshift)); + UFSD("uspi->s_bshift = %d,uspi->s_fshift = %d", uspi->s_bshift, + uspi->s_fshift); uspi->s_fpbshift = fs32_to_cpu(sb, usb1->fs_fragshift); uspi->s_fsbtodb = fs32_to_cpu(sb, usb1->fs_fsbtodb); /* s_sbsize already set */ @@ -922,8 +979,8 @@ #endif uspi->s_spc = fs32_to_cpu(sb, usb1->fs_spc); uspi->s_ipg = fs32_to_cpu(sb, usb1->fs_ipg); uspi->s_fpg = fs32_to_cpu(sb, usb1->fs_fpg); - uspi->s_cpc = fs32_to_cpu(sb, usb2->fs_cpc); - uspi->s_contigsumsize = fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_contigsumsize); + uspi->s_cpc = fs32_to_cpu(sb, usb2->fs_un.fs_u1.fs_cpc); + uspi->s_contigsumsize = fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_contigsumsize); uspi->s_qbmask = ufs_get_fs_qbmask(sb, usb3); uspi->s_qfmask = ufs_get_fs_qfmask(sb, usb3); uspi->s_postblformat = fs32_to_cpu(sb, usb3->fs_postblformat); @@ -935,12 +992,11 @@ #endif * Compute another frequently used values */ uspi->s_fpbmask = uspi->s_fpb - 1; - if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { + if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) uspi->s_apbshift = uspi->s_bshift - 3; - } - else { + else uspi->s_apbshift = uspi->s_bshift - 2; - } + uspi->s_2apbshift = uspi->s_apbshift * 2; uspi->s_3apbshift = uspi->s_apbshift * 3; uspi->s_apb = 1 << uspi->s_apbshift; @@ -956,7 +1012,7 @@ #endif if ((sbi->s_mount_opt & UFS_MOUNT_UFSTYPE) == UFS_MOUNT_UFSTYPE_44BSD) uspi->s_maxsymlinklen = - fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_maxsymlinklen); + fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_maxsymlinklen); sbi->s_flags = flags; @@ -967,7 +1023,7 @@ #endif if (!sb->s_root) goto dalloc_failed; - + ufs_setup_cstotal(sb); /* * Read cylinder group structures */ @@ -975,7 +1031,7 @@ #endif if (!ufs_read_cylinder_structures(sb)) goto failed; - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return 0; dalloc_failed: @@ -986,15 +1042,16 @@ failed: kfree (uspi); kfree(sbi); sb->s_fs_info = NULL; - UFSD(("EXIT (FAILED)\n")) + UFSD("EXIT (FAILED)\n"); return -EINVAL; failed_nomem: - UFSD(("EXIT (NOMEM)\n")) + UFSD("EXIT (NOMEM)\n"); return -ENOMEM; } -static void ufs_write_super (struct super_block *sb) { +static void ufs_write_super(struct super_block *sb) +{ struct ufs_sb_private_info * uspi; struct ufs_super_block_first * usb1; struct ufs_super_block_third * usb3; @@ -1002,7 +1059,7 @@ static void ufs_write_super (struct supe lock_kernel(); - UFSD(("ENTER\n")) + UFSD("ENTER\n"); flags = UFS_SB(sb)->s_flags; uspi = UFS_SB(sb)->s_uspi; usb1 = ubh_get_usb_first(uspi); @@ -1014,26 +1071,27 @@ static void ufs_write_super (struct supe || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) ufs_set_fs_state(sb, usb1, usb3, UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time)); - ubh_mark_buffer_dirty (USPI_UBH); + ufs_put_cstotal(sb); } sb->s_dirt = 0; - UFSD(("EXIT\n")) + UFSD("EXIT\n"); unlock_kernel(); } -static void ufs_put_super (struct super_block *sb) +static void ufs_put_super(struct super_block *sb) { struct ufs_sb_info * sbi = UFS_SB(sb); - UFSD(("ENTER\n")) + UFSD("ENTER\n"); if (!(sb->s_flags & MS_RDONLY)) - ufs_put_cylinder_structures (sb); + ufs_put_super_internal(sb); ubh_brelse_uspi (sbi->s_uspi); kfree (sbi->s_uspi); kfree (sbi); sb->s_fs_info = NULL; + UFSD("EXIT\n"); return; } @@ -1062,8 +1120,7 @@ static int ufs_remount (struct super_blo return -EINVAL; if (!(new_mount_opt & UFS_MOUNT_UFSTYPE)) { new_mount_opt |= ufstype; - } - else if ((new_mount_opt & UFS_MOUNT_UFSTYPE) != ufstype) { + } else if ((new_mount_opt & UFS_MOUNT_UFSTYPE) != ufstype) { printk("ufstype can't be changed during remount\n"); return -EINVAL; } @@ -1077,20 +1134,19 @@ static int ufs_remount (struct super_blo * fs was mouted as rw, remounting ro */ if (*mount_flags & MS_RDONLY) { - ufs_put_cylinder_structures(sb); + ufs_put_super_internal(sb); usb1->fs_time = cpu_to_fs32(sb, get_seconds()); if ((flags & UFS_ST_MASK) == UFS_ST_SUN || (flags & UFS_ST_MASK) == UFS_ST_SUNx86) ufs_set_fs_state(sb, usb1, usb3, UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time)); - ubh_mark_buffer_dirty (USPI_UBH); + ubh_mark_buffer_dirty (USPI_UBH(uspi)); sb->s_dirt = 0; sb->s_flags |= MS_RDONLY; - } + } else { /* * fs was mounted as ro, remounting rw */ - else { #ifndef CONFIG_UFS_FS_WRITE printk("ufs was compiled with read-only support, " "can't be mounted as read-write\n"); @@ -1102,7 +1158,7 @@ #else printk("this ufstype is read-only supported\n"); return -EINVAL; } - if (!ufs_read_cylinder_structures (sb)) { + if (!ufs_read_cylinder_structures(sb)) { printk("failed during remounting\n"); return -EPERM; } @@ -1113,36 +1169,31 @@ #endif return 0; } -static int ufs_statfs (struct super_block *sb, struct kstatfs *buf) +static int ufs_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct ufs_sb_private_info * uspi; - struct ufs_super_block_first * usb1; - struct ufs_super_block * usb; - unsigned flags = 0; + struct super_block *sb = dentry->d_sb; + struct ufs_sb_private_info *uspi= UFS_SB(sb)->s_uspi; + unsigned flags = UFS_SB(sb)->s_flags; + struct ufs_super_block_first *usb1; + struct ufs_super_block_second *usb2; + struct ufs_super_block_third *usb3; lock_kernel(); - uspi = UFS_SB(sb)->s_uspi; - usb1 = ubh_get_usb_first (uspi); - usb = (struct ufs_super_block *) - ((struct ufs_buffer_head *)uspi)->bh[0]->b_data ; + usb1 = ubh_get_usb_first(uspi); + usb2 = ubh_get_usb_second(uspi); + usb3 = ubh_get_usb_third(uspi); - flags = UFS_SB(sb)->s_flags; if ((flags & UFS_TYPE_MASK) == UFS_TYPE_UFS2) { buf->f_type = UFS2_MAGIC; - buf->f_blocks = fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_dsize); - buf->f_bfree = ufs_blkstofrags(fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_cstotal.cs_nbfree)) + - fs64_to_cpu(sb, usb->fs_u11.fs_u2.fs_cstotal.cs_nffree); - buf->f_ffree = fs64_to_cpu(sb, - usb->fs_u11.fs_u2.fs_cstotal.cs_nifree); - } - else { + buf->f_blocks = fs64_to_cpu(sb, usb3->fs_un1.fs_u2.fs_dsize); + } else { buf->f_type = UFS_MAGIC; buf->f_blocks = uspi->s_dsize; - buf->f_bfree = ufs_blkstofrags(fs32_to_cpu(sb, usb1->fs_cstotal.cs_nbfree)) + - fs32_to_cpu(sb, usb1->fs_cstotal.cs_nffree); - buf->f_ffree = fs32_to_cpu(sb, usb1->fs_cstotal.cs_nifree); } + buf->f_bfree = ufs_blkstofrags(uspi->cs_total.cs_nbfree) + + uspi->cs_total.cs_nffree; + buf->f_ffree = uspi->cs_total.cs_nifree; buf->f_bsize = sb->s_blocksize; buf->f_bavail = (buf->f_bfree > (((long)buf->f_blocks / 100) * uspi->s_minfree)) ? (buf->f_bfree - (((long)buf->f_blocks / 100) * uspi->s_minfree)) : 0; @@ -1275,7 +1326,7 @@ static ssize_t ufs_quota_write(struct su size_t towrite = len; struct buffer_head *bh; - mutex_lock(&inode->i_mutex); + mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA); while (towrite > 0) { tocopy = sb->s_blocksize - offset < towrite ? sb->s_blocksize - offset : towrite; @@ -1311,10 +1362,10 @@ out: #endif -static struct super_block *ufs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int ufs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, ufs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, ufs_fill_super, mnt); } static struct file_system_type ufs_fs_type = { diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 02e8629..c9b5587 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -49,14 +49,6 @@ #include #include "swab.h" #include "util.h" -#undef UFS_TRUNCATE_DEBUG - -#ifdef UFS_TRUNCATE_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - /* * Secure deletion currently doesn't work. It interacts very badly * with buffers shared with memory mappings, and for that reason @@ -82,7 +74,7 @@ static int ufs_trunc_direct (struct inod unsigned i, tmp; int retry; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -105,7 +97,7 @@ static int ufs_trunc_direct (struct inod block2 = ufs_fragstoblks (frag3); } - UFSD(("frag1 %u, frag2 %u, block1 %u, block2 %u, frag3 %u, frag4 %u\n", frag1, frag2, block1, block2, frag3, frag4)) + UFSD("frag1 %u, frag2 %u, block1 %u, block2 %u, frag3 %u, frag4 %u\n", frag1, frag2, block1, block2, frag3, frag4); if (frag1 >= frag2) goto next1; @@ -120,9 +112,8 @@ static int ufs_trunc_direct (struct inod frag1 = ufs_fragnum (frag1); frag2 = ufs_fragnum (frag2); - inode->i_blocks -= (frag2-frag1) << uspi->s_nspfshift; - mark_inode_dirty(inode); ufs_free_fragments (inode, tmp + frag1, frag2 - frag1); + mark_inode_dirty(inode); frag_to_free = tmp + frag1; next1: @@ -136,8 +127,7 @@ next1: continue; *p = 0; - inode->i_blocks -= uspi->s_nspb; - mark_inode_dirty(inode); + if (free_count == 0) { frag_to_free = tmp; free_count = uspi->s_fpb; @@ -148,6 +138,7 @@ next1: frag_to_free = tmp; free_count = uspi->s_fpb; } + mark_inode_dirty(inode); } if (free_count > 0) @@ -166,12 +157,12 @@ next1: frag4 = ufs_fragnum (frag4); *p = 0; - inode->i_blocks -= frag4 << uspi->s_nspfshift; - mark_inode_dirty(inode); + ufs_free_fragments (inode, tmp, frag4); + mark_inode_dirty(inode); next3: - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return retry; } @@ -186,7 +177,7 @@ static int ufs_trunc_indirect (struct in unsigned frag_to_free, free_count; int retry; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -227,7 +218,7 @@ static int ufs_trunc_indirect (struct in frag_to_free = tmp; free_count = uspi->s_fpb; } - inode->i_blocks -= uspi->s_nspb; + mark_inode_dirty(inode); } @@ -238,26 +229,21 @@ static int ufs_trunc_indirect (struct in if (*ubh_get_addr32(ind_ubh,i)) break; if (i >= uspi->s_apb) { - if (ubh_max_bcount(ind_ubh) != 1) { - retry = 1; - } - else { - tmp = fs32_to_cpu(sb, *p); - *p = 0; - inode->i_blocks -= uspi->s_nspb; - mark_inode_dirty(inode); - ufs_free_blocks (inode, tmp, uspi->s_fpb); - ubh_bforget(ind_ubh); - ind_ubh = NULL; - } + tmp = fs32_to_cpu(sb, *p); + *p = 0; + + ufs_free_blocks (inode, tmp, uspi->s_fpb); + mark_inode_dirty(inode); + ubh_bforget(ind_ubh); + ind_ubh = NULL; } if (IS_SYNC(inode) && ind_ubh && ubh_buffer_dirty(ind_ubh)) { - ubh_ll_rw_block (SWRITE, 1, &ind_ubh); + ubh_ll_rw_block(SWRITE, ind_ubh); ubh_wait_on_buffer (ind_ubh); } ubh_brelse (ind_ubh); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return retry; } @@ -271,7 +257,7 @@ static int ufs_trunc_dindirect (struct i __fs32 * dind; int retry = 0; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -306,25 +292,21 @@ static int ufs_trunc_dindirect (struct i if (*ubh_get_addr32 (dind_bh, i)) break; if (i >= uspi->s_apb) { - if (ubh_max_bcount(dind_bh) != 1) - retry = 1; - else { - tmp = fs32_to_cpu(sb, *p); - *p = 0; - inode->i_blocks -= uspi->s_nspb; - mark_inode_dirty(inode); - ufs_free_blocks (inode, tmp, uspi->s_fpb); - ubh_bforget(dind_bh); - dind_bh = NULL; - } + tmp = fs32_to_cpu(sb, *p); + *p = 0; + + ufs_free_blocks(inode, tmp, uspi->s_fpb); + mark_inode_dirty(inode); + ubh_bforget(dind_bh); + dind_bh = NULL; } if (IS_SYNC(inode) && dind_bh && ubh_buffer_dirty(dind_bh)) { - ubh_ll_rw_block (SWRITE, 1, &dind_bh); + ubh_ll_rw_block(SWRITE, dind_bh); ubh_wait_on_buffer (dind_bh); } ubh_brelse (dind_bh); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return retry; } @@ -339,7 +321,7 @@ static int ufs_trunc_tindirect (struct i __fs32 * tind, * p; int retry; - UFSD(("ENTER\n")) + UFSD("ENTER\n"); sb = inode->i_sb; uspi = UFS_SB(sb)->s_uspi; @@ -370,45 +352,114 @@ static int ufs_trunc_tindirect (struct i if (*ubh_get_addr32 (tind_bh, i)) break; if (i >= uspi->s_apb) { - if (ubh_max_bcount(tind_bh) != 1) - retry = 1; - else { - tmp = fs32_to_cpu(sb, *p); - *p = 0; - inode->i_blocks -= uspi->s_nspb; - mark_inode_dirty(inode); - ufs_free_blocks (inode, tmp, uspi->s_fpb); - ubh_bforget(tind_bh); - tind_bh = NULL; - } + tmp = fs32_to_cpu(sb, *p); + *p = 0; + + ufs_free_blocks(inode, tmp, uspi->s_fpb); + mark_inode_dirty(inode); + ubh_bforget(tind_bh); + tind_bh = NULL; } if (IS_SYNC(inode) && tind_bh && ubh_buffer_dirty(tind_bh)) { - ubh_ll_rw_block (SWRITE, 1, &tind_bh); + ubh_ll_rw_block(SWRITE, tind_bh); ubh_wait_on_buffer (tind_bh); } ubh_brelse (tind_bh); - UFSD(("EXIT\n")) + UFSD("EXIT\n"); return retry; } - -void ufs_truncate (struct inode * inode) + +static int ufs_alloc_lastblock(struct inode *inode) { + int err = 0; + struct address_space *mapping = inode->i_mapping; + struct ufs_sb_private_info *uspi = UFS_SB(inode->i_sb)->s_uspi; struct ufs_inode_info *ufsi = UFS_I(inode); - struct super_block * sb; - struct ufs_sb_private_info * uspi; - int retry; + unsigned lastfrag, i, end; + struct page *lastpage; + struct buffer_head *bh; + + lastfrag = (i_size_read(inode) + uspi->s_fsize - 1) >> uspi->s_fshift; + + if (!lastfrag) { + ufsi->i_lastfrag = 0; + goto out; + } + lastfrag--; + + lastpage = ufs_get_locked_page(mapping, lastfrag >> + (PAGE_CACHE_SHIFT - inode->i_blkbits)); + if (IS_ERR(lastpage)) { + err = -EIO; + goto out; + } + + end = lastfrag & ((1 << (PAGE_CACHE_SHIFT - inode->i_blkbits)) - 1); + bh = page_buffers(lastpage); + for (i = 0; i < end; ++i) + bh = bh->b_this_page; + + if (!buffer_mapped(bh)) { + err = ufs_getfrag_block(inode, lastfrag, bh, 1); + + if (unlikely(err)) + goto out_unlock; + + if (buffer_new(bh)) { + clear_buffer_new(bh); + unmap_underlying_metadata(bh->b_bdev, + bh->b_blocknr); + /* + * we do not zeroize fragment, because of + * if it maped to hole, it already contains zeroes + */ + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + set_page_dirty(lastpage); + } + } +out_unlock: + ufs_put_locked_page(lastpage); +out: + return err; +} + +int ufs_truncate(struct inode *inode, loff_t old_i_size) +{ + struct ufs_inode_info *ufsi = UFS_I(inode); + struct super_block *sb = inode->i_sb; + struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi; + int retry, err = 0; - UFSD(("ENTER\n")) - sb = inode->i_sb; - uspi = UFS_SB(sb)->s_uspi; + UFSD("ENTER\n"); - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) - return; + if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || + S_ISLNK(inode->i_mode))) + return -EINVAL; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; + return -EPERM; + + if (inode->i_size > old_i_size) { + /* + * if we expand file we should care about + * allocation of block for last byte first of all + */ + err = ufs_alloc_lastblock(inode); + + if (err) { + i_size_write(inode, old_i_size); + goto out; + } + /* + * go away, because of we expand file, and we do not + * need free blocks, and zeroizes page + */ + lock_kernel(); + goto almost_end; + } - block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block); + block_truncate_page(inode->i_mapping, inode->i_size, ufs_getfrag_block); lock_kernel(); while (1) { @@ -426,9 +477,58 @@ void ufs_truncate (struct inode * inode) yield(); } + if (inode->i_size < old_i_size) { + /* + * now we should have enough space + * to allocate block for last byte + */ + err = ufs_alloc_lastblock(inode); + if (err) + /* + * looks like all the same - we have no space, + * but we truncate file already + */ + inode->i_size = (ufsi->i_lastfrag - 1) * uspi->s_fsize; + } +almost_end: inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; - ufsi->i_lastfrag = DIRECT_FRAGMENT; unlock_kernel(); mark_inode_dirty(inode); - UFSD(("EXIT\n")) +out: + UFSD("EXIT: err %d\n", err); + return err; } + + +/* + * We don't define our `inode->i_op->truncate', and call it here, + * because of: + * - there is no way to know old size + * - there is no way inform user about error, if it happens in `truncate' + */ +static int ufs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + unsigned int ia_valid = attr->ia_valid; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + + if (ia_valid & ATTR_SIZE && + attr->ia_size != i_size_read(inode)) { + loff_t old_i_size = inode->i_size; + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + error = ufs_truncate(inode, old_i_size); + if (error) + return error; + } + return inode_setattr(inode, attr); +} + +struct inode_operations ufs_file_inode_operations = { + .setattr = ufs_setattr, +}; diff --git a/fs/ufs/util.c b/fs/ufs/util.c index 59acc8f..337cf2c 100644 --- a/fs/ufs/util.c +++ b/fs/ufs/util.c @@ -14,15 +14,6 @@ #include #include "swab.h" #include "util.h" -#undef UFS_UTILS_DEBUG - -#ifdef UFS_UTILS_DEBUG -#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; -#else -#define UFSD(x) -#endif - - struct ufs_buffer_head * _ubh_bread_ (struct ufs_sb_private_info * uspi, struct super_block *sb, u64 fragment, u64 size) { @@ -63,17 +54,17 @@ struct ufs_buffer_head * ubh_bread_uspi count = size >> uspi->s_fshift; if (count <= 0 || count > UFS_MAXFRAG) return NULL; - USPI_UBH->fragment = fragment; - USPI_UBH->count = count; + USPI_UBH(uspi)->fragment = fragment; + USPI_UBH(uspi)->count = count; for (i = 0; i < count; i++) - if (!(USPI_UBH->bh[i] = sb_bread(sb, fragment + i))) + if (!(USPI_UBH(uspi)->bh[i] = sb_bread(sb, fragment + i))) goto failed; for (; i < UFS_MAXFRAG; i++) - USPI_UBH->bh[i] = NULL; - return USPI_UBH; + USPI_UBH(uspi)->bh[i] = NULL; + return USPI_UBH(uspi); failed: for (j = 0; j < i; j++) - brelse (USPI_UBH->bh[j]); + brelse (USPI_UBH(uspi)->bh[j]); return NULL; } @@ -90,11 +81,11 @@ void ubh_brelse (struct ufs_buffer_head void ubh_brelse_uspi (struct ufs_sb_private_info * uspi) { unsigned i; - if (!USPI_UBH) + if (!USPI_UBH(uspi)) return; - for ( i = 0; i < USPI_UBH->count; i++ ) { - brelse (USPI_UBH->bh[i]); - USPI_UBH->bh[i] = NULL; + for ( i = 0; i < USPI_UBH(uspi)->count; i++ ) { + brelse (USPI_UBH(uspi)->bh[i]); + USPI_UBH(uspi)->bh[i] = NULL; } } @@ -121,13 +112,12 @@ void ubh_mark_buffer_uptodate (struct uf } } -void ubh_ll_rw_block (int rw, unsigned nr, struct ufs_buffer_head * ubh[]) +void ubh_ll_rw_block(int rw, struct ufs_buffer_head *ubh) { - unsigned i; if (!ubh) return; - for ( i = 0; i < nr; i++ ) - ll_rw_block (rw, ubh[i]->count, ubh[i]->bh); + + ll_rw_block(rw, ubh->count, ubh->bh); } void ubh_wait_on_buffer (struct ufs_buffer_head * ubh) @@ -139,18 +129,6 @@ void ubh_wait_on_buffer (struct ufs_buff wait_on_buffer (ubh->bh[i]); } -unsigned ubh_max_bcount (struct ufs_buffer_head * ubh) -{ - unsigned i; - unsigned max = 0; - if (!ubh) - return 0; - for ( i = 0; i < ubh->count; i++ ) - if ( atomic_read(&ubh->bh[i]->b_count) > max ) - max = atomic_read(&ubh->bh[i]->b_count); - return max; -} - void ubh_bforget (struct ufs_buffer_head * ubh) { unsigned i; @@ -255,3 +233,57 @@ ufs_set_inode_dev(struct super_block *sb else ufsi->i_u1.i_data[0] = fs32; } + +/** + * ufs_get_locked_page() - locate, pin and lock a pagecache page, if not exist + * read it from disk. + * @mapping: the address_space to search + * @index: the page index + * + * Locates the desired pagecache page, if not exist we'll read it, + * locks it, increments its reference + * count and returns its address. + * + */ + +struct page *ufs_get_locked_page(struct address_space *mapping, + pgoff_t index) +{ + struct page *page; + +try_again: + page = find_lock_page(mapping, index); + if (!page) { + page = read_cache_page(mapping, index, + (filler_t*)mapping->a_ops->readpage, + NULL); + if (IS_ERR(page)) { + printk(KERN_ERR "ufs_change_blocknr: " + "read_cache_page error: ino %lu, index: %lu\n", + mapping->host->i_ino, index); + goto out; + } + + lock_page(page); + + if (!PageUptodate(page) || PageError(page)) { + unlock_page(page); + page_cache_release(page); + + printk(KERN_ERR "ufs_change_blocknr: " + "can not read page: ino %lu, index: %lu\n", + mapping->host->i_ino, index); + + page = ERR_PTR(-EIO); + goto out; + } + } + + if (unlikely(!page->mapping || !page_has_buffers(page))) { + unlock_page(page); + page_cache_release(page); + goto try_again;/*we really need these buffers*/ + } +out: + return page; +} diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 48d6d9b..28fce6c 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -17,10 +17,16 @@ #include "swab.h" #define in_range(b,first,len) ((b)>=(first)&&(b)<(first)+(len)) /* - * macros used for retyping + * functions used for retyping */ -#define UCPI_UBH ((struct ufs_buffer_head *)ucpi) -#define USPI_UBH ((struct ufs_buffer_head *)uspi) +static inline struct ufs_buffer_head *UCPI_UBH(struct ufs_cg_private_info *cpi) +{ + return &cpi->c_ubh; +} +static inline struct ufs_buffer_head *USPI_UBH(struct ufs_sb_private_info *spi) +{ + return &spi->s_ubh; +} @@ -33,12 +39,12 @@ ufs_get_fs_state(struct super_block *sb, { switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) { case UFS_ST_SUN: - return fs32_to_cpu(sb, usb3->fs_u2.fs_sun.fs_state); + return fs32_to_cpu(sb, usb3->fs_un2.fs_sun.fs_state); case UFS_ST_SUNx86: return fs32_to_cpu(sb, usb1->fs_u1.fs_sunx86.fs_state); case UFS_ST_44BSD: default: - return fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_state); + return fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_state); } } @@ -48,13 +54,13 @@ ufs_set_fs_state(struct super_block *sb, { switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) { case UFS_ST_SUN: - usb3->fs_u2.fs_sun.fs_state = cpu_to_fs32(sb, value); + usb3->fs_un2.fs_sun.fs_state = cpu_to_fs32(sb, value); break; case UFS_ST_SUNx86: usb1->fs_u1.fs_sunx86.fs_state = cpu_to_fs32(sb, value); break; case UFS_ST_44BSD: - usb3->fs_u2.fs_44.fs_state = cpu_to_fs32(sb, value); + usb3->fs_un2.fs_44.fs_state = cpu_to_fs32(sb, value); break; } } @@ -64,7 +70,7 @@ ufs_get_fs_npsect(struct super_block *sb struct ufs_super_block_third *usb3) { if ((UFS_SB(sb)->s_flags & UFS_ST_MASK) == UFS_ST_SUNx86) - return fs32_to_cpu(sb, usb3->fs_u2.fs_sunx86.fs_npsect); + return fs32_to_cpu(sb, usb3->fs_un2.fs_sunx86.fs_npsect); else return fs32_to_cpu(sb, usb1->fs_u1.fs_sun.fs_npsect); } @@ -76,16 +82,16 @@ ufs_get_fs_qbmask(struct super_block *sb switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) { case UFS_ST_SUN: - ((__fs32 *)&tmp)[0] = usb3->fs_u2.fs_sun.fs_qbmask[0]; - ((__fs32 *)&tmp)[1] = usb3->fs_u2.fs_sun.fs_qbmask[1]; + ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sun.fs_qbmask[0]; + ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sun.fs_qbmask[1]; break; case UFS_ST_SUNx86: - ((__fs32 *)&tmp)[0] = usb3->fs_u2.fs_sunx86.fs_qbmask[0]; - ((__fs32 *)&tmp)[1] = usb3->fs_u2.fs_sunx86.fs_qbmask[1]; + ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sunx86.fs_qbmask[0]; + ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sunx86.fs_qbmask[1]; break; case UFS_ST_44BSD: - ((__fs32 *)&tmp)[0] = usb3->fs_u2.fs_44.fs_qbmask[0]; - ((__fs32 *)&tmp)[1] = usb3->fs_u2.fs_44.fs_qbmask[1]; + ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_44.fs_qbmask[0]; + ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_44.fs_qbmask[1]; break; } @@ -99,16 +105,16 @@ ufs_get_fs_qfmask(struct super_block *sb switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) { case UFS_ST_SUN: - ((__fs32 *)&tmp)[0] = usb3->fs_u2.fs_sun.fs_qfmask[0]; - ((__fs32 *)&tmp)[1] = usb3->fs_u2.fs_sun.fs_qfmask[1]; + ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sun.fs_qfmask[0]; + ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sun.fs_qfmask[1]; break; case UFS_ST_SUNx86: - ((__fs32 *)&tmp)[0] = usb3->fs_u2.fs_sunx86.fs_qfmask[0]; - ((__fs32 *)&tmp)[1] = usb3->fs_u2.fs_sunx86.fs_qfmask[1]; + ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_sunx86.fs_qfmask[0]; + ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_sunx86.fs_qfmask[1]; break; case UFS_ST_44BSD: - ((__fs32 *)&tmp)[0] = usb3->fs_u2.fs_44.fs_qfmask[0]; - ((__fs32 *)&tmp)[1] = usb3->fs_u2.fs_44.fs_qfmask[1]; + ((__fs32 *)&tmp)[0] = usb3->fs_un2.fs_44.fs_qfmask[0]; + ((__fs32 *)&tmp)[1] = usb3->fs_un2.fs_44.fs_qfmask[1]; break; } @@ -236,9 +242,8 @@ extern void ubh_brelse (struct ufs_buffe extern void ubh_brelse_uspi (struct ufs_sb_private_info *); extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *); extern void ubh_mark_buffer_uptodate (struct ufs_buffer_head *, int); -extern void ubh_ll_rw_block (int, unsigned, struct ufs_buffer_head **); +extern void ubh_ll_rw_block(int, struct ufs_buffer_head *); extern void ubh_wait_on_buffer (struct ufs_buffer_head *); -extern unsigned ubh_max_bcount (struct ufs_buffer_head *); extern void ubh_bforget (struct ufs_buffer_head *); extern int ubh_buffer_dirty (struct ufs_buffer_head *); #define ubh_ubhcpymem(mem,ubh,size) _ubh_ubhcpymem_(uspi,mem,ubh,size) @@ -246,6 +251,14 @@ extern void _ubh_ubhcpymem_(struct ufs_s #define ubh_memcpyubh(ubh,mem,size) _ubh_memcpyubh_(uspi,ubh,mem,size) extern void _ubh_memcpyubh_(struct ufs_sb_private_info *, struct ufs_buffer_head *, unsigned char *, unsigned); +/* This functions works with cache pages*/ +extern struct page *ufs_get_locked_page(struct address_space *mapping, + pgoff_t index); +static inline void ufs_put_locked_page(struct page *page) +{ + unlock_page(page); + page_cache_release(page); +} /* @@ -297,40 +310,26 @@ #define ubh_get_addr ubh_get_addr8 #define ubh_blkmap(ubh,begin,bit) \ ((*ubh_get_addr(ubh, (begin) + ((bit) >> 3)) >> ((bit) & 7)) & (0xff >> (UFS_MAXFRAG - uspi->s_fpb))) - -/* - * Macros for access to superblock array structures - */ -#define ubh_postbl(ubh,cylno,i) \ - ((uspi->s_postblformat != UFS_DYNAMICPOSTBLFMT) \ - ? (*(__s16*)(ubh_get_addr(ubh, \ - (unsigned)(&((struct ufs_super_block *)0)->fs_opostbl) \ - + (((cylno) * 16 + (i)) << 1) ) )) \ - : (*(__s16*)(ubh_get_addr(ubh, \ - uspi->s_postbloff + (((cylno) * uspi->s_nrpos + (i)) << 1) )))) - -#define ubh_rotbl(ubh,i) \ - ((uspi->s_postblformat != UFS_DYNAMICPOSTBLFMT) \ - ? (*(__u8*)(ubh_get_addr(ubh, \ - (unsigned)(&((struct ufs_super_block *)0)->fs_space) + (i)))) \ - : (*(__u8*)(ubh_get_addr(ubh, uspi->s_rotbloff + (i))))) - /* * Determine the number of available frags given a * percentage to hold in reserve. */ -#define ufs_freespace(usb, percentreserved) \ - (ufs_blkstofrags(fs32_to_cpu(sb, (usb)->fs_cstotal.cs_nbfree)) + \ - fs32_to_cpu(sb, (usb)->fs_cstotal.cs_nffree) - (uspi->s_dsize * (percentreserved) / 100)) +static inline u64 +ufs_freespace(struct ufs_sb_private_info *uspi, int percentreserved) +{ + return ufs_blkstofrags(uspi->cs_total.cs_nbfree) + + uspi->cs_total.cs_nffree - + (uspi->s_dsize * (percentreserved) / 100); +} /* * Macros to access cylinder group array structures */ #define ubh_cg_blktot(ucpi,cylno) \ - (*((__fs32*)ubh_get_addr(UCPI_UBH, (ucpi)->c_btotoff + ((cylno) << 2)))) + (*((__fs32*)ubh_get_addr(UCPI_UBH(ucpi), (ucpi)->c_btotoff + ((cylno) << 2)))) #define ubh_cg_blks(ucpi,cylno,rpos) \ - (*((__fs16*)ubh_get_addr(UCPI_UBH, \ + (*((__fs16*)ubh_get_addr(UCPI_UBH(ucpi), \ (ucpi)->c_boff + (((cylno) * uspi->s_nrpos + (rpos)) << 1 )))) /* @@ -508,29 +507,3 @@ static inline void ufs_fragacct (struct if (fragsize > 0 && fragsize < uspi->s_fpb) fs32_add(sb, &fraglist[fragsize], cnt); } - -#define ubh_scanc(ubh,begin,size,table,mask) _ubh_scanc_(uspi,ubh,begin,size,table,mask) -static inline unsigned _ubh_scanc_(struct ufs_sb_private_info * uspi, struct ufs_buffer_head * ubh, - unsigned begin, unsigned size, unsigned char * table, unsigned char mask) -{ - unsigned rest, offset; - unsigned char * cp; - - - offset = begin & ~uspi->s_fmask; - begin >>= uspi->s_fshift; - for (;;) { - if ((offset + size) < uspi->s_fsize) - rest = size; - else - rest = uspi->s_fsize - offset; - size -= rest; - cp = ubh->bh[begin]->b_data + offset; - while ((table[*cp++] & mask) == 0 && --rest); - if (rest || !size) - break; - begin++; - offset = 0; - } - return (size + rest); -} diff --git a/fs/vfat/namei.c b/fs/vfat/namei.c index a56cec3..9a8f48b 100644 --- a/fs/vfat/namei.c +++ b/fs/vfat/namei.c @@ -1023,11 +1023,12 @@ static int vfat_fill_super(struct super_ return 0; } -static struct super_block *vfat_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int vfat_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super, + mnt); } static struct file_system_type vfat_fs_type = { diff --git a/fs/xattr.c b/fs/xattr.c index e416190..c32f15b 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -242,7 +242,7 @@ sys_fsetxattr(int fd, char __user *name, if (!f) return error; dentry = f->f_dentry; - audit_inode(NULL, dentry->d_inode, 0); + audit_inode(NULL, dentry->d_inode); error = setxattr(dentry, name, value, size, flags); fput(f); return error; @@ -469,7 +469,7 @@ sys_fremovexattr(int fd, char __user *na if (!f) return error; dentry = f->f_dentry; - audit_inode(NULL, dentry->d_inode, 0); + audit_inode(NULL, dentry->d_inode); error = removexattr(dentry, name); fput(f); return error; diff --git a/fs/xfs/Kconfig b/fs/xfs/Kconfig index bac27d6..26b364c 100644 --- a/fs/xfs/Kconfig +++ b/fs/xfs/Kconfig @@ -1,6 +1,5 @@ config XFS_FS tristate "XFS filesystem support" - select EXPORTFS if NFSD!=n help XFS is a high performance journaling filesystem which originated on the SGI IRIX platform. It is completely multi-threaded, can @@ -18,11 +17,6 @@ config XFS_FS system of your root partition is compiled as a module, you'll need to use an initial ramdisk (initrd) to boot. -config XFS_EXPORT - bool - depends on XFS_FS && EXPORTFS - default y - config XFS_QUOTA bool "XFS Quota support" depends on XFS_FS @@ -65,18 +59,19 @@ config XFS_POSIX_ACL If you don't know what Access Control Lists are, say N. config XFS_RT - bool "XFS Realtime support (EXPERIMENTAL)" - depends on XFS_FS && EXPERIMENTAL + bool "XFS Realtime subvolume support" + depends on XFS_FS help If you say Y here you will be able to mount and use XFS filesystems - which contain a realtime subvolume. The realtime subvolume is a - separate area of disk space where only file data is stored. The - realtime subvolume is designed to provide very deterministic - data rates suitable for media streaming applications. - - See the xfs man page in section 5 for a bit more information. + which contain a realtime subvolume. The realtime subvolume is a + separate area of disk space where only file data is stored. It was + originally designed to provide deterministic data rates suitable + for media streaming applications, but is also useful as a generic + mechanism for ensuring data and metadata/log I/Os are completely + separated. Regular file I/Os are isolated to a separate device + from all other requests, and this can be done quite transparently + to applications via the inherit-realtime directory inode flag. - This feature is unsupported at this time, is not yet fully - functional, and may cause serious problems. + See the xfs man page in section 5 for additional information. If unsure, say N. diff --git a/fs/xfs/Makefile-linux-2.6 b/fs/xfs/Makefile-linux-2.6 index 5d73eaa..9e7f859 100644 --- a/fs/xfs/Makefile-linux-2.6 +++ b/fs/xfs/Makefile-linux-2.6 @@ -59,7 +59,6 @@ xfs-$(CONFIG_XFS_POSIX_ACL) += xfs_acl.o xfs-$(CONFIG_PROC_FS) += $(XFS_LINUX)/xfs_stats.o xfs-$(CONFIG_SYSCTL) += $(XFS_LINUX)/xfs_sysctl.o xfs-$(CONFIG_COMPAT) += $(XFS_LINUX)/xfs_ioctl32.o -xfs-$(CONFIG_XFS_EXPORT) += $(XFS_LINUX)/xfs_export.o xfs-y += xfs_alloc.o \ @@ -73,14 +72,12 @@ xfs-y += xfs_alloc.o \ xfs_btree.o \ xfs_buf_item.o \ xfs_da_btree.o \ - xfs_dir.o \ xfs_dir2.o \ xfs_dir2_block.o \ xfs_dir2_data.o \ xfs_dir2_leaf.o \ xfs_dir2_node.o \ xfs_dir2_sf.o \ - xfs_dir_leaf.o \ xfs_error.o \ xfs_extfree_item.o \ xfs_fsops.o \ @@ -117,6 +114,7 @@ xfs-y += $(addprefix $(XFS_LINUX)/, \ kmem.o \ xfs_aops.o \ xfs_buf.o \ + xfs_export.o \ xfs_file.o \ xfs_fs_subr.o \ xfs_globals.o \ diff --git a/fs/xfs/linux-2.6/kmem.h b/fs/xfs/linux-2.6/kmem.h index 2cfd33d..939bd84 100644 --- a/fs/xfs/linux-2.6/kmem.h +++ b/fs/xfs/linux-2.6/kmem.h @@ -23,42 +23,6 @@ #include #include /* - * Process flags handling - */ - -#define PFLAGS_TEST_NOIO() (current->flags & PF_NOIO) -#define PFLAGS_TEST_FSTRANS() (current->flags & PF_FSTRANS) - -#define PFLAGS_SET_NOIO() do { \ - current->flags |= PF_NOIO; \ -} while (0) - -#define PFLAGS_CLEAR_NOIO() do { \ - current->flags &= ~PF_NOIO; \ -} while (0) - -/* these could be nested, so we save state */ -#define PFLAGS_SET_FSTRANS(STATEP) do { \ - *(STATEP) = current->flags; \ - current->flags |= PF_FSTRANS; \ -} while (0) - -#define PFLAGS_CLEAR_FSTRANS(STATEP) do { \ - *(STATEP) = current->flags; \ - current->flags &= ~PF_FSTRANS; \ -} while (0) - -/* Restore the PF_FSTRANS state to what was saved in STATEP */ -#define PFLAGS_RESTORE_FSTRANS(STATEP) do { \ - current->flags = ((current->flags & ~PF_FSTRANS) | \ - (*(STATEP) & PF_FSTRANS)); \ -} while (0) - -#define PFLAGS_DUP(OSTATEP, NSTATEP) do { \ - *(NSTATEP) = *(OSTATEP); \ -} while (0) - -/* * General memory allocation interfaces */ @@ -83,7 +47,7 @@ kmem_flags_convert(unsigned int __nocast lflags = GFP_ATOMIC | __GFP_NOWARN; } else { lflags = GFP_KERNEL | __GFP_NOWARN; - if (PFLAGS_TEST_FSTRANS() || (flags & KM_NOFS)) + if ((current->flags & PF_FSTRANS) || (flags & KM_NOFS)) lflags &= ~__GFP_FS; } return lflags; diff --git a/fs/xfs/linux-2.6/mrlock.h b/fs/xfs/linux-2.6/mrlock.h index 1b262b7..32e1ce0 100644 --- a/fs/xfs/linux-2.6/mrlock.h +++ b/fs/xfs/linux-2.6/mrlock.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -28,7 +28,7 @@ typedef struct { } mrlock_t; #define mrinit(mrp, name) \ - ( (mrp)->mr_writer = 0, init_rwsem(&(mrp)->mr_lock) ) + do { (mrp)->mr_writer = 0; init_rwsem(&(mrp)->mr_lock); } while (0) #define mrlock_init(mrp, t,n,s) mrinit(mrp, n) #define mrfree(mrp) do { } while (0) #define mraccess(mrp) mraccessf(mrp, 0) diff --git a/fs/xfs/linux-2.6/sema.h b/fs/xfs/linux-2.6/sema.h index 194a844..b250900 100644 --- a/fs/xfs/linux-2.6/sema.h +++ b/fs/xfs/linux-2.6/sema.h @@ -34,20 +34,21 @@ #define initsema(sp, val) sema_init(sp, #define initnsema(sp, val, name) sema_init(sp, val) #define psema(sp, b) down(sp) #define vsema(sp) up(sp) -#define valusema(sp) (atomic_read(&(sp)->count)) -#define freesema(sema) +#define freesema(sema) do { } while (0) + +static inline int issemalocked(sema_t *sp) +{ + return down_trylock(sp) || (up(sp), 0); +} /* * Map cpsema (try to get the sema) to down_trylock. We need to switch * the return values since cpsema returns 1 (acquired) 0 (failed) and * down_trylock returns the reverse 0 (acquired) 1 (failed). */ - -#define cpsema(sp) (down_trylock(sp) ? 0 : 1) - -/* - * Didn't do cvsema(sp). Not sure how to map this to up/down/... - * It does a vsema if the values is < 0 other wise nothing. - */ +static inline int cpsema(sema_t *sp) +{ + return down_trylock(sp) ? 0 : 1; +} #endif /* __XFS_SUPPORT_SEMA_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 4d191ef..c40f81b 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -21,7 +21,6 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_trans.h" #include "xfs_dmapi.h" @@ -29,7 +28,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -76,7 +74,7 @@ xfs_page_trace( int mask) { xfs_inode_t *ip; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); loff_t isize = i_size_read(inode); loff_t offset = page_offset(page); int delalloc = -1, unmapped = -1, unwritten = -1; @@ -136,9 +134,10 @@ xfs_destroy_ioend( for (bh = ioend->io_buffer_head; bh; bh = next) { next = bh->b_private; - bh->b_end_io(bh, ioend->io_uptodate); + bh->b_end_io(bh, !ioend->io_error); } - + if (unlikely(ioend->io_error)) + vn_ioerror(ioend->io_vnode, ioend->io_error, __FILE__,__LINE__); vn_iowake(ioend->io_vnode); mempool_free(ioend, xfs_ioend_pool); } @@ -180,13 +179,12 @@ xfs_end_bio_unwritten( void *data) { xfs_ioend_t *ioend = data; - vnode_t *vp = ioend->io_vnode; + bhv_vnode_t *vp = ioend->io_vnode; xfs_off_t offset = ioend->io_offset; size_t size = ioend->io_size; - int error; - if (ioend->io_uptodate) - VOP_BMAP(vp, offset, size, BMAPI_UNWRITTEN, NULL, NULL, error); + if (likely(!ioend->io_error)) + bhv_vop_bmap(vp, offset, size, BMAPI_UNWRITTEN, NULL, NULL); xfs_destroy_ioend(ioend); } @@ -211,7 +209,7 @@ xfs_alloc_ioend( * all the I/O from calling the completion routine too early. */ atomic_set(&ioend->io_remaining, 1); - ioend->io_uptodate = 1; /* cleared if any I/O fails */ + ioend->io_error = 0; ioend->io_list = NULL; ioend->io_type = type; ioend->io_vnode = vn_from_inode(inode); @@ -239,10 +237,10 @@ xfs_map_blocks( xfs_iomap_t *mapp, int flags) { - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); int error, nmaps = 1; - VOP_BMAP(vp, offset, count, flags, mapp, &nmaps, error); + error = bhv_vop_bmap(vp, offset, count, flags, mapp, &nmaps); if (!error && (flags & (BMAPI_WRITE|BMAPI_ALLOCATE))) VMODIFY(vp); return -error; @@ -271,16 +269,14 @@ xfs_end_bio( if (bio->bi_size) return 1; - ASSERT(ioend); ASSERT(atomic_read(&bio->bi_cnt) >= 1); + ioend->io_error = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : error; /* Toss bio and pass work off to an xfsdatad thread */ - if (!test_bit(BIO_UPTODATE, &bio->bi_flags)) - ioend->io_uptodate = 0; bio->bi_private = NULL; bio->bi_end_io = NULL; - bio_put(bio); + xfs_finish_ioend(ioend); return 0; } @@ -1127,7 +1123,7 @@ xfs_vm_writepage( * then mark the page dirty again and leave the page * as is. */ - if (PFLAGS_TEST_FSTRANS() && need_trans) + if (current_test_flags(PF_FSTRANS) && need_trans) goto out_fail; /* @@ -1158,6 +1154,18 @@ out_unlock: return error; } +STATIC int +xfs_vm_writepages( + struct address_space *mapping, + struct writeback_control *wbc) +{ + struct bhv_vnode *vp = vn_from_inode(mapping->host); + + if (VN_TRUNC(vp)) + VUNTRUNCATE(vp); + return generic_writepages(mapping, wbc); +} + /* * Called to move a page into cleanable state - and from there * to be released. Possibly the page is already clean. We always @@ -1204,7 +1212,7 @@ xfs_vm_releasepage( /* If we are already inside a transaction or the thread cannot * do I/O, we cannot release this page. */ - if (PFLAGS_TEST_FSTRANS()) + if (current_test_flags(PF_FSTRANS)) return 0; /* @@ -1231,7 +1239,7 @@ __xfs_get_blocks( int direct, bmapi_flags_t flags) { - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); xfs_iomap_t iomap; xfs_off_t offset; ssize_t size; @@ -1241,8 +1249,8 @@ __xfs_get_blocks( offset = (xfs_off_t)iblock << inode->i_blkbits; ASSERT(bh_result->b_size >= (1 << inode->i_blkbits)); size = bh_result->b_size; - VOP_BMAP(vp, offset, size, - create ? flags : BMAPI_READ, &iomap, &niomap, error); + error = bhv_vop_bmap(vp, offset, size, + create ? flags : BMAPI_READ, &iomap, &niomap); if (error) return -error; if (niomap == 0) @@ -1370,13 +1378,13 @@ xfs_vm_direct_IO( { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); xfs_iomap_t iomap; int maps = 1; int error; ssize_t ret; - VOP_BMAP(vp, offset, 0, BMAPI_DEVICE, &iomap, &maps, error); + error = bhv_vop_bmap(vp, offset, 0, BMAPI_DEVICE, &iomap, &maps); if (error) return -error; @@ -1409,14 +1417,12 @@ xfs_vm_bmap( sector_t block) { struct inode *inode = (struct inode *)mapping->host; - vnode_t *vp = vn_from_inode(inode); - int error; + bhv_vnode_t *vp = vn_from_inode(inode); vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); - - VOP_RWLOCK(vp, VRWLOCK_READ); - VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error); - VOP_RWUNLOCK(vp, VRWLOCK_READ); + bhv_vop_rwlock(vp, VRWLOCK_READ); + bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF); + bhv_vop_rwunlock(vp, VRWLOCK_READ); return generic_block_bmap(mapping, block, xfs_get_blocks); } @@ -1448,10 +1454,11 @@ xfs_vm_invalidatepage( block_invalidatepage(page, offset); } -struct address_space_operations xfs_address_space_operations = { +const struct address_space_operations xfs_address_space_operations = { .readpage = xfs_vm_readpage, .readpages = xfs_vm_readpages, .writepage = xfs_vm_writepage, + .writepages = xfs_vm_writepages, .sync_page = block_sync_page, .releasepage = xfs_vm_releasepage, .invalidatepage = xfs_vm_invalidatepage, diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h index 6071654..2244e51 100644 --- a/fs/xfs/linux-2.6/xfs_aops.h +++ b/fs/xfs/linux-2.6/xfs_aops.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Silicon Graphics, Inc. + * Copyright (c) 2005-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -30,9 +30,9 @@ typedef void (*xfs_ioend_func_t)(void *) typedef struct xfs_ioend { struct xfs_ioend *io_list; /* next ioend in chain */ unsigned int io_type; /* delalloc / unwritten */ - unsigned int io_uptodate; /* I/O status register */ + int io_error; /* I/O error code */ atomic_t io_remaining; /* hold count */ - struct vnode *io_vnode; /* file being written to */ + struct bhv_vnode *io_vnode; /* file being written to */ struct buffer_head *io_buffer_head;/* buffer linked list head */ struct buffer_head *io_buffer_tail;/* buffer linked list tail */ size_t io_size; /* size of the extent */ @@ -40,7 +40,7 @@ typedef struct xfs_ioend { struct work_struct io_work; /* xfsdatad work queue */ } xfs_ioend_t; -extern struct address_space_operations xfs_address_space_operations; +extern const struct address_space_operations xfs_address_space_operations; extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int); -#endif /* __XFS_IOPS_H__ */ +#endif /* __XFS_AOPS_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 26fed07..2af528d 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -1520,7 +1520,7 @@ xfs_mapping_buftarg( struct backing_dev_info *bdi; struct inode *inode; struct address_space *mapping; - static struct address_space_operations mapping_aops = { + static const struct address_space_operations mapping_aops = { .sync_page = block_sync_page, .migratepage = fail_migrate_page, }; diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 4dd6592..ceda3a2 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -18,7 +18,6 @@ #ifndef __XFS_BUF_H__ #define __XFS_BUF_H__ -#include #include #include #include diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index b768ea9..5fb75d9 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -21,7 +21,6 @@ #include "xfs_dmapi.h" #include "xfs_log.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_mount.h" #include "xfs_export.h" @@ -97,7 +96,7 @@ xfs_fs_encode_fh( int len; int is64 = 0; #if XFS_BIG_INUMS - vfs_t *vfs = vfs_from_sb(inode->i_sb); + bhv_vfs_t *vfs = vfs_from_sb(inode->i_sb); if (!(vfs->vfs_flag & VFS_32BITINODES)) { /* filesystem may contain 64bit inode numbers */ @@ -136,13 +135,13 @@ xfs_fs_get_dentry( struct super_block *sb, void *data) { - vnode_t *vp; + bhv_vnode_t *vp; struct inode *inode; struct dentry *result; - vfs_t *vfsp = vfs_from_sb(sb); + bhv_vfs_t *vfsp = vfs_from_sb(sb); int error; - VFS_VGET(vfsp, &vp, (fid_t *)data, error); + error = bhv_vfs_vget(vfsp, &vp, (fid_t *)data); if (error || vp == NULL) return ERR_PTR(-ESTALE) ; @@ -160,12 +159,12 @@ xfs_fs_get_parent( struct dentry *child) { int error; - vnode_t *vp, *cvp; + bhv_vnode_t *vp, *cvp; struct dentry *parent; cvp = NULL; vp = vn_from_inode(child->d_inode); - VOP_LOOKUP(vp, &dotdot, &cvp, 0, NULL, NULL, error); + error = bhv_vop_lookup(vp, &dotdot, &cvp, 0, NULL, NULL); if (unlikely(error)) return ERR_PTR(-error); diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index c847416..3d4f6df 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -21,7 +21,6 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_trans.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_ialloc_btree.h" #include "xfs_alloc.h" #include "xfs_btree.h" #include "xfs_attr_sf.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" @@ -58,15 +56,12 @@ __xfs_file_read( { struct iovec iov = {buf, count}; struct file *file = iocb->ki_filp; - vnode_t *vp = vn_from_inode(file->f_dentry->d_inode); - ssize_t rval; + bhv_vnode_t *vp = vn_from_inode(file->f_dentry->d_inode); BUG_ON(iocb->ki_pos != pos); - if (unlikely(file->f_flags & O_DIRECT)) ioflags |= IO_ISDIRECT; - VOP_READ(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL, rval); - return rval; + return bhv_vop_read(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL); } STATIC ssize_t @@ -100,15 +95,12 @@ __xfs_file_write( struct iovec iov = {(void __user *)buf, count}; struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; - vnode_t *vp = vn_from_inode(inode); - ssize_t rval; + bhv_vnode_t *vp = vn_from_inode(inode); BUG_ON(iocb->ki_pos != pos); if (unlikely(file->f_flags & O_DIRECT)) ioflags |= IO_ISDIRECT; - - VOP_WRITE(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL, rval); - return rval; + return bhv_vop_write(vp, iocb, &iov, 1, &iocb->ki_pos, ioflags, NULL); } STATIC ssize_t @@ -140,7 +132,7 @@ __xfs_file_readv( loff_t *ppos) { struct inode *inode = file->f_mapping->host; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); struct kiocb kiocb; ssize_t rval; @@ -149,7 +141,8 @@ __xfs_file_readv( if (unlikely(file->f_flags & O_DIRECT)) ioflags |= IO_ISDIRECT; - VOP_READ(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, ioflags, NULL, rval); + rval = bhv_vop_read(vp, &kiocb, iov, nr_segs, + &kiocb.ki_pos, ioflags, NULL); *ppos = kiocb.ki_pos; return rval; @@ -184,7 +177,7 @@ __xfs_file_writev( loff_t *ppos) { struct inode *inode = file->f_mapping->host; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); struct kiocb kiocb; ssize_t rval; @@ -193,7 +186,8 @@ __xfs_file_writev( if (unlikely(file->f_flags & O_DIRECT)) ioflags |= IO_ISDIRECT; - VOP_WRITE(vp, &kiocb, iov, nr_segs, &kiocb.ki_pos, ioflags, NULL, rval); + rval = bhv_vop_write(vp, &kiocb, iov, nr_segs, + &kiocb.ki_pos, ioflags, NULL); *ppos = kiocb.ki_pos; return rval; @@ -227,11 +221,8 @@ xfs_file_sendfile( read_actor_t actor, void *target) { - vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode); - ssize_t rval; - - VOP_SENDFILE(vp, filp, pos, 0, count, actor, target, NULL, rval); - return rval; + return bhv_vop_sendfile(vn_from_inode(filp->f_dentry->d_inode), + filp, pos, 0, count, actor, target, NULL); } STATIC ssize_t @@ -242,11 +233,8 @@ xfs_file_sendfile_invis( read_actor_t actor, void *target) { - vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode); - ssize_t rval; - - VOP_SENDFILE(vp, filp, pos, IO_INVIS, count, actor, target, NULL, rval); - return rval; + return bhv_vop_sendfile(vn_from_inode(filp->f_dentry->d_inode), + filp, pos, IO_INVIS, count, actor, target, NULL); } STATIC ssize_t @@ -257,11 +245,8 @@ xfs_file_splice_read( size_t len, unsigned int flags) { - vnode_t *vp = vn_from_inode(infilp->f_dentry->d_inode); - ssize_t rval; - - VOP_SPLICE_READ(vp, infilp, ppos, pipe, len, flags, 0, NULL, rval); - return rval; + return bhv_vop_splice_read(vn_from_inode(infilp->f_dentry->d_inode), + infilp, ppos, pipe, len, flags, 0, NULL); } STATIC ssize_t @@ -272,11 +257,9 @@ xfs_file_splice_read_invis( size_t len, unsigned int flags) { - vnode_t *vp = vn_from_inode(infilp->f_dentry->d_inode); - ssize_t rval; - - VOP_SPLICE_READ(vp, infilp, ppos, pipe, len, flags, IO_INVIS, NULL, rval); - return rval; + return bhv_vop_splice_read(vn_from_inode(infilp->f_dentry->d_inode), + infilp, ppos, pipe, len, flags, IO_INVIS, + NULL); } STATIC ssize_t @@ -287,11 +270,8 @@ xfs_file_splice_write( size_t len, unsigned int flags) { - vnode_t *vp = vn_from_inode(outfilp->f_dentry->d_inode); - ssize_t rval; - - VOP_SPLICE_WRITE(vp, pipe, outfilp, ppos, len, flags, 0, NULL, rval); - return rval; + return bhv_vop_splice_write(vn_from_inode(outfilp->f_dentry->d_inode), + pipe, outfilp, ppos, len, flags, 0, NULL); } STATIC ssize_t @@ -302,11 +282,9 @@ xfs_file_splice_write_invis( size_t len, unsigned int flags) { - vnode_t *vp = vn_from_inode(outfilp->f_dentry->d_inode); - ssize_t rval; - - VOP_SPLICE_WRITE(vp, pipe, outfilp, ppos, len, flags, IO_INVIS, NULL, rval); - return rval; + return bhv_vop_splice_write(vn_from_inode(outfilp->f_dentry->d_inode), + pipe, outfilp, ppos, len, flags, IO_INVIS, + NULL); } STATIC int @@ -314,13 +292,18 @@ xfs_file_open( struct inode *inode, struct file *filp) { - vnode_t *vp = vn_from_inode(inode); - int error; - if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) return -EFBIG; - VOP_OPEN(vp, NULL, error); - return -error; + return -bhv_vop_open(vn_from_inode(inode), NULL); +} + +STATIC int +xfs_file_close( + struct file *filp, + fl_owner_t id) +{ + return -bhv_vop_close(vn_from_inode(filp->f_dentry->d_inode), 0, + file_count(filp) > 1 ? L_FALSE : L_TRUE, NULL); } STATIC int @@ -328,12 +311,11 @@ xfs_file_release( struct inode *inode, struct file *filp) { - vnode_t *vp = vn_from_inode(inode); - int error = 0; + bhv_vnode_t *vp = vn_from_inode(inode); if (vp) - VOP_RELEASE(vp, error); - return -error; + return -bhv_vop_release(vp); + return 0; } STATIC int @@ -342,15 +324,14 @@ xfs_file_fsync( struct dentry *dentry, int datasync) { - struct inode *inode = dentry->d_inode; - vnode_t *vp = vn_from_inode(inode); - int error; + bhv_vnode_t *vp = vn_from_inode(dentry->d_inode); int flags = FSYNC_WAIT; if (datasync) flags |= FSYNC_DATA; - VOP_FSYNC(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1, error); - return -error; + if (VN_TRUNC(vp)) + VUNTRUNCATE(vp); + return -bhv_vop_fsync(vp, flags, NULL, (xfs_off_t)0, (xfs_off_t)-1); } #ifdef CONFIG_XFS_DMAPI @@ -361,16 +342,11 @@ xfs_vm_nopage( int *type) { struct inode *inode = area->vm_file->f_dentry->d_inode; - vnode_t *vp = vn_from_inode(inode); - xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp); - int error; + bhv_vnode_t *vp = vn_from_inode(inode); ASSERT_ALWAYS(vp->v_vfsp->vfs_flag & VFS_DMI); - - error = XFS_SEND_MMAP(mp, area, 0); - if (error) + if (XFS_SEND_MMAP(XFS_VFSTOM(vp->v_vfsp), area, 0)) return NULL; - return filemap_nopage(area, address, type); } #endif /* CONFIG_XFS_DMAPI */ @@ -382,7 +358,7 @@ xfs_file_readdir( filldir_t filldir) { int error = 0; - vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode); + bhv_vnode_t *vp = vn_from_inode(filp->f_dentry->d_inode); uio_t uio; iovec_t iov; int eof = 0; @@ -417,7 +393,7 @@ xfs_file_readdir( start_offset = uio.uio_offset; - VOP_READDIR(vp, &uio, NULL, &eof, error); + error = bhv_vop_readdir(vp, &uio, NULL, &eof); if ((uio.uio_offset == start_offset) || error) { size = 0; break; @@ -456,38 +432,28 @@ xfs_file_mmap( struct file *filp, struct vm_area_struct *vma) { - struct inode *ip = filp->f_dentry->d_inode; - vnode_t *vp = vn_from_inode(ip); - vattr_t vattr; - int error; - vma->vm_ops = &xfs_file_vm_ops; #ifdef CONFIG_XFS_DMAPI - if (vp->v_vfsp->vfs_flag & VFS_DMI) { + if (vn_from_inode(filp->f_dentry->d_inode)->v_vfsp->vfs_flag & VFS_DMI) vma->vm_ops = &xfs_dmapi_file_vm_ops; - } #endif /* CONFIG_XFS_DMAPI */ - vattr.va_mask = XFS_AT_UPDATIME; - VOP_SETATTR(vp, &vattr, XFS_AT_UPDATIME, NULL, error); - if (likely(!error)) - __vn_revalidate(vp, &vattr); /* update flags */ + file_accessed(filp); return 0; } - STATIC long xfs_file_ioctl( struct file *filp, unsigned int cmd, - unsigned long arg) + unsigned long p) { int error; struct inode *inode = filp->f_dentry->d_inode; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); - VOP_IOCTL(vp, inode, filp, 0, cmd, (void __user *)arg, error); + error = bhv_vop_ioctl(vp, inode, filp, 0, cmd, (void __user *)p); VMODIFY(vp); /* NOTE: some of the ioctl's return positive #'s as a @@ -503,13 +469,13 @@ STATIC long xfs_file_ioctl_invis( struct file *filp, unsigned int cmd, - unsigned long arg) + unsigned long p) { - struct inode *inode = filp->f_dentry->d_inode; - vnode_t *vp = vn_from_inode(inode); int error; + struct inode *inode = filp->f_dentry->d_inode; + bhv_vnode_t *vp = vn_from_inode(inode); - VOP_IOCTL(vp, inode, filp, IO_INVIS, cmd, (void __user *)arg, error); + error = bhv_vop_ioctl(vp, inode, filp, IO_INVIS, cmd, (void __user *)p); VMODIFY(vp); /* NOTE: some of the ioctl's return positive #'s as a @@ -528,7 +494,7 @@ xfs_vm_mprotect( struct vm_area_struct *vma, unsigned int newflags) { - vnode_t *vp = vn_from_inode(vma->vm_file->f_dentry->d_inode); + bhv_vnode_t *vp = vn_from_inode(vma->vm_file->f_dentry->d_inode); int error = 0; if (vp->v_vfsp->vfs_flag & VFS_DMI) { @@ -554,24 +520,19 @@ STATIC int xfs_file_open_exec( struct inode *inode) { - vnode_t *vp = vn_from_inode(inode); - xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp); - int error = 0; - xfs_inode_t *ip; + bhv_vnode_t *vp = vn_from_inode(inode); - if (vp->v_vfsp->vfs_flag & VFS_DMI) { - ip = xfs_vtoi(vp); - if (!ip) { - error = -EINVAL; - goto open_exec_out; - } - if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ)) { - error = -XFS_SEND_DATA(mp, DM_EVENT_READ, vp, + if (unlikely(vp->v_vfsp->vfs_flag & VFS_DMI)) { + xfs_mount_t *mp = XFS_VFSTOM(vp->v_vfsp); + xfs_inode_t *ip = xfs_vtoi(vp); + + if (!ip) + return -EINVAL; + if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ)) + return -XFS_SEND_DATA(mp, DM_EVENT_READ, vp, 0, 0, 0, NULL); - } } -open_exec_out: - return error; + return 0; } #endif /* HAVE_FOP_OPEN_EXEC */ @@ -592,6 +553,7 @@ #ifdef CONFIG_COMPAT #endif .mmap = xfs_file_mmap, .open = xfs_file_open, + .flush = xfs_file_close, .release = xfs_file_release, .fsync = xfs_file_fsync, #ifdef HAVE_FOP_OPEN_EXEC @@ -616,6 +578,7 @@ #ifdef CONFIG_COMPAT #endif .mmap = xfs_file_mmap, .open = xfs_file_open, + .flush = xfs_file_close, .release = xfs_file_release, .fsync = xfs_file_fsync, }; diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.c b/fs/xfs/linux-2.6/xfs_fs_subr.c index 575f2a7..dc05628 100644 --- a/fs/xfs/linux-2.6/xfs_fs_subr.c +++ b/fs/xfs/linux-2.6/xfs_fs_subr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2002,2005-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -15,40 +15,12 @@ * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - #include "xfs.h" -/* - * Stub for no-op vnode operations that return error status. - */ -int -fs_noerr(void) -{ - return 0; -} +int fs_noerr(void) { return 0; } +int fs_nosys(void) { return ENOSYS; } +void fs_noval(void) { return; } -/* - * Operation unsupported under this file system. - */ -int -fs_nosys(void) -{ - return ENOSYS; -} - -/* - * Stub for inactive, strategy, and read/write lock/unlock. Does nothing. - */ -/* ARGSUSED */ -void -fs_noval(void) -{ -} - -/* - * vnode pcache layer for vnode_tosspages. - * 'last' parameter unused but left in for IRIX compatibility - */ void fs_tosspages( bhv_desc_t *bdp, @@ -56,18 +28,13 @@ fs_tosspages( xfs_off_t last, int fiopt) { - vnode_t *vp = BHV_TO_VNODE(bdp); + bhv_vnode_t *vp = BHV_TO_VNODE(bdp); struct inode *ip = vn_to_inode(vp); if (VN_CACHED(vp)) truncate_inode_pages(ip->i_mapping, first); } - -/* - * vnode pcache layer for vnode_flushinval_pages. - * 'last' parameter unused but left in for IRIX compatibility - */ void fs_flushinval_pages( bhv_desc_t *bdp, @@ -75,20 +42,17 @@ fs_flushinval_pages( xfs_off_t last, int fiopt) { - vnode_t *vp = BHV_TO_VNODE(bdp); + bhv_vnode_t *vp = BHV_TO_VNODE(bdp); struct inode *ip = vn_to_inode(vp); if (VN_CACHED(vp)) { + if (VN_TRUNC(vp)) + VUNTRUNCATE(vp); filemap_write_and_wait(ip->i_mapping); - truncate_inode_pages(ip->i_mapping, first); } } -/* - * vnode pcache layer for vnode_flush_pages. - * 'last' parameter unused but left in for IRIX compatibility - */ int fs_flush_pages( bhv_desc_t *bdp, @@ -97,15 +61,16 @@ fs_flush_pages( uint64_t flags, int fiopt) { - vnode_t *vp = BHV_TO_VNODE(bdp); + bhv_vnode_t *vp = BHV_TO_VNODE(bdp); struct inode *ip = vn_to_inode(vp); - if (VN_CACHED(vp)) { + if (VN_DIRTY(vp)) { + if (VN_TRUNC(vp)) + VUNTRUNCATE(vp); filemap_fdatawrite(ip->i_mapping); if (flags & XFS_B_ASYNC) return 0; filemap_fdatawait(ip->i_mapping); } - return 0; } diff --git a/fs/xfs/linux-2.6/xfs_globals.c b/fs/xfs/linux-2.6/xfs_globals.c index 6e8085f..6c162c3 100644 --- a/fs/xfs/linux-2.6/xfs_globals.c +++ b/fs/xfs/linux-2.6/xfs_globals.c @@ -45,6 +45,7 @@ xfs_param_t xfs_params = { .xfs_buf_age = { 1*100, 15*100, 7200*100}, .inherit_nosym = { 0, 0, 1 }, .rotorstep = { 1, 1, 255 }, + .inherit_nodfrg = { 0, 1, 1 }, }; /* diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 8447849..6e52a5d 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -31,7 +30,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_attr_sf.h" #include "xfs_dir2_sf.h" #include "xfs_dinode.h" @@ -78,7 +76,7 @@ xfs_find_handle( xfs_handle_t handle; xfs_fsop_handlereq_t hreq; struct inode *inode; - struct vnode *vp; + bhv_vnode_t *vp; if (copy_from_user(&hreq, arg, sizeof(hreq))) return -XFS_ERROR(EFAULT); @@ -192,7 +190,7 @@ xfs_vget_fsop_handlereq( xfs_mount_t *mp, struct inode *parinode, /* parent inode pointer */ xfs_fsop_handlereq_t *hreq, - vnode_t **vp, + bhv_vnode_t **vp, struct inode **inode) { void __user *hanp; @@ -202,7 +200,7 @@ xfs_vget_fsop_handlereq( xfs_handle_t handle; xfs_inode_t *ip; struct inode *inodep; - vnode_t *vpp; + bhv_vnode_t *vpp; xfs_ino_t ino; __u32 igen; int error; @@ -277,7 +275,7 @@ xfs_open_by_handle( struct file *filp; struct inode *inode; struct dentry *dentry; - vnode_t *vp; + bhv_vnode_t *vp; xfs_fsop_handlereq_t hreq; if (!capable(CAP_SYS_ADMIN)) @@ -362,7 +360,7 @@ xfs_readlink_by_handle( struct uio auio; struct inode *inode; xfs_fsop_handlereq_t hreq; - vnode_t *vp; + bhv_vnode_t *vp; __u32 olen; if (!capable(CAP_SYS_ADMIN)) @@ -393,9 +391,11 @@ xfs_readlink_by_handle( auio.uio_segflg = UIO_USERSPACE; auio.uio_resid = olen; - VOP_READLINK(vp, &auio, IO_INVIS, NULL, error); - + error = bhv_vop_readlink(vp, &auio, IO_INVIS, NULL); VN_RELE(vp); + if (error) + return -error; + return (olen - auio.uio_resid); } @@ -411,7 +411,7 @@ xfs_fssetdm_by_handle( xfs_fsop_setdm_handlereq_t dmhreq; struct inode *inode; bhv_desc_t *bdp; - vnode_t *vp; + bhv_vnode_t *vp; if (!capable(CAP_MKNOD)) return -XFS_ERROR(EPERM); @@ -452,7 +452,7 @@ xfs_attrlist_by_handle( attrlist_cursor_kern_t *cursor; xfs_fsop_attrlist_handlereq_t al_hreq; struct inode *inode; - vnode_t *vp; + bhv_vnode_t *vp; char *kbuf; if (!capable(CAP_SYS_ADMIN)) @@ -472,8 +472,8 @@ xfs_attrlist_by_handle( goto out_vn_rele; cursor = (attrlist_cursor_kern_t *)&al_hreq.pos; - VOP_ATTR_LIST(vp, kbuf, al_hreq.buflen, al_hreq.flags, - cursor, NULL, error); + error = bhv_vop_attr_list(vp, kbuf, al_hreq.buflen, al_hreq.flags, + cursor, NULL); if (error) goto out_kfree; @@ -490,7 +490,7 @@ xfs_attrlist_by_handle( STATIC int xfs_attrmulti_attr_get( - struct vnode *vp, + bhv_vnode_t *vp, char *name, char __user *ubuf, __uint32_t *len, @@ -505,7 +505,7 @@ xfs_attrmulti_attr_get( if (!kbuf) return ENOMEM; - VOP_ATTR_GET(vp, name, kbuf, len, flags, NULL, error); + error = bhv_vop_attr_get(vp, name, kbuf, len, flags, NULL); if (error) goto out_kfree; @@ -519,7 +519,7 @@ xfs_attrmulti_attr_get( STATIC int xfs_attrmulti_attr_set( - struct vnode *vp, + bhv_vnode_t *vp, char *name, const char __user *ubuf, __uint32_t len, @@ -542,7 +542,7 @@ xfs_attrmulti_attr_set( if (copy_from_user(kbuf, ubuf, len)) goto out_kfree; - VOP_ATTR_SET(vp, name, kbuf, len, flags, NULL, error); + error = bhv_vop_attr_set(vp, name, kbuf, len, flags, NULL); out_kfree: kfree(kbuf); @@ -551,20 +551,15 @@ xfs_attrmulti_attr_set( STATIC int xfs_attrmulti_attr_remove( - struct vnode *vp, + bhv_vnode_t *vp, char *name, __uint32_t flags) { - int error; - - if (IS_RDONLY(&vp->v_inode)) return -EROFS; if (IS_IMMUTABLE(&vp->v_inode) || IS_APPEND(&vp->v_inode)) return EPERM; - - VOP_ATTR_REMOVE(vp, name, flags, NULL, error); - return error; + return bhv_vop_attr_remove(vp, name, flags, NULL); } STATIC int @@ -578,7 +573,7 @@ xfs_attrmulti_by_handle( xfs_attr_multiop_t *ops; xfs_fsop_attrmulti_handlereq_t am_hreq; struct inode *inode; - vnode_t *vp; + bhv_vnode_t *vp; unsigned int i, size; char *attr_name; @@ -658,7 +653,7 @@ xfs_attrmulti_by_handle( STATIC int xfs_ioc_space( bhv_desc_t *bdp, - vnode_t *vp, + bhv_vnode_t *vp, struct file *filp, int flags, unsigned int cmd, @@ -682,7 +677,7 @@ xfs_ioc_fsgeometry( STATIC int xfs_ioc_xattr( - vnode_t *vp, + bhv_vnode_t *vp, xfs_inode_t *ip, struct file *filp, unsigned int cmd, @@ -711,7 +706,7 @@ xfs_ioctl( void __user *arg) { int error; - vnode_t *vp; + bhv_vnode_t *vp; xfs_inode_t *ip; xfs_mount_t *mp; @@ -962,7 +957,7 @@ xfs_ioctl( STATIC int xfs_ioc_space( bhv_desc_t *bdp, - vnode_t *vp, + bhv_vnode_t *vp, struct file *filp, int ioflags, unsigned int cmd, @@ -1153,14 +1148,14 @@ xfs_di2lxflags( STATIC int xfs_ioc_xattr( - vnode_t *vp, + bhv_vnode_t *vp, xfs_inode_t *ip, struct file *filp, unsigned int cmd, void __user *arg) { struct fsxattr fa; - struct vattr *vattr; + struct bhv_vattr *vattr; int error = 0; int attr_flags; unsigned int flags; @@ -1173,7 +1168,7 @@ xfs_ioc_xattr( case XFS_IOC_FSGETXATTR: { vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \ XFS_AT_NEXTENTS | XFS_AT_PROJID; - VOP_GETATTR(vp, vattr, 0, NULL, error); + error = bhv_vop_getattr(vp, vattr, 0, NULL); if (unlikely(error)) { error = -error; break; @@ -1206,7 +1201,7 @@ xfs_ioc_xattr( vattr->va_extsize = fa.fsx_extsize; vattr->va_projid = fa.fsx_projid; - VOP_SETATTR(vp, vattr, attr_flags, NULL, error); + error = bhv_vop_setattr(vp, vattr, attr_flags, NULL); if (likely(!error)) __vn_revalidate(vp, vattr); /* update flags */ error = -error; @@ -1216,7 +1211,7 @@ xfs_ioc_xattr( case XFS_IOC_FSGETXATTRA: { vattr->va_mask = XFS_AT_XFLAGS | XFS_AT_EXTSIZE | \ XFS_AT_ANEXTENTS | XFS_AT_PROJID; - VOP_GETATTR(vp, vattr, 0, NULL, error); + error = bhv_vop_getattr(vp, vattr, 0, NULL); if (unlikely(error)) { error = -error; break; @@ -1262,7 +1257,7 @@ xfs_ioc_xattr( vattr->va_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip)); - VOP_SETATTR(vp, vattr, attr_flags, NULL, error); + error = bhv_vop_setattr(vp, vattr, attr_flags, NULL); if (likely(!error)) __vn_revalidate(vp, vattr); /* update flags */ error = -error; diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 251bfe4..270db0f 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -15,7 +15,6 @@ * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include #include #include @@ -114,7 +113,7 @@ xfs_compat_ioctl( unsigned long arg) { struct inode *inode = file->f_dentry->d_inode; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); int error; switch (cmd) { @@ -193,7 +192,7 @@ #endif return -ENOIOCTLCMD; } - VOP_IOCTL(vp, inode, file, mode, cmd, (void __user *)arg, error); + error = bhv_vop_ioctl(vp, inode, file, mode, cmd, (void __user *)arg); VMODIFY(vp); return error; diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 2e2e275..d918002 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -61,7 +59,7 @@ #include */ xfs_inode_t * xfs_vtoi( - struct vnode *vp) + bhv_vnode_t *vp) { bhv_desc_t *bdp; @@ -80,7 +78,7 @@ void xfs_synchronize_atime( xfs_inode_t *ip) { - vnode_t *vp; + bhv_vnode_t *vp; vp = XFS_ITOV_NULL(ip); if (vp) { @@ -200,14 +198,10 @@ xfs_ichgtime_fast( STATIC void xfs_validate_fields( struct inode *ip, - struct vattr *vattr) + bhv_vattr_t *vattr) { - vnode_t *vp = vn_from_inode(ip); - int error; - vattr->va_mask = XFS_AT_NLINK|XFS_AT_SIZE|XFS_AT_NBLOCKS; - VOP_GETATTR(vp, vattr, ATTR_LAZY, NULL, error); - if (likely(!error)) { + if (!bhv_vop_getattr(vn_from_inode(ip), vattr, ATTR_LAZY, NULL)) { ip->i_nlink = vattr->va_nlink; ip->i_blocks = vattr->va_nblocks; @@ -225,7 +219,7 @@ xfs_validate_fields( */ STATIC int xfs_init_security( - struct vnode *vp, + bhv_vnode_t *vp, struct inode *dir) { struct inode *ip = vn_to_inode(vp); @@ -241,7 +235,7 @@ xfs_init_security( return -error; } - VOP_ATTR_SET(vp, name, value, length, ATTR_SECURE, NULL, error); + error = bhv_vop_attr_set(vp, name, value, length, ATTR_SECURE, NULL); if (!error) VMODIFY(vp); @@ -264,13 +258,12 @@ xfs_has_fs_struct(struct task_struct *ta STATIC inline void xfs_cleanup_inode( - vnode_t *dvp, - vnode_t *vp, + bhv_vnode_t *dvp, + bhv_vnode_t *vp, struct dentry *dentry, int mode) { struct dentry teardown = {}; - int error; /* Oh, the horror. * If we can't add the ACL or we fail in @@ -281,9 +274,9 @@ xfs_cleanup_inode( teardown.d_name = dentry->d_name; if (S_ISDIR(mode)) - VOP_RMDIR(dvp, &teardown, NULL, error); + bhv_vop_rmdir(dvp, &teardown, NULL); else - VOP_REMOVE(dvp, &teardown, NULL, error); + bhv_vop_remove(dvp, &teardown, NULL); VN_RELE(vp); } @@ -295,8 +288,8 @@ xfs_vn_mknod( dev_t rdev) { struct inode *ip; - vattr_t vattr = { 0 }; - vnode_t *vp = NULL, *dvp = vn_from_inode(dir); + bhv_vattr_t vattr = { 0 }; + bhv_vnode_t *vp = NULL, *dvp = vn_from_inode(dir); xfs_acl_t *default_acl = NULL; attrexists_t test_default_acl = _ACL_DEFAULT_EXISTS; int error; @@ -330,10 +323,10 @@ xfs_vn_mknod( vattr.va_mask |= XFS_AT_RDEV; /*FALLTHROUGH*/ case S_IFREG: - VOP_CREATE(dvp, dentry, &vattr, &vp, NULL, error); + error = bhv_vop_create(dvp, dentry, &vattr, &vp, NULL); break; case S_IFDIR: - VOP_MKDIR(dvp, dentry, &vattr, &vp, NULL, error); + error = bhv_vop_mkdir(dvp, dentry, &vattr, &vp, NULL); break; default: error = EINVAL; @@ -396,14 +389,14 @@ xfs_vn_lookup( struct dentry *dentry, struct nameidata *nd) { - struct vnode *vp = vn_from_inode(dir), *cvp; + bhv_vnode_t *vp = vn_from_inode(dir), *cvp; int error; if (dentry->d_name.len >= MAXNAMELEN) return ERR_PTR(-ENAMETOOLONG); - VOP_LOOKUP(vp, dentry, &cvp, 0, NULL, NULL, error); - if (error) { + error = bhv_vop_lookup(vp, dentry, &cvp, 0, NULL, NULL); + if (unlikely(error)) { if (unlikely(error != ENOENT)) return ERR_PTR(-error); d_add(dentry, NULL); @@ -420,22 +413,21 @@ xfs_vn_link( struct dentry *dentry) { struct inode *ip; /* inode of guy being linked to */ - vnode_t *tdvp; /* target directory for new name/link */ - vnode_t *vp; /* vp of name being linked */ - vattr_t vattr; + bhv_vnode_t *tdvp; /* target directory for new name/link */ + bhv_vnode_t *vp; /* vp of name being linked */ + bhv_vattr_t vattr; int error; ip = old_dentry->d_inode; /* inode being linked to */ - if (S_ISDIR(ip->i_mode)) - return -EPERM; - tdvp = vn_from_inode(dir); vp = vn_from_inode(ip); - VOP_LINK(tdvp, vp, dentry, NULL, error); - if (likely(!error)) { + VN_HOLD(vp); + error = bhv_vop_link(tdvp, vp, dentry, NULL); + if (unlikely(error)) { + VN_RELE(vp); + } else { VMODIFY(tdvp); - VN_HOLD(vp); xfs_validate_fields(ip, &vattr); d_instantiate(dentry, ip); } @@ -448,14 +440,14 @@ xfs_vn_unlink( struct dentry *dentry) { struct inode *inode; - vnode_t *dvp; /* directory containing name to remove */ - vattr_t vattr; + bhv_vnode_t *dvp; /* directory containing name to remove */ + bhv_vattr_t vattr; int error; inode = dentry->d_inode; dvp = vn_from_inode(dir); - VOP_REMOVE(dvp, dentry, NULL, error); + error = bhv_vop_remove(dvp, dentry, NULL); if (likely(!error)) { xfs_validate_fields(dir, &vattr); /* size needs update */ xfs_validate_fields(inode, &vattr); @@ -470,27 +462,26 @@ xfs_vn_symlink( const char *symname) { struct inode *ip; - vattr_t vattr = { 0 }; - vnode_t *dvp; /* directory containing name of symlink */ - vnode_t *cvp; /* used to lookup symlink to put in dentry */ + bhv_vattr_t va = { 0 }; + bhv_vnode_t *dvp; /* directory containing name of symlink */ + bhv_vnode_t *cvp; /* used to lookup symlink to put in dentry */ int error; dvp = vn_from_inode(dir); cvp = NULL; - vattr.va_mode = S_IFLNK | + va.va_mode = S_IFLNK | (irix_symlink_mode ? 0777 & ~current->fs->umask : S_IRWXUGO); - vattr.va_mask = XFS_AT_TYPE|XFS_AT_MODE; + va.va_mask = XFS_AT_TYPE|XFS_AT_MODE; - error = 0; - VOP_SYMLINK(dvp, dentry, &vattr, (char *)symname, &cvp, NULL, error); + error = bhv_vop_symlink(dvp, dentry, &va, (char *)symname, &cvp, NULL); if (likely(!error && cvp)) { error = xfs_init_security(cvp, dir); if (likely(!error)) { ip = vn_to_inode(cvp); d_instantiate(dentry, ip); - xfs_validate_fields(dir, &vattr); - xfs_validate_fields(ip, &vattr); + xfs_validate_fields(dir, &va); + xfs_validate_fields(ip, &va); } else { xfs_cleanup_inode(dvp, cvp, dentry, 0); } @@ -504,11 +495,11 @@ xfs_vn_rmdir( struct dentry *dentry) { struct inode *inode = dentry->d_inode; - vnode_t *dvp = vn_from_inode(dir); - vattr_t vattr; + bhv_vnode_t *dvp = vn_from_inode(dir); + bhv_vattr_t vattr; int error; - VOP_RMDIR(dvp, dentry, NULL, error); + error = bhv_vop_rmdir(dvp, dentry, NULL); if (likely(!error)) { xfs_validate_fields(inode, &vattr); xfs_validate_fields(dir, &vattr); @@ -524,15 +515,15 @@ xfs_vn_rename( struct dentry *ndentry) { struct inode *new_inode = ndentry->d_inode; - vnode_t *fvp; /* from directory */ - vnode_t *tvp; /* target directory */ - vattr_t vattr; + bhv_vnode_t *fvp; /* from directory */ + bhv_vnode_t *tvp; /* target directory */ + bhv_vattr_t vattr; int error; fvp = vn_from_inode(odir); tvp = vn_from_inode(ndir); - VOP_RENAME(fvp, odentry, tvp, ndentry, NULL, error); + error = bhv_vop_rename(fvp, odentry, tvp, ndentry, NULL); if (likely(!error)) { if (new_inode) xfs_validate_fields(new_inode, &vattr); @@ -553,7 +544,7 @@ xfs_vn_follow_link( struct dentry *dentry, struct nameidata *nd) { - vnode_t *vp; + bhv_vnode_t *vp; uio_t *uio; iovec_t iov; int error; @@ -586,8 +577,8 @@ xfs_vn_follow_link( uio->uio_resid = MAXPATHLEN; uio->uio_iovcnt = 1; - VOP_READLINK(vp, uio, 0, NULL, error); - if (error) { + error = bhv_vop_readlink(vp, uio, 0, NULL); + if (unlikely(error)) { kfree(link); link = ERR_PTR(-error); } else { @@ -618,12 +609,7 @@ xfs_vn_permission( int mode, struct nameidata *nd) { - vnode_t *vp = vn_from_inode(inode); - int error; - - mode <<= 6; /* convert from linux to vnode access bits */ - VOP_ACCESS(vp, mode, NULL, error); - return -error; + return -bhv_vop_access(vn_from_inode(inode), mode << 6, NULL); } #else #define xfs_vn_permission NULL @@ -636,14 +622,14 @@ xfs_vn_getattr( struct kstat *stat) { struct inode *inode = dentry->d_inode; - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); int error = 0; if (unlikely(vp->v_flag & VMODIFIED)) error = vn_revalidate(vp); if (!error) generic_fillattr(inode, stat); - return 0; + return -error; } STATIC int @@ -653,8 +639,8 @@ xfs_vn_setattr( { struct inode *inode = dentry->d_inode; unsigned int ia_valid = attr->ia_valid; - vnode_t *vp = vn_from_inode(inode); - vattr_t vattr = { 0 }; + bhv_vnode_t *vp = vn_from_inode(inode); + bhv_vattr_t vattr = { 0 }; int flags = 0; int error; @@ -697,7 +683,7 @@ #ifdef ATTR_NO_BLOCK flags |= ATTR_NONBLOCK; #endif - VOP_SETATTR(vp, &vattr, flags, NULL, error); + error = bhv_vop_setattr(vp, &vattr, flags, NULL); if (likely(!error)) __vn_revalidate(vp, &vattr); return -error; @@ -718,7 +704,7 @@ xfs_vn_setxattr( size_t size, int flags) { - vnode_t *vp = vn_from_inode(dentry->d_inode); + bhv_vnode_t *vp = vn_from_inode(dentry->d_inode); char *attr = (char *)name; attrnames_t *namesp; int xflags = 0; @@ -748,7 +734,7 @@ xfs_vn_getxattr( void *data, size_t size) { - vnode_t *vp = vn_from_inode(dentry->d_inode); + bhv_vnode_t *vp = vn_from_inode(dentry->d_inode); char *attr = (char *)name; attrnames_t *namesp; int xflags = 0; @@ -777,7 +763,7 @@ xfs_vn_listxattr( char *data, size_t size) { - vnode_t *vp = vn_from_inode(dentry->d_inode); + bhv_vnode_t *vp = vn_from_inode(dentry->d_inode); int error, xflags = ATTR_KERNAMELS; ssize_t result; @@ -796,7 +782,7 @@ xfs_vn_removexattr( struct dentry *dentry, const char *name) { - vnode_t *vp = vn_from_inode(dentry->d_inode); + bhv_vnode_t *vp = vn_from_inode(dentry->d_inode); char *attr = (char *)name; attrnames_t *namesp; int xflags = 0; diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index e9fe43d..8c021dc 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -19,7 +19,6 @@ #ifndef __XFS_LINUX__ #define __XFS_LINUX__ #include -#include /* * Some types are conditional depending on the target system. @@ -134,14 +133,19 @@ #define xfs_buf_timer_centisecs xfs_para #define xfs_buf_age_centisecs xfs_params.xfs_buf_age.val #define xfs_inherit_nosymlinks xfs_params.inherit_nosym.val #define xfs_rotorstep xfs_params.rotorstep.val +#define xfs_inherit_nodefrag xfs_params.inherit_nodfrg.val -#ifndef raw_smp_processor_id -#define raw_smp_processor_id() smp_processor_id() -#endif -#define current_cpu() raw_smp_processor_id() +#define current_cpu() (raw_smp_processor_id()) #define current_pid() (current->pid) #define current_fsuid(cred) (current->fsuid) #define current_fsgid(cred) (current->fsgid) +#define current_test_flags(f) (current->flags & (f)) +#define current_set_flags_nested(sp, f) \ + (*(sp) = current->flags, current->flags |= (f)) +#define current_clear_flags_nested(sp, f) \ + (*(sp) = current->flags, current->flags &= ~(f)) +#define current_restore_flags_nested(sp, f) \ + (current->flags = ((current->flags & ~(f)) | (*(sp) & (f)))) #define NBPP PAGE_SIZE #define DPPSHFT (PAGE_SHIFT - 9) @@ -187,25 +191,9 @@ #define io_ctob(x) ((__psunsigned_t)(x)< /* bytes to clicks */ #define btoc(x) (((__psunsigned_t)(x)+(NBPC-1))>>BPCSHIFT) -#ifndef ENOATTR #define ENOATTR ENODATA /* Attribute not found */ -#endif - -/* Note: EWRONGFS never visible outside the kernel */ -#define EWRONGFS EINVAL /* Mount with wrong filesystem type */ - -/* - * XXX EFSCORRUPTED needs a real value in errno.h. asm-i386/errno.h won't - * return codes out of its known range in errno. - * XXX Also note: needs to be < 1000 and fairly unique on Linux (mustn't - * conflict with any code we use already or any code a driver may use) - * XXX Some options (currently we do #2): - * 1/ New error code ["Filesystem is corrupted", _after_ glibc updated] - * 2/ 990 ["Unknown error 990"] - * 3/ EUCLEAN ["Structure needs cleaning"] - * 4/ Convert EFSCORRUPTED to EIO [just prior to return into userspace] - */ -#define EFSCORRUPTED 990 /* Filesystem is corrupted */ +#define EWRONGFS EINVAL /* Mount with wrong filesystem type */ +#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ #define SYNCHRONIZE() barrier() #define __return_address __builtin_return_address(0) diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c index 67efe33..5d9cfd9 100644 --- a/fs/xfs/linux-2.6/xfs_lrw.c +++ b/fs/xfs/linux-2.6/xfs_lrw.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -206,7 +204,7 @@ xfs_read( xfs_fsize_t n; xfs_inode_t *ip; xfs_mount_t *mp; - vnode_t *vp; + bhv_vnode_t *vp; unsigned long seg; ip = XFS_BHVTOI(bdp); @@ -258,7 +256,7 @@ xfs_read( if (DM_EVENT_ENABLED(vp->v_vfsp, ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) { - vrwlock_t locktype = VRWLOCK_READ; + bhv_vrwlock_t locktype = VRWLOCK_READ; int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags); ret = -XFS_SEND_DATA(mp, DM_EVENT_READ, @@ -271,7 +269,7 @@ xfs_read( } if (unlikely((ioflags & IO_ISDIRECT) && VN_CACHED(vp))) - VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(*offset)), + bhv_vop_flushinval_pages(vp, ctooff(offtoct(*offset)), -1, FI_REMAPF_LOCKED); xfs_rw_enter_trace(XFS_READ_ENTER, &ip->i_iocore, @@ -313,7 +311,7 @@ xfs_sendfile( if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) && (!(ioflags & IO_INVIS))) { - vrwlock_t locktype = VRWLOCK_READ; + bhv_vrwlock_t locktype = VRWLOCK_READ; int error; error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp), @@ -357,7 +355,7 @@ xfs_splice_read( if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_READ) && (!(ioflags & IO_INVIS))) { - vrwlock_t locktype = VRWLOCK_READ; + bhv_vrwlock_t locktype = VRWLOCK_READ; int error; error = XFS_SEND_DATA(mp, DM_EVENT_READ, BHV_TO_VNODE(bdp), @@ -401,7 +399,7 @@ xfs_splice_write( if (DM_EVENT_ENABLED(BHV_TO_VNODE(bdp)->v_vfsp, ip, DM_EVENT_WRITE) && (!(ioflags & IO_INVIS))) { - vrwlock_t locktype = VRWLOCK_WRITE; + bhv_vrwlock_t locktype = VRWLOCK_WRITE; int error; error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, BHV_TO_VNODE(bdp), @@ -458,7 +456,7 @@ xfs_zero_last_block( last_fsb = XFS_B_TO_FSBT(mp, isize); nimaps = 1; error = XFS_BMAPI(mp, NULL, io, last_fsb, 1, 0, NULL, 0, &imap, - &nimaps, NULL); + &nimaps, NULL, NULL); if (error) { return error; } @@ -499,7 +497,7 @@ xfs_zero_last_block( int /* error (positive) */ xfs_zero_eof( - vnode_t *vp, + bhv_vnode_t *vp, xfs_iocore_t *io, xfs_off_t offset, /* starting I/O offset */ xfs_fsize_t isize, /* current inode size */ @@ -510,7 +508,6 @@ xfs_zero_eof( xfs_fileoff_t end_zero_fsb; xfs_fileoff_t zero_count_fsb; xfs_fileoff_t last_fsb; - xfs_extlen_t buf_len_fsb; xfs_mount_t *mp = io->io_mount; int nimaps; int error = 0; @@ -556,7 +553,7 @@ xfs_zero_eof( nimaps = 1; zero_count_fsb = end_zero_fsb - start_zero_fsb + 1; error = XFS_BMAPI(mp, NULL, io, start_zero_fsb, zero_count_fsb, - 0, NULL, 0, &imap, &nimaps, NULL); + 0, NULL, 0, &imap, &nimaps, NULL, NULL); if (error) { ASSERT(ismrlocked(io->io_lock, MR_UPDATE)); ASSERT(ismrlocked(io->io_iolock, MR_UPDATE)); @@ -579,16 +576,7 @@ xfs_zero_eof( } /* - * There are blocks in the range requested. - * Zero them a single write at a time. We actually - * don't zero the entire range returned if it is - * too big and simply loop around to get the rest. - * That is not the most efficient thing to do, but it - * is simple and this path should not be exercised often. - */ - buf_len_fsb = XFS_FILBLKS_MIN(imap.br_blockcount, - mp->m_writeio_blocks << 8); - /* + * There are blocks we need to zero. * Drop the inode lock while we're doing the I/O. * We'll still have the iolock to protect us. */ @@ -596,14 +584,13 @@ xfs_zero_eof( error = xfs_iozero(ip, XFS_FSB_TO_B(mp, start_zero_fsb), - XFS_FSB_TO_B(mp, buf_len_fsb), + XFS_FSB_TO_B(mp, imap.br_blockcount), end_size); - if (error) { goto out_lock; } - start_zero_fsb = imap.br_startoff + buf_len_fsb; + start_zero_fsb = imap.br_startoff + imap.br_blockcount; ASSERT(start_zero_fsb <= (end_zero_fsb + 1)); XFS_ILOCK(mp, io, XFS_ILOCK_EXCL|XFS_EXTSIZE_RD); @@ -637,11 +624,11 @@ xfs_write( ssize_t ret = 0, error = 0; xfs_fsize_t isize, new_size; xfs_iocore_t *io; - vnode_t *vp; + bhv_vnode_t *vp; unsigned long seg; int iolock; int eventsent = 0; - vrwlock_t locktype; + bhv_vrwlock_t locktype; size_t ocount = 0, count; loff_t pos; int need_i_mutex = 1, need_flush = 0; @@ -679,11 +666,11 @@ xfs_write( io = &xip->i_iocore; mp = io->io_mount; + vfs_wait_for_freeze(vp->v_vfsp, SB_FREEZE_WRITE); + if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; - fs_check_frozen(vp->v_vfsp, SB_FREEZE_WRITE); - if (ioflags & IO_ISDIRECT) { xfs_buftarg_t *target = (xip->i_d.di_flags & XFS_DIFLAG_REALTIME) ? @@ -814,7 +801,7 @@ retry: if (need_flush) { xfs_inval_cached_trace(io, pos, -1, ctooff(offtoct(pos)), -1); - VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(pos)), + bhv_vop_flushinval_pages(vp, ctooff(offtoct(pos)), -1, FI_REMAPF_LOCKED); } @@ -903,79 +890,9 @@ retry: /* Handle various SYNC-type writes */ if ((file->f_flags & O_SYNC) || IS_SYNC(inode)) { - /* - * If we're treating this as O_DSYNC and we have not updated the - * size, force the log. - */ - if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC) && - !(xip->i_update_size)) { - xfs_inode_log_item_t *iip = xip->i_itemp; - - /* - * If an allocation transaction occurred - * without extending the size, then we have to force - * the log up the proper point to ensure that the - * allocation is permanent. We can't count on - * the fact that buffered writes lock out direct I/O - * writes - the direct I/O write could have extended - * the size nontransactionally, then finished before - * we started. xfs_write_file will think that the file - * didn't grow but the update isn't safe unless the - * size change is logged. - * - * Force the log if we've committed a transaction - * against the inode or if someone else has and - * the commit record hasn't gone to disk (e.g. - * the inode is pinned). This guarantees that - * all changes affecting the inode are permanent - * when we return. - */ - if (iip && iip->ili_last_lsn) { - xfs_log_force(mp, iip->ili_last_lsn, - XFS_LOG_FORCE | XFS_LOG_SYNC); - } else if (xfs_ipincount(xip) > 0) { - xfs_log_force(mp, (xfs_lsn_t)0, - XFS_LOG_FORCE | XFS_LOG_SYNC); - } - - } else { - xfs_trans_t *tp; - - /* - * O_SYNC or O_DSYNC _with_ a size update are handled - * the same way. - * - * If the write was synchronous then we need to make - * sure that the inode modification time is permanent. - * We'll have updated the timestamp above, so here - * we use a synchronous transaction to log the inode. - * It's not fast, but it's necessary. - * - * If this a dsync write and the size got changed - * non-transactionally, then we need to ensure that - * the size change gets logged in a synchronous - * transaction. - */ - - tp = xfs_trans_alloc(mp, XFS_TRANS_WRITE_SYNC); - if ((error = xfs_trans_reserve(tp, 0, - XFS_SWRITE_LOG_RES(mp), - 0, 0, 0))) { - /* Transaction reserve failed */ - xfs_trans_cancel(tp, 0); - } else { - /* Transaction reserve successful */ - xfs_ilock(xip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, xip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, xip); - xfs_trans_log_inode(tp, xip, XFS_ILOG_CORE); - xfs_trans_set_sync(tp); - error = xfs_trans_commit(tp, 0, NULL); - xfs_iunlock(xip, XFS_ILOCK_EXCL); - } - if (error) - goto out_unlock_internal; - } + error = xfs_write_sync_logforce(mp, xip); + if (error) + goto out_unlock_internal; xfs_rwunlock(bdp, locktype); if (need_i_mutex) diff --git a/fs/xfs/linux-2.6/xfs_lrw.h b/fs/xfs/linux-2.6/xfs_lrw.h index 8f45399..c77e62e 100644 --- a/fs/xfs/linux-2.6/xfs_lrw.h +++ b/fs/xfs/linux-2.6/xfs_lrw.h @@ -18,8 +18,8 @@ #ifndef __XFS_LRW_H__ #define __XFS_LRW_H__ -struct vnode; struct bhv_desc; +struct bhv_vnode; struct xfs_mount; struct xfs_iocore; struct xfs_inode; @@ -49,7 +49,7 @@ #define XFS_CTRUNC3 13 #define XFS_CTRUNC4 14 #define XFS_CTRUNC5 15 #define XFS_CTRUNC6 16 -#define XFS_BUNMAPI 17 +#define XFS_BUNMAP 17 #define XFS_INVAL_CACHED 18 #define XFS_DIORD_ENTER 19 #define XFS_DIOWR_ENTER 20 @@ -82,7 +82,7 @@ extern int xfsbdstrat(struct xfs_mount * extern int xfs_bdstrat_cb(struct xfs_buf *); extern int xfs_dev_is_read_only(struct xfs_mount *, char *); -extern int xfs_zero_eof(struct vnode *, struct xfs_iocore *, xfs_off_t, +extern int xfs_zero_eof(struct bhv_vnode *, struct xfs_iocore *, xfs_off_t, xfs_fsize_t, xfs_fsize_t); extern ssize_t xfs_read(struct bhv_desc *, struct kiocb *, const struct iovec *, unsigned int, diff --git a/fs/xfs/linux-2.6/xfs_stats.c b/fs/xfs/linux-2.6/xfs_stats.c index 1f0589a..e480b61 100644 --- a/fs/xfs/linux-2.6/xfs_stats.c +++ b/fs/xfs/linux-2.6/xfs_stats.c @@ -62,7 +62,7 @@ xfs_read_xfsstats( while (j < xstats[i].endpoint) { val = 0; /* sum over all cpus */ - for_each_cpu(c) + for_each_possible_cpu(c) val += *(((__u32*)&per_cpu(xfsstats, c) + j)); len += sprintf(buffer + len, " %u", val); j++; @@ -70,7 +70,7 @@ xfs_read_xfsstats( buffer[len++] = '\n'; } /* extra precision counters */ - for_each_cpu(i) { + for_each_possible_cpu(i) { xs_xstrat_bytes += per_cpu(xfsstats, i).xs_xstrat_bytes; xs_write_bytes += per_cpu(xfsstats, i).xs_write_bytes; xs_read_bytes += per_cpu(xfsstats, i).xs_read_bytes; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 68f4793..9bdef9d 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -151,7 +149,7 @@ xfs_set_inodeops( STATIC __inline__ void xfs_revalidate_inode( xfs_mount_t *mp, - vnode_t *vp, + bhv_vnode_t *vp, xfs_inode_t *ip) { struct inode *inode = vn_to_inode(vp); @@ -206,7 +204,7 @@ xfs_revalidate_inode( void xfs_initialize_vnode( bhv_desc_t *bdp, - vnode_t *vp, + bhv_vnode_t *vp, bhv_desc_t *inode_bhv, int unlock) { @@ -336,7 +334,7 @@ STATIC struct inode * xfs_fs_alloc_inode( struct super_block *sb) { - vnode_t *vp; + bhv_vnode_t *vp; vp = kmem_zone_alloc(xfs_vnode_zone, KM_SLEEP); if (unlikely(!vp)) @@ -359,13 +357,13 @@ xfs_fs_inode_init_once( { if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) - inode_init_once(vn_to_inode((vnode_t *)vnode)); + inode_init_once(vn_to_inode((bhv_vnode_t *)vnode)); } STATIC int xfs_init_zones(void) { - xfs_vnode_zone = kmem_zone_init_flags(sizeof(vnode_t), "xfs_vnode_t", + xfs_vnode_zone = kmem_zone_init_flags(sizeof(bhv_vnode_t), "xfs_vnode", KM_ZONE_HWALIGN | KM_ZONE_RECLAIM | KM_ZONE_SPREAD, xfs_fs_inode_init_once); @@ -409,22 +407,17 @@ xfs_fs_write_inode( struct inode *inode, int sync) { - vnode_t *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); int error = 0, flags = FLUSH_INODE; if (vp) { vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); if (sync) flags |= FLUSH_SYNC; - VOP_IFLUSH(vp, flags, error); - if (error == EAGAIN) { - if (sync) - VOP_IFLUSH(vp, flags | FLUSH_LOG, error); - else - error = 0; - } + error = bhv_vop_iflush(vp, flags); + if (error == EAGAIN) + error = sync? bhv_vop_iflush(vp, flags | FLUSH_LOG) : 0; } - return -error; } @@ -432,8 +425,7 @@ STATIC void xfs_fs_clear_inode( struct inode *inode) { - vnode_t *vp = vn_from_inode(inode); - int error, cache; + bhv_vnode_t *vp = vn_from_inode(inode); vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); @@ -446,20 +438,18 @@ xfs_fs_clear_inode( * This can happen because xfs_iget_core calls xfs_idestroy if we * find an inode with di_mode == 0 but without IGET_CREATE set. */ - if (vp->v_fbhv) - VOP_INACTIVE(vp, NULL, cache); + if (VNHEAD(vp)) + bhv_vop_inactive(vp, NULL); VN_LOCK(vp); vp->v_flag &= ~VMODIFIED; VN_UNLOCK(vp, 0); - if (vp->v_fbhv) { - VOP_RECLAIM(vp, error); - if (error) - panic("vn_purge: cannot reclaim"); - } + if (VNHEAD(vp)) + if (bhv_vop_reclaim(vp)) + panic("%s: cannot reclaim 0x%p\n", __FUNCTION__, vp); - ASSERT(vp->v_fbhv == NULL); + ASSERT(VNHEAD(vp) == NULL); #ifdef XFS_VNODE_TRACE ktrace_free(vp->v_trace); @@ -475,13 +465,13 @@ #endif */ STATIC void xfs_syncd_queue_work( - struct vfs *vfs, + struct bhv_vfs *vfs, void *data, - void (*syncer)(vfs_t *, void *)) + void (*syncer)(bhv_vfs_t *, void *)) { - vfs_sync_work_t *work; + struct bhv_vfs_sync_work *work; - work = kmem_alloc(sizeof(struct vfs_sync_work), KM_SLEEP); + work = kmem_alloc(sizeof(struct bhv_vfs_sync_work), KM_SLEEP); INIT_LIST_HEAD(&work->w_list); work->w_syncer = syncer; work->w_data = data; @@ -500,7 +490,7 @@ xfs_syncd_queue_work( */ STATIC void xfs_flush_inode_work( - vfs_t *vfs, + bhv_vfs_t *vfs, void *inode) { filemap_flush(((struct inode *)inode)->i_mapping); @@ -512,7 +502,7 @@ xfs_flush_inode( xfs_inode_t *ip) { struct inode *inode = vn_to_inode(XFS_ITOV(ip)); - struct vfs *vfs = XFS_MTOVFS(ip->i_mount); + struct bhv_vfs *vfs = XFS_MTOVFS(ip->i_mount); igrab(inode); xfs_syncd_queue_work(vfs, inode, xfs_flush_inode_work); @@ -525,7 +515,7 @@ xfs_flush_inode( */ STATIC void xfs_flush_device_work( - vfs_t *vfs, + bhv_vfs_t *vfs, void *inode) { sync_blockdev(vfs->vfs_super->s_bdev); @@ -537,7 +527,7 @@ xfs_flush_device( xfs_inode_t *ip) { struct inode *inode = vn_to_inode(XFS_ITOV(ip)); - struct vfs *vfs = XFS_MTOVFS(ip->i_mount); + struct bhv_vfs *vfs = XFS_MTOVFS(ip->i_mount); igrab(inode); xfs_syncd_queue_work(vfs, inode, xfs_flush_device_work); @@ -545,16 +535,16 @@ xfs_flush_device( xfs_log_force(ip->i_mount, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC); } -#define SYNCD_FLAGS (SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR|SYNC_REFCACHE) STATIC void vfs_sync_worker( - vfs_t *vfsp, + bhv_vfs_t *vfsp, void *unused) { int error; if (!(vfsp->vfs_flag & VFS_RDONLY)) - VFS_SYNC(vfsp, SYNCD_FLAGS, NULL, error); + error = bhv_vfs_sync(vfsp, SYNC_FSDATA | SYNC_BDFLUSH | \ + SYNC_ATTR | SYNC_REFCACHE, NULL); vfsp->vfs_sync_seq++; wmb(); wake_up(&vfsp->vfs_wait_single_sync_task); @@ -565,8 +555,8 @@ xfssyncd( void *arg) { long timeleft; - vfs_t *vfsp = (vfs_t *) arg; - struct vfs_sync_work *work, *n; + bhv_vfs_t *vfsp = (bhv_vfs_t *) arg; + bhv_vfs_sync_work_t *work, *n; LIST_HEAD (tmp); timeleft = xfs_syncd_centisecs * msecs_to_jiffies(10); @@ -600,7 +590,7 @@ xfssyncd( list_del(&work->w_list); if (work == &vfsp->vfs_sync_work) continue; - kmem_free(work, sizeof(struct vfs_sync_work)); + kmem_free(work, sizeof(struct bhv_vfs_sync_work)); } } @@ -609,7 +599,7 @@ xfssyncd( STATIC int xfs_fs_start_syncd( - vfs_t *vfsp) + bhv_vfs_t *vfsp) { vfsp->vfs_sync_work.w_syncer = vfs_sync_worker; vfsp->vfs_sync_work.w_vfs = vfsp; @@ -621,7 +611,7 @@ xfs_fs_start_syncd( STATIC void xfs_fs_stop_syncd( - vfs_t *vfsp) + bhv_vfs_t *vfsp) { kthread_stop(vfsp->vfs_sync_task); } @@ -630,35 +620,26 @@ STATIC void xfs_fs_put_super( struct super_block *sb) { - vfs_t *vfsp = vfs_from_sb(sb); + bhv_vfs_t *vfsp = vfs_from_sb(sb); int error; xfs_fs_stop_syncd(vfsp); - VFS_SYNC(vfsp, SYNC_ATTR|SYNC_DELWRI, NULL, error); - if (!error) - VFS_UNMOUNT(vfsp, 0, NULL, error); + bhv_vfs_sync(vfsp, SYNC_ATTR | SYNC_DELWRI, NULL); + error = bhv_vfs_unmount(vfsp, 0, NULL); if (error) { - printk("XFS unmount got error %d\n", error); - printk("%s: vfsp/0x%p left dangling!\n", __FUNCTION__, vfsp); - return; + printk("XFS: unmount got error=%d\n", error); + printk("%s: vfs=0x%p left dangling!\n", __FUNCTION__, vfsp); + } else { + vfs_deallocate(vfsp); } - - vfs_deallocate(vfsp); } STATIC void xfs_fs_write_super( struct super_block *sb) { - vfs_t *vfsp = vfs_from_sb(sb); - int error; - - if (sb->s_flags & MS_RDONLY) { - sb->s_dirt = 0; /* paranoia */ - return; - } - /* Push the log and superblock a little */ - VFS_SYNC(vfsp, SYNC_FSDATA, NULL, error); + if (!(sb->s_flags & MS_RDONLY)) + bhv_vfs_sync(vfs_from_sb(sb), SYNC_FSDATA, NULL); sb->s_dirt = 0; } @@ -667,16 +648,16 @@ xfs_fs_sync_super( struct super_block *sb, int wait) { - vfs_t *vfsp = vfs_from_sb(sb); - int error; - int flags = SYNC_FSDATA; + bhv_vfs_t *vfsp = vfs_from_sb(sb); + int error; + int flags; if (unlikely(sb->s_frozen == SB_FREEZE_WRITE)) flags = SYNC_QUIESCE; else flags = SYNC_FSDATA | (wait ? SYNC_WAIT : 0); - VFS_SYNC(vfsp, flags, NULL, error); + error = bhv_vfs_sync(vfsp, flags, NULL); sb->s_dirt = 0; if (unlikely(laptop_mode)) { @@ -703,14 +684,11 @@ xfs_fs_sync_super( STATIC int xfs_fs_statfs( - struct super_block *sb, + struct dentry *dentry, struct kstatfs *statp) { - vfs_t *vfsp = vfs_from_sb(sb); - int error; - - VFS_STATVFS(vfsp, statp, NULL, error); - return -error; + return -bhv_vfs_statvfs(vfs_from_sb(dentry->d_sb), statp, + vn_from_inode(dentry->d_inode)); } STATIC int @@ -719,13 +697,13 @@ xfs_fs_remount( int *flags, char *options) { - vfs_t *vfsp = vfs_from_sb(sb); + bhv_vfs_t *vfsp = vfs_from_sb(sb); struct xfs_mount_args *args = xfs_args_allocate(sb, 0); int error; - VFS_PARSEARGS(vfsp, options, args, 1, error); + error = bhv_vfs_parseargs(vfsp, options, args, 1); if (!error) - VFS_MNTUPDATE(vfsp, flags, args, error); + error = bhv_vfs_mntupdate(vfsp, flags, args); kmem_free(args, sizeof(*args)); return -error; } @@ -734,7 +712,7 @@ STATIC void xfs_fs_lockfs( struct super_block *sb) { - VFS_FREEZE(vfs_from_sb(sb)); + bhv_vfs_freeze(vfs_from_sb(sb)); } STATIC int @@ -742,11 +720,7 @@ xfs_fs_show_options( struct seq_file *m, struct vfsmount *mnt) { - struct vfs *vfsp = vfs_from_sb(mnt->mnt_sb); - int error; - - VFS_SHOWARGS(vfsp, m, error); - return error; + return -bhv_vfs_showargs(vfs_from_sb(mnt->mnt_sb), m); } STATIC int @@ -754,11 +728,7 @@ xfs_fs_quotasync( struct super_block *sb, int type) { - struct vfs *vfsp = vfs_from_sb(sb); - int error; - - VFS_QUOTACTL(vfsp, Q_XQUOTASYNC, 0, (caddr_t)NULL, error); - return -error; + return -bhv_vfs_quotactl(vfs_from_sb(sb), Q_XQUOTASYNC, 0, NULL); } STATIC int @@ -766,11 +736,7 @@ xfs_fs_getxstate( struct super_block *sb, struct fs_quota_stat *fqs) { - struct vfs *vfsp = vfs_from_sb(sb); - int error; - - VFS_QUOTACTL(vfsp, Q_XGETQSTAT, 0, (caddr_t)fqs, error); - return -error; + return -bhv_vfs_quotactl(vfs_from_sb(sb), Q_XGETQSTAT, 0, (caddr_t)fqs); } STATIC int @@ -779,11 +745,7 @@ xfs_fs_setxstate( unsigned int flags, int op) { - struct vfs *vfsp = vfs_from_sb(sb); - int error; - - VFS_QUOTACTL(vfsp, op, 0, (caddr_t)&flags, error); - return -error; + return -bhv_vfs_quotactl(vfs_from_sb(sb), op, 0, (caddr_t)&flags); } STATIC int @@ -793,13 +755,10 @@ xfs_fs_getxquota( qid_t id, struct fs_disk_quota *fdq) { - struct vfs *vfsp = vfs_from_sb(sb); - int error, getmode; - - getmode = (type == USRQUOTA) ? Q_XGETQUOTA : - ((type == GRPQUOTA) ? Q_XGETGQUOTA : Q_XGETPQUOTA); - VFS_QUOTACTL(vfsp, getmode, id, (caddr_t)fdq, error); - return -error; + return -bhv_vfs_quotactl(vfs_from_sb(sb), + (type == USRQUOTA) ? Q_XGETQUOTA : + ((type == GRPQUOTA) ? Q_XGETGQUOTA : + Q_XGETPQUOTA), id, (caddr_t)fdq); } STATIC int @@ -809,13 +768,10 @@ xfs_fs_setxquota( qid_t id, struct fs_disk_quota *fdq) { - struct vfs *vfsp = vfs_from_sb(sb); - int error, setmode; - - setmode = (type == USRQUOTA) ? Q_XSETQLIM : - ((type == GRPQUOTA) ? Q_XSETGQLIM : Q_XSETPQLIM); - VFS_QUOTACTL(vfsp, setmode, id, (caddr_t)fdq, error); - return -error; + return -bhv_vfs_quotactl(vfs_from_sb(sb), + (type == USRQUOTA) ? Q_XSETQLIM : + ((type == GRPQUOTA) ? Q_XSETGQLIM : + Q_XSETPQLIM), id, (caddr_t)fdq); } STATIC int @@ -824,34 +780,32 @@ xfs_fs_fill_super( void *data, int silent) { - vnode_t *rootvp; - struct vfs *vfsp = vfs_allocate(sb); + struct bhv_vnode *rootvp; + struct bhv_vfs *vfsp = vfs_allocate(sb); struct xfs_mount_args *args = xfs_args_allocate(sb, silent); struct kstatfs statvfs; - int error, error2; + int error; bhv_insert_all_vfsops(vfsp); - VFS_PARSEARGS(vfsp, (char *)data, args, 0, error); + error = bhv_vfs_parseargs(vfsp, (char *)data, args, 0); if (error) { bhv_remove_all_vfsops(vfsp, 1); goto fail_vfsop; } sb_min_blocksize(sb, BBSIZE); -#ifdef CONFIG_XFS_EXPORT sb->s_export_op = &xfs_export_operations; -#endif sb->s_qcop = &xfs_quotactl_operations; sb->s_op = &xfs_super_operations; - VFS_MOUNT(vfsp, args, NULL, error); + error = bhv_vfs_mount(vfsp, args, NULL); if (error) { bhv_remove_all_vfsops(vfsp, 1); goto fail_vfsop; } - VFS_STATVFS(vfsp, &statvfs, NULL, error); + error = bhv_vfs_statvfs(vfsp, &statvfs, NULL); if (error) goto fail_unmount; @@ -863,7 +817,7 @@ #endif sb->s_time_gran = 1; set_posix_acl_flag(sb); - VFS_ROOT(vfsp, &rootvp, error); + error = bhv_vfs_root(vfsp, &rootvp); if (error) goto fail_unmount; @@ -892,7 +846,7 @@ fail_vnrele: } fail_unmount: - VFS_UNMOUNT(vfsp, 0, NULL, error2); + bhv_vfs_unmount(vfsp, 0, NULL); fail_vfsop: vfs_deallocate(vfsp); @@ -900,14 +854,16 @@ fail_vfsop: return -error; } -STATIC struct super_block * +STATIC int xfs_fs_get_sb( struct file_system_type *fs_type, int flags, const char *dev_name, - void *data) + void *data, + struct vfsmount *mnt) { - return get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super); + return get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super, + mnt); } STATIC struct super_operations xfs_super_operations = { diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h index 376b96c..33dd1ca 100644 --- a/fs/xfs/linux-2.6/xfs_super.h +++ b/fs/xfs/linux-2.6/xfs_super.h @@ -105,7 +105,7 @@ struct block_device; extern __uint64_t xfs_max_file_offset(unsigned int); -extern void xfs_initialize_vnode(bhv_desc_t *, vnode_t *, bhv_desc_t *, int); +extern void xfs_initialize_vnode(bhv_desc_t *, bhv_vnode_t *, bhv_desc_t *, int); extern void xfs_flush_inode(struct xfs_inode *); extern void xfs_flush_device(struct xfs_inode *); diff --git a/fs/xfs/linux-2.6/xfs_sysctl.c b/fs/xfs/linux-2.6/xfs_sysctl.c index 7079cc8..af24653 100644 --- a/fs/xfs/linux-2.6/xfs_sysctl.c +++ b/fs/xfs/linux-2.6/xfs_sysctl.c @@ -38,7 +38,7 @@ xfs_stats_clear_proc_handler( if (!ret && write && *valp) { printk("XFS Clearing xfsstats\n"); - for_each_cpu(c) { + for_each_possible_cpu(c) { preempt_disable(); /* save vn_active, it's a universal truth! */ vn_active = per_cpu(xfsstats, c).vn_active; @@ -120,6 +120,11 @@ STATIC ctl_table xfs_table[] = { &sysctl_intvec, NULL, &xfs_params.rotorstep.min, &xfs_params.rotorstep.max}, + {XFS_INHERIT_NODFRG, "inherit_nodefrag", &xfs_params.inherit_nodfrg.val, + sizeof(int), 0644, NULL, &proc_dointvec_minmax, + &sysctl_intvec, NULL, + &xfs_params.inherit_nodfrg.min, &xfs_params.inherit_nodfrg.max}, + /* please keep this the last entry */ #ifdef CONFIG_PROC_FS {XFS_STATS_CLEAR, "stats_clear", &xfs_params.stats_clear.val, diff --git a/fs/xfs/linux-2.6/xfs_sysctl.h b/fs/xfs/linux-2.6/xfs_sysctl.h index bc8c11f..a631fb8 100644 --- a/fs/xfs/linux-2.6/xfs_sysctl.h +++ b/fs/xfs/linux-2.6/xfs_sysctl.h @@ -46,6 +46,7 @@ typedef struct xfs_param { xfs_sysctl_val_t xfs_buf_age; /* Metadata buffer age before flush. */ xfs_sysctl_val_t inherit_nosym; /* Inherit the "nosymlinks" flag. */ xfs_sysctl_val_t rotorstep; /* inode32 AG rotoring control knob */ + xfs_sysctl_val_t inherit_nodfrg;/* Inherit the "nodefrag" inode flag. */ } xfs_param_t; /* @@ -84,6 +85,7 @@ enum { /* XFS_IO_BYPASS = 18 */ XFS_INHERIT_NOSYM = 19, XFS_ROTORSTEP = 20, + XFS_INHERIT_NODFRG = 21, }; extern xfs_param_t xfs_params; diff --git a/fs/xfs/linux-2.6/xfs_vfs.c b/fs/xfs/linux-2.6/xfs_vfs.c index 6f7c9f7..6145e8b 100644 --- a/fs/xfs/linux-2.6/xfs_vfs.c +++ b/fs/xfs/linux-2.6/xfs_vfs.c @@ -23,7 +23,6 @@ #include "xfs_clnt.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_imap.h" #include "xfs_alloc.h" @@ -104,7 +103,7 @@ vfs_mntupdate( int vfs_root( struct bhv_desc *bdp, - struct vnode **vpp) + struct bhv_vnode **vpp) { struct bhv_desc *next = bdp; @@ -117,15 +116,15 @@ vfs_root( int vfs_statvfs( struct bhv_desc *bdp, - xfs_statfs_t *sp, - struct vnode *vp) + bhv_statvfs_t *statp, + struct bhv_vnode *vp) { struct bhv_desc *next = bdp; ASSERT(next); while (! (bhvtovfsops(next))->vfs_statvfs) next = BHV_NEXT(next); - return ((*bhvtovfsops(next)->vfs_statvfs)(next, sp, vp)); + return ((*bhvtovfsops(next)->vfs_statvfs)(next, statp, vp)); } int @@ -145,7 +144,7 @@ vfs_sync( int vfs_vget( struct bhv_desc *bdp, - struct vnode **vpp, + struct bhv_vnode **vpp, struct fid *fidp) { struct bhv_desc *next = bdp; @@ -187,7 +186,7 @@ vfs_quotactl( void vfs_init_vnode( struct bhv_desc *bdp, - struct vnode *vp, + struct bhv_vnode *vp, struct bhv_desc *bp, int unlock) { @@ -226,13 +225,13 @@ vfs_freeze( ((*bhvtovfsops(next)->vfs_freeze)(next)); } -vfs_t * +bhv_vfs_t * vfs_allocate( struct super_block *sb) { - struct vfs *vfsp; + struct bhv_vfs *vfsp; - vfsp = kmem_zalloc(sizeof(vfs_t), KM_SLEEP); + vfsp = kmem_zalloc(sizeof(bhv_vfs_t), KM_SLEEP); bhv_head_init(VFS_BHVHEAD(vfsp), "vfs"); INIT_LIST_HEAD(&vfsp->vfs_sync_list); spin_lock_init(&vfsp->vfs_sync_lock); @@ -247,25 +246,25 @@ vfs_allocate( return vfsp; } -vfs_t * +bhv_vfs_t * vfs_from_sb( struct super_block *sb) { - return (vfs_t *)sb->s_fs_info; + return (bhv_vfs_t *)sb->s_fs_info; } void vfs_deallocate( - struct vfs *vfsp) + struct bhv_vfs *vfsp) { bhv_head_destroy(VFS_BHVHEAD(vfsp)); - kmem_free(vfsp, sizeof(vfs_t)); + kmem_free(vfsp, sizeof(bhv_vfs_t)); } void vfs_insertops( - struct vfs *vfsp, - struct bhv_vfsops *vfsops) + struct bhv_vfs *vfsp, + struct bhv_module_vfsops *vfsops) { struct bhv_desc *bdp; @@ -276,9 +275,9 @@ vfs_insertops( void vfs_insertbhv( - struct vfs *vfsp, + struct bhv_vfs *vfsp, struct bhv_desc *bdp, - struct vfsops *vfsops, + struct bhv_vfsops *vfsops, void *mount) { bhv_desc_init(bdp, mount, vfsp, vfsops); @@ -287,7 +286,7 @@ vfs_insertbhv( void bhv_remove_vfsops( - struct vfs *vfsp, + struct bhv_vfs *vfsp, int pos) { struct bhv_desc *bhv; @@ -301,7 +300,7 @@ bhv_remove_vfsops( void bhv_remove_all_vfsops( - struct vfs *vfsp, + struct bhv_vfs *vfsp, int freebase) { struct xfs_mount *mp; @@ -317,7 +316,7 @@ bhv_remove_all_vfsops( void bhv_insert_all_vfsops( - struct vfs *vfsp) + struct bhv_vfs *vfsp) { struct xfs_mount *mp; diff --git a/fs/xfs/linux-2.6/xfs_vfs.h b/fs/xfs/linux-2.6/xfs_vfs.h index 841200c..91fc2c4 100644 --- a/fs/xfs/linux-2.6/xfs_vfs.h +++ b/fs/xfs/linux-2.6/xfs_vfs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -21,42 +21,40 @@ #define __XFS_VFS_H__ #include #include "xfs_fs.h" +struct bhv_vfs; +struct bhv_vnode; + struct fid; -struct vfs; struct cred; -struct vnode; -struct kstatfs; struct seq_file; struct super_block; struct xfs_mount_args; -typedef struct kstatfs xfs_statfs_t; +typedef struct kstatfs bhv_statvfs_t; -typedef struct vfs_sync_work { +typedef struct bhv_vfs_sync_work { struct list_head w_list; - struct vfs *w_vfs; + struct bhv_vfs *w_vfs; void *w_data; /* syncer routine argument */ - void (*w_syncer)(struct vfs *, void *); -} vfs_sync_work_t; + void (*w_syncer)(struct bhv_vfs *, void *); +} bhv_vfs_sync_work_t; -typedef struct vfs { +typedef struct bhv_vfs { u_int vfs_flag; /* flags */ xfs_fsid_t vfs_fsid; /* file system ID */ xfs_fsid_t *vfs_altfsid; /* An ID fixed for life of FS */ bhv_head_t vfs_bh; /* head of vfs behavior chain */ struct super_block *vfs_super; /* generic superblock pointer */ struct task_struct *vfs_sync_task; /* generalised sync thread */ - vfs_sync_work_t vfs_sync_work; /* work item for VFS_SYNC */ + bhv_vfs_sync_work_t vfs_sync_work; /* work item for VFS_SYNC */ struct list_head vfs_sync_list; /* sync thread work item list */ spinlock_t vfs_sync_lock; /* work item list lock */ - int vfs_sync_seq; /* sync thread generation no. */ + int vfs_sync_seq; /* sync thread generation no. */ wait_queue_head_t vfs_wait_single_sync_task; -} vfs_t; - -#define vfs_fbhv vfs_bh.bh_first /* 1st on vfs behavior chain */ +} bhv_vfs_t; -#define bhvtovfs(bdp) ( (struct vfs *)BHV_VOBJ(bdp) ) -#define bhvtovfsops(bdp) ( (struct vfsops *)BHV_OPS(bdp) ) +#define bhvtovfs(bdp) ( (struct bhv_vfs *)BHV_VOBJ(bdp) ) +#define bhvtovfsops(bdp) ( (struct bhv_vfsops *)BHV_OPS(bdp) ) #define VFS_BHVHEAD(vfs) ( &(vfs)->vfs_bh ) #define VFS_REMOVEBHV(vfs, bdp) ( bhv_remove(VFS_BHVHEAD(vfs), bdp) ) @@ -71,7 +69,7 @@ typedef enum { VFS_BHV_QM, /* quota manager */ VFS_BHV_IO, /* IO path */ VFS_BHV_END /* housekeeping end-of-range */ -} vfs_bhv_t; +} bhv_vfs_type_t; #define VFS_POSITION_XFS (BHV_POSITION_BASE) #define VFS_POSITION_DM (VFS_POSITION_BASE+10) @@ -81,8 +79,9 @@ #define VFS_POSITION_IO (VFS_POSITION_B #define VFS_RDONLY 0x0001 /* read-only vfs */ #define VFS_GRPID 0x0002 /* group-ID assigned from directory */ #define VFS_DMI 0x0004 /* filesystem has the DMI enabled */ -#define VFS_32BITINODES 0x0008 /* do not use inums above 32 bits */ -#define VFS_END 0x0008 /* max flag */ +#define VFS_UMOUNT 0x0008 /* unmount in progress */ +#define VFS_32BITINODES 0x0010 /* do not use inums above 32 bits */ +#define VFS_END 0x0010 /* max flag */ #define SYNC_ATTR 0x0001 /* sync attributes */ #define SYNC_CLOSE 0x0002 /* close file system down */ @@ -92,7 +91,14 @@ #define SYNC_BDFLUSH 0x0010 /* BDFLUSH #define SYNC_FSDATA 0x0020 /* flush fs data (e.g. superblocks) */ #define SYNC_REFCACHE 0x0040 /* prune some of the nfs ref cache */ #define SYNC_REMOUNT 0x0080 /* remount readonly, no dummy LRs */ -#define SYNC_QUIESCE 0x0100 /* quiesce filesystem for a snapshot */ +#define SYNC_QUIESCE 0x0100 /* quiesce fileystem for a snapshot */ + +#define SHUTDOWN_META_IO_ERROR 0x0001 /* write attempt to metadata failed */ +#define SHUTDOWN_LOG_IO_ERROR 0x0002 /* write attempt to the log failed */ +#define SHUTDOWN_FORCE_UMOUNT 0x0004 /* shutdown from a forced unmount */ +#define SHUTDOWN_CORRUPT_INCORE 0x0008 /* corrupt in-memory data structures */ +#define SHUTDOWN_REMOTE_REQ 0x0010 /* shutdown came from remote cell */ +#define SHUTDOWN_DEVICE_REQ 0x0020 /* failed all paths to the device */ typedef int (*vfs_mount_t)(bhv_desc_t *, struct xfs_mount_args *, struct cred *); @@ -102,18 +108,19 @@ typedef int (*vfs_showargs_t)(bhv_desc_t typedef int (*vfs_unmount_t)(bhv_desc_t *, int, struct cred *); typedef int (*vfs_mntupdate_t)(bhv_desc_t *, int *, struct xfs_mount_args *); -typedef int (*vfs_root_t)(bhv_desc_t *, struct vnode **); -typedef int (*vfs_statvfs_t)(bhv_desc_t *, xfs_statfs_t *, struct vnode *); +typedef int (*vfs_root_t)(bhv_desc_t *, struct bhv_vnode **); +typedef int (*vfs_statvfs_t)(bhv_desc_t *, bhv_statvfs_t *, + struct bhv_vnode *); typedef int (*vfs_sync_t)(bhv_desc_t *, int, struct cred *); -typedef int (*vfs_vget_t)(bhv_desc_t *, struct vnode **, struct fid *); +typedef int (*vfs_vget_t)(bhv_desc_t *, struct bhv_vnode **, struct fid *); typedef int (*vfs_dmapiops_t)(bhv_desc_t *, caddr_t); typedef int (*vfs_quotactl_t)(bhv_desc_t *, int, int, caddr_t); typedef void (*vfs_init_vnode_t)(bhv_desc_t *, - struct vnode *, bhv_desc_t *, int); + struct bhv_vnode *, bhv_desc_t *, int); typedef void (*vfs_force_shutdown_t)(bhv_desc_t *, int, char *, int); typedef void (*vfs_freeze_t)(bhv_desc_t *); -typedef struct vfsops { +typedef struct bhv_vfsops { bhv_position_t vf_position; /* behavior chain position */ vfs_mount_t vfs_mount; /* mount file system */ vfs_parseargs_t vfs_parseargs; /* parse mount options */ @@ -129,82 +136,82 @@ typedef struct vfsops { vfs_init_vnode_t vfs_init_vnode; /* initialize a new vnode */ vfs_force_shutdown_t vfs_force_shutdown; /* crash and burn */ vfs_freeze_t vfs_freeze; /* freeze fs for snapshot */ -} vfsops_t; +} bhv_vfsops_t; /* - * VFS's. Operates on vfs structure pointers (starts at bhv head). + * Virtual filesystem operations, operating from head bhv. */ -#define VHEAD(v) ((v)->vfs_fbhv) -#define VFS_MOUNT(v, ma,cr, rv) ((rv) = vfs_mount(VHEAD(v), ma,cr)) -#define VFS_PARSEARGS(v, o,ma,f, rv) ((rv) = vfs_parseargs(VHEAD(v), o,ma,f)) -#define VFS_SHOWARGS(v, m, rv) ((rv) = vfs_showargs(VHEAD(v), m)) -#define VFS_UNMOUNT(v, f, cr, rv) ((rv) = vfs_unmount(VHEAD(v), f,cr)) -#define VFS_MNTUPDATE(v, fl, args, rv) ((rv) = vfs_mntupdate(VHEAD(v), fl, args)) -#define VFS_ROOT(v, vpp, rv) ((rv) = vfs_root(VHEAD(v), vpp)) -#define VFS_STATVFS(v, sp,vp, rv) ((rv) = vfs_statvfs(VHEAD(v), sp,vp)) -#define VFS_SYNC(v, flag,cr, rv) ((rv) = vfs_sync(VHEAD(v), flag,cr)) -#define VFS_VGET(v, vpp,fidp, rv) ((rv) = vfs_vget(VHEAD(v), vpp,fidp)) -#define VFS_DMAPIOPS(v, p, rv) ((rv) = vfs_dmapiops(VHEAD(v), p)) -#define VFS_QUOTACTL(v, c,id,p, rv) ((rv) = vfs_quotactl(VHEAD(v), c,id,p)) -#define VFS_INIT_VNODE(v, vp,b,ul) ( vfs_init_vnode(VHEAD(v), vp,b,ul) ) -#define VFS_FORCE_SHUTDOWN(v, fl,f,l) ( vfs_force_shutdown(VHEAD(v), fl,f,l) ) -#define VFS_FREEZE(v) ( vfs_freeze(VHEAD(v)) ) +#define VFSHEAD(v) ((v)->vfs_bh.bh_first) +#define bhv_vfs_mount(v, ma,cr) vfs_mount(VFSHEAD(v), ma,cr) +#define bhv_vfs_parseargs(v, o,ma,f) vfs_parseargs(VFSHEAD(v), o,ma,f) +#define bhv_vfs_showargs(v, m) vfs_showargs(VFSHEAD(v), m) +#define bhv_vfs_unmount(v, f,cr) vfs_unmount(VFSHEAD(v), f,cr) +#define bhv_vfs_mntupdate(v, fl,args) vfs_mntupdate(VFSHEAD(v), fl,args) +#define bhv_vfs_root(v, vpp) vfs_root(VFSHEAD(v), vpp) +#define bhv_vfs_statvfs(v, sp,vp) vfs_statvfs(VFSHEAD(v), sp,vp) +#define bhv_vfs_sync(v, flag,cr) vfs_sync(VFSHEAD(v), flag,cr) +#define bhv_vfs_vget(v, vpp,fidp) vfs_vget(VFSHEAD(v), vpp,fidp) +#define bhv_vfs_dmapiops(v, p) vfs_dmapiops(VFSHEAD(v), p) +#define bhv_vfs_quotactl(v, c,id,p) vfs_quotactl(VFSHEAD(v), c,id,p) +#define bhv_vfs_init_vnode(v, vp,b,ul) vfs_init_vnode(VFSHEAD(v), vp,b,ul) +#define bhv_vfs_force_shutdown(v,u,f,l) vfs_force_shutdown(VFSHEAD(v), u,f,l) +#define bhv_vfs_freeze(v) vfs_freeze(VFSHEAD(v)) /* - * PVFS's. Operates on behavior descriptor pointers. + * Virtual filesystem operations, operating from next bhv. */ -#define PVFS_MOUNT(b, ma,cr, rv) ((rv) = vfs_mount(b, ma,cr)) -#define PVFS_PARSEARGS(b, o,ma,f, rv) ((rv) = vfs_parseargs(b, o,ma,f)) -#define PVFS_SHOWARGS(b, m, rv) ((rv) = vfs_showargs(b, m)) -#define PVFS_UNMOUNT(b, f,cr, rv) ((rv) = vfs_unmount(b, f,cr)) -#define PVFS_MNTUPDATE(b, fl, args, rv) ((rv) = vfs_mntupdate(b, fl, args)) -#define PVFS_ROOT(b, vpp, rv) ((rv) = vfs_root(b, vpp)) -#define PVFS_STATVFS(b, sp,vp, rv) ((rv) = vfs_statvfs(b, sp,vp)) -#define PVFS_SYNC(b, flag,cr, rv) ((rv) = vfs_sync(b, flag,cr)) -#define PVFS_VGET(b, vpp,fidp, rv) ((rv) = vfs_vget(b, vpp,fidp)) -#define PVFS_DMAPIOPS(b, p, rv) ((rv) = vfs_dmapiops(b, p)) -#define PVFS_QUOTACTL(b, c,id,p, rv) ((rv) = vfs_quotactl(b, c,id,p)) -#define PVFS_INIT_VNODE(b, vp,b2,ul) ( vfs_init_vnode(b, vp,b2,ul) ) -#define PVFS_FORCE_SHUTDOWN(b, fl,f,l) ( vfs_force_shutdown(b, fl,f,l) ) -#define PVFS_FREEZE(b) ( vfs_freeze(b) ) +#define bhv_next_vfs_mount(b, ma,cr) vfs_mount(b, ma,cr) +#define bhv_next_vfs_parseargs(b, o,ma,f) vfs_parseargs(b, o,ma,f) +#define bhv_next_vfs_showargs(b, m) vfs_showargs(b, m) +#define bhv_next_vfs_unmount(b, f,cr) vfs_unmount(b, f,cr) +#define bhv_next_vfs_mntupdate(b, fl,args) vfs_mntupdate(b, fl, args) +#define bhv_next_vfs_root(b, vpp) vfs_root(b, vpp) +#define bhv_next_vfs_statvfs(b, sp,vp) vfs_statvfs(b, sp,vp) +#define bhv_next_vfs_sync(b, flag,cr) vfs_sync(b, flag,cr) +#define bhv_next_vfs_vget(b, vpp,fidp) vfs_vget(b, vpp,fidp) +#define bhv_next_vfs_dmapiops(b, p) vfs_dmapiops(b, p) +#define bhv_next_vfs_quotactl(b, c,id,p) vfs_quotactl(b, c,id,p) +#define bhv_next_vfs_init_vnode(b, vp,b2,ul) vfs_init_vnode(b, vp,b2,ul) +#define bhv_next_force_shutdown(b, fl,f,l) vfs_force_shutdown(b, fl,f,l) +#define bhv_next_vfs_freeze(b) vfs_freeze(b) extern int vfs_mount(bhv_desc_t *, struct xfs_mount_args *, struct cred *); extern int vfs_parseargs(bhv_desc_t *, char *, struct xfs_mount_args *, int); extern int vfs_showargs(bhv_desc_t *, struct seq_file *); extern int vfs_unmount(bhv_desc_t *, int, struct cred *); extern int vfs_mntupdate(bhv_desc_t *, int *, struct xfs_mount_args *); -extern int vfs_root(bhv_desc_t *, struct vnode **); -extern int vfs_statvfs(bhv_desc_t *, xfs_statfs_t *, struct vnode *); +extern int vfs_root(bhv_desc_t *, struct bhv_vnode **); +extern int vfs_statvfs(bhv_desc_t *, bhv_statvfs_t *, struct bhv_vnode *); extern int vfs_sync(bhv_desc_t *, int, struct cred *); -extern int vfs_vget(bhv_desc_t *, struct vnode **, struct fid *); +extern int vfs_vget(bhv_desc_t *, struct bhv_vnode **, struct fid *); extern int vfs_dmapiops(bhv_desc_t *, caddr_t); extern int vfs_quotactl(bhv_desc_t *, int, int, caddr_t); -extern void vfs_init_vnode(bhv_desc_t *, struct vnode *, bhv_desc_t *, int); +extern void vfs_init_vnode(bhv_desc_t *, struct bhv_vnode *, bhv_desc_t *, int); extern void vfs_force_shutdown(bhv_desc_t *, int, char *, int); extern void vfs_freeze(bhv_desc_t *); -typedef struct bhv_vfsops { - struct vfsops bhv_common; +#define vfs_test_for_freeze(vfs) ((vfs)->vfs_super->s_frozen) +#define vfs_wait_for_freeze(vfs,l) vfs_check_frozen((vfs)->vfs_super, (l)) + +typedef struct bhv_module_vfsops { + struct bhv_vfsops bhv_common; void * bhv_custom; -} bhv_vfsops_t; +} bhv_module_vfsops_t; -#define vfs_bhv_lookup(v, id) ( bhv_lookup_range(&(v)->vfs_bh, (id), (id)) ) -#define vfs_bhv_custom(b) ( ((bhv_vfsops_t *)BHV_OPS(b))->bhv_custom ) -#define vfs_bhv_set_custom(b,o) ( (b)->bhv_custom = (void *)(o)) -#define vfs_bhv_clr_custom(b) ( (b)->bhv_custom = NULL ) +#define vfs_bhv_lookup(v, id) (bhv_lookup_range(&(v)->vfs_bh, (id), (id))) +#define vfs_bhv_custom(b) (((bhv_module_vfsops_t*)BHV_OPS(b))->bhv_custom) +#define vfs_bhv_set_custom(b,o) ((b)->bhv_custom = (void *)(o)) +#define vfs_bhv_clr_custom(b) ((b)->bhv_custom = NULL) -extern vfs_t *vfs_allocate(struct super_block *); -extern vfs_t *vfs_from_sb(struct super_block *); -extern void vfs_deallocate(vfs_t *); -extern void vfs_insertops(vfs_t *, bhv_vfsops_t *); -extern void vfs_insertbhv(vfs_t *, bhv_desc_t *, vfsops_t *, void *); +extern bhv_vfs_t *vfs_allocate(struct super_block *); +extern bhv_vfs_t *vfs_from_sb(struct super_block *); +extern void vfs_deallocate(bhv_vfs_t *); +extern void vfs_insertbhv(bhv_vfs_t *, bhv_desc_t *, bhv_vfsops_t *, void *); -extern void bhv_insert_all_vfsops(struct vfs *); -extern void bhv_remove_all_vfsops(struct vfs *, int); -extern void bhv_remove_vfsops(struct vfs *, int); +extern void vfs_insertops(bhv_vfs_t *, bhv_module_vfsops_t *); -#define fs_frozen(vfsp) ((vfsp)->vfs_super->s_frozen) -#define fs_check_frozen(vfsp, level) \ - vfs_check_frozen(vfsp->vfs_super, level); +extern void bhv_insert_all_vfsops(struct bhv_vfs *); +extern void bhv_remove_all_vfsops(struct bhv_vfs *, int); +extern void bhv_remove_vfsops(struct bhv_vfs *, int); #endif /* __XFS_VFS_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_vnode.c b/fs/xfs/linux-2.6/xfs_vnode.c index d27c25b..6628d96 100644 --- a/fs/xfs/linux-2.6/xfs_vnode.c +++ b/fs/xfs/linux-2.6/xfs_vnode.c @@ -39,7 +39,7 @@ vn_init(void) void vn_iowait( - struct vnode *vp) + bhv_vnode_t *vp) { wait_queue_head_t *wq = vptosync(vp); @@ -48,17 +48,33 @@ vn_iowait( void vn_iowake( - struct vnode *vp) + bhv_vnode_t *vp) { if (atomic_dec_and_test(&vp->v_iocount)) wake_up(vptosync(vp)); } -struct vnode * +/* + * Volume managers supporting multiple paths can send back ENODEV when the + * final path disappears. In this case continuing to fill the page cache + * with dirty data which cannot be written out is evil, so prevent that. + */ +void +vn_ioerror( + bhv_vnode_t *vp, + int error, + char *f, + int l) +{ + if (unlikely(error == -ENODEV)) + bhv_vfs_force_shutdown(vp->v_vfsp, SHUTDOWN_DEVICE_REQ, f, l); +} + +bhv_vnode_t * vn_initialize( struct inode *inode) { - struct vnode *vp = vn_from_inode(inode); + bhv_vnode_t *vp = vn_from_inode(inode); XFS_STATS_INC(vn_active); XFS_STATS_INC(vn_alloc); @@ -94,8 +110,8 @@ #endif /* XFS_VNODE_TRACE */ */ void vn_revalidate_core( - struct vnode *vp, - vattr_t *vap) + bhv_vnode_t *vp, + bhv_vattr_t *vap) { struct inode *inode = vn_to_inode(vp); @@ -130,14 +146,14 @@ vn_revalidate_core( */ int __vn_revalidate( - struct vnode *vp, - struct vattr *vattr) + bhv_vnode_t *vp, + bhv_vattr_t *vattr) { int error; vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); vattr->va_mask = XFS_AT_STAT | XFS_AT_XFLAGS; - VOP_GETATTR(vp, vattr, 0, NULL, error); + error = bhv_vop_getattr(vp, vattr, 0, NULL); if (likely(!error)) { vn_revalidate_core(vp, vattr); VUNMODIFY(vp); @@ -147,9 +163,9 @@ __vn_revalidate( int vn_revalidate( - struct vnode *vp) + bhv_vnode_t *vp) { - vattr_t vattr; + bhv_vattr_t vattr; return __vn_revalidate(vp, &vattr); } @@ -157,9 +173,9 @@ vn_revalidate( /* * Add a reference to a referenced vnode. */ -struct vnode * +bhv_vnode_t * vn_hold( - struct vnode *vp) + bhv_vnode_t *vp) { struct inode *inode; @@ -192,31 +208,31 @@ #define KTRACE_ENTER(vp, vk, s, line, ra * Vnode tracing code. */ void -vn_trace_entry(vnode_t *vp, const char *func, inst_t *ra) +vn_trace_entry(bhv_vnode_t *vp, const char *func, inst_t *ra) { KTRACE_ENTER(vp, VNODE_KTRACE_ENTRY, func, 0, ra); } void -vn_trace_exit(vnode_t *vp, const char *func, inst_t *ra) +vn_trace_exit(bhv_vnode_t *vp, const char *func, inst_t *ra) { KTRACE_ENTER(vp, VNODE_KTRACE_EXIT, func, 0, ra); } void -vn_trace_hold(vnode_t *vp, char *file, int line, inst_t *ra) +vn_trace_hold(bhv_vnode_t *vp, char *file, int line, inst_t *ra) { KTRACE_ENTER(vp, VNODE_KTRACE_HOLD, file, line, ra); } void -vn_trace_ref(vnode_t *vp, char *file, int line, inst_t *ra) +vn_trace_ref(bhv_vnode_t *vp, char *file, int line, inst_t *ra) { KTRACE_ENTER(vp, VNODE_KTRACE_REF, file, line, ra); } void -vn_trace_rele(vnode_t *vp, char *file, int line, inst_t *ra) +vn_trace_rele(bhv_vnode_t *vp, char *file, int line, inst_t *ra) { KTRACE_ENTER(vp, VNODE_KTRACE_RELE, file, line, ra); } diff --git a/fs/xfs/linux-2.6/xfs_vnode.h b/fs/xfs/linux-2.6/xfs_vnode.h index 2a8e16c..c42b322 100644 --- a/fs/xfs/linux-2.6/xfs_vnode.h +++ b/fs/xfs/linux-2.6/xfs_vnode.h @@ -14,57 +14,35 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Portions Copyright (c) 1989, 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. - * 3. 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. */ #ifndef __XFS_VNODE_H__ #define __XFS_VNODE_H__ struct uio; struct file; -struct vattr; +struct bhv_vfs; +struct bhv_vattr; struct xfs_iomap; struct attrlist_cursor_kern; +typedef struct dentry bhv_vname_t; +typedef __u64 bhv_vnumber_t; -typedef xfs_ino_t vnumber_t; -typedef struct dentry vname_t; -typedef bhv_head_t vn_bhv_head_t; +typedef enum bhv_vflags { + VMODIFIED = 0x08, /* XFS inode state possibly differs */ + /* to the Linux inode state. */ + VTRUNCATED = 0x40, /* truncated down so flush-on-close */ +} bhv_vflags_t; /* * MP locking protocols: * v_flag, v_vfsp VN_LOCK/VN_UNLOCK */ -typedef struct vnode { - __u32 v_flag; /* vnode flags (see below) */ - struct vfs *v_vfsp; /* ptr to containing VFS */ - vnumber_t v_number; /* in-core vnode number */ - vn_bhv_head_t v_bh; /* behavior head */ +typedef struct bhv_vnode { + bhv_vflags_t v_flag; /* vnode flags (see above) */ + bhv_vfs_t *v_vfsp; /* ptr to containing VFS */ + bhv_vnumber_t v_number; /* in-core vnode number */ + bhv_head_t v_bh; /* behavior head */ spinlock_t v_lock; /* VN_LOCK/VN_UNLOCK */ atomic_t v_iocount; /* outstanding I/O count */ #ifdef XFS_VNODE_TRACE @@ -72,7 +50,7 @@ #ifdef XFS_VNODE_TRACE #endif struct inode v_inode; /* Linux inode */ /* inode MUST be last */ -} vnode_t; +} bhv_vnode_t; #define VN_ISLNK(vp) S_ISLNK((vp)->v_inode.i_mode) #define VN_ISREG(vp) S_ISREG((vp)->v_inode.i_mode) @@ -80,9 +58,6 @@ #define VN_ISDIR(vp) S_ISDIR((vp)->v_ino #define VN_ISCHR(vp) S_ISCHR((vp)->v_inode.i_mode) #define VN_ISBLK(vp) S_ISBLK((vp)->v_inode.i_mode) -#define v_fbhv v_bh.bh_first /* first behavior */ -#define v_fops v_bh.bh_first->bd_ops /* first behavior ops */ - #define VNODE_POSITION_BASE BHV_POSITION_BASE /* chain bottom */ #define VNODE_POSITION_TOP BHV_POSITION_TOP /* chain top */ #define VNODE_POSITION_INVALID BHV_POSITION_INVALID /* invalid pos. num */ @@ -104,8 +79,8 @@ #define VNODE_POSITION_IO (VNODE_POSITIO /* * Macros for dealing with the behavior descriptor inside of the vnode. */ -#define BHV_TO_VNODE(bdp) ((vnode_t *)BHV_VOBJ(bdp)) -#define BHV_TO_VNODE_NULL(bdp) ((vnode_t *)BHV_VOBJNULL(bdp)) +#define BHV_TO_VNODE(bdp) ((bhv_vnode_t *)BHV_VOBJ(bdp)) +#define BHV_TO_VNODE_NULL(bdp) ((bhv_vnode_t *)BHV_VOBJNULL(bdp)) #define VN_BHV_HEAD(vp) ((bhv_head_t *)(&((vp)->v_bh))) #define vn_bhv_head_init(bhp,name) bhv_head_init(bhp,name) @@ -116,35 +91,29 @@ #define vn_bhv_lookup_unlocked(bhp,ops) /* * Vnode to Linux inode mapping. */ -static inline struct vnode *vn_from_inode(struct inode *inode) +static inline struct bhv_vnode *vn_from_inode(struct inode *inode) { - return (vnode_t *)list_entry(inode, vnode_t, v_inode); + return container_of(inode, bhv_vnode_t, v_inode); } -static inline struct inode *vn_to_inode(struct vnode *vnode) +static inline struct inode *vn_to_inode(struct bhv_vnode *vnode) { return &vnode->v_inode; } /* - * Vnode flags. - */ -#define VMODIFIED 0x8 /* XFS inode state possibly differs */ - /* to the Linux inode state. */ - -/* - * Values for the VOP_RWLOCK and VOP_RWUNLOCK flags parameter. + * Values for the vop_rwlock/rwunlock flags parameter. */ -typedef enum vrwlock { +typedef enum bhv_vrwlock { VRWLOCK_NONE, VRWLOCK_READ, VRWLOCK_WRITE, VRWLOCK_WRITE_DIRECT, VRWLOCK_TRY_READ, VRWLOCK_TRY_WRITE -} vrwlock_t; +} bhv_vrwlock_t; /* - * Return values for VOP_INACTIVE. A return value of + * Return values for bhv_vop_inactive. A return value of * VN_INACTIVE_NOCACHE implies that the file system behavior * has disassociated its state and bhv_desc_t from the vnode. */ @@ -152,18 +121,20 @@ #define VN_INACTIVE_CACHE 0 #define VN_INACTIVE_NOCACHE 1 /* - * Values for the cmd code given to VOP_VNODE_CHANGE. + * Values for the cmd code given to vop_vnode_change. */ -typedef enum vchange { +typedef enum bhv_vchange { VCHANGE_FLAGS_FRLOCKS = 0, VCHANGE_FLAGS_ENF_LOCKING = 1, VCHANGE_FLAGS_TRUNCATED = 2, VCHANGE_FLAGS_PAGE_DIRTY = 3, VCHANGE_FLAGS_IOEXCL_COUNT = 4 -} vchange_t; +} bhv_vchange_t; +typedef enum { L_FALSE, L_TRUE } lastclose_t; typedef int (*vop_open_t)(bhv_desc_t *, struct cred *); +typedef int (*vop_close_t)(bhv_desc_t *, int, lastclose_t, struct cred *); typedef ssize_t (*vop_read_t)(bhv_desc_t *, struct kiocb *, const struct iovec *, unsigned int, loff_t *, int, struct cred *); @@ -181,27 +152,27 @@ typedef ssize_t (*vop_splice_write_t)(bh struct cred *); typedef int (*vop_ioctl_t)(bhv_desc_t *, struct inode *, struct file *, int, unsigned int, void __user *); -typedef int (*vop_getattr_t)(bhv_desc_t *, struct vattr *, int, +typedef int (*vop_getattr_t)(bhv_desc_t *, struct bhv_vattr *, int, struct cred *); -typedef int (*vop_setattr_t)(bhv_desc_t *, struct vattr *, int, +typedef int (*vop_setattr_t)(bhv_desc_t *, struct bhv_vattr *, int, struct cred *); typedef int (*vop_access_t)(bhv_desc_t *, int, struct cred *); -typedef int (*vop_lookup_t)(bhv_desc_t *, vname_t *, vnode_t **, - int, vnode_t *, struct cred *); -typedef int (*vop_create_t)(bhv_desc_t *, vname_t *, struct vattr *, - vnode_t **, struct cred *); -typedef int (*vop_remove_t)(bhv_desc_t *, vname_t *, struct cred *); -typedef int (*vop_link_t)(bhv_desc_t *, vnode_t *, vname_t *, - struct cred *); -typedef int (*vop_rename_t)(bhv_desc_t *, vname_t *, vnode_t *, vname_t *, +typedef int (*vop_lookup_t)(bhv_desc_t *, bhv_vname_t *, bhv_vnode_t **, + int, bhv_vnode_t *, struct cred *); +typedef int (*vop_create_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr *, + bhv_vnode_t **, struct cred *); +typedef int (*vop_remove_t)(bhv_desc_t *, bhv_vname_t *, struct cred *); +typedef int (*vop_link_t)(bhv_desc_t *, bhv_vnode_t *, bhv_vname_t *, struct cred *); -typedef int (*vop_mkdir_t)(bhv_desc_t *, vname_t *, struct vattr *, - vnode_t **, struct cred *); -typedef int (*vop_rmdir_t)(bhv_desc_t *, vname_t *, struct cred *); +typedef int (*vop_rename_t)(bhv_desc_t *, bhv_vname_t *, bhv_vnode_t *, + bhv_vname_t *, struct cred *); +typedef int (*vop_mkdir_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr *, + bhv_vnode_t **, struct cred *); +typedef int (*vop_rmdir_t)(bhv_desc_t *, bhv_vname_t *, struct cred *); typedef int (*vop_readdir_t)(bhv_desc_t *, struct uio *, struct cred *, int *); -typedef int (*vop_symlink_t)(bhv_desc_t *, vname_t *, struct vattr *, - char *, vnode_t **, struct cred *); +typedef int (*vop_symlink_t)(bhv_desc_t *, bhv_vname_t *, struct bhv_vattr*, + char *, bhv_vnode_t **, struct cred *); typedef int (*vop_readlink_t)(bhv_desc_t *, struct uio *, int, struct cred *); typedef int (*vop_fsync_t)(bhv_desc_t *, int, struct cred *, @@ -209,8 +180,8 @@ typedef int (*vop_fsync_t)(bhv_desc_t *, typedef int (*vop_inactive_t)(bhv_desc_t *, struct cred *); typedef int (*vop_fid2_t)(bhv_desc_t *, struct fid *); typedef int (*vop_release_t)(bhv_desc_t *); -typedef int (*vop_rwlock_t)(bhv_desc_t *, vrwlock_t); -typedef void (*vop_rwunlock_t)(bhv_desc_t *, vrwlock_t); +typedef int (*vop_rwlock_t)(bhv_desc_t *, bhv_vrwlock_t); +typedef void (*vop_rwunlock_t)(bhv_desc_t *, bhv_vrwlock_t); typedef int (*vop_bmap_t)(bhv_desc_t *, xfs_off_t, ssize_t, int, struct xfs_iomap *, int *); typedef int (*vop_reclaim_t)(bhv_desc_t *); @@ -222,8 +193,8 @@ typedef int (*vop_attr_remove_t)(bhv_des int, struct cred *); typedef int (*vop_attr_list_t)(bhv_desc_t *, char *, int, int, struct attrlist_cursor_kern *, struct cred *); -typedef void (*vop_link_removed_t)(bhv_desc_t *, vnode_t *, int); -typedef void (*vop_vnode_change_t)(bhv_desc_t *, vchange_t, __psint_t); +typedef void (*vop_link_removed_t)(bhv_desc_t *, bhv_vnode_t *, int); +typedef void (*vop_vnode_change_t)(bhv_desc_t *, bhv_vchange_t, __psint_t); typedef void (*vop_ptossvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, int); typedef void (*vop_pflushinvalvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, int); typedef int (*vop_pflushvp_t)(bhv_desc_t *, xfs_off_t, xfs_off_t, @@ -231,9 +202,10 @@ typedef int (*vop_pflushvp_t)(bhv_desc_t typedef int (*vop_iflush_t)(bhv_desc_t *, int); -typedef struct vnodeops { +typedef struct bhv_vnodeops { bhv_position_t vn_position; /* position within behavior chain */ vop_open_t vop_open; + vop_close_t vop_close; vop_read_t vop_read; vop_write_t vop_write; vop_sendfile_t vop_sendfile; @@ -271,103 +243,80 @@ typedef struct vnodeops { vop_pflushvp_t vop_flush_pages; vop_release_t vop_release; vop_iflush_t vop_iflush; -} vnodeops_t; +} bhv_vnodeops_t; /* - * VOP's. - */ -#define _VOP_(op, vp) (*((vnodeops_t *)(vp)->v_fops)->op) - -#define VOP_READ(vp,file,iov,segs,offset,ioflags,cr,rv) \ - rv = _VOP_(vop_read, vp)((vp)->v_fbhv,file,iov,segs,offset,ioflags,cr) -#define VOP_WRITE(vp,file,iov,segs,offset,ioflags,cr,rv) \ - rv = _VOP_(vop_write, vp)((vp)->v_fbhv,file,iov,segs,offset,ioflags,cr) -#define VOP_SENDFILE(vp,f,off,ioflags,cnt,act,targ,cr,rv) \ - rv = _VOP_(vop_sendfile, vp)((vp)->v_fbhv,f,off,ioflags,cnt,act,targ,cr) -#define VOP_SPLICE_READ(vp,f,o,pipe,cnt,fl,iofl,cr,rv) \ - rv = _VOP_(vop_splice_read, vp)((vp)->v_fbhv,f,o,pipe,cnt,fl,iofl,cr) -#define VOP_SPLICE_WRITE(vp,f,o,pipe,cnt,fl,iofl,cr,rv) \ - rv = _VOP_(vop_splice_write, vp)((vp)->v_fbhv,f,o,pipe,cnt,fl,iofl,cr) -#define VOP_BMAP(vp,of,sz,rw,b,n,rv) \ - rv = _VOP_(vop_bmap, vp)((vp)->v_fbhv,of,sz,rw,b,n) -#define VOP_OPEN(vp, cr, rv) \ - rv = _VOP_(vop_open, vp)((vp)->v_fbhv, cr) -#define VOP_GETATTR(vp, vap, f, cr, rv) \ - rv = _VOP_(vop_getattr, vp)((vp)->v_fbhv, vap, f, cr) -#define VOP_SETATTR(vp, vap, f, cr, rv) \ - rv = _VOP_(vop_setattr, vp)((vp)->v_fbhv, vap, f, cr) -#define VOP_ACCESS(vp, mode, cr, rv) \ - rv = _VOP_(vop_access, vp)((vp)->v_fbhv, mode, cr) -#define VOP_LOOKUP(vp,d,vpp,f,rdir,cr,rv) \ - rv = _VOP_(vop_lookup, vp)((vp)->v_fbhv,d,vpp,f,rdir,cr) -#define VOP_CREATE(dvp,d,vap,vpp,cr,rv) \ - rv = _VOP_(vop_create, dvp)((dvp)->v_fbhv,d,vap,vpp,cr) -#define VOP_REMOVE(dvp,d,cr,rv) \ - rv = _VOP_(vop_remove, dvp)((dvp)->v_fbhv,d,cr) -#define VOP_LINK(tdvp,fvp,d,cr,rv) \ - rv = _VOP_(vop_link, tdvp)((tdvp)->v_fbhv,fvp,d,cr) -#define VOP_RENAME(fvp,fnm,tdvp,tnm,cr,rv) \ - rv = _VOP_(vop_rename, fvp)((fvp)->v_fbhv,fnm,tdvp,tnm,cr) -#define VOP_MKDIR(dp,d,vap,vpp,cr,rv) \ - rv = _VOP_(vop_mkdir, dp)((dp)->v_fbhv,d,vap,vpp,cr) -#define VOP_RMDIR(dp,d,cr,rv) \ - rv = _VOP_(vop_rmdir, dp)((dp)->v_fbhv,d,cr) -#define VOP_READDIR(vp,uiop,cr,eofp,rv) \ - rv = _VOP_(vop_readdir, vp)((vp)->v_fbhv,uiop,cr,eofp) -#define VOP_SYMLINK(dvp,d,vap,tnm,vpp,cr,rv) \ - rv = _VOP_(vop_symlink, dvp) ((dvp)->v_fbhv,d,vap,tnm,vpp,cr) -#define VOP_READLINK(vp,uiop,fl,cr,rv) \ - rv = _VOP_(vop_readlink, vp)((vp)->v_fbhv,uiop,fl,cr) -#define VOP_FSYNC(vp,f,cr,b,e,rv) \ - rv = _VOP_(vop_fsync, vp)((vp)->v_fbhv,f,cr,b,e) -#define VOP_INACTIVE(vp, cr, rv) \ - rv = _VOP_(vop_inactive, vp)((vp)->v_fbhv, cr) -#define VOP_RELEASE(vp, rv) \ - rv = _VOP_(vop_release, vp)((vp)->v_fbhv) -#define VOP_FID2(vp, fidp, rv) \ - rv = _VOP_(vop_fid2, vp)((vp)->v_fbhv, fidp) -#define VOP_RWLOCK(vp,i) \ - (void)_VOP_(vop_rwlock, vp)((vp)->v_fbhv, i) -#define VOP_RWLOCK_TRY(vp,i) \ - _VOP_(vop_rwlock, vp)((vp)->v_fbhv, i) -#define VOP_RWUNLOCK(vp,i) \ - (void)_VOP_(vop_rwunlock, vp)((vp)->v_fbhv, i) -#define VOP_FRLOCK(vp,c,fl,flags,offset,fr,rv) \ - rv = _VOP_(vop_frlock, vp)((vp)->v_fbhv,c,fl,flags,offset,fr) -#define VOP_RECLAIM(vp, rv) \ - rv = _VOP_(vop_reclaim, vp)((vp)->v_fbhv) -#define VOP_ATTR_GET(vp, name, val, vallenp, fl, cred, rv) \ - rv = _VOP_(vop_attr_get, vp)((vp)->v_fbhv,name,val,vallenp,fl,cred) -#define VOP_ATTR_SET(vp, name, val, vallen, fl, cred, rv) \ - rv = _VOP_(vop_attr_set, vp)((vp)->v_fbhv,name,val,vallen,fl,cred) -#define VOP_ATTR_REMOVE(vp, name, flags, cred, rv) \ - rv = _VOP_(vop_attr_remove, vp)((vp)->v_fbhv,name,flags,cred) -#define VOP_ATTR_LIST(vp, buf, buflen, fl, cursor, cred, rv) \ - rv = _VOP_(vop_attr_list, vp)((vp)->v_fbhv,buf,buflen,fl,cursor,cred) -#define VOP_LINK_REMOVED(vp, dvp, linkzero) \ - (void)_VOP_(vop_link_removed, vp)((vp)->v_fbhv, dvp, linkzero) -#define VOP_VNODE_CHANGE(vp, cmd, val) \ - (void)_VOP_(vop_vnode_change, vp)((vp)->v_fbhv,cmd,val) -/* - * These are page cache functions that now go thru VOPs. - * 'last' parameter is unused and left in for IRIX compatibility + * Virtual node operations, operating from head bhv. */ -#define VOP_TOSS_PAGES(vp, first, last, fiopt) \ - _VOP_(vop_tosspages, vp)((vp)->v_fbhv,first, last, fiopt) -/* - * 'last' parameter is unused and left in for IRIX compatibility - */ -#define VOP_FLUSHINVAL_PAGES(vp, first, last, fiopt) \ - _VOP_(vop_flushinval_pages, vp)((vp)->v_fbhv,first,last,fiopt) -/* - * 'last' parameter is unused and left in for IRIX compatibility - */ -#define VOP_FLUSH_PAGES(vp, first, last, flags, fiopt, rv) \ - rv = _VOP_(vop_flush_pages, vp)((vp)->v_fbhv,first,last,flags,fiopt) -#define VOP_IOCTL(vp, inode, filp, fl, cmd, arg, rv) \ - rv = _VOP_(vop_ioctl, vp)((vp)->v_fbhv,inode,filp,fl,cmd,arg) -#define VOP_IFLUSH(vp, flags, rv) \ - rv = _VOP_(vop_iflush, vp)((vp)->v_fbhv, flags) +#define VNHEAD(vp) ((vp)->v_bh.bh_first) +#define VOP(op, vp) (*((bhv_vnodeops_t *)VNHEAD(vp)->bd_ops)->op) +#define bhv_vop_open(vp, cr) VOP(vop_open, vp)(VNHEAD(vp),cr) +#define bhv_vop_close(vp, f,last,cr) VOP(vop_close, vp)(VNHEAD(vp),f,last,cr) +#define bhv_vop_read(vp,file,iov,segs,offset,ioflags,cr) \ + VOP(vop_read, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) +#define bhv_vop_write(vp,file,iov,segs,offset,ioflags,cr) \ + VOP(vop_write, vp)(VNHEAD(vp),file,iov,segs,offset,ioflags,cr) +#define bhv_vop_sendfile(vp,f,off,ioflags,cnt,act,targ,cr) \ + VOP(vop_sendfile, vp)(VNHEAD(vp),f,off,ioflags,cnt,act,targ,cr) +#define bhv_vop_splice_read(vp,f,o,pipe,cnt,fl,iofl,cr) \ + VOP(vop_splice_read, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr) +#define bhv_vop_splice_write(vp,f,o,pipe,cnt,fl,iofl,cr) \ + VOP(vop_splice_write, vp)(VNHEAD(vp),f,o,pipe,cnt,fl,iofl,cr) +#define bhv_vop_bmap(vp,of,sz,rw,b,n) \ + VOP(vop_bmap, vp)(VNHEAD(vp),of,sz,rw,b,n) +#define bhv_vop_getattr(vp, vap,f,cr) \ + VOP(vop_getattr, vp)(VNHEAD(vp), vap,f,cr) +#define bhv_vop_setattr(vp, vap,f,cr) \ + VOP(vop_setattr, vp)(VNHEAD(vp), vap,f,cr) +#define bhv_vop_access(vp, mode,cr) VOP(vop_access, vp)(VNHEAD(vp), mode,cr) +#define bhv_vop_lookup(vp,d,vpp,f,rdir,cr) \ + VOP(vop_lookup, vp)(VNHEAD(vp),d,vpp,f,rdir,cr) +#define bhv_vop_create(dvp,d,vap,vpp,cr) \ + VOP(vop_create, dvp)(VNHEAD(dvp),d,vap,vpp,cr) +#define bhv_vop_remove(dvp,d,cr) VOP(vop_remove, dvp)(VNHEAD(dvp),d,cr) +#define bhv_vop_link(dvp,fvp,d,cr) VOP(vop_link, dvp)(VNHEAD(dvp),fvp,d,cr) +#define bhv_vop_rename(fvp,fnm,tdvp,tnm,cr) \ + VOP(vop_rename, fvp)(VNHEAD(fvp),fnm,tdvp,tnm,cr) +#define bhv_vop_mkdir(dp,d,vap,vpp,cr) \ + VOP(vop_mkdir, dp)(VNHEAD(dp),d,vap,vpp,cr) +#define bhv_vop_rmdir(dp,d,cr) VOP(vop_rmdir, dp)(VNHEAD(dp),d,cr) +#define bhv_vop_readdir(vp,uiop,cr,eofp) \ + VOP(vop_readdir, vp)(VNHEAD(vp),uiop,cr,eofp) +#define bhv_vop_symlink(dvp,d,vap,tnm,vpp,cr) \ + VOP(vop_symlink, dvp)(VNHEAD(dvp),d,vap,tnm,vpp,cr) +#define bhv_vop_readlink(vp,uiop,fl,cr) \ + VOP(vop_readlink, vp)(VNHEAD(vp),uiop,fl,cr) +#define bhv_vop_fsync(vp,f,cr,b,e) VOP(vop_fsync, vp)(VNHEAD(vp),f,cr,b,e) +#define bhv_vop_inactive(vp,cr) VOP(vop_inactive, vp)(VNHEAD(vp),cr) +#define bhv_vop_release(vp) VOP(vop_release, vp)(VNHEAD(vp)) +#define bhv_vop_fid2(vp,fidp) VOP(vop_fid2, vp)(VNHEAD(vp),fidp) +#define bhv_vop_rwlock(vp,i) VOP(vop_rwlock, vp)(VNHEAD(vp),i) +#define bhv_vop_rwlock_try(vp,i) VOP(vop_rwlock, vp)(VNHEAD(vp),i) +#define bhv_vop_rwunlock(vp,i) VOP(vop_rwunlock, vp)(VNHEAD(vp),i) +#define bhv_vop_frlock(vp,c,fl,flags,offset,fr) \ + VOP(vop_frlock, vp)(VNHEAD(vp),c,fl,flags,offset,fr) +#define bhv_vop_reclaim(vp) VOP(vop_reclaim, vp)(VNHEAD(vp)) +#define bhv_vop_attr_get(vp, name, val, vallenp, fl, cred) \ + VOP(vop_attr_get, vp)(VNHEAD(vp),name,val,vallenp,fl,cred) +#define bhv_vop_attr_set(vp, name, val, vallen, fl, cred) \ + VOP(vop_attr_set, vp)(VNHEAD(vp),name,val,vallen,fl,cred) +#define bhv_vop_attr_remove(vp, name, flags, cred) \ + VOP(vop_attr_remove, vp)(VNHEAD(vp),name,flags,cred) +#define bhv_vop_attr_list(vp, buf, buflen, fl, cursor, cred) \ + VOP(vop_attr_list, vp)(VNHEAD(vp),buf,buflen,fl,cursor,cred) +#define bhv_vop_link_removed(vp, dvp, linkzero) \ + VOP(vop_link_removed, vp)(VNHEAD(vp), dvp, linkzero) +#define bhv_vop_vnode_change(vp, cmd, val) \ + VOP(vop_vnode_change, vp)(VNHEAD(vp), cmd, val) +#define bhv_vop_toss_pages(vp, first, last, fiopt) \ + VOP(vop_tosspages, vp)(VNHEAD(vp), first, last, fiopt) +#define bhv_vop_flushinval_pages(vp, first, last, fiopt) \ + VOP(vop_flushinval_pages, vp)(VNHEAD(vp),first,last,fiopt) +#define bhv_vop_flush_pages(vp, first, last, flags, fiopt) \ + VOP(vop_flush_pages, vp)(VNHEAD(vp),first,last,flags,fiopt) +#define bhv_vop_ioctl(vp, inode, filp, fl, cmd, arg) \ + VOP(vop_ioctl, vp)(VNHEAD(vp),inode,filp,fl,cmd,arg) +#define bhv_vop_iflush(vp, flags) VOP(vop_iflush, vp)(VNHEAD(vp), flags) /* * Flags for read/write calls - same values as IRIX @@ -377,7 +326,7 @@ #define IO_ISDIRECT 0x00004 /* bypass p #define IO_INVIS 0x00020 /* don't update inode timestamps */ /* - * Flags for VOP_IFLUSH call + * Flags for vop_iflush call */ #define FLUSH_SYNC 1 /* wait for flush to complete */ #define FLUSH_INODE 2 /* flush the inode itself */ @@ -385,8 +334,7 @@ #define FLUSH_LOG 4 /* force the last l * this inode out to disk */ /* - * Flush/Invalidate options for VOP_TOSS_PAGES, VOP_FLUSHINVAL_PAGES and - * VOP_FLUSH_PAGES. + * Flush/Invalidate options for vop_toss/flush/flushinval_pages. */ #define FI_NONE 0 /* none */ #define FI_REMAPF 1 /* Do a remapf prior to the operation */ @@ -398,7 +346,7 @@ #define FI_REMAPF_LOCKED 2 /* Do a remap * Vnode attributes. va_mask indicates those attributes the caller * wants to set or extract. */ -typedef struct vattr { +typedef struct bhv_vattr { int va_mask; /* bit-mask of attributes present */ mode_t va_mode; /* file access mode and type */ xfs_nlink_t va_nlink; /* number of references to file */ @@ -418,7 +366,7 @@ typedef struct vattr { u_long va_nextents; /* number of extents in file */ u_long va_anextents; /* number of attr extents in file */ prid_t va_projid; /* project id */ -} vattr_t; +} bhv_vattr_t; /* * setattr or getattr attributes @@ -492,29 +440,17 @@ #define MANDLOCK(vp, mode) \ (VN_ISREG(vp) && ((mode) & (VSGID|(VEXEC>>3))) == VSGID) extern void vn_init(void); -extern vnode_t *vn_initialize(struct inode *); - -/* - * vnode_map structures _must_ match vn_epoch and vnode structure sizes. - */ -typedef struct vnode_map { - vfs_t *v_vfsp; - vnumber_t v_number; /* in-core vnode number */ - xfs_ino_t v_ino; /* inode # */ -} vmap_t; - -#define VMAP(vp, vmap) {(vmap).v_vfsp = (vp)->v_vfsp, \ - (vmap).v_number = (vp)->v_number, \ - (vmap).v_ino = (vp)->v_inode.i_ino; } +extern bhv_vnode_t *vn_initialize(struct inode *); +extern int vn_revalidate(struct bhv_vnode *); +extern int __vn_revalidate(struct bhv_vnode *, bhv_vattr_t *); +extern void vn_revalidate_core(struct bhv_vnode *, bhv_vattr_t *); -extern int vn_revalidate(struct vnode *); -extern int __vn_revalidate(struct vnode *, vattr_t *); -extern void vn_revalidate_core(struct vnode *, vattr_t *); +extern void vn_iowait(struct bhv_vnode *vp); +extern void vn_iowake(struct bhv_vnode *vp); -extern void vn_iowait(struct vnode *vp); -extern void vn_iowake(struct vnode *vp); +extern void vn_ioerror(struct bhv_vnode *vp, int error, char *f, int l); -static inline int vn_count(struct vnode *vp) +static inline int vn_count(struct bhv_vnode *vp) { return atomic_read(&vn_to_inode(vp)->i_count); } @@ -522,7 +458,7 @@ static inline int vn_count(struct vnode /* * Vnode reference counting functions (and macros for compatibility). */ -extern vnode_t *vn_hold(struct vnode *); +extern bhv_vnode_t *vn_hold(struct bhv_vnode *); #if defined(XFS_VNODE_TRACE) #define VN_HOLD(vp) \ @@ -536,7 +472,7 @@ #define VN_HOLD(vp) ((void)vn_hold(vp)) #define VN_RELE(vp) (iput(vn_to_inode(vp))) #endif -static inline struct vnode *vn_grab(struct vnode *vp) +static inline struct bhv_vnode *vn_grab(struct bhv_vnode *vp) { struct inode *inode = igrab(vn_to_inode(vp)); return inode ? vn_from_inode(inode) : NULL; @@ -554,32 +490,39 @@ #define VNAME_TO_VNODE(dentry) (vn_from_ */ #define VN_LOCK(vp) mutex_spinlock(&(vp)->v_lock) #define VN_UNLOCK(vp, s) mutex_spinunlock(&(vp)->v_lock, s) -#define VN_FLAGSET(vp,b) vn_flagset(vp,b) -#define VN_FLAGCLR(vp,b) vn_flagclr(vp,b) -static __inline__ void vn_flagset(struct vnode *vp, uint flag) +static __inline__ void vn_flagset(struct bhv_vnode *vp, uint flag) { spin_lock(&vp->v_lock); vp->v_flag |= flag; spin_unlock(&vp->v_lock); } -static __inline__ void vn_flagclr(struct vnode *vp, uint flag) +static __inline__ uint vn_flagclr(struct bhv_vnode *vp, uint flag) { + uint cleared; + spin_lock(&vp->v_lock); + cleared = (vp->v_flag & flag); vp->v_flag &= ~flag; spin_unlock(&vp->v_lock); + return cleared; } +#define VMODIFY(vp) vn_flagset(vp, VMODIFIED) +#define VUNMODIFY(vp) vn_flagclr(vp, VMODIFIED) +#define VTRUNCATE(vp) vn_flagset(vp, VTRUNCATED) +#define VUNTRUNCATE(vp) vn_flagclr(vp, VTRUNCATED) + /* * Dealing with bad inodes */ -static inline void vn_mark_bad(struct vnode *vp) +static inline void vn_mark_bad(struct bhv_vnode *vp) { make_bad_inode(vn_to_inode(vp)); } -static inline int VN_BAD(struct vnode *vp) +static inline int VN_BAD(struct bhv_vnode *vp) { return is_bad_inode(vn_to_inode(vp)); } @@ -587,18 +530,18 @@ static inline int VN_BAD(struct vnode *v /* * Extracting atime values in various formats */ -static inline void vn_atime_to_bstime(struct vnode *vp, xfs_bstime_t *bs_atime) +static inline void vn_atime_to_bstime(bhv_vnode_t *vp, xfs_bstime_t *bs_atime) { bs_atime->tv_sec = vp->v_inode.i_atime.tv_sec; bs_atime->tv_nsec = vp->v_inode.i_atime.tv_nsec; } -static inline void vn_atime_to_timespec(struct vnode *vp, struct timespec *ts) +static inline void vn_atime_to_timespec(bhv_vnode_t *vp, struct timespec *ts) { *ts = vp->v_inode.i_atime; } -static inline void vn_atime_to_time_t(struct vnode *vp, time_t *tt) +static inline void vn_atime_to_time_t(bhv_vnode_t *vp, time_t *tt) { *tt = vp->v_inode.i_atime.tv_sec; } @@ -610,11 +553,10 @@ #define VN_MAPPED(vp) mapping_mapped(vn_ #define VN_CACHED(vp) (vn_to_inode(vp)->i_mapping->nrpages) #define VN_DIRTY(vp) mapping_tagged(vn_to_inode(vp)->i_mapping, \ PAGECACHE_TAG_DIRTY) -#define VMODIFY(vp) VN_FLAGSET(vp, VMODIFIED) -#define VUNMODIFY(vp) VN_FLAGCLR(vp, VMODIFIED) +#define VN_TRUNC(vp) ((vp)->v_flag & VTRUNCATED) /* - * Flags to VOP_SETATTR/VOP_GETATTR. + * Flags to vop_setattr/getattr. */ #define ATTR_UTIME 0x01 /* non-default utime(2) request */ #define ATTR_DMI 0x08 /* invocation from a DMI function */ @@ -624,7 +566,7 @@ #define ATTR_NOLOCK 0x200 /* Don't grab #define ATTR_NOSIZETOK 0x400 /* Don't get the SIZE token */ /* - * Flags to VOP_FSYNC and VOP_RECLAIM. + * Flags to vop_fsync/reclaim. */ #define FSYNC_NOWAIT 0 /* asynchronous flush */ #define FSYNC_WAIT 0x1 /* synchronous fsync or forced reclaim */ @@ -643,11 +585,11 @@ #define VNODE_KTRACE_HOLD 3 #define VNODE_KTRACE_REF 4 #define VNODE_KTRACE_RELE 5 -extern void vn_trace_entry(struct vnode *, const char *, inst_t *); -extern void vn_trace_exit(struct vnode *, const char *, inst_t *); -extern void vn_trace_hold(struct vnode *, char *, int, inst_t *); -extern void vn_trace_ref(struct vnode *, char *, int, inst_t *); -extern void vn_trace_rele(struct vnode *, char *, int, inst_t *); +extern void vn_trace_entry(struct bhv_vnode *, const char *, inst_t *); +extern void vn_trace_exit(struct bhv_vnode *, const char *, inst_t *); +extern void vn_trace_hold(struct bhv_vnode *, char *, int, inst_t *); +extern void vn_trace_ref(struct bhv_vnode *, char *, int, inst_t *); +extern void vn_trace_rele(struct bhv_vnode *, char *, int, inst_t *); #define VN_TRACE(vp) \ vn_trace_ref(vp, __FILE__, __LINE__, (inst_t *)__return_address) diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 772ac48..3aa7715 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -444,7 +442,7 @@ xfs_qm_dqalloc( XFS_BMAPI_METADATA | XFS_BMAPI_WRITE, &firstblock, XFS_QM_DQALLOC_SPACE_RES(mp), - &map, &nmaps, &flist))) { + &map, &nmaps, &flist, NULL))) { goto error0; } ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB); @@ -559,7 +557,7 @@ xfs_qm_dqtobp( error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset, XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA, - NULL, 0, &map, &nmaps, NULL); + NULL, 0, &map, &nmaps, NULL, NULL); xfs_iunlock(quotip, XFS_ILOCK_SHARED); if (error) @@ -1261,7 +1259,7 @@ xfs_qm_dqflush( if (xfs_qm_dqcheck(&dqp->q_core, be32_to_cpu(ddqp->d_id), 0, XFS_QMOPT_DOWARN, "dqflush (incore copy)")) { - xfs_force_shutdown(dqp->q_mount, XFS_CORRUPT_INCORE); + xfs_force_shutdown(dqp->q_mount, SHUTDOWN_CORRUPT_INCORE); return XFS_ERROR(EIO); } diff --git a/fs/xfs/quota/xfs_dquot.h b/fs/xfs/quota/xfs_dquot.h index c0c6296..78d3ab9 100644 --- a/fs/xfs/quota/xfs_dquot.h +++ b/fs/xfs/quota/xfs_dquot.h @@ -119,7 +119,7 @@ #endif */ #define xfs_dqflock(dqp) { psema(&((dqp)->q_flock), PINOD | PRECALC);\ (dqp)->dq_flags |= XFS_DQ_FLOCKED; } -#define xfs_dqfunlock(dqp) { ASSERT(valusema(&((dqp)->q_flock)) <= 0); \ +#define xfs_dqfunlock(dqp) { ASSERT(issemalocked(&((dqp)->q_flock))); \ vsema(&((dqp)->q_flock)); \ (dqp)->dq_flags &= ~(XFS_DQ_FLOCKED); } @@ -128,7 +128,7 @@ #define XFS_DQ_PINLOCK(dqp) mutex_spi #define XFS_DQ_PINUNLOCK(dqp, s) mutex_spinunlock( \ &(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s) -#define XFS_DQ_IS_FLUSH_LOCKED(dqp) (valusema(&((dqp)->q_flock)) <= 0) +#define XFS_DQ_IS_FLUSH_LOCKED(dqp) (issemalocked(&((dqp)->q_flock))) #define XFS_DQ_IS_ON_FREELIST(dqp) ((dqp)->dq_flnext != (dqp)) #define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY) #define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index 546f48a..5b2dcc5 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -248,7 +246,7 @@ xfs_qm_dquot_logitem_pushbuf( * inode flush completed and the inode was taken off the AIL. * So, just get out. */ - if ((valusema(&(dqp->q_flock)) > 0) || + if (!issemalocked(&(dqp->q_flock)) || ((qip->qli_item.li_flags & XFS_LI_IN_AIL) == 0)) { qip->qli_pushbuf_flag = 0; xfs_dqunlock(dqp); @@ -261,7 +259,7 @@ xfs_qm_dquot_logitem_pushbuf( if (bp != NULL) { if (XFS_BUF_ISDELAYWRITE(bp)) { dopush = ((qip->qli_item.li_flags & XFS_LI_IN_AIL) && - (valusema(&(dqp->q_flock)) <= 0)); + issemalocked(&(dqp->q_flock))); qip->qli_pushbuf_flag = 0; xfs_dqunlock(dqp); diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 7fb5eca..e23e455 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -24,7 +24,6 @@ #include "xfs_clnt.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -33,7 +32,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -1603,7 +1601,7 @@ xfs_qm_dqiterate( maxlblkcnt - lblkno, XFS_BMAPI_METADATA, NULL, - 0, map, &nmaps, NULL); + 0, map, &nmaps, NULL, NULL); xfs_iunlock(qip, XFS_ILOCK_SHARED); if (error) break; @@ -1905,9 +1903,7 @@ xfs_qm_quotacheck( */ if ((error = xfs_bulkstat(mp, &lastino, &count, xfs_qm_dqusage_adjust, NULL, - structsz, NULL, - BULKSTAT_FG_IGET|BULKSTAT_FG_VFSLOCKED, - &done))) + structsz, NULL, BULKSTAT_FG_IGET, &done))) break; } while (! done); diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c index 6838b36..e95e99f 100644 --- a/fs/xfs/quota/xfs_qm_bhv.c +++ b/fs/xfs/quota/xfs_qm_bhv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -24,7 +24,6 @@ #include "xfs_clnt.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -33,7 +32,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -129,7 +127,7 @@ xfs_qm_parseargs( return XFS_ERROR(EINVAL); } - PVFS_PARSEARGS(BHV_NEXT(bhv), options, args, update, error); + error = bhv_next_vfs_parseargs(BHV_NEXT(bhv), options, args, update); if (!error && !referenced) bhv_remove_vfsops(bhvtovfs(bhv), VFS_POSITION_QM); return error; @@ -140,9 +138,8 @@ xfs_qm_showargs( struct bhv_desc *bhv, struct seq_file *m) { - struct vfs *vfsp = bhvtovfs(bhv); + struct bhv_vfs *vfsp = bhvtovfs(bhv); struct xfs_mount *mp = XFS_VFSTOM(vfsp); - int error; if (mp->m_qflags & XFS_UQUOTA_ACCT) { (mp->m_qflags & XFS_UQUOTA_ENFD) ? @@ -165,8 +162,7 @@ xfs_qm_showargs( if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT)) seq_puts(m, "," MNTOPT_NOQUOTA); - PVFS_SHOWARGS(BHV_NEXT(bhv), m, error); - return error; + return bhv_next_vfs_showargs(BHV_NEXT(bhv), m); } STATIC int @@ -175,14 +171,67 @@ xfs_qm_mount( struct xfs_mount_args *args, struct cred *cr) { - struct vfs *vfsp = bhvtovfs(bhv); + struct bhv_vfs *vfsp = bhvtovfs(bhv); struct xfs_mount *mp = XFS_VFSTOM(vfsp); - int error; if (args->flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA | XFSMNT_PQUOTA)) xfs_qm_mount_quotainit(mp, args->flags); - PVFS_MOUNT(BHV_NEXT(bhv), args, cr, error); - return error; + return bhv_next_vfs_mount(BHV_NEXT(bhv), args, cr); +} + +/* + * Directory tree accounting is implemented using project quotas, where + * the project identifier is inherited from parent directories. + * A statvfs (df, etc.) of a directory that is using project quota should + * return a statvfs of the project, not the entire filesystem. + * This makes such trees appear as if they are filesystems in themselves. + */ +STATIC int +xfs_qm_statvfs( + struct bhv_desc *bhv, + bhv_statvfs_t *statp, + struct bhv_vnode *vnode) +{ + xfs_mount_t *mp; + xfs_inode_t *ip; + xfs_dquot_t *dqp; + xfs_disk_dquot_t *dp; + __uint64_t limit; + int error; + + error = bhv_next_vfs_statvfs(BHV_NEXT(bhv), statp, vnode); + if (error || !vnode) + return error; + + mp = XFS_BHVTOM(bhv); + ip = xfs_vtoi(vnode); + + if (!(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)) + return 0; + if (!(mp->m_qflags & XFS_PQUOTA_ACCT)) + return 0; + if (!(mp->m_qflags & XFS_OQUOTA_ENFD)) + return 0; + + if (xfs_qm_dqget(mp, NULL, ip->i_d.di_projid, XFS_DQ_PROJ, 0, &dqp)) + return 0; + dp = &dqp->q_core; + + limit = dp->d_blk_softlimit ? dp->d_blk_softlimit : dp->d_blk_hardlimit; + if (limit && statp->f_blocks > limit) { + statp->f_blocks = limit; + statp->f_bfree = (statp->f_blocks > dp->d_bcount) ? + (statp->f_blocks - dp->d_bcount) : 0; + } + limit = dp->d_ino_softlimit ? dp->d_ino_softlimit : dp->d_ino_hardlimit; + if (limit && statp->f_files > limit) { + statp->f_files = limit; + statp->f_ffree = (statp->f_files > dp->d_icount) ? + (statp->f_ffree - dp->d_icount) : 0; + } + + xfs_qm_dqput(dqp); + return 0; } STATIC int @@ -191,7 +240,7 @@ xfs_qm_syncall( int flags, cred_t *credp) { - struct vfs *vfsp = bhvtovfs(bhv); + struct bhv_vfs *vfsp = bhvtovfs(bhv); struct xfs_mount *mp = XFS_VFSTOM(vfsp); int error; @@ -210,8 +259,7 @@ xfs_qm_syncall( } } } - PVFS_SYNC(BHV_NEXT(bhv), flags, credp, error); - return error; + return bhv_next_vfs_sync(BHV_NEXT(bhv), flags, credp); } STATIC int @@ -346,11 +394,12 @@ STATIC struct xfs_qmops xfs_qmcore_xfs = .xfs_dqtrxops = &xfs_trans_dquot_ops, }; -struct bhv_vfsops xfs_qmops = { { +struct bhv_module_vfsops xfs_qmops = { { BHV_IDENTITY_INIT(VFS_BHV_QM, VFS_POSITION_QM), .vfs_parseargs = xfs_qm_parseargs, .vfs_showargs = xfs_qm_showargs, .vfs_mount = xfs_qm_mount, + .vfs_statvfs = xfs_qm_statvfs, .vfs_sync = xfs_qm_syncall, .vfs_quotactl = xfs_qm_quotactl, }, }; diff --git a/fs/xfs/quota/xfs_qm_stats.c b/fs/xfs/quota/xfs_qm_stats.c index 0570f77..6f858fb 100644 --- a/fs/xfs/quota/xfs_qm_stats.c +++ b/fs/xfs/quota/xfs_qm_stats.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index c55db46..ed620c4 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -26,7 +26,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -35,7 +34,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -91,8 +89,8 @@ xfs_qm_quotactl( xfs_caddr_t addr) { xfs_mount_t *mp; + bhv_vfs_t *vfsp; int error; - struct vfs *vfsp; vfsp = bhvtovfs(bdp); mp = XFS_VFSTOM(vfsp); @@ -1035,7 +1033,7 @@ xfs_qm_dqrele_all_inodes( { xfs_inode_t *ip, *topino; uint ireclaims; - vnode_t *vp; + bhv_vnode_t *vp; boolean_t vnode_refd; ASSERT(mp->m_quotainfo); diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c index 9168918..0242e96 100644 --- a/fs/xfs/quota/xfs_trans_dquot.c +++ b/fs/xfs/quota/xfs_trans_dquot.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -33,7 +32,6 @@ #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" #include "xfs_attr_sf.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" diff --git a/fs/xfs/support/debug.c b/fs/xfs/support/debug.c index b08b3d9..36fbecc 100644 --- a/fs/xfs/support/debug.c +++ b/fs/xfs/support/debug.c @@ -47,7 +47,7 @@ cmn_err(register int level, char *fmt, . va_start(ap, fmt); if (*fmt == '!') fp++; len = vsprintf(message, fp, ap); - if (message[len-1] != '\n') + if (level != CE_DEBUG && message[len-1] != '\n') strcat(message, "\n"); printk("%s%s", err_level[level], message); va_end(ap); @@ -68,7 +68,7 @@ icmn_err(register int level, char *fmt, level = XFS_MAX_ERR_LEVEL; spin_lock_irqsave(&xfs_err_lock,flags); len = vsprintf(message, fmt, ap); - if (message[len-1] != '\n') + if (level != CE_DEBUG && message[len-1] != '\n') strcat(message, "\n"); spin_unlock_irqrestore(&xfs_err_lock,flags); printk("%s%s", err_level[level], message); diff --git a/fs/xfs/support/debug.h b/fs/xfs/support/debug.h index e3bf581..4f54dca 100644 --- a/fs/xfs/support/debug.h +++ b/fs/xfs/support/debug.h @@ -33,9 +33,6 @@ extern void cmn_err(int, char *, ...) __attribute__ ((format (printf, 2, 3))); extern void assfail(char *expr, char *f, int l); -#define prdev(fmt,targ,args...) \ - printk("Device %s - " fmt "\n", XFS_BUFTARG_NAME(targ), ## args) - #define ASSERT_ALWAYS(expr) \ (unlikely((expr) != 0) ? (void)0 : assfail(#expr, __FILE__, __LINE__)) diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c index 2539af3..4b0cb47 100644 --- a/fs/xfs/xfs_acl.c +++ b/fs/xfs/xfs_acl.c @@ -21,12 +21,10 @@ #include "xfs_types.h" #include "xfs_bit.h" #include "xfs_inum.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -39,15 +37,15 @@ #include "xfs_attr.h" #include #include -STATIC int xfs_acl_setmode(vnode_t *, xfs_acl_t *, int *); +STATIC int xfs_acl_setmode(bhv_vnode_t *, xfs_acl_t *, int *); STATIC void xfs_acl_filter_mode(mode_t, xfs_acl_t *); STATIC void xfs_acl_get_endian(xfs_acl_t *); STATIC int xfs_acl_access(uid_t, gid_t, xfs_acl_t *, mode_t, cred_t *); STATIC int xfs_acl_invalid(xfs_acl_t *); STATIC void xfs_acl_sync_mode(mode_t, xfs_acl_t *); -STATIC void xfs_acl_get_attr(vnode_t *, xfs_acl_t *, int, int, int *); -STATIC void xfs_acl_set_attr(vnode_t *, xfs_acl_t *, int, int *); -STATIC int xfs_acl_allow_set(vnode_t *, int); +STATIC void xfs_acl_get_attr(bhv_vnode_t *, xfs_acl_t *, int, int, int *); +STATIC void xfs_acl_set_attr(bhv_vnode_t *, xfs_acl_t *, int, int *); +STATIC int xfs_acl_allow_set(bhv_vnode_t *, int); kmem_zone_t *xfs_acl_zone; @@ -57,7 +55,7 @@ kmem_zone_t *xfs_acl_zone; */ int xfs_acl_vhasacl_access( - vnode_t *vp) + bhv_vnode_t *vp) { int error; @@ -70,7 +68,7 @@ xfs_acl_vhasacl_access( */ int xfs_acl_vhasacl_default( - vnode_t *vp) + bhv_vnode_t *vp) { int error; @@ -209,7 +207,7 @@ posix_acl_xfs_to_xattr( int xfs_acl_vget( - vnode_t *vp, + bhv_vnode_t *vp, void *acl, size_t size, int kind) @@ -241,10 +239,10 @@ xfs_acl_vget( goto out; } if (kind == _ACL_TYPE_ACCESS) { - vattr_t va; + bhv_vattr_t va; va.va_mask = XFS_AT_MODE; - VOP_GETATTR(vp, &va, 0, sys_cred, error); + error = bhv_vop_getattr(vp, &va, 0, sys_cred); if (error) goto out; xfs_acl_sync_mode(va.va_mode, xfs_acl); @@ -260,7 +258,7 @@ out: int xfs_acl_vremove( - vnode_t *vp, + bhv_vnode_t *vp, int kind) { int error; @@ -268,9 +266,9 @@ xfs_acl_vremove( VN_HOLD(vp); error = xfs_acl_allow_set(vp, kind); if (!error) { - VOP_ATTR_REMOVE(vp, kind == _ACL_TYPE_DEFAULT? - SGI_ACL_DEFAULT: SGI_ACL_FILE, - ATTR_ROOT, sys_cred, error); + error = bhv_vop_attr_remove(vp, kind == _ACL_TYPE_DEFAULT? + SGI_ACL_DEFAULT: SGI_ACL_FILE, + ATTR_ROOT, sys_cred); if (error == ENOATTR) error = 0; /* 'scool */ } @@ -280,7 +278,7 @@ xfs_acl_vremove( int xfs_acl_vset( - vnode_t *vp, + bhv_vnode_t *vp, void *acl, size_t size, int kind) @@ -370,10 +368,10 @@ xfs_acl_iaccess( STATIC int xfs_acl_allow_set( - vnode_t *vp, + bhv_vnode_t *vp, int kind) { - vattr_t va; + bhv_vattr_t va; int error; if (vp->v_inode.i_flags & (S_IMMUTABLE|S_APPEND)) @@ -383,7 +381,7 @@ xfs_acl_allow_set( if (vp->v_vfsp->vfs_flag & VFS_RDONLY) return EROFS; va.va_mask = XFS_AT_UID; - VOP_GETATTR(vp, &va, 0, NULL, error); + error = bhv_vop_getattr(vp, &va, 0, NULL); if (error) return error; if (va.va_uid != current->fsuid && !capable(CAP_FOWNER)) @@ -606,7 +604,7 @@ xfs_acl_get_endian( */ STATIC void xfs_acl_get_attr( - vnode_t *vp, + bhv_vnode_t *vp, xfs_acl_t *aclp, int kind, int flags, @@ -616,9 +614,9 @@ xfs_acl_get_attr( ASSERT((flags & ATTR_KERNOVAL) ? (aclp == NULL) : 1); flags |= ATTR_ROOT; - VOP_ATTR_GET(vp, - kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE : SGI_ACL_DEFAULT, - (char *)aclp, &len, flags, sys_cred, *error); + *error = bhv_vop_attr_get(vp, kind == _ACL_TYPE_ACCESS ? + SGI_ACL_FILE : SGI_ACL_DEFAULT, + (char *)aclp, &len, flags, sys_cred); if (*error || (flags & ATTR_KERNOVAL)) return; xfs_acl_get_endian(aclp); @@ -629,7 +627,7 @@ xfs_acl_get_attr( */ STATIC void xfs_acl_set_attr( - vnode_t *vp, + bhv_vnode_t *vp, xfs_acl_t *aclp, int kind, int *error) @@ -654,19 +652,19 @@ xfs_acl_set_attr( INT_SET(newace->ae_perm, ARCH_CONVERT, ace->ae_perm); } INT_SET(newacl->acl_cnt, ARCH_CONVERT, aclp->acl_cnt); - VOP_ATTR_SET(vp, - kind == _ACL_TYPE_ACCESS ? SGI_ACL_FILE: SGI_ACL_DEFAULT, - (char *)newacl, len, ATTR_ROOT, sys_cred, *error); + *error = bhv_vop_attr_set(vp, kind == _ACL_TYPE_ACCESS ? + SGI_ACL_FILE: SGI_ACL_DEFAULT, + (char *)newacl, len, ATTR_ROOT, sys_cred); _ACL_FREE(newacl); } int xfs_acl_vtoacl( - vnode_t *vp, + bhv_vnode_t *vp, xfs_acl_t *access_acl, xfs_acl_t *default_acl) { - vattr_t va; + bhv_vattr_t va; int error = 0; if (access_acl) { @@ -678,7 +676,7 @@ xfs_acl_vtoacl( if (!error) { /* Got the ACL, need the mode... */ va.va_mask = XFS_AT_MODE; - VOP_GETATTR(vp, &va, 0, sys_cred, error); + error = bhv_vop_getattr(vp, &va, 0, sys_cred); } if (error) @@ -701,8 +699,8 @@ xfs_acl_vtoacl( */ int xfs_acl_inherit( - vnode_t *vp, - vattr_t *vap, + bhv_vnode_t *vp, + bhv_vattr_t *vap, xfs_acl_t *pdaclp) { xfs_acl_t *cacl; @@ -757,11 +755,11 @@ xfs_acl_inherit( */ STATIC int xfs_acl_setmode( - vnode_t *vp, + bhv_vnode_t *vp, xfs_acl_t *acl, int *basicperms) { - vattr_t va; + bhv_vattr_t va; xfs_acl_entry_t *ap; xfs_acl_entry_t *gap = NULL; int i, error, nomask = 1; @@ -776,7 +774,7 @@ xfs_acl_setmode( * mode. The m:: bits take precedence over the g:: bits. */ va.va_mask = XFS_AT_MODE; - VOP_GETATTR(vp, &va, 0, sys_cred, error); + error = bhv_vop_getattr(vp, &va, 0, sys_cred); if (error) return error; @@ -810,8 +808,7 @@ xfs_acl_setmode( if (gap && nomask) va.va_mode |= gap->ae_perm << 3; - VOP_SETATTR(vp, &va, 0, sys_cred, error); - return error; + return bhv_vop_setattr(vp, &va, 0, sys_cred); } /* diff --git a/fs/xfs/xfs_acl.h b/fs/xfs/xfs_acl.h index 538d0d6..f853cf1 100644 --- a/fs/xfs/xfs_acl.h +++ b/fs/xfs/xfs_acl.h @@ -50,7 +50,7 @@ #define SGI_ACL_DEFAULT_SIZE (sizeof(SGI #ifdef CONFIG_XFS_POSIX_ACL struct vattr; -struct vnode; +struct bhv_vnode; struct xfs_inode; extern struct kmem_zone *xfs_acl_zone; @@ -58,14 +58,14 @@ #define xfs_acl_zone_init(zone, name) \ (zone) = kmem_zone_init(sizeof(xfs_acl_t), (name)) #define xfs_acl_zone_destroy(zone) kmem_zone_destroy(zone) -extern int xfs_acl_inherit(struct vnode *, struct vattr *, xfs_acl_t *); +extern int xfs_acl_inherit(struct bhv_vnode *, struct bhv_vattr *, xfs_acl_t *); extern int xfs_acl_iaccess(struct xfs_inode *, mode_t, cred_t *); -extern int xfs_acl_vtoacl(struct vnode *, xfs_acl_t *, xfs_acl_t *); -extern int xfs_acl_vhasacl_access(struct vnode *); -extern int xfs_acl_vhasacl_default(struct vnode *); -extern int xfs_acl_vset(struct vnode *, void *, size_t, int); -extern int xfs_acl_vget(struct vnode *, void *, size_t, int); -extern int xfs_acl_vremove(struct vnode *vp, int); +extern int xfs_acl_vtoacl(struct bhv_vnode *, xfs_acl_t *, xfs_acl_t *); +extern int xfs_acl_vhasacl_access(struct bhv_vnode *); +extern int xfs_acl_vhasacl_default(struct bhv_vnode *); +extern int xfs_acl_vset(struct bhv_vnode *, void *, size_t, int); +extern int xfs_acl_vget(struct bhv_vnode *, void *, size_t, int); +extern int xfs_acl_vremove(struct bhv_vnode *, int); #define _ACL_TYPE_ACCESS 1 #define _ACL_TYPE_DEFAULT 2 diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 8558226..eef6763 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -1862,7 +1860,7 @@ xfs_alloc_fix_freelist( (pag->pagf_longest - delta) : (pag->pagf_flcount > 0 || pag->pagf_longest > 0); if (args->minlen + args->alignment + args->minalignslop - 1 > longest || - (args->minleft && + (!(flags & XFS_ALLOC_FLAG_FREEING) && (int)(pag->pagf_freeblks + pag->pagf_flcount - need - args->total) < (int)args->minleft)) { @@ -1898,7 +1896,7 @@ xfs_alloc_fix_freelist( longest = (longest > delta) ? (longest - delta) : (be32_to_cpu(agf->agf_flcount) > 0 || longest > 0); if (args->minlen + args->alignment + args->minalignslop - 1 > longest || - (args->minleft && + (!(flags & XFS_ALLOC_FLAG_FREEING) && (int)(be32_to_cpu(agf->agf_freeblks) + be32_to_cpu(agf->agf_flcount) - need - args->total) < (int)args->minleft)) { @@ -1951,8 +1949,14 @@ xfs_alloc_fix_freelist( * the restrictions correctly. Can happen for free calls * on a completely full ag. */ - if (targs.agbno == NULLAGBLOCK) + if (targs.agbno == NULLAGBLOCK) { + if (!(flags & XFS_ALLOC_FLAG_FREEING)) { + xfs_trans_brelse(tp, agflbp); + args->agbp = NULL; + return 0; + } break; + } /* * Put each allocated block on the list. */ @@ -2360,8 +2364,19 @@ #endif if (args->agno == sagno && type == XFS_ALLOCTYPE_START_BNO) args->type = XFS_ALLOCTYPE_THIS_AG; - if (++(args->agno) == mp->m_sb.sb_agcount) - args->agno = 0; + /* + * For the first allocation, we can try any AG to get + * space. However, if we already have allocated a + * block, we don't want to try AGs whose number is below + * sagno. Otherwise, we may end up with out-of-order + * locking of AGF, which might cause deadlock. + */ + if (++(args->agno) == mp->m_sb.sb_agcount) { + if (args->firstblock != NULLFSBLOCK) + args->agno = sagno; + else + args->agno = 0; + } /* * Reached the starting a.g., must either be done * or switch to non-trylock mode. @@ -2443,7 +2458,7 @@ #endif args.minlen = args.minleft = args.minalignslop = 0; down_read(&args.mp->m_peraglock); args.pag = &args.mp->m_perag[args.agno]; - if ((error = xfs_alloc_fix_freelist(&args, 0))) + if ((error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING))) goto error0; #ifdef DEBUG ASSERT(args.agbp != NULL); diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h index 2d1f892..650591f 100644 --- a/fs/xfs/xfs_alloc.h +++ b/fs/xfs/xfs_alloc.h @@ -41,6 +41,7 @@ typedef enum xfs_alloctype * Flags for xfs_alloc_fix_freelist. */ #define XFS_ALLOC_FLAG_TRYLOCK 0x00000001 /* use trylock for buffer locking */ +#define XFS_ALLOC_FLAG_FREEING 0x00000002 /* indicate caller is freeing extents*/ /* * Argument structure for xfs_alloc routines. @@ -70,6 +71,7 @@ typedef struct xfs_alloc_arg { char wasfromfl; /* set if allocation is from freelist */ char isfl; /* set if is freelist blocks - !acctg */ char userdata; /* set if this is user data */ + xfs_fsblock_t firstblock; /* io first block allocated */ } xfs_alloc_arg_t; /* diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c index a1d92da..7446556 100644 --- a/fs/xfs/xfs_alloc_btree.c +++ b/fs/xfs/xfs_alloc_btree.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index b6e1e02..1a21010 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -27,7 +27,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -35,7 +34,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -1910,7 +1908,7 @@ xfs_attr_rmtval_get(xfs_da_args_t *args) error = xfs_bmapi(args->trans, args->dp, (xfs_fileoff_t)lblkno, args->rmtblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, - NULL, 0, map, &nmap, NULL); + NULL, 0, map, &nmap, NULL, NULL); if (error) return(error); ASSERT(nmap >= 1); @@ -1988,7 +1986,7 @@ xfs_attr_rmtval_set(xfs_da_args_t *args) XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA | XFS_BMAPI_WRITE, args->firstblock, args->total, &map, &nmap, - args->flist); + args->flist, NULL); if (!error) { error = xfs_bmap_finish(&args->trans, args->flist, *args->firstblock, &committed); @@ -2039,7 +2037,8 @@ xfs_attr_rmtval_set(xfs_da_args_t *args) error = xfs_bmapi(NULL, dp, (xfs_fileoff_t)lblkno, args->rmtblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, - args->firstblock, 0, &map, &nmap, NULL); + args->firstblock, 0, &map, &nmap, + NULL, NULL); if (error) { return(error); } @@ -2104,7 +2103,7 @@ xfs_attr_rmtval_remove(xfs_da_args_t *ar args->rmtblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, args->firstblock, 0, &map, &nmap, - args->flist); + args->flist, NULL); if (error) { return(error); } @@ -2142,7 +2141,8 @@ xfs_attr_rmtval_remove(xfs_da_args_t *ar XFS_BMAP_INIT(args->flist, args->firstblock); error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, - 1, args->firstblock, args->flist, &done); + 1, args->firstblock, args->flist, + NULL, &done); if (!error) { error = xfs_bmap_finish(&args->trans, args->flist, *args->firstblock, &committed); @@ -2322,56 +2322,56 @@ #endif /* XFS_ATTR_TRACE */ STATIC int posix_acl_access_set( - vnode_t *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { return xfs_acl_vset(vp, data, size, _ACL_TYPE_ACCESS); } STATIC int posix_acl_access_remove( - struct vnode *vp, char *name, int xflags) + bhv_vnode_t *vp, char *name, int xflags) { return xfs_acl_vremove(vp, _ACL_TYPE_ACCESS); } STATIC int posix_acl_access_get( - vnode_t *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { return xfs_acl_vget(vp, data, size, _ACL_TYPE_ACCESS); } STATIC int posix_acl_access_exists( - vnode_t *vp) + bhv_vnode_t *vp) { return xfs_acl_vhasacl_access(vp); } STATIC int posix_acl_default_set( - vnode_t *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { return xfs_acl_vset(vp, data, size, _ACL_TYPE_DEFAULT); } STATIC int posix_acl_default_get( - vnode_t *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { return xfs_acl_vget(vp, data, size, _ACL_TYPE_DEFAULT); } STATIC int posix_acl_default_remove( - struct vnode *vp, char *name, int xflags) + bhv_vnode_t *vp, char *name, int xflags) { return xfs_acl_vremove(vp, _ACL_TYPE_DEFAULT); } STATIC int posix_acl_default_exists( - vnode_t *vp) + bhv_vnode_t *vp) { return xfs_acl_vhasacl_default(vp); } @@ -2404,21 +2404,18 @@ STATIC struct attrnames *attr_system_nam STATIC int attr_generic_set( - struct vnode *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { - int error; - - VOP_ATTR_SET(vp, name, data, size, xflags, NULL, error); - return -error; + return -bhv_vop_attr_set(vp, name, data, size, xflags, NULL); } STATIC int attr_generic_get( - struct vnode *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { int error, asize = size; - VOP_ATTR_GET(vp, name, data, &asize, xflags, NULL, error); + error = bhv_vop_attr_get(vp, name, data, &asize, xflags, NULL); if (!error) return asize; return -error; @@ -2426,12 +2423,9 @@ attr_generic_get( STATIC int attr_generic_remove( - struct vnode *vp, char *name, int xflags) + bhv_vnode_t *vp, char *name, int xflags) { - int error; - - VOP_ATTR_REMOVE(vp, name, xflags, NULL, error); - return -error; + return -bhv_vop_attr_remove(vp, name, xflags, NULL); } STATIC int @@ -2459,7 +2453,7 @@ attr_generic_listadd( STATIC int attr_system_list( - struct vnode *vp, + bhv_vnode_t *vp, void *data, size_t size, ssize_t *result) @@ -2481,12 +2475,12 @@ attr_system_list( int attr_generic_list( - struct vnode *vp, void *data, size_t size, int xflags, ssize_t *result) + bhv_vnode_t *vp, void *data, size_t size, int xflags, ssize_t *result) { attrlist_cursor_kern_t cursor = { 0 }; int error; - VOP_ATTR_LIST(vp, data, size, xflags, &cursor, NULL, error); + error = bhv_vop_attr_list(vp, data, size, xflags, &cursor, NULL); if (error > 0) return -error; *result = -error; @@ -2514,7 +2508,7 @@ attr_lookup_namespace( */ STATIC int attr_user_capable( - struct vnode *vp, + bhv_vnode_t *vp, cred_t *cred) { struct inode *inode = vn_to_inode(vp); @@ -2532,7 +2526,7 @@ attr_user_capable( STATIC int attr_trusted_capable( - struct vnode *vp, + bhv_vnode_t *vp, cred_t *cred) { struct inode *inode = vn_to_inode(vp); @@ -2546,7 +2540,7 @@ attr_trusted_capable( STATIC int attr_secure_capable( - struct vnode *vp, + bhv_vnode_t *vp, cred_t *cred) { return -ENOSECURITY; @@ -2554,7 +2548,7 @@ attr_secure_capable( STATIC int attr_system_set( - struct vnode *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { attrnames_t *namesp; int error; @@ -2573,7 +2567,7 @@ attr_system_set( STATIC int attr_system_get( - struct vnode *vp, char *name, void *data, size_t size, int xflags) + bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags) { attrnames_t *namesp; @@ -2585,7 +2579,7 @@ attr_system_get( STATIC int attr_system_remove( - struct vnode *vp, char *name, int xflags) + bhv_vnode_t *vp, char *name, int xflags) { attrnames_t *namesp; diff --git a/fs/xfs/xfs_attr.h b/fs/xfs/xfs_attr.h index b2c7b9f..981633f 100644 --- a/fs/xfs/xfs_attr.h +++ b/fs/xfs/xfs_attr.h @@ -36,13 +36,13 @@ #define __XFS_ATTR_H__ *========================================================================*/ struct cred; -struct vnode; +struct bhv_vnode; -typedef int (*attrset_t)(struct vnode *, char *, void *, size_t, int); -typedef int (*attrget_t)(struct vnode *, char *, void *, size_t, int); -typedef int (*attrremove_t)(struct vnode *, char *, int); -typedef int (*attrexists_t)(struct vnode *); -typedef int (*attrcapable_t)(struct vnode *, struct cred *); +typedef int (*attrset_t)(struct bhv_vnode *, char *, void *, size_t, int); +typedef int (*attrget_t)(struct bhv_vnode *, char *, void *, size_t, int); +typedef int (*attrremove_t)(struct bhv_vnode *, char *, int); +typedef int (*attrexists_t)(struct bhv_vnode *); +typedef int (*attrcapable_t)(struct bhv_vnode *, struct cred *); typedef struct attrnames { char * attr_name; @@ -63,7 +63,7 @@ extern struct attrnames attr_trusted; extern struct attrnames *attr_namespaces[ATTR_NAMECOUNT]; extern attrnames_t *attr_lookup_namespace(char *, attrnames_t **, int); -extern int attr_generic_list(struct vnode *, void *, size_t, int, ssize_t *); +extern int attr_generic_list(struct bhv_vnode *, void *, size_t, int, ssize_t *); #define ATTR_DONTFOLLOW 0x0001 /* -- unused, from IRIX -- */ #define ATTR_ROOT 0x0002 /* use attrs in root (trusted) namespace */ diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index 9462be8..9455051 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -24,7 +24,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -34,7 +33,6 @@ #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" #include "xfs_alloc.h" #include "xfs_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -2990,7 +2988,7 @@ xfs_attr_leaf_freextent(xfs_trans_t **tr nmap = 1; error = xfs_bmapi(*trans, dp, (xfs_fileoff_t)tblkno, tblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, - NULL, 0, &map, &nmap, NULL); + NULL, 0, &map, &nmap, NULL, NULL); if (error) { return(error); } diff --git a/fs/xfs/xfs_behavior.h b/fs/xfs/xfs_behavior.h index 1d8ff10..6e6e56f 100644 --- a/fs/xfs/xfs_behavior.h +++ b/fs/xfs/xfs_behavior.h @@ -78,15 +78,12 @@ #define __XFS_BEHAVIOR_H__ * */ -struct bhv_head_lock; - /* * Behavior head. Head of the chain of behaviors. * Contained within each virtualized object data structure. */ typedef struct bhv_head { struct bhv_desc *bh_first; /* first behavior in chain */ - struct bhv_head_lock *bh_lockp; /* pointer to lock info struct */ } bhv_head_t; /* diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 26939d3..3a61375 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -24,13 +24,11 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -40,13 +38,15 @@ #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_ialloc.h" #include "xfs_itable.h" +#include "xfs_dir2_data.h" +#include "xfs_dir2_leaf.h" +#include "xfs_dir2_block.h" #include "xfs_inode_item.h" #include "xfs_extfree_item.h" #include "xfs_alloc.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" #include "xfs_error.h" -#include "xfs_dir_leaf.h" #include "xfs_attr_leaf.h" #include "xfs_rw.h" #include "xfs_quota.h" @@ -101,6 +101,7 @@ xfs_bmap_add_extent( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd); /* OK to allocate reserved blocks */ @@ -118,6 +119,7 @@ xfs_bmap_add_extent_delay_real( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd); /* OK to allocate reserved blocks */ /* @@ -131,6 +133,7 @@ xfs_bmap_add_extent_hole_delay( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp,/* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd); /* OK to allocate reserved blocks */ /* @@ -144,6 +147,7 @@ xfs_bmap_add_extent_hole_real( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork); /* data or attr fork */ /* @@ -156,7 +160,8 @@ xfs_bmap_add_extent_unwritten_real( xfs_extnum_t idx, /* extent number to update/insert */ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ - int *logflagsp); /* inode logging flags */ + int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta); /* Change made to incore extents */ /* * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file. @@ -203,6 +208,7 @@ xfs_bmap_del_extent( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp,/* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd); /* OK to allocate reserved blocks */ @@ -510,7 +516,7 @@ xfs_bmap_add_attrfork_local( dargs.total = mp->m_dirblkfsbs; dargs.whichfork = XFS_DATA_FORK; dargs.trans = tp; - error = XFS_DIR_SHORTFORM_TO_SINGLE(mp, &dargs); + error = xfs_dir2_sf_to_block(&dargs); } else error = xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags, XFS_DATA_FORK); @@ -530,6 +536,7 @@ xfs_bmap_add_extent( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd) /* OK to use reserved data blocks */ { @@ -567,6 +574,15 @@ #endif logflags = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); } else logflags = 0; + /* DELTA: single new extent */ + if (delta) { + if (delta->xed_startoff > new->br_startoff) + delta->xed_startoff = new->br_startoff; + if (delta->xed_blockcount < + new->br_startoff + new->br_blockcount) + delta->xed_blockcount = new->br_startoff + + new->br_blockcount; + } } /* * Any kind of new delayed allocation goes here. @@ -576,7 +592,7 @@ #endif ASSERT((cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL) == 0); if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, cur, new, - &logflags, rsvd))) + &logflags, delta, rsvd))) goto done; } /* @@ -587,7 +603,7 @@ #endif ASSERT((cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL) == 0); if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new, - &logflags, whichfork))) + &logflags, delta, whichfork))) goto done; } else { xfs_bmbt_irec_t prev; /* old extent at offset idx */ @@ -612,17 +628,17 @@ #endif XFS_BTCUR_BPRV_WASDEL); if ((error = xfs_bmap_add_extent_delay_real(ip, idx, &cur, new, &da_new, first, flist, - &logflags, rsvd))) + &logflags, delta, rsvd))) goto done; } else if (new->br_state == XFS_EXT_NORM) { ASSERT(new->br_state == XFS_EXT_NORM); if ((error = xfs_bmap_add_extent_unwritten_real( - ip, idx, &cur, new, &logflags))) + ip, idx, &cur, new, &logflags, delta))) goto done; } else { ASSERT(new->br_state == XFS_EXT_UNWRITTEN); if ((error = xfs_bmap_add_extent_unwritten_real( - ip, idx, &cur, new, &logflags))) + ip, idx, &cur, new, &logflags, delta))) goto done; } ASSERT(*curp == cur || *curp == NULL); @@ -635,7 +651,7 @@ #endif ASSERT((cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL) == 0); if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, - new, &logflags, whichfork))) + new, &logflags, delta, whichfork))) goto done; } } @@ -700,6 +716,7 @@ xfs_bmap_add_extent_delay_real( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd) /* OK to use reserved data block allocation */ { xfs_btree_cur_t *cur; /* btree cursor */ @@ -716,8 +733,8 @@ #endif /* left is 0, right is 1, prev is 2 */ int rval=0; /* return value (logging flags) */ int state = 0;/* state bits, accessed thru macros */ - xfs_filblks_t temp; /* value for dnew calculations */ - xfs_filblks_t temp2; /* value for dnew calculations */ + xfs_filblks_t temp=0; /* value for dnew calculations */ + xfs_filblks_t temp2=0;/* value for dnew calculations */ int tmp_rval; /* partial logging flags */ enum { /* bit number definitions for state */ LEFT_CONTIG, RIGHT_CONTIG, @@ -839,6 +856,11 @@ #define SWITCH_STATE \ goto done; } *dnew = 0; + /* DELTA: Three in-core extents are replaced by one. */ + temp = LEFT.br_startoff; + temp2 = LEFT.br_blockcount + + PREV.br_blockcount + + RIGHT.br_blockcount; break; case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG): @@ -872,6 +894,10 @@ #define SWITCH_STATE \ goto done; } *dnew = 0; + /* DELTA: Two in-core extents are replaced by one. */ + temp = LEFT.br_startoff; + temp2 = LEFT.br_blockcount + + PREV.br_blockcount; break; case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG): @@ -906,6 +932,10 @@ #define SWITCH_STATE \ goto done; } *dnew = 0; + /* DELTA: Two in-core extents are replaced by one. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount + + RIGHT.br_blockcount; break; case MASK2(LEFT_FILLING, RIGHT_FILLING): @@ -936,6 +966,9 @@ #define SWITCH_STATE \ ASSERT(i == 1); } *dnew = 0; + /* DELTA: The in-core extent described by new changed type. */ + temp = new->br_startoff; + temp2 = new->br_blockcount; break; case MASK2(LEFT_FILLING, LEFT_CONTIG): @@ -978,6 +1011,10 @@ #define SWITCH_STATE \ xfs_bmap_trace_post_update(fname, "LF|LC", ip, idx, XFS_DATA_FORK); *dnew = temp; + /* DELTA: The boundary between two in-core extents moved. */ + temp = LEFT.br_startoff; + temp2 = LEFT.br_blockcount + + PREV.br_blockcount; break; case MASK(LEFT_FILLING): @@ -1025,6 +1062,9 @@ #define SWITCH_STATE \ xfs_bmap_trace_post_update(fname, "LF", ip, idx + 1, XFS_DATA_FORK); *dnew = temp; + /* DELTA: One in-core extent is split in two. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount; break; case MASK2(RIGHT_FILLING, RIGHT_CONTIG): @@ -1067,6 +1107,10 @@ #define SWITCH_STATE \ xfs_bmap_trace_post_update(fname, "RF|RC", ip, idx, XFS_DATA_FORK); *dnew = temp; + /* DELTA: The boundary between two in-core extents moved. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount + + RIGHT.br_blockcount; break; case MASK(RIGHT_FILLING): @@ -1112,6 +1156,9 @@ #define SWITCH_STATE \ xfs_bmbt_set_startblock(ep, NULLSTARTBLOCK((int)temp)); xfs_bmap_trace_post_update(fname, "RF", ip, idx, XFS_DATA_FORK); *dnew = temp; + /* DELTA: One in-core extent is split in two. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount; break; case 0: @@ -1194,6 +1241,9 @@ #define SWITCH_STATE \ xfs_bmap_trace_post_update(fname, "0", ip, idx + 2, XFS_DATA_FORK); *dnew = temp + temp2; + /* DELTA: One in-core extent is split in three. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount; break; case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): @@ -1209,6 +1259,13 @@ #define SWITCH_STATE \ ASSERT(0); } *curp = cur; + if (delta) { + temp2 += temp; + if (delta->xed_startoff > temp) + delta->xed_startoff = temp; + if (delta->xed_blockcount < temp2) + delta->xed_blockcount = temp2; + } done: *logflagsp = rval; return error; @@ -1235,7 +1292,8 @@ xfs_bmap_add_extent_unwritten_real( xfs_extnum_t idx, /* extent number to update/insert */ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ - int *logflagsp) /* inode logging flags */ + int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta) /* Change made to incore extents */ { xfs_btree_cur_t *cur; /* btree cursor */ xfs_bmbt_rec_t *ep; /* extent entry for idx */ @@ -1252,6 +1310,8 @@ #endif /* left is 0, right is 1, prev is 2 */ int rval=0; /* return value (logging flags) */ int state = 0;/* state bits, accessed thru macros */ + xfs_filblks_t temp=0; + xfs_filblks_t temp2=0; enum { /* bit number definitions for state */ LEFT_CONTIG, RIGHT_CONTIG, LEFT_FILLING, RIGHT_FILLING, @@ -1380,6 +1440,11 @@ #define SWITCH_STATE \ RIGHT.br_blockcount, LEFT.br_state))) goto done; } + /* DELTA: Three in-core extents are replaced by one. */ + temp = LEFT.br_startoff; + temp2 = LEFT.br_blockcount + + PREV.br_blockcount + + RIGHT.br_blockcount; break; case MASK3(LEFT_FILLING, RIGHT_FILLING, LEFT_CONTIG): @@ -1419,6 +1484,10 @@ #define SWITCH_STATE \ LEFT.br_state))) goto done; } + /* DELTA: Two in-core extents are replaced by one. */ + temp = LEFT.br_startoff; + temp2 = LEFT.br_blockcount + + PREV.br_blockcount; break; case MASK3(LEFT_FILLING, RIGHT_FILLING, RIGHT_CONTIG): @@ -1459,6 +1528,10 @@ #define SWITCH_STATE \ newext))) goto done; } + /* DELTA: Two in-core extents are replaced by one. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount + + RIGHT.br_blockcount; break; case MASK2(LEFT_FILLING, RIGHT_FILLING): @@ -1487,6 +1560,9 @@ #define SWITCH_STATE \ newext))) goto done; } + /* DELTA: The in-core extent described by new changed type. */ + temp = new->br_startoff; + temp2 = new->br_blockcount; break; case MASK2(LEFT_FILLING, LEFT_CONTIG): @@ -1534,6 +1610,10 @@ #define SWITCH_STATE \ LEFT.br_state)) goto done; } + /* DELTA: The boundary between two in-core extents moved. */ + temp = LEFT.br_startoff; + temp2 = LEFT.br_blockcount + + PREV.br_blockcount; break; case MASK(LEFT_FILLING): @@ -1574,6 +1654,9 @@ #define SWITCH_STATE \ goto done; ASSERT(i == 1); } + /* DELTA: One in-core extent is split in two. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount; break; case MASK2(RIGHT_FILLING, RIGHT_CONTIG): @@ -1617,6 +1700,10 @@ #define SWITCH_STATE \ newext))) goto done; } + /* DELTA: The boundary between two in-core extents moved. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount + + RIGHT.br_blockcount; break; case MASK(RIGHT_FILLING): @@ -1657,6 +1744,9 @@ #define SWITCH_STATE \ goto done; ASSERT(i == 1); } + /* DELTA: One in-core extent is split in two. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount; break; case 0: @@ -1710,6 +1800,9 @@ #define SWITCH_STATE \ goto done; ASSERT(i == 1); } + /* DELTA: One in-core extent is split in three. */ + temp = PREV.br_startoff; + temp2 = PREV.br_blockcount; break; case MASK3(LEFT_FILLING, LEFT_CONTIG, RIGHT_CONTIG): @@ -1725,6 +1818,13 @@ #define SWITCH_STATE \ ASSERT(0); } *curp = cur; + if (delta) { + temp2 += temp; + if (delta->xed_startoff > temp) + delta->xed_startoff = temp; + if (delta->xed_blockcount < temp2) + delta->xed_blockcount = temp2; + } done: *logflagsp = rval; return error; @@ -1753,6 +1853,7 @@ xfs_bmap_add_extent_hole_delay( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd) /* OK to allocate reserved blocks */ { xfs_bmbt_rec_t *ep; /* extent record for idx */ @@ -1765,7 +1866,8 @@ #endif xfs_filblks_t oldlen=0; /* old indirect size */ xfs_bmbt_irec_t right; /* right neighbor extent entry */ int state; /* state bits, accessed thru macros */ - xfs_filblks_t temp; /* temp for indirect calculations */ + xfs_filblks_t temp=0; /* temp for indirect calculations */ + xfs_filblks_t temp2=0; enum { /* bit number definitions for state */ LEFT_CONTIG, RIGHT_CONTIG, LEFT_DELAY, RIGHT_DELAY, @@ -1844,6 +1946,9 @@ #define SWITCH_STATE (state & MASK2(LEF XFS_DATA_FORK); xfs_iext_remove(ifp, idx, 1); ip->i_df.if_lastex = idx - 1; + /* DELTA: Two in-core extents were replaced by one. */ + temp2 = temp; + temp = left.br_startoff; break; case MASK(LEFT_CONTIG): @@ -1864,6 +1969,9 @@ #define SWITCH_STATE (state & MASK2(LEF xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, XFS_DATA_FORK); ip->i_df.if_lastex = idx - 1; + /* DELTA: One in-core extent grew into a hole. */ + temp2 = temp; + temp = left.br_startoff; break; case MASK(RIGHT_CONTIG): @@ -1881,6 +1989,9 @@ #define SWITCH_STATE (state & MASK2(LEF NULLSTARTBLOCK((int)newlen), temp, right.br_state); xfs_bmap_trace_post_update(fname, "RC", ip, idx, XFS_DATA_FORK); ip->i_df.if_lastex = idx; + /* DELTA: One in-core extent grew into a hole. */ + temp2 = temp; + temp = new->br_startoff; break; case 0: @@ -1894,6 +2005,9 @@ #define SWITCH_STATE (state & MASK2(LEF XFS_DATA_FORK); xfs_iext_insert(ifp, idx, 1, new); ip->i_df.if_lastex = idx; + /* DELTA: A new in-core extent was added in a hole. */ + temp2 = new->br_blockcount; + temp = new->br_startoff; break; } if (oldlen != newlen) { @@ -1904,6 +2018,13 @@ #define SWITCH_STATE (state & MASK2(LEF * Nothing to do for disk quota accounting here. */ } + if (delta) { + temp2 += temp; + if (delta->xed_startoff > temp) + delta->xed_startoff = temp; + if (delta->xed_blockcount < temp2) + delta->xed_blockcount = temp2; + } *logflagsp = 0; return 0; #undef MASK @@ -1925,6 +2046,7 @@ xfs_bmap_add_extent_hole_real( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork) /* data or attr fork */ { xfs_bmbt_rec_t *ep; /* pointer to extent entry ins. point */ @@ -1936,7 +2058,10 @@ #endif xfs_ifork_t *ifp; /* inode fork pointer */ xfs_bmbt_irec_t left; /* left neighbor extent entry */ xfs_bmbt_irec_t right; /* right neighbor extent entry */ + int rval=0; /* return value (logging flags) */ int state; /* state bits, accessed thru macros */ + xfs_filblks_t temp=0; + xfs_filblks_t temp2=0; enum { /* bit number definitions for state */ LEFT_CONTIG, RIGHT_CONTIG, LEFT_DELAY, RIGHT_DELAY, @@ -1993,6 +2118,7 @@ #define SWITCH_STATE (state & MASK2(LEF left.br_blockcount + new->br_blockcount + right.br_blockcount <= MAXEXTLEN)); + error = 0; /* * Select which case we're in here, and implement it. */ @@ -2018,25 +2144,35 @@ #define SWITCH_STATE (state & MASK2(LEF XFS_IFORK_NEXT_SET(ip, whichfork, XFS_IFORK_NEXTENTS(ip, whichfork) - 1); if (cur == NULL) { - *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); - return 0; + rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); + } else { + rval = XFS_ILOG_CORE; + if ((error = xfs_bmbt_lookup_eq(cur, + right.br_startoff, + right.br_startblock, + right.br_blockcount, &i))) + goto done; + ASSERT(i == 1); + if ((error = xfs_bmbt_delete(cur, &i))) + goto done; + ASSERT(i == 1); + if ((error = xfs_bmbt_decrement(cur, 0, &i))) + goto done; + ASSERT(i == 1); + if ((error = xfs_bmbt_update(cur, left.br_startoff, + left.br_startblock, + left.br_blockcount + + new->br_blockcount + + right.br_blockcount, + left.br_state))) + goto done; } - *logflagsp = XFS_ILOG_CORE; - if ((error = xfs_bmbt_lookup_eq(cur, right.br_startoff, - right.br_startblock, right.br_blockcount, &i))) - return error; - ASSERT(i == 1); - if ((error = xfs_bmbt_delete(cur, &i))) - return error; - ASSERT(i == 1); - if ((error = xfs_bmbt_decrement(cur, 0, &i))) - return error; - ASSERT(i == 1); - error = xfs_bmbt_update(cur, left.br_startoff, - left.br_startblock, - left.br_blockcount + new->br_blockcount + - right.br_blockcount, left.br_state); - return error; + /* DELTA: Two in-core extents were replaced by one. */ + temp = left.br_startoff; + temp2 = left.br_blockcount + + new->br_blockcount + + right.br_blockcount; + break; case MASK(LEFT_CONTIG): /* @@ -2050,19 +2186,27 @@ #define SWITCH_STATE (state & MASK2(LEF xfs_bmap_trace_post_update(fname, "LC", ip, idx - 1, whichfork); ifp->if_lastex = idx - 1; if (cur == NULL) { - *logflagsp = XFS_ILOG_FEXT(whichfork); - return 0; + rval = XFS_ILOG_FEXT(whichfork); + } else { + rval = 0; + if ((error = xfs_bmbt_lookup_eq(cur, + left.br_startoff, + left.br_startblock, + left.br_blockcount, &i))) + goto done; + ASSERT(i == 1); + if ((error = xfs_bmbt_update(cur, left.br_startoff, + left.br_startblock, + left.br_blockcount + + new->br_blockcount, + left.br_state))) + goto done; } - *logflagsp = 0; - if ((error = xfs_bmbt_lookup_eq(cur, left.br_startoff, - left.br_startblock, left.br_blockcount, &i))) - return error; - ASSERT(i == 1); - error = xfs_bmbt_update(cur, left.br_startoff, - left.br_startblock, - left.br_blockcount + new->br_blockcount, - left.br_state); - return error; + /* DELTA: One in-core extent grew. */ + temp = left.br_startoff; + temp2 = left.br_blockcount + + new->br_blockcount; + break; case MASK(RIGHT_CONTIG): /* @@ -2077,19 +2221,27 @@ #define SWITCH_STATE (state & MASK2(LEF xfs_bmap_trace_post_update(fname, "RC", ip, idx, whichfork); ifp->if_lastex = idx; if (cur == NULL) { - *logflagsp = XFS_ILOG_FEXT(whichfork); - return 0; + rval = XFS_ILOG_FEXT(whichfork); + } else { + rval = 0; + if ((error = xfs_bmbt_lookup_eq(cur, + right.br_startoff, + right.br_startblock, + right.br_blockcount, &i))) + goto done; + ASSERT(i == 1); + if ((error = xfs_bmbt_update(cur, new->br_startoff, + new->br_startblock, + new->br_blockcount + + right.br_blockcount, + right.br_state))) + goto done; } - *logflagsp = 0; - if ((error = xfs_bmbt_lookup_eq(cur, right.br_startoff, - right.br_startblock, right.br_blockcount, &i))) - return error; - ASSERT(i == 1); - error = xfs_bmbt_update(cur, new->br_startoff, - new->br_startblock, - new->br_blockcount + right.br_blockcount, - right.br_state); - return error; + /* DELTA: One in-core extent grew. */ + temp = new->br_startoff; + temp2 = new->br_blockcount + + right.br_blockcount; + break; case 0: /* @@ -2104,29 +2256,41 @@ #define SWITCH_STATE (state & MASK2(LEF XFS_IFORK_NEXT_SET(ip, whichfork, XFS_IFORK_NEXTENTS(ip, whichfork) + 1); if (cur == NULL) { - *logflagsp = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); - return 0; + rval = XFS_ILOG_CORE | XFS_ILOG_FEXT(whichfork); + } else { + rval = XFS_ILOG_CORE; + if ((error = xfs_bmbt_lookup_eq(cur, + new->br_startoff, + new->br_startblock, + new->br_blockcount, &i))) + goto done; + ASSERT(i == 0); + cur->bc_rec.b.br_state = new->br_state; + if ((error = xfs_bmbt_insert(cur, &i))) + goto done; + ASSERT(i == 1); } - *logflagsp = XFS_ILOG_CORE; - if ((error = xfs_bmbt_lookup_eq(cur, new->br_startoff, - new->br_startblock, new->br_blockcount, &i))) - return error; - ASSERT(i == 0); - cur->bc_rec.b.br_state = new->br_state; - if ((error = xfs_bmbt_insert(cur, &i))) - return error; - ASSERT(i == 1); - return 0; + /* DELTA: A new extent was added in a hole. */ + temp = new->br_startoff; + temp2 = new->br_blockcount; + break; + } + if (delta) { + temp2 += temp; + if (delta->xed_startoff > temp) + delta->xed_startoff = temp; + if (delta->xed_blockcount < temp2) + delta->xed_blockcount = temp2; } +done: + *logflagsp = rval; + return error; #undef MASK #undef MASK2 #undef STATE_SET #undef STATE_TEST #undef STATE_SET_TEST #undef SWITCH_STATE - /* NOTREACHED */ - ASSERT(0); - return 0; /* keep gcc quite */ } /* @@ -2598,6 +2762,7 @@ xfs_bmap_btalloc( args.mp = mp; args.fsbno = ap->rval; args.maxlen = MIN(ap->alen, mp->m_sb.sb_agblocks); + args.firstblock = ap->firstblock; blen = 0; if (nullfb) { args.type = XFS_ALLOCTYPE_START_BNO; @@ -2657,7 +2822,7 @@ xfs_bmap_btalloc( else args.minlen = ap->alen; } else if (ap->low) { - args.type = XFS_ALLOCTYPE_FIRST_AG; + args.type = XFS_ALLOCTYPE_START_BNO; args.total = args.minlen = ap->minlen; } else { args.type = XFS_ALLOCTYPE_NEAR_BNO; @@ -2669,7 +2834,7 @@ xfs_bmap_btalloc( args.prod = ap->ip->i_d.di_extsize; if ((args.mod = (xfs_extlen_t)do_mod(ap->off, args.prod))) args.mod = (xfs_extlen_t)(args.prod - args.mod); - } else if (unlikely(mp->m_sb.sb_blocksize >= NBPP)) { + } else if (mp->m_sb.sb_blocksize >= NBPP) { args.prod = 1; args.mod = 0; } else { @@ -2885,6 +3050,7 @@ xfs_bmap_del_extent( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *del, /* data to remove from extents */ int *logflagsp, /* inode logging flags */ + xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd) /* OK to allocate reserved blocks */ { @@ -3193,6 +3359,14 @@ #endif if (da_old > da_new) xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, (int)(da_old - da_new), rsvd); + if (delta) { + /* DELTA: report the original extent. */ + if (delta->xed_startoff > got.br_startoff) + delta->xed_startoff = got.br_startoff; + if (delta->xed_blockcount < got.br_startoff+got.br_blockcount) + delta->xed_blockcount = got.br_startoff + + got.br_blockcount; + } done: *logflagsp = flags; return error; @@ -3279,6 +3453,7 @@ xfs_bmap_extents_to_btree( XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_BTREE); args.tp = tp; args.mp = mp; + args.firstblock = *firstblock; if (*firstblock == NULLFSBLOCK) { args.type = XFS_ALLOCTYPE_START_BNO; args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino); @@ -3414,6 +3589,7 @@ #endif args.tp = tp; args.mp = ip->i_mount; + args.firstblock = *firstblock; ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE); /* @@ -3753,7 +3929,7 @@ xfs_bunmap_trace( if (ip->i_rwtrace == NULL) return; ktrace_enter(ip->i_rwtrace, - (void *)(__psint_t)XFS_BUNMAPI, + (void *)(__psint_t)XFS_BUNMAP, (void *)ip, (void *)(__psint_t)((ip->i_d.di_size >> 32) & 0xffffffff), (void *)(__psint_t)(ip->i_d.di_size & 0xffffffff), @@ -4087,8 +4263,8 @@ xfs_bmap_finish( if (!XFS_FORCED_SHUTDOWN(mp)) xfs_force_shutdown(mp, (error == EFSCORRUPTED) ? - XFS_CORRUPT_INCORE : - XFS_METADATA_IO_ERROR); + SHUTDOWN_CORRUPT_INCORE : + SHUTDOWN_META_IO_ERROR); return error; } xfs_trans_log_efd_extent(ntp, efd, free->xbfi_startblock, @@ -4538,7 +4714,8 @@ xfs_bmapi( xfs_extlen_t total, /* total blocks needed */ xfs_bmbt_irec_t *mval, /* output: map values */ int *nmap, /* i/o: mval size/count */ - xfs_bmap_free_t *flist) /* i/o: list extents to free */ + xfs_bmap_free_t *flist, /* i/o: list extents to free */ + xfs_extdelta_t *delta) /* o: change made to incore extents */ { xfs_fsblock_t abno; /* allocated block number */ xfs_extlen_t alen; /* allocated extent length */ @@ -4650,6 +4827,10 @@ #endif end = bno + len; obno = bno; bma.ip = NULL; + if (delta) { + delta->xed_startoff = NULLFILEOFF; + delta->xed_blockcount = 0; + } while (bno < end && n < *nmap) { /* * Reading past eof, act as though there's a hole @@ -4886,8 +5067,8 @@ #endif got.br_state = XFS_EXT_UNWRITTEN; } error = xfs_bmap_add_extent(ip, lastx, &cur, &got, - firstblock, flist, &tmp_logflags, whichfork, - (flags & XFS_BMAPI_RSVBLOCKS)); + firstblock, flist, &tmp_logflags, delta, + whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); logflags |= tmp_logflags; if (error) goto error0; @@ -4983,8 +5164,8 @@ #endif } mval->br_state = XFS_EXT_NORM; error = xfs_bmap_add_extent(ip, lastx, &cur, mval, - firstblock, flist, &tmp_logflags, whichfork, - (flags & XFS_BMAPI_RSVBLOCKS)); + firstblock, flist, &tmp_logflags, delta, + whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); logflags |= tmp_logflags; if (error) goto error0; @@ -5073,7 +5254,14 @@ #endif ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE || XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max); error = 0; - + if (delta && delta->xed_startoff != NULLFILEOFF) { + /* A change was actually made. + * Note that delta->xed_blockount is an offset at this + * point and needs to be converted to a block count. + */ + ASSERT(delta->xed_blockcount > delta->xed_startoff); + delta->xed_blockcount -= delta->xed_startoff; + } error0: /* * Log everything. Do this after conversion, there's no point in @@ -5185,6 +5373,8 @@ xfs_bunmapi( xfs_fsblock_t *firstblock, /* first allocated block controls a.g. for allocs */ xfs_bmap_free_t *flist, /* i/o: list extents to free */ + xfs_extdelta_t *delta, /* o: change made to incore + extents */ int *done) /* set if not done yet */ { xfs_btree_cur_t *cur; /* bmap btree cursor */ @@ -5242,6 +5432,10 @@ xfs_bunmapi( bno = start + len - 1; ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, &prev); + if (delta) { + delta->xed_startoff = NULLFILEOFF; + delta->xed_blockcount = 0; + } /* * Check to see if the given block number is past the end of the * file, back up to the last block if so... @@ -5340,7 +5534,8 @@ xfs_bunmapi( } del.br_state = XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx, &cur, &del, - firstblock, flist, &logflags, XFS_DATA_FORK, 0); + firstblock, flist, &logflags, delta, + XFS_DATA_FORK, 0); if (error) goto error0; goto nodelete; @@ -5394,7 +5589,7 @@ xfs_bunmapi( prev.br_state = XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx - 1, &cur, &prev, firstblock, flist, &logflags, - XFS_DATA_FORK, 0); + delta, XFS_DATA_FORK, 0); if (error) goto error0; goto nodelete; @@ -5403,7 +5598,7 @@ xfs_bunmapi( del.br_state = XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx, &cur, &del, firstblock, flist, &logflags, - XFS_DATA_FORK, 0); + delta, XFS_DATA_FORK, 0); if (error) goto error0; goto nodelete; @@ -5456,7 +5651,7 @@ xfs_bunmapi( goto error0; } error = xfs_bmap_del_extent(ip, tp, lastx, flist, cur, &del, - &tmp_logflags, whichfork, rsvd); + &tmp_logflags, delta, whichfork, rsvd); logflags |= tmp_logflags; if (error) goto error0; @@ -5513,6 +5708,14 @@ nodelete: ASSERT(ifp->if_ext_max == XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); error = 0; + if (delta && delta->xed_startoff != NULLFILEOFF) { + /* A change was actually made. + * Note that delta->xed_blockount is an offset at this + * point and needs to be converted to a block count. + */ + ASSERT(delta->xed_blockcount > delta->xed_startoff); + delta->xed_blockcount -= delta->xed_startoff; + } error0: /* * Log everything. Do this after conversion, there's no point in @@ -5556,7 +5759,7 @@ xfs_getbmap( __int64_t fixlen; /* length for -1 case */ int i; /* extent number */ xfs_inode_t *ip; /* xfs incore inode pointer */ - vnode_t *vp; /* corresponding vnode */ + bhv_vnode_t *vp; /* corresponding vnode */ int lock; /* lock state */ xfs_bmbt_irec_t *map; /* buffer for user's data */ xfs_mount_t *mp; /* file system mount point */ @@ -5653,7 +5856,7 @@ xfs_getbmap( if (whichfork == XFS_DATA_FORK && ip->i_delayed_blks) { /* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */ - VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error); + error = bhv_vop_flush_pages(vp, (xfs_off_t)0, -1, 0, FI_REMAPF); } ASSERT(whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0); @@ -5689,7 +5892,8 @@ xfs_getbmap( nmap = (nexleft > subnex) ? subnex : nexleft; error = xfs_bmapi(NULL, ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset), XFS_BB_TO_FSB(mp, bmv->bmv_length), - bmapi_flags, NULL, 0, map, &nmap, NULL); + bmapi_flags, NULL, 0, map, &nmap, + NULL, NULL); if (error) goto unlock_and_return; ASSERT(nmap <= subnex); diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h index 8e0d73d..80e9340 100644 --- a/fs/xfs/xfs_bmap.h +++ b/fs/xfs/xfs_bmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -26,6 +26,20 @@ struct xfs_mount; struct xfs_trans; /* + * DELTA: describe a change to the in-core extent list. + * + * Internally the use of xed_blockount is somewhat funky. + * xed_blockcount contains an offset much of the time because this + * makes merging changes easier. (xfs_fileoff_t and xfs_filblks_t are + * the same underlying type). + */ +typedef struct xfs_extdelta +{ + xfs_fileoff_t xed_startoff; /* offset of range */ + xfs_filblks_t xed_blockcount; /* blocks in range */ +} xfs_extdelta_t; + +/* * List of extents to be free "later". * The list is kept sorted on xbf_startblock. */ @@ -275,7 +289,9 @@ xfs_bmapi( xfs_extlen_t total, /* total blocks needed */ struct xfs_bmbt_irec *mval, /* output: map values */ int *nmap, /* i/o: mval size/count */ - xfs_bmap_free_t *flist); /* i/o: list extents to free */ + xfs_bmap_free_t *flist, /* i/o: list extents to free */ + xfs_extdelta_t *delta); /* o: change made to incore + extents */ /* * Map file blocks to filesystem blocks, simple version. @@ -309,6 +325,8 @@ xfs_bunmapi( xfs_fsblock_t *firstblock, /* first allocated block controls a.g. for allocs */ xfs_bmap_free_t *flist, /* i/o: list extents to free */ + xfs_extdelta_t *delta, /* o: change made to incore + extents */ int *done); /* set if not done yet */ /* diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c index bea4470..18fb738 100644 --- a/fs/xfs/xfs_bmap_btree.c +++ b/fs/xfs/xfs_bmap_btree.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -1569,12 +1567,11 @@ #endif lbno = XFS_DADDR_TO_FSB(args.mp, XFS_BUF_ADDR(lbp)); left = XFS_BUF_TO_BMBT_BLOCK(lbp); args.fsbno = cur->bc_private.b.firstblock; + args.firstblock = args.fsbno; if (args.fsbno == NULLFSBLOCK) { args.fsbno = lbno; args.type = XFS_ALLOCTYPE_START_BNO; - } else if (cur->bc_private.b.flist->xbf_low) - args.type = XFS_ALLOCTYPE_FIRST_AG; - else + } else args.type = XFS_ALLOCTYPE_NEAR_BNO; args.mod = args.minleft = args.alignment = args.total = args.isfl = args.userdata = args.minalignslop = 0; @@ -2356,6 +2353,7 @@ #endif args.userdata = args.minalignslop = 0; args.minlen = args.maxlen = args.prod = 1; args.wasdel = cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL; + args.firstblock = args.fsbno; if (args.fsbno == NULLFSBLOCK) { #ifdef DEBUG if ((error = xfs_btree_check_lptr(cur, INT_GET(*pp, ARCH_CONVERT), level))) { @@ -2365,9 +2363,7 @@ #ifdef DEBUG #endif args.fsbno = INT_GET(*pp, ARCH_CONVERT); args.type = XFS_ALLOCTYPE_START_BNO; - } else if (args.wasdel) - args.type = XFS_ALLOCTYPE_FIRST_AG; - else + } else args.type = XFS_ALLOCTYPE_NEAR_BNO; if ((error = xfs_alloc_vextent(&args))) { XFS_BMBT_TRACE_CURSOR(cur, ERROR); diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 52d5d09..ee2255b 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 5fed156..a4aa539 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -23,7 +23,6 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_buf_item.h" @@ -1030,9 +1029,9 @@ xfs_buf_iodone_callbacks( if ((XFS_BUF_TARGET(bp) != lasttarg) || (time_after(jiffies, (lasttime + 5*HZ)))) { lasttime = jiffies; - prdev("XFS write error in file system meta-data " - "block 0x%llx in %s", - XFS_BUF_TARGET(bp), + cmn_err(CE_ALERT, "Device %s, XFS metadata write error" + " block 0x%llx in %s", + XFS_BUFTARG_NAME(XFS_BUF_TARGET(bp)), (__uint64_t)XFS_BUF_ADDR(bp), mp->m_fsname); } lasttarg = XFS_BUF_TARGET(bp); @@ -1108,7 +1107,7 @@ xfs_buf_error_relse( XFS_BUF_ERROR(bp,0); xfs_buftrace("BUF_ERROR_RELSE", bp); if (! XFS_FORCED_SHUTDOWN(mp)) - xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); /* * We have to unpin the pinned buffers so do the * callbacks. diff --git a/fs/xfs/xfs_cap.h b/fs/xfs/xfs_cap.h index d0035c6..7a0e482 100644 --- a/fs/xfs/xfs_cap.h +++ b/fs/xfs/xfs_cap.h @@ -49,12 +49,12 @@ #ifdef CONFIG_FS_POSIX_CAP #include -struct vnode; +struct bhv_vnode; -extern int xfs_cap_vhascap(struct vnode *); -extern int xfs_cap_vset(struct vnode *, void *, size_t); -extern int xfs_cap_vget(struct vnode *, void *, size_t); -extern int xfs_cap_vremove(struct vnode *vp); +extern int xfs_cap_vhascap(struct bhv_vnode *); +extern int xfs_cap_vset(struct bhv_vnode *, void *, size_t); +extern int xfs_cap_vget(struct bhv_vnode *, void *, size_t); +extern int xfs_cap_vremove(struct bhv_vnode *); #define _CAP_EXISTS xfs_cap_vhascap diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 8988b90..32ab61d 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -24,7 +24,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -32,7 +31,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -43,7 +41,6 @@ #include "xfs_btree.h" #include "xfs_bmap.h" #include "xfs_attr.h" #include "xfs_attr_leaf.h" -#include "xfs_dir_leaf.h" #include "xfs_dir2_data.h" #include "xfs_dir2_leaf.h" #include "xfs_dir2_block.h" @@ -159,7 +156,7 @@ xfs_da_split(xfs_da_state_t *state) max = state->path.active - 1; ASSERT((max >= 0) && (max < XFS_DA_NODE_MAXDEPTH)); ASSERT(state->path.blk[max].magic == XFS_ATTR_LEAF_MAGIC || - state->path.blk[max].magic == XFS_DIRX_LEAF_MAGIC(state->mp)); + state->path.blk[max].magic == XFS_DIR2_LEAFN_MAGIC); addblk = &state->path.blk[max]; /* initial dummy value */ for (i = max; (i >= 0) && addblk; state->path.active--, i--) { @@ -199,38 +196,7 @@ xfs_da_split(xfs_da_state_t *state) return(error); /* GROT: attr inconsistent */ addblk = newblk; break; - case XFS_DIR_LEAF_MAGIC: - ASSERT(XFS_DIR_IS_V1(state->mp)); - error = xfs_dir_leaf_split(state, oldblk, newblk); - if ((error != 0) && (error != ENOSPC)) { - return(error); /* GROT: dir is inconsistent */ - } - if (!error) { - addblk = newblk; - break; - } - /* - * Entry wouldn't fit, split the leaf again. - */ - state->extravalid = 1; - if (state->inleaf) { - state->extraafter = 0; /* before newblk */ - error = xfs_dir_leaf_split(state, oldblk, - &state->extrablk); - if (error) - return(error); /* GROT: dir incon. */ - addblk = newblk; - } else { - state->extraafter = 1; /* after newblk */ - error = xfs_dir_leaf_split(state, newblk, - &state->extrablk); - if (error) - return(error); /* GROT: dir incon. */ - addblk = newblk; - } - break; case XFS_DIR2_LEAFN_MAGIC: - ASSERT(XFS_DIR_IS_V2(state->mp)); error = xfs_dir2_leafn_split(state, oldblk, newblk); if (error) return error; @@ -363,7 +329,6 @@ xfs_da_root_split(xfs_da_state_t *state, size = (int)((char *)&oldroot->btree[be16_to_cpu(oldroot->hdr.count)] - (char *)oldroot); } else { - ASSERT(XFS_DIR_IS_V2(mp)); ASSERT(be16_to_cpu(oldroot->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC); leaf = (xfs_dir2_leaf_t *)oldroot; size = (int)((char *)&leaf->ents[be16_to_cpu(leaf->hdr.count)] - @@ -379,8 +344,7 @@ xfs_da_root_split(xfs_da_state_t *state, * Set up the new root node. */ error = xfs_da_node_create(args, - args->whichfork == XFS_DATA_FORK && - XFS_DIR_IS_V2(mp) ? mp->m_dirleafblk : 0, + (args->whichfork == XFS_DATA_FORK) ? mp->m_dirleafblk : 0, be16_to_cpu(node->hdr.level) + 1, &bp, args->whichfork); if (error) return(error); @@ -427,10 +391,9 @@ xfs_da_node_split(xfs_da_state_t *state, ASSERT(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC); /* - * With V2 the extra block is data or freespace. + * With V2 dirs the extra block is data or freespace. */ - useextra = state->extravalid && (XFS_DIR_IS_V1(state->mp) || - state->args->whichfork == XFS_ATTR_FORK); + useextra = state->extravalid && state->args->whichfork == XFS_ATTR_FORK; newcount = 1 + useextra; /* * Do we have to split the node? @@ -624,7 +587,7 @@ xfs_da_node_add(xfs_da_state_t *state, x ASSERT(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC); ASSERT((oldblk->index >= 0) && (oldblk->index <= be16_to_cpu(node->hdr.count))); ASSERT(newblk->blkno != 0); - if (state->args->whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) + if (state->args->whichfork == XFS_DATA_FORK) ASSERT(newblk->blkno >= mp->m_dirleafblk && newblk->blkno < mp->m_dirfreeblk); @@ -670,7 +633,7 @@ xfs_da_join(xfs_da_state_t *state) save_blk = &state->altpath.blk[ state->path.active-1 ]; ASSERT(state->path.blk[0].magic == XFS_DA_NODE_MAGIC); ASSERT(drop_blk->magic == XFS_ATTR_LEAF_MAGIC || - drop_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp)); + drop_blk->magic == XFS_DIR2_LEAFN_MAGIC); /* * Walk back up the tree joining/deallocating as necessary. @@ -693,17 +656,7 @@ xfs_da_join(xfs_da_state_t *state) return(0); xfs_attr_leaf_unbalance(state, drop_blk, save_blk); break; - case XFS_DIR_LEAF_MAGIC: - ASSERT(XFS_DIR_IS_V1(state->mp)); - error = xfs_dir_leaf_toosmall(state, &action); - if (error) - return(error); - if (action == 0) - return(0); - xfs_dir_leaf_unbalance(state, drop_blk, save_blk); - break; case XFS_DIR2_LEAFN_MAGIC: - ASSERT(XFS_DIR_IS_V2(state->mp)); error = xfs_dir2_leafn_toosmall(state, &action); if (error) return error; @@ -790,7 +743,7 @@ xfs_da_root_join(xfs_da_state_t *state, ASSERT(bp != NULL); blkinfo = bp->data; if (be16_to_cpu(oldroot->hdr.level) == 1) { - ASSERT(be16_to_cpu(blkinfo->magic) == XFS_DIRX_LEAF_MAGIC(state->mp) || + ASSERT(be16_to_cpu(blkinfo->magic) == XFS_DIR2_LEAFN_MAGIC || be16_to_cpu(blkinfo->magic) == XFS_ATTR_LEAF_MAGIC); } else { ASSERT(be16_to_cpu(blkinfo->magic) == XFS_DA_NODE_MAGIC); @@ -951,14 +904,7 @@ xfs_da_fixhashpath(xfs_da_state_t *state if (count == 0) return; break; - case XFS_DIR_LEAF_MAGIC: - ASSERT(XFS_DIR_IS_V1(state->mp)); - lasthash = xfs_dir_leaf_lasthash(blk->bp, &count); - if (count == 0) - return; - break; case XFS_DIR2_LEAFN_MAGIC: - ASSERT(XFS_DIR_IS_V2(state->mp)); lasthash = xfs_dir2_leafn_lasthash(blk->bp, &count); if (count == 0) return; @@ -1117,10 +1063,7 @@ xfs_da_node_lookup_int(xfs_da_state_t *s * Descend thru the B-tree searching each level for the right * node to use, until the right hashval is found. */ - if (args->whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(state->mp)) - blkno = state->mp->m_dirleafblk; - else - blkno = 0; + blkno = (args->whichfork == XFS_DATA_FORK)? state->mp->m_dirleafblk : 0; for (blk = &state->path.blk[0], state->path.active = 1; state->path.active <= XFS_DA_NODE_MAXDEPTH; blk++, state->path.active++) { @@ -1137,7 +1080,7 @@ xfs_da_node_lookup_int(xfs_da_state_t *s } curr = blk->bp->data; ASSERT(be16_to_cpu(curr->magic) == XFS_DA_NODE_MAGIC || - be16_to_cpu(curr->magic) == XFS_DIRX_LEAF_MAGIC(state->mp) || + be16_to_cpu(curr->magic) == XFS_DIR2_LEAFN_MAGIC || be16_to_cpu(curr->magic) == XFS_ATTR_LEAF_MAGIC); /* @@ -1190,16 +1133,10 @@ xfs_da_node_lookup_int(xfs_da_state_t *s blk->index = probe; blkno = be32_to_cpu(btree->before); } - } - else if (be16_to_cpu(curr->magic) == XFS_ATTR_LEAF_MAGIC) { + } else if (be16_to_cpu(curr->magic) == XFS_ATTR_LEAF_MAGIC) { blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL); break; - } - else if (be16_to_cpu(curr->magic) == XFS_DIR_LEAF_MAGIC) { - blk->hashval = xfs_dir_leaf_lasthash(blk->bp, NULL); - break; - } - else if (be16_to_cpu(curr->magic) == XFS_DIR2_LEAFN_MAGIC) { + } else if (be16_to_cpu(curr->magic) == XFS_DIR2_LEAFN_MAGIC) { blk->hashval = xfs_dir2_leafn_lasthash(blk->bp, NULL); break; } @@ -1212,12 +1149,7 @@ xfs_da_node_lookup_int(xfs_da_state_t *s * next leaf and keep searching. */ for (;;) { - if (blk->magic == XFS_DIR_LEAF_MAGIC) { - ASSERT(XFS_DIR_IS_V1(state->mp)); - retval = xfs_dir_leaf_lookup_int(blk->bp, args, - &blk->index); - } else if (blk->magic == XFS_DIR2_LEAFN_MAGIC) { - ASSERT(XFS_DIR_IS_V2(state->mp)); + if (blk->magic == XFS_DIR2_LEAFN_MAGIC) { retval = xfs_dir2_leafn_lookup_int(blk->bp, args, &blk->index, state); } @@ -1270,7 +1202,7 @@ xfs_da_blk_link(xfs_da_state_t *state, x old_info = old_blk->bp->data; new_info = new_blk->bp->data; ASSERT(old_blk->magic == XFS_DA_NODE_MAGIC || - old_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp) || + old_blk->magic == XFS_DIR2_LEAFN_MAGIC || old_blk->magic == XFS_ATTR_LEAF_MAGIC); ASSERT(old_blk->magic == be16_to_cpu(old_info->magic)); ASSERT(new_blk->magic == be16_to_cpu(new_info->magic)); @@ -1280,12 +1212,7 @@ xfs_da_blk_link(xfs_da_state_t *state, x case XFS_ATTR_LEAF_MAGIC: before = xfs_attr_leaf_order(old_blk->bp, new_blk->bp); break; - case XFS_DIR_LEAF_MAGIC: - ASSERT(XFS_DIR_IS_V1(state->mp)); - before = xfs_dir_leaf_order(old_blk->bp, new_blk->bp); - break; case XFS_DIR2_LEAFN_MAGIC: - ASSERT(XFS_DIR_IS_V2(state->mp)); before = xfs_dir2_leafn_order(old_blk->bp, new_blk->bp); break; case XFS_DA_NODE_MAGIC: @@ -1404,7 +1331,7 @@ xfs_da_blk_unlink(xfs_da_state_t *state, save_info = save_blk->bp->data; drop_info = drop_blk->bp->data; ASSERT(save_blk->magic == XFS_DA_NODE_MAGIC || - save_blk->magic == XFS_DIRX_LEAF_MAGIC(state->mp) || + save_blk->magic == XFS_DIR2_LEAFN_MAGIC || save_blk->magic == XFS_ATTR_LEAF_MAGIC); ASSERT(save_blk->magic == be16_to_cpu(save_info->magic)); ASSERT(drop_blk->magic == be16_to_cpu(drop_info->magic)); @@ -1529,7 +1456,7 @@ xfs_da_path_shift(xfs_da_state_t *state, ASSERT(blk->bp != NULL); info = blk->bp->data; ASSERT(be16_to_cpu(info->magic) == XFS_DA_NODE_MAGIC || - be16_to_cpu(info->magic) == XFS_DIRX_LEAF_MAGIC(state->mp) || + be16_to_cpu(info->magic) == XFS_DIR2_LEAFN_MAGIC || be16_to_cpu(info->magic) == XFS_ATTR_LEAF_MAGIC); blk->magic = be16_to_cpu(info->magic); if (blk->magic == XFS_DA_NODE_MAGIC) { @@ -1548,20 +1475,13 @@ xfs_da_path_shift(xfs_da_state_t *state, blk->hashval = xfs_attr_leaf_lasthash(blk->bp, NULL); break; - case XFS_DIR_LEAF_MAGIC: - ASSERT(XFS_DIR_IS_V1(state->mp)); - blk->hashval = xfs_dir_leaf_lasthash(blk->bp, - NULL); - break; case XFS_DIR2_LEAFN_MAGIC: - ASSERT(XFS_DIR_IS_V2(state->mp)); blk->hashval = xfs_dir2_leafn_lasthash(blk->bp, NULL); break; default: ASSERT(blk->magic == XFS_ATTR_LEAF_MAGIC || - blk->magic == - XFS_DIRX_LEAF_MAGIC(state->mp)); + blk->magic == XFS_DIR2_LEAFN_MAGIC); break; } } @@ -1620,7 +1540,6 @@ xfs_da_grow_inode(xfs_da_args_t *args, x xfs_bmbt_irec_t *mapp; xfs_inode_t *dp; int nmap, error, w, count, c, got, i, mapi; - xfs_fsize_t size; xfs_trans_t *tp; xfs_mount_t *mp; @@ -1631,7 +1550,7 @@ xfs_da_grow_inode(xfs_da_args_t *args, x /* * For new directories adjust the file offset and block count. */ - if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) { + if (w == XFS_DATA_FORK) { bno = mp->m_dirleafblk; count = mp->m_dirblkfsbs; } else { @@ -1641,10 +1560,9 @@ xfs_da_grow_inode(xfs_da_args_t *args, x /* * Find a spot in the file space to put the new block. */ - if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, w))) { + if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, w))) return error; - } - if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) + if (w == XFS_DATA_FORK) ASSERT(bno >= mp->m_dirleafblk && bno < mp->m_dirfreeblk); /* * Try mapping it in one filesystem block. @@ -1655,7 +1573,7 @@ xfs_da_grow_inode(xfs_da_args_t *args, x XFS_BMAPI_AFLAG(w)|XFS_BMAPI_WRITE|XFS_BMAPI_METADATA| XFS_BMAPI_CONTIG, args->firstblock, args->total, &map, &nmap, - args->flist))) { + args->flist, NULL))) { return error; } ASSERT(nmap <= 1); @@ -1676,7 +1594,8 @@ xfs_da_grow_inode(xfs_da_args_t *args, x XFS_BMAPI_AFLAG(w)|XFS_BMAPI_WRITE| XFS_BMAPI_METADATA, args->firstblock, args->total, - &mapp[mapi], &nmap, args->flist))) { + &mapp[mapi], &nmap, args->flist, + NULL))) { kmem_free(mapp, sizeof(*mapp) * count); return error; } @@ -1705,19 +1624,6 @@ xfs_da_grow_inode(xfs_da_args_t *args, x if (mapp != &map) kmem_free(mapp, sizeof(*mapp) * count); *new_blkno = (xfs_dablk_t)bno; - /* - * For version 1 directories, adjust the file size if it changed. - */ - if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) { - ASSERT(mapi == 1); - if ((error = xfs_bmap_last_offset(tp, dp, &bno, w))) - return error; - size = XFS_FSB_TO_B(mp, bno); - if (size != dp->i_d.di_size) { - dp->i_d.di_size = size; - xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); - } - } return 0; } @@ -1742,7 +1648,6 @@ xfs_da_swap_lastblock(xfs_da_args_t *arg int error, w, entno, level, dead_level; xfs_da_blkinfo_t *dead_info, *sib_info; xfs_da_intnode_t *par_node, *dead_node; - xfs_dir_leafblock_t *dead_leaf; xfs_dir2_leaf_t *dead_leaf2; xfs_dahash_t dead_hash; @@ -1753,11 +1658,8 @@ xfs_da_swap_lastblock(xfs_da_args_t *arg w = args->whichfork; ASSERT(w == XFS_DATA_FORK); mp = ip->i_mount; - if (XFS_DIR_IS_V2(mp)) { - lastoff = mp->m_dirfreeblk; - error = xfs_bmap_last_before(tp, ip, &lastoff, w); - } else - error = xfs_bmap_last_offset(tp, ip, &lastoff, w); + lastoff = mp->m_dirfreeblk; + error = xfs_bmap_last_before(tp, ip, &lastoff, w); if (error) return error; if (unlikely(lastoff == 0)) { @@ -1780,14 +1682,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *arg /* * Get values from the moved block. */ - if (be16_to_cpu(dead_info->magic) == XFS_DIR_LEAF_MAGIC) { - ASSERT(XFS_DIR_IS_V1(mp)); - dead_leaf = (xfs_dir_leafblock_t *)dead_info; - dead_level = 0; - dead_hash = - INT_GET(dead_leaf->entries[INT_GET(dead_leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT); - } else if (be16_to_cpu(dead_info->magic) == XFS_DIR2_LEAFN_MAGIC) { - ASSERT(XFS_DIR_IS_V2(mp)); + if (be16_to_cpu(dead_info->magic) == XFS_DIR2_LEAFN_MAGIC) { dead_leaf2 = (xfs_dir2_leaf_t *)dead_info; dead_level = 0; dead_hash = be32_to_cpu(dead_leaf2->ents[be16_to_cpu(dead_leaf2->hdr.count) - 1].hashval); @@ -1842,7 +1737,7 @@ xfs_da_swap_lastblock(xfs_da_args_t *arg xfs_da_buf_done(sib_buf); sib_buf = NULL; } - par_blkno = XFS_DIR_IS_V1(mp) ? 0 : mp->m_dirleafblk; + par_blkno = mp->m_dirleafblk; level = -1; /* * Walk down the tree looking for the parent of the moved block. @@ -1941,8 +1836,6 @@ xfs_da_shrink_inode(xfs_da_args_t *args, { xfs_inode_t *dp; int done, error, w, count; - xfs_fileoff_t bno; - xfs_fsize_t size; xfs_trans_t *tp; xfs_mount_t *mp; @@ -1950,7 +1843,7 @@ xfs_da_shrink_inode(xfs_da_args_t *args, w = args->whichfork; tp = args->trans; mp = dp->i_mount; - if (w == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) + if (w == XFS_DATA_FORK) count = mp->m_dirblkfsbs; else count = 1; @@ -1961,34 +1854,17 @@ xfs_da_shrink_inode(xfs_da_args_t *args, */ if ((error = xfs_bunmapi(tp, dp, dead_blkno, count, XFS_BMAPI_AFLAG(w)|XFS_BMAPI_METADATA, - 0, args->firstblock, args->flist, + 0, args->firstblock, args->flist, NULL, &done)) == ENOSPC) { if (w != XFS_DATA_FORK) - goto done; + break; if ((error = xfs_da_swap_lastblock(args, &dead_blkno, &dead_buf))) - goto done; - } else if (error) - goto done; - else + break; + } else { break; - } - ASSERT(done); - xfs_da_binval(tp, dead_buf); - /* - * Adjust the directory size for version 1. - */ - if (w == XFS_DATA_FORK && XFS_DIR_IS_V1(mp)) { - if ((error = xfs_bmap_last_offset(tp, dp, &bno, w))) - return error; - size = XFS_FSB_TO_B(dp->i_mount, bno); - if (size != dp->i_d.di_size) { - dp->i_d.di_size = size; - xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); } } - return 0; -done: xfs_da_binval(tp, dead_buf); return error; } @@ -2049,10 +1925,7 @@ xfs_da_do_buf( xfs_dabuf_t *rbp; mp = dp->i_mount; - if (whichfork == XFS_DATA_FORK && XFS_DIR_IS_V2(mp)) - nfsb = mp->m_dirblkfsbs; - else - nfsb = 1; + nfsb = (whichfork == XFS_DATA_FORK) ? mp->m_dirblkfsbs : 1; mappedbno = *mappedbnop; /* * Caller doesn't have a mapping. -2 means don't complain @@ -2086,7 +1959,7 @@ xfs_da_do_buf( nfsb, XFS_BMAPI_METADATA | XFS_BMAPI_AFLAG(whichfork), - NULL, 0, mapp, &nmap, NULL))) + NULL, 0, mapp, &nmap, NULL, NULL))) goto exit0; } } else { @@ -2198,7 +2071,6 @@ xfs_da_do_buf( magic1 = be32_to_cpu(data->hdr.magic); if (unlikely( XFS_TEST_ERROR((magic != XFS_DA_NODE_MAGIC) && - (magic != XFS_DIR_LEAF_MAGIC) && (magic != XFS_ATTR_LEAF_MAGIC) && (magic != XFS_DIR2_LEAF1_MAGIC) && (magic != XFS_DIR2_LEAFN_MAGIC) && diff --git a/fs/xfs/xfs_da_btree.h b/fs/xfs/xfs_da_btree.h index 243a730..4ab865e 100644 --- a/fs/xfs/xfs_da_btree.h +++ b/fs/xfs/xfs_da_btree.h @@ -36,14 +36,10 @@ struct zone; * level in the Btree, and to identify which type of block this is. */ #define XFS_DA_NODE_MAGIC 0xfebe /* magic number: non-leaf blocks */ -#define XFS_DIR_LEAF_MAGIC 0xfeeb /* magic number: directory leaf blks */ #define XFS_ATTR_LEAF_MAGIC 0xfbee /* magic number: attribute leaf blks */ #define XFS_DIR2_LEAF1_MAGIC 0xd2f1 /* magic number: v2 dirlf single blks */ #define XFS_DIR2_LEAFN_MAGIC 0xd2ff /* magic number: v2 dirlf multi blks */ -#define XFS_DIRX_LEAF_MAGIC(mp) \ - (XFS_DIR_IS_V1(mp) ? XFS_DIR_LEAF_MAGIC : XFS_DIR2_LEAFN_MAGIC) - typedef struct xfs_da_blkinfo { __be32 forw; /* previous block in list */ __be32 back; /* following block in list */ diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index 4968a63..80562b6 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -54,24 +52,14 @@ xfs_swapext( xfs_swapext_t __user *sxu) { xfs_swapext_t *sxp; - xfs_inode_t *ip=NULL, *tip=NULL, *ips[2]; - xfs_trans_t *tp; + xfs_inode_t *ip=NULL, *tip=NULL; xfs_mount_t *mp; - xfs_bstat_t *sbp; struct file *fp = NULL, *tfp = NULL; - vnode_t *vp, *tvp; - static uint lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL; - int ilf_fields, tilf_fields; + bhv_vnode_t *vp, *tvp; int error = 0; - xfs_ifork_t *tempifp, *ifp, *tifp; - __uint64_t tmp; - int aforkblks = 0; - int taforkblks = 0; - char locked = 0; sxp = kmem_alloc(sizeof(xfs_swapext_t), KM_MAYFAIL); - tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL); - if (!sxp || !tempifp) { + if (!sxp) { error = XFS_ERROR(ENOMEM); goto error0; } @@ -118,14 +106,56 @@ xfs_swapext( mp = ip->i_mount; - sbp = &sxp->sx_stat; - if (XFS_FORCED_SHUTDOWN(mp)) { error = XFS_ERROR(EIO); goto error0; } - locked = 1; + error = XFS_SWAP_EXTENTS(mp, &ip->i_iocore, &tip->i_iocore, sxp); + + error0: + if (fp != NULL) + fput(fp); + if (tfp != NULL) + fput(tfp); + + if (sxp != NULL) + kmem_free(sxp, sizeof(xfs_swapext_t)); + + return error; +} + +int +xfs_swap_extents( + xfs_inode_t *ip, + xfs_inode_t *tip, + xfs_swapext_t *sxp) +{ + xfs_mount_t *mp; + xfs_inode_t *ips[2]; + xfs_trans_t *tp; + xfs_bstat_t *sbp = &sxp->sx_stat; + bhv_vnode_t *vp, *tvp; + xfs_ifork_t *tempifp, *ifp, *tifp; + int ilf_fields, tilf_fields; + static uint lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL; + int error = 0; + int aforkblks = 0; + int taforkblks = 0; + __uint64_t tmp; + char locked = 0; + + mp = ip->i_mount; + + tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL); + if (!tempifp) { + error = XFS_ERROR(ENOMEM); + goto error0; + } + + sbp = &sxp->sx_stat; + vp = XFS_ITOV(ip); + tvp = XFS_ITOV(tip); /* Lock in i_ino order */ if (ip->i_ino < tip->i_ino) { @@ -137,6 +167,7 @@ xfs_swapext( } xfs_lock_inodes(ips, 2, 0, lock_flags); + locked = 1; /* Check permissions */ error = xfs_iaccess(ip, S_IWUSR, NULL); @@ -169,7 +200,7 @@ xfs_swapext( if (VN_CACHED(tvp) != 0) { xfs_inval_cached_trace(&tip->i_iocore, 0, -1, 0, -1); - VOP_FLUSHINVAL_PAGES(tvp, 0, -1, FI_REMAPF_LOCKED); + bhv_vop_flushinval_pages(tvp, 0, -1, FI_REMAPF_LOCKED); } /* Verify O_DIRECT for ftmp */ @@ -214,7 +245,7 @@ xfs_swapext( /* We need to fail if the file is memory mapped. Once we have tossed * all existing pages, the page fault will have no option * but to go to the filesystem for pages. By making the page fault call - * VOP_READ (or write in the case of autogrow) they block on the iolock + * vop_read (or write in the case of autogrow) they block on the iolock * until we have switched the extents. */ if (VN_MAPPED(vp)) { @@ -233,7 +264,7 @@ xfs_swapext( * fields change. */ - VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF); + bhv_vop_toss_pages(vp, 0, -1, FI_REMAPF); tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT); if ((error = xfs_trans_reserve(tp, 0, @@ -360,16 +391,7 @@ xfs_swapext( xfs_iunlock(ip, lock_flags); xfs_iunlock(tip, lock_flags); } - - if (fp != NULL) - fput(fp); - if (tfp != NULL) - fput(tfp); - - if (sxp != NULL) - kmem_free(sxp, sizeof(xfs_swapext_t)); if (tempifp != NULL) kmem_free(tempifp, sizeof(xfs_ifork_t)); - return error; } diff --git a/fs/xfs/xfs_dfrag.h b/fs/xfs/xfs_dfrag.h index f678559..da17820 100644 --- a/fs/xfs/xfs_dfrag.h +++ b/fs/xfs/xfs_dfrag.h @@ -48,6 +48,9 @@ #ifdef __KERNEL__ */ int xfs_swapext(struct xfs_swapext __user *sx); +int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip, + struct xfs_swapext *sxp); + #endif /* __KERNEL__ */ #endif /* __XFS_DFRAG_H__ */ diff --git a/fs/xfs/xfs_dinode.h b/fs/xfs/xfs_dinode.h index 79d0d9e..b338269 100644 --- a/fs/xfs/xfs_dinode.h +++ b/fs/xfs/xfs_dinode.h @@ -85,7 +85,6 @@ typedef struct xfs_dinode union { xfs_bmdr_block_t di_bmbt; /* btree root block */ xfs_bmbt_rec_32_t di_bmx[1]; /* extent list */ - xfs_dir_shortform_t di_dirsf; /* shortform directory */ xfs_dir2_sf_t di_dir2sf; /* shortform directory v2 */ char di_c[1]; /* local contents */ xfs_dev_t di_dev; /* device for S_IFCHR/S_IFBLK */ @@ -257,6 +256,7 @@ #define XFS_DIFLAG_PROJINHERIT_BIT 9 / #define XFS_DIFLAG_NOSYMLINKS_BIT 10 /* disallow symlink creation */ #define XFS_DIFLAG_EXTSIZE_BIT 11 /* inode extent size allocator hint */ #define XFS_DIFLAG_EXTSZINHERIT_BIT 12 /* inherit inode extent size */ +#define XFS_DIFLAG_NODEFRAG_BIT 13 /* do not reorganize/defragment */ #define XFS_DIFLAG_REALTIME (1 << XFS_DIFLAG_REALTIME_BIT) #define XFS_DIFLAG_PREALLOC (1 << XFS_DIFLAG_PREALLOC_BIT) #define XFS_DIFLAG_NEWRTBM (1 << XFS_DIFLAG_NEWRTBM_BIT) @@ -270,12 +270,13 @@ #define XFS_DIFLAG_PROJINHERIT (1 << X #define XFS_DIFLAG_NOSYMLINKS (1 << XFS_DIFLAG_NOSYMLINKS_BIT) #define XFS_DIFLAG_EXTSIZE (1 << XFS_DIFLAG_EXTSIZE_BIT) #define XFS_DIFLAG_EXTSZINHERIT (1 << XFS_DIFLAG_EXTSZINHERIT_BIT) +#define XFS_DIFLAG_NODEFRAG (1 << XFS_DIFLAG_NODEFRAG_BIT) #define XFS_DIFLAG_ANY \ (XFS_DIFLAG_REALTIME | XFS_DIFLAG_PREALLOC | XFS_DIFLAG_NEWRTBM | \ XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND | XFS_DIFLAG_SYNC | \ XFS_DIFLAG_NOATIME | XFS_DIFLAG_NODUMP | XFS_DIFLAG_RTINHERIT | \ XFS_DIFLAG_PROJINHERIT | XFS_DIFLAG_NOSYMLINKS | XFS_DIFLAG_EXTSIZE | \ - XFS_DIFLAG_EXTSZINHERIT) + XFS_DIFLAG_EXTSZINHERIT | XFS_DIFLAG_NODEFRAG) #endif /* __XFS_DINODE_H__ */ diff --git a/fs/xfs/xfs_dir.c b/fs/xfs/xfs_dir.c deleted file mode 100644 index 9cc702a..0000000 --- a/fs/xfs/xfs_dir.c +++ /dev/null @@ -1,1217 +0,0 @@ -/* - * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "xfs.h" -#include "xfs_fs.h" -#include "xfs_types.h" -#include "xfs_log.h" -#include "xfs_inum.h" -#include "xfs_trans.h" -#include "xfs_sb.h" -#include "xfs_dir.h" -#include "xfs_dir2.h" -#include "xfs_dmapi.h" -#include "xfs_mount.h" -#include "xfs_da_btree.h" -#include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_alloc.h" -#include "xfs_btree.h" -#include "xfs_dir_sf.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" -#include "xfs_inode.h" -#include "xfs_bmap.h" -#include "xfs_dir_leaf.h" -#include "xfs_error.h" - -/* - * xfs_dir.c - * - * Provide the external interfaces to manage directories. - */ - -/*======================================================================== - * Function prototypes for the kernel. - *========================================================================*/ - -/* - * Functions for the dirops interfaces. - */ -static void xfs_dir_mount(struct xfs_mount *mp); - -static int xfs_dir_isempty(struct xfs_inode *dp); - -static int xfs_dir_init(struct xfs_trans *trans, - struct xfs_inode *dir, - struct xfs_inode *parent_dir); - -static int xfs_dir_createname(struct xfs_trans *trans, - struct xfs_inode *dp, - char *name_string, - int name_len, - xfs_ino_t inode_number, - xfs_fsblock_t *firstblock, - xfs_bmap_free_t *flist, - xfs_extlen_t total); - -static int xfs_dir_lookup(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name_string, - int name_length, - xfs_ino_t *inode_number); - -static int xfs_dir_removename(struct xfs_trans *trans, - struct xfs_inode *dp, - char *name_string, - int name_length, - xfs_ino_t ino, - xfs_fsblock_t *firstblock, - xfs_bmap_free_t *flist, - xfs_extlen_t total); - -static int xfs_dir_getdents(struct xfs_trans *tp, - struct xfs_inode *dp, - struct uio *uiop, - int *eofp); - -static int xfs_dir_replace(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name_string, - int name_length, - xfs_ino_t inode_number, - xfs_fsblock_t *firstblock, - xfs_bmap_free_t *flist, - xfs_extlen_t total); - -static int xfs_dir_canenter(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name_string, - int name_length); - -static int xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, - xfs_dinode_t *dip); - -xfs_dirops_t xfsv1_dirops = { - .xd_mount = xfs_dir_mount, - .xd_isempty = xfs_dir_isempty, - .xd_init = xfs_dir_init, - .xd_createname = xfs_dir_createname, - .xd_lookup = xfs_dir_lookup, - .xd_removename = xfs_dir_removename, - .xd_getdents = xfs_dir_getdents, - .xd_replace = xfs_dir_replace, - .xd_canenter = xfs_dir_canenter, - .xd_shortform_validate_ondisk = xfs_dir_shortform_validate_ondisk, - .xd_shortform_to_single = xfs_dir_shortform_to_leaf, -}; - -/* - * Internal routines when dirsize == XFS_LBSIZE(mp). - */ -STATIC int xfs_dir_leaf_lookup(xfs_da_args_t *args); -STATIC int xfs_dir_leaf_removename(xfs_da_args_t *args, int *number_entries, - int *total_namebytes); -STATIC int xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, - uio_t *uio, int *eofp, - xfs_dirent_t *dbp, - xfs_dir_put_t put); -STATIC int xfs_dir_leaf_replace(xfs_da_args_t *args); - -/* - * Internal routines when dirsize > XFS_LBSIZE(mp). - */ -STATIC int xfs_dir_node_addname(xfs_da_args_t *args); -STATIC int xfs_dir_node_lookup(xfs_da_args_t *args); -STATIC int xfs_dir_node_removename(xfs_da_args_t *args); -STATIC int xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, - uio_t *uio, int *eofp, - xfs_dirent_t *dbp, - xfs_dir_put_t put); -STATIC int xfs_dir_node_replace(xfs_da_args_t *args); - -#if defined(XFS_DIR_TRACE) -ktrace_t *xfs_dir_trace_buf; -#endif - - -/*======================================================================== - * Overall external interface routines. - *========================================================================*/ - -xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot; - -/* - * One-time startup routine called from xfs_init(). - */ -void -xfs_dir_startup(void) -{ - xfs_dir_hash_dot = xfs_da_hashname(".", 1); - xfs_dir_hash_dotdot = xfs_da_hashname("..", 2); -} - -/* - * Initialize directory-related fields in the mount structure. - */ -static void -xfs_dir_mount(xfs_mount_t *mp) -{ - uint shortcount, leafcount, count; - - mp->m_dirversion = 1; - if (!(mp->m_flags & XFS_MOUNT_ATTR2)) { - shortcount = (mp->m_attroffset - - (uint)sizeof(xfs_dir_sf_hdr_t)) / - (uint)sizeof(xfs_dir_sf_entry_t); - leafcount = (XFS_LBSIZE(mp) - - (uint)sizeof(xfs_dir_leaf_hdr_t)) / - ((uint)sizeof(xfs_dir_leaf_entry_t) + - (uint)sizeof(xfs_dir_leaf_name_t)); - } else { - shortcount = (XFS_BMDR_SPACE_CALC(MINABTPTRS) - - (uint)sizeof(xfs_dir_sf_hdr_t)) / - (uint)sizeof(xfs_dir_sf_entry_t); - leafcount = (XFS_LBSIZE(mp) - - (uint)sizeof(xfs_dir_leaf_hdr_t)) / - ((uint)sizeof(xfs_dir_leaf_entry_t) + - (uint)sizeof(xfs_dir_leaf_name_t)); - } - count = shortcount > leafcount ? shortcount : leafcount; - mp->m_dircook_elog = xfs_da_log2_roundup(count + 1); - ASSERT(mp->m_dircook_elog <= mp->m_sb.sb_blocklog); - mp->m_dir_node_ents = mp->m_attr_node_ents = - (XFS_LBSIZE(mp) - (uint)sizeof(xfs_da_node_hdr_t)) / - (uint)sizeof(xfs_da_node_entry_t); - mp->m_dir_magicpct = (XFS_LBSIZE(mp) * 37) / 100; - mp->m_dirblksize = mp->m_sb.sb_blocksize; - mp->m_dirblkfsbs = 1; -} - -/* - * Return 1 if directory contains only "." and "..". - */ -static int -xfs_dir_isempty(xfs_inode_t *dp) -{ - xfs_dir_sf_hdr_t *hdr; - - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - if (dp->i_d.di_size == 0) - return(1); - if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp)) - return(0); - hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data; - return(hdr->count == 0); -} - -/* - * Initialize a directory with its "." and ".." entries. - */ -static int -xfs_dir_init(xfs_trans_t *trans, xfs_inode_t *dir, xfs_inode_t *parent_dir) -{ - xfs_da_args_t args; - int error; - - memset((char *)&args, 0, sizeof(args)); - args.dp = dir; - args.trans = trans; - - ASSERT((dir->i_d.di_mode & S_IFMT) == S_IFDIR); - if ((error = xfs_dir_ino_validate(trans->t_mountp, parent_dir->i_ino))) - return error; - - return(xfs_dir_shortform_create(&args, parent_dir->i_ino)); -} - -/* - * Generic handler routine to add a name to a directory. - * Transitions directory from shortform to Btree as necessary. - */ -static int /* error */ -xfs_dir_createname(xfs_trans_t *trans, xfs_inode_t *dp, char *name, - int namelen, xfs_ino_t inum, xfs_fsblock_t *firstblock, - xfs_bmap_free_t *flist, xfs_extlen_t total) -{ - xfs_da_args_t args; - int retval, newsize, done; - - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - - if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum))) - return (retval); - - XFS_STATS_INC(xs_dir_create); - /* - * Fill in the arg structure for this request. - */ - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); - args.inumber = inum; - args.dp = dp; - args.firstblock = firstblock; - args.flist = flist; - args.total = total; - args.whichfork = XFS_DATA_FORK; - args.trans = trans; - args.justcheck = 0; - args.addname = args.oknoent = 1; - - /* - * Decide on what work routines to call based on the inode size. - */ - done = 0; - if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { - newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen); - if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) { - retval = xfs_dir_shortform_addname(&args); - done = 1; - } else { - if (total == 0) - return XFS_ERROR(ENOSPC); - retval = xfs_dir_shortform_to_leaf(&args); - done = retval != 0; - } - } - if (!done && xfs_bmap_one_block(dp, XFS_DATA_FORK)) { - retval = xfs_dir_leaf_addname(&args); - done = retval != ENOSPC; - if (!done) { - if (total == 0) - return XFS_ERROR(ENOSPC); - retval = xfs_dir_leaf_to_node(&args); - done = retval != 0; - } - } - if (!done) { - retval = xfs_dir_node_addname(&args); - } - return(retval); -} - -/* - * Generic handler routine to check if a name can be added to a directory, - * without adding any blocks to the directory. - */ -static int /* error */ -xfs_dir_canenter(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen) -{ - xfs_da_args_t args; - int retval, newsize; - - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - /* - * Fill in the arg structure for this request. - */ - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); - args.inumber = 0; - args.dp = dp; - args.firstblock = NULL; - args.flist = NULL; - args.total = 0; - args.whichfork = XFS_DATA_FORK; - args.trans = trans; - args.justcheck = args.addname = args.oknoent = 1; - - /* - * Decide on what work routines to call based on the inode size. - */ - if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { - newsize = XFS_DIR_SF_ENTSIZE_BYNAME(args.namelen); - if ((dp->i_d.di_size + newsize) <= XFS_IFORK_DSIZE(dp)) - retval = 0; - else - retval = XFS_ERROR(ENOSPC); - } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { - retval = xfs_dir_leaf_addname(&args); - } else { - retval = xfs_dir_node_addname(&args); - } - return(retval); -} - -/* - * Generic handler routine to remove a name from a directory. - * Transitions directory from Btree to shortform as necessary. - */ -static int /* error */ -xfs_dir_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name, - int namelen, xfs_ino_t ino, xfs_fsblock_t *firstblock, - xfs_bmap_free_t *flist, xfs_extlen_t total) -{ - xfs_da_args_t args; - int count, totallen, newsize, retval; - - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - XFS_STATS_INC(xs_dir_remove); - /* - * Fill in the arg structure for this request. - */ - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); - args.inumber = ino; - args.dp = dp; - args.firstblock = firstblock; - args.flist = flist; - args.total = total; - args.whichfork = XFS_DATA_FORK; - args.trans = trans; - args.justcheck = args.addname = args.oknoent = 0; - - /* - * Decide on what work routines to call based on the inode size. - */ - if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { - retval = xfs_dir_shortform_removename(&args); - } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { - retval = xfs_dir_leaf_removename(&args, &count, &totallen); - if (retval == 0) { - newsize = XFS_DIR_SF_ALLFIT(count, totallen); - if (newsize <= XFS_IFORK_DSIZE(dp)) { - retval = xfs_dir_leaf_to_shortform(&args); - } - } - } else { - retval = xfs_dir_node_removename(&args); - } - return(retval); -} - -static int /* error */ -xfs_dir_lookup(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen, - xfs_ino_t *inum) -{ - xfs_da_args_t args; - int retval; - - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - - XFS_STATS_INC(xs_dir_lookup); - /* - * Fill in the arg structure for this request. - */ - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); - args.inumber = 0; - args.dp = dp; - args.firstblock = NULL; - args.flist = NULL; - args.total = 0; - args.whichfork = XFS_DATA_FORK; - args.trans = trans; - args.justcheck = args.addname = 0; - args.oknoent = 1; - - /* - * Decide on what work routines to call based on the inode size. - */ - if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { - retval = xfs_dir_shortform_lookup(&args); - } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { - retval = xfs_dir_leaf_lookup(&args); - } else { - retval = xfs_dir_node_lookup(&args); - } - if (retval == EEXIST) - retval = 0; - *inum = args.inumber; - return(retval); -} - -/* - * Implement readdir. - */ -static int /* error */ -xfs_dir_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, int *eofp) -{ - xfs_dirent_t *dbp; - int alignment, retval; - xfs_dir_put_t put; - - XFS_STATS_INC(xs_dir_getdents); - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - - /* - * If our caller has given us a single contiguous memory buffer, - * just work directly within that buffer. If it's in user memory, - * lock it down first. - */ - alignment = sizeof(xfs_off_t) - 1; - if ((uio->uio_iovcnt == 1) && - (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) && - ((uio->uio_iov[0].iov_len & alignment) == 0)) { - dbp = NULL; - put = xfs_dir_put_dirent64_direct; - } else { - dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP); - put = xfs_dir_put_dirent64_uio; - } - - /* - * Decide on what work routines to call based on the inode size. - */ - *eofp = 0; - - if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { - retval = xfs_dir_shortform_getdents(dp, uio, eofp, dbp, put); - } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { - retval = xfs_dir_leaf_getdents(trans, dp, uio, eofp, dbp, put); - } else { - retval = xfs_dir_node_getdents(trans, dp, uio, eofp, dbp, put); - } - if (dbp != NULL) - kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN); - - return(retval); -} - -static int /* error */ -xfs_dir_replace(xfs_trans_t *trans, xfs_inode_t *dp, char *name, int namelen, - xfs_ino_t inum, xfs_fsblock_t *firstblock, - xfs_bmap_free_t *flist, xfs_extlen_t total) -{ - xfs_da_args_t args; - int retval; - - ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - - if ((retval = xfs_dir_ino_validate(trans->t_mountp, inum))) - return retval; - - /* - * Fill in the arg structure for this request. - */ - args.name = name; - args.namelen = namelen; - args.hashval = xfs_da_hashname(name, namelen); - args.inumber = inum; - args.dp = dp; - args.firstblock = firstblock; - args.flist = flist; - args.total = total; - args.whichfork = XFS_DATA_FORK; - args.trans = trans; - args.justcheck = args.addname = args.oknoent = 0; - - /* - * Decide on what work routines to call based on the inode size. - */ - if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { - retval = xfs_dir_shortform_replace(&args); - } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) { - retval = xfs_dir_leaf_replace(&args); - } else { - retval = xfs_dir_node_replace(&args); - } - - return(retval); -} - -static int -xfs_dir_shortform_validate_ondisk(xfs_mount_t *mp, xfs_dinode_t *dp) -{ - xfs_ino_t ino; - int namelen_sum; - int count; - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - int i; - - - - if ((INT_GET(dp->di_core.di_mode, ARCH_CONVERT) & S_IFMT) != S_IFDIR) { - return 0; - } - if (INT_GET(dp->di_core.di_format, ARCH_CONVERT) != XFS_DINODE_FMT_LOCAL) { - return 0; - } - if (INT_GET(dp->di_core.di_size, ARCH_CONVERT) < sizeof(sf->hdr)) { - xfs_fs_cmn_err(CE_WARN, mp, "Invalid shortform size: dp 0x%p", - dp); - return 1; - } - sf = (xfs_dir_shortform_t *)(&dp->di_u.di_dirsf); - ino = XFS_GET_DIR_INO8(sf->hdr.parent); - if (xfs_dir_ino_validate(mp, ino)) - return 1; - - count = sf->hdr.count; - if ((count < 0) || ((count * 10) > XFS_LITINO(mp))) { - xfs_fs_cmn_err(CE_WARN, mp, - "Invalid shortform count: dp 0x%p", dp); - return(1); - } - - if (count == 0) { - return 0; - } - - namelen_sum = 0; - sfe = &sf->list[0]; - for (i = sf->hdr.count - 1; i >= 0; i--) { - ino = XFS_GET_DIR_INO8(sfe->inumber); - xfs_dir_ino_validate(mp, ino); - if (sfe->namelen >= XFS_LITINO(mp)) { - xfs_fs_cmn_err(CE_WARN, mp, - "Invalid shortform namelen: dp 0x%p", dp); - return 1; - } - namelen_sum += sfe->namelen; - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - } - if (namelen_sum >= XFS_LITINO(mp)) { - xfs_fs_cmn_err(CE_WARN, mp, - "Invalid shortform namelen: dp 0x%p", dp); - return 1; - } - - return 0; -} - -/*======================================================================== - * External routines when dirsize == XFS_LBSIZE(dp->i_mount). - *========================================================================*/ - -/* - * Add a name to the leaf directory structure - * This is the external routine. - */ -int -xfs_dir_leaf_addname(xfs_da_args_t *args) -{ - int index, retval; - xfs_dabuf_t *bp; - - retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, - XFS_DATA_FORK); - if (retval) - return(retval); - ASSERT(bp != NULL); - - retval = xfs_dir_leaf_lookup_int(bp, args, &index); - if (retval == ENOENT) - retval = xfs_dir_leaf_add(bp, args, index); - xfs_da_buf_done(bp); - return(retval); -} - -/* - * Remove a name from the leaf directory structure - * This is the external routine. - */ -STATIC int -xfs_dir_leaf_removename(xfs_da_args_t *args, int *count, int *totallen) -{ - xfs_dir_leafblock_t *leaf; - int index, retval; - xfs_dabuf_t *bp; - - retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, - XFS_DATA_FORK); - if (retval) - return(retval); - ASSERT(bp != NULL); - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - retval = xfs_dir_leaf_lookup_int(bp, args, &index); - if (retval == EEXIST) { - (void)xfs_dir_leaf_remove(args->trans, bp, index); - *count = INT_GET(leaf->hdr.count, ARCH_CONVERT); - *totallen = INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); - retval = 0; - } - xfs_da_buf_done(bp); - return(retval); -} - -/* - * Look up a name in a leaf directory structure. - * This is the external routine. - */ -STATIC int -xfs_dir_leaf_lookup(xfs_da_args_t *args) -{ - int index, retval; - xfs_dabuf_t *bp; - - retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, - XFS_DATA_FORK); - if (retval) - return(retval); - ASSERT(bp != NULL); - retval = xfs_dir_leaf_lookup_int(bp, args, &index); - xfs_da_brelse(args->trans, bp); - return(retval); -} - -/* - * Copy out directory entries for getdents(), for leaf directories. - */ -STATIC int -xfs_dir_leaf_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, - int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put) -{ - xfs_dabuf_t *bp; - int retval, eob; - - retval = xfs_da_read_buf(dp->i_transp, dp, 0, -1, &bp, XFS_DATA_FORK); - if (retval) - return(retval); - ASSERT(bp != NULL); - retval = xfs_dir_leaf_getdents_int(bp, dp, 0, uio, &eob, dbp, put, -1); - xfs_da_brelse(trans, bp); - *eofp = (eob == 0); - return(retval); -} - -/* - * Look up a name in a leaf directory structure, replace the inode number. - * This is the external routine. - */ -STATIC int -xfs_dir_leaf_replace(xfs_da_args_t *args) -{ - int index, retval; - xfs_dabuf_t *bp; - xfs_ino_t inum; - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - - inum = args->inumber; - retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp, - XFS_DATA_FORK); - if (retval) - return(retval); - ASSERT(bp != NULL); - retval = xfs_dir_leaf_lookup_int(bp, args, &index); - if (retval == EEXIST) { - leaf = bp->data; - entry = &leaf->entries[index]; - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - /* XXX - replace assert? */ - XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber); - xfs_da_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber))); - xfs_da_buf_done(bp); - retval = 0; - } else - xfs_da_brelse(args->trans, bp); - return(retval); -} - - -/*======================================================================== - * External routines when dirsize > XFS_LBSIZE(mp). - *========================================================================*/ - -/* - * Add a name to a Btree-format directory. - * - * This will involve walking down the Btree, and may involve splitting - * leaf nodes and even splitting intermediate nodes up to and including - * the root node (a special case of an intermediate node). - */ -STATIC int -xfs_dir_node_addname(xfs_da_args_t *args) -{ - xfs_da_state_t *state; - xfs_da_state_blk_t *blk; - int retval, error; - - /* - * Fill in bucket of arguments/results/context to carry around. - */ - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; - state->blocksize = state->mp->m_sb.sb_blocksize; - state->node_ents = state->mp->m_dir_node_ents; - - /* - * Search to see if name already exists, and get back a pointer - * to where it should go. - */ - error = xfs_da_node_lookup_int(state, &retval); - if (error) - retval = error; - if (retval != ENOENT) - goto error; - blk = &state->path.blk[ state->path.active-1 ]; - ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); - retval = xfs_dir_leaf_add(blk->bp, args, blk->index); - if (retval == 0) { - /* - * Addition succeeded, update Btree hashvals. - */ - if (!args->justcheck) - xfs_da_fixhashpath(state, &state->path); - } else { - /* - * Addition failed, split as many Btree elements as required. - */ - if (args->total == 0) { - ASSERT(retval == ENOSPC); - goto error; - } - retval = xfs_da_split(state); - } -error: - xfs_da_state_free(state); - - return(retval); -} - -/* - * Remove a name from a B-tree directory. - * - * This will involve walking down the Btree, and may involve joining - * leaf nodes and even joining intermediate nodes up to and including - * the root node (a special case of an intermediate node). - */ -STATIC int -xfs_dir_node_removename(xfs_da_args_t *args) -{ - xfs_da_state_t *state; - xfs_da_state_blk_t *blk; - int retval, error; - - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; - state->blocksize = state->mp->m_sb.sb_blocksize; - state->node_ents = state->mp->m_dir_node_ents; - - /* - * Search to see if name exists, and get back a pointer to it. - */ - error = xfs_da_node_lookup_int(state, &retval); - if (error) - retval = error; - if (retval != EEXIST) { - xfs_da_state_free(state); - return(retval); - } - - /* - * Remove the name and update the hashvals in the tree. - */ - blk = &state->path.blk[ state->path.active-1 ]; - ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); - retval = xfs_dir_leaf_remove(args->trans, blk->bp, blk->index); - xfs_da_fixhashpath(state, &state->path); - - /* - * Check to see if the tree needs to be collapsed. - */ - error = 0; - if (retval) { - error = xfs_da_join(state); - } - - xfs_da_state_free(state); - if (error) - return(error); - return(0); -} - -/* - * Look up a filename in a int directory. - * Use an internal routine to actually do all the work. - */ -STATIC int -xfs_dir_node_lookup(xfs_da_args_t *args) -{ - xfs_da_state_t *state; - int retval, error, i; - - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; - state->blocksize = state->mp->m_sb.sb_blocksize; - state->node_ents = state->mp->m_dir_node_ents; - - /* - * Search to see if name exists, - * and get back a pointer to it. - */ - error = xfs_da_node_lookup_int(state, &retval); - if (error) { - retval = error; - } - - /* - * If not in a transaction, we have to release all the buffers. - */ - for (i = 0; i < state->path.active; i++) { - xfs_da_brelse(args->trans, state->path.blk[i].bp); - state->path.blk[i].bp = NULL; - } - - xfs_da_state_free(state); - return(retval); -} - -STATIC int -xfs_dir_node_getdents(xfs_trans_t *trans, xfs_inode_t *dp, uio_t *uio, - int *eofp, xfs_dirent_t *dbp, xfs_dir_put_t put) -{ - xfs_da_intnode_t *node; - xfs_da_node_entry_t *btree; - xfs_dir_leafblock_t *leaf = NULL; - xfs_dablk_t bno, nextbno; - xfs_dahash_t cookhash; - xfs_mount_t *mp; - int error, eob, i; - xfs_dabuf_t *bp; - xfs_daddr_t nextda; - - /* - * Pick up our context. - */ - mp = dp->i_mount; - bp = NULL; - bno = XFS_DA_COOKIE_BNO(mp, uio->uio_offset); - cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset); - - xfs_dir_trace_g_du("node: start", dp, uio); - - /* - * Re-find our place, even if we're confused about what our place is. - * - * First we check the block number from the magic cookie, it is a - * cache of where we ended last time. If we find a leaf block, and - * the starting hashval in that block is less than our desired - * hashval, then we run with it. - */ - if (bno > 0) { - error = xfs_da_read_buf(trans, dp, bno, -2, &bp, XFS_DATA_FORK); - if ((error != 0) && (error != EFSCORRUPTED)) - return(error); - if (bp) - leaf = bp->data; - if (bp && be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC) { - xfs_dir_trace_g_dub("node: block not a leaf", - dp, uio, bno); - xfs_da_brelse(trans, bp); - bp = NULL; - } - if (bp && INT_GET(leaf->entries[0].hashval, ARCH_CONVERT) > cookhash) { - xfs_dir_trace_g_dub("node: leaf hash too large", - dp, uio, bno); - xfs_da_brelse(trans, bp); - bp = NULL; - } - if (bp && - cookhash > INT_GET(leaf->entries[INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1].hashval, ARCH_CONVERT)) { - xfs_dir_trace_g_dub("node: leaf hash too small", - dp, uio, bno); - xfs_da_brelse(trans, bp); - bp = NULL; - } - } - - /* - * If we did not find a leaf block from the blockno in the cookie, - * or we there was no blockno in the cookie (eg: first time thru), - * the we start at the top of the Btree and re-find our hashval. - */ - if (bp == NULL) { - xfs_dir_trace_g_du("node: start at root" , dp, uio); - bno = 0; - for (;;) { - error = xfs_da_read_buf(trans, dp, bno, -1, &bp, - XFS_DATA_FORK); - if (error) - return(error); - if (bp == NULL) - return(XFS_ERROR(EFSCORRUPTED)); - node = bp->data; - if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) - break; - btree = &node->btree[0]; - xfs_dir_trace_g_dun("node: node detail", dp, uio, node); - for (i = 0; i < be16_to_cpu(node->hdr.count); btree++, i++) { - if (be32_to_cpu(btree->hashval) >= cookhash) { - bno = be32_to_cpu(btree->before); - break; - } - } - if (i == be16_to_cpu(node->hdr.count)) { - xfs_da_brelse(trans, bp); - xfs_dir_trace_g_du("node: hash beyond EOF", - dp, uio); - uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0, - XFS_DA_MAXHASH); - *eofp = 1; - return(0); - } - xfs_dir_trace_g_dub("node: going to block", - dp, uio, bno); - xfs_da_brelse(trans, bp); - } - } - ASSERT(cookhash != XFS_DA_MAXHASH); - - /* - * We've dropped down to the (first) leaf block that contains the - * hashval we are interested in. Continue rolling upward thru the - * leaf blocks until we fill up our buffer. - */ - for (;;) { - leaf = bp->data; - if (unlikely(be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC)) { - xfs_dir_trace_g_dul("node: not a leaf", dp, uio, leaf); - xfs_da_brelse(trans, bp); - XFS_CORRUPTION_ERROR("xfs_dir_node_getdents(1)", - XFS_ERRLEVEL_LOW, mp, leaf); - return XFS_ERROR(EFSCORRUPTED); - } - xfs_dir_trace_g_dul("node: leaf detail", dp, uio, leaf); - if ((nextbno = be32_to_cpu(leaf->hdr.info.forw))) { - nextda = xfs_da_reada_buf(trans, dp, nextbno, - XFS_DATA_FORK); - } else - nextda = -1; - error = xfs_dir_leaf_getdents_int(bp, dp, bno, uio, &eob, dbp, - put, nextda); - xfs_da_brelse(trans, bp); - bno = nextbno; - if (eob) { - xfs_dir_trace_g_dub("node: E-O-B", dp, uio, bno); - *eofp = 0; - return(error); - } - if (bno == 0) - break; - error = xfs_da_read_buf(trans, dp, bno, nextda, &bp, - XFS_DATA_FORK); - if (error) - return(error); - if (unlikely(bp == NULL)) { - XFS_ERROR_REPORT("xfs_dir_node_getdents(2)", - XFS_ERRLEVEL_LOW, mp); - return(XFS_ERROR(EFSCORRUPTED)); - } - } - *eofp = 1; - xfs_dir_trace_g_du("node: E-O-F", dp, uio); - return(0); -} - -/* - * Look up a filename in an int directory, replace the inode number. - * Use an internal routine to actually do the lookup. - */ -STATIC int -xfs_dir_node_replace(xfs_da_args_t *args) -{ - xfs_da_state_t *state; - xfs_da_state_blk_t *blk; - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - xfs_ino_t inum; - int retval, error, i; - xfs_dabuf_t *bp; - - state = xfs_da_state_alloc(); - state->args = args; - state->mp = args->dp->i_mount; - state->blocksize = state->mp->m_sb.sb_blocksize; - state->node_ents = state->mp->m_dir_node_ents; - inum = args->inumber; - - /* - * Search to see if name exists, - * and get back a pointer to it. - */ - error = xfs_da_node_lookup_int(state, &retval); - if (error) { - retval = error; - } - - if (retval == EEXIST) { - blk = &state->path.blk[state->path.active - 1]; - ASSERT(blk->magic == XFS_DIR_LEAF_MAGIC); - bp = blk->bp; - leaf = bp->data; - entry = &leaf->entries[blk->index]; - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - /* XXX - replace assert ? */ - XFS_DIR_SF_PUT_DIRINO(&inum, &namest->inumber); - xfs_da_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, namest, sizeof(namest->inumber))); - xfs_da_buf_done(bp); - blk->bp = NULL; - retval = 0; - } else { - i = state->path.active - 1; - xfs_da_brelse(args->trans, state->path.blk[i].bp); - state->path.blk[i].bp = NULL; - } - for (i = 0; i < state->path.active - 1; i++) { - xfs_da_brelse(args->trans, state->path.blk[i].bp); - state->path.blk[i].bp = NULL; - } - - xfs_da_state_free(state); - return(retval); -} - -#if defined(XFS_DIR_TRACE) -/* - * Add a trace buffer entry for an inode and a uio. - */ -void -xfs_dir_trace_g_du(char *where, xfs_inode_t *dp, uio_t *uio) -{ - xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DU, where, - (void *)dp, (void *)dp->i_mount, - (void *)((unsigned long)(uio->uio_offset >> 32)), - (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), - (void *)(unsigned long)uio->uio_resid, - NULL, NULL, NULL, NULL, NULL, NULL, NULL); -} - -/* - * Add a trace buffer entry for an inode and a uio. - */ -void -xfs_dir_trace_g_dub(char *where, xfs_inode_t *dp, uio_t *uio, xfs_dablk_t bno) -{ - xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUB, where, - (void *)dp, (void *)dp->i_mount, - (void *)((unsigned long)(uio->uio_offset >> 32)), - (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), - (void *)(unsigned long)uio->uio_resid, - (void *)(unsigned long)bno, - NULL, NULL, NULL, NULL, NULL, NULL); -} - -/* - * Add a trace buffer entry for an inode and a uio. - */ -void -xfs_dir_trace_g_dun(char *where, xfs_inode_t *dp, uio_t *uio, - xfs_da_intnode_t *node) -{ - int last = be16_to_cpu(node->hdr.count) - 1; - - xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUN, where, - (void *)dp, (void *)dp->i_mount, - (void *)((unsigned long)(uio->uio_offset >> 32)), - (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), - (void *)(unsigned long)uio->uio_resid, - (void *)(unsigned long)be32_to_cpu(node->hdr.info.forw), - (void *)(unsigned long) - be16_to_cpu(node->hdr.count), - (void *)(unsigned long) - be32_to_cpu(node->btree[0].hashval), - (void *)(unsigned long) - be32_to_cpu(node->btree[last].hashval), - NULL, NULL, NULL); -} - -/* - * Add a trace buffer entry for an inode and a uio. - */ -void -xfs_dir_trace_g_dul(char *where, xfs_inode_t *dp, uio_t *uio, - xfs_dir_leafblock_t *leaf) -{ - int last = INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1; - - xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUL, where, - (void *)dp, (void *)dp->i_mount, - (void *)((unsigned long)(uio->uio_offset >> 32)), - (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), - (void *)(unsigned long)uio->uio_resid, - (void *)(unsigned long)be32_to_cpu(leaf->hdr.info.forw), - (void *)(unsigned long) - INT_GET(leaf->hdr.count, ARCH_CONVERT), - (void *)(unsigned long) - INT_GET(leaf->entries[0].hashval, ARCH_CONVERT), - (void *)(unsigned long) - INT_GET(leaf->entries[last].hashval, ARCH_CONVERT), - NULL, NULL, NULL); -} - -/* - * Add a trace buffer entry for an inode and a uio. - */ -void -xfs_dir_trace_g_due(char *where, xfs_inode_t *dp, uio_t *uio, - xfs_dir_leaf_entry_t *entry) -{ - xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUE, where, - (void *)dp, (void *)dp->i_mount, - (void *)((unsigned long)(uio->uio_offset >> 32)), - (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), - (void *)(unsigned long)uio->uio_resid, - (void *)(unsigned long) - INT_GET(entry->hashval, ARCH_CONVERT), - NULL, NULL, NULL, NULL, NULL, NULL); -} - -/* - * Add a trace buffer entry for an inode and a uio. - */ -void -xfs_dir_trace_g_duc(char *where, xfs_inode_t *dp, uio_t *uio, xfs_off_t cookie) -{ - xfs_dir_trace_enter(XFS_DIR_KTRACE_G_DUC, where, - (void *)dp, (void *)dp->i_mount, - (void *)((unsigned long)(uio->uio_offset >> 32)), - (void *)((unsigned long)(uio->uio_offset & 0xFFFFFFFF)), - (void *)(unsigned long)uio->uio_resid, - (void *)((unsigned long)(cookie >> 32)), - (void *)((unsigned long)(cookie & 0xFFFFFFFF)), - NULL, NULL, NULL, NULL, NULL); -} - -/* - * Add a trace buffer entry for the arguments given to the routine, - * generic form. - */ -void -xfs_dir_trace_enter(int type, char *where, - void * a0, void * a1, - void * a2, void * a3, - void * a4, void * a5, - void * a6, void * a7, - void * a8, void * a9, - void * a10, void * a11) -{ - ASSERT(xfs_dir_trace_buf); - ktrace_enter(xfs_dir_trace_buf, (void *)(unsigned long)type, - (void *)where, - (void *)a0, (void *)a1, (void *)a2, - (void *)a3, (void *)a4, (void *)a5, - (void *)a6, (void *)a7, (void *)a8, - (void *)a9, (void *)a10, (void *)a11, - NULL, NULL); -} -#endif /* XFS_DIR_TRACE */ diff --git a/fs/xfs/xfs_dir.h b/fs/xfs/xfs_dir.h deleted file mode 100644 index 8cc8afb..0000000 --- a/fs/xfs/xfs_dir.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2000,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_DIR_H__ -#define __XFS_DIR_H__ - -/* - * Large directories are structured around Btrees where all the data - * elements are in the leaf nodes. Filenames are hashed into an int, - * then that int is used as the index into the Btree. Since the hashval - * of a filename may not be unique, we may have duplicate keys. The - * internal links in the Btree are logical block offsets into the file. - * - * Small directories use a different format and are packed as tightly - * as possible so as to fit into the literal area of the inode. - */ - -/*======================================================================== - * Function prototypes for the kernel. - *========================================================================*/ - -struct uio; -struct xfs_bmap_free; -struct xfs_da_args; -struct xfs_dinode; -struct xfs_inode; -struct xfs_mount; -struct xfs_trans; - -/* - * Directory function types. - * Put in structures (xfs_dirops_t) for v1 and v2 directories. - */ -typedef void (*xfs_dir_mount_t)(struct xfs_mount *mp); -typedef int (*xfs_dir_isempty_t)(struct xfs_inode *dp); -typedef int (*xfs_dir_init_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - struct xfs_inode *pdp); -typedef int (*xfs_dir_createname_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name, - int namelen, - xfs_ino_t inum, - xfs_fsblock_t *first, - struct xfs_bmap_free *flist, - xfs_extlen_t total); -typedef int (*xfs_dir_lookup_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name, - int namelen, - xfs_ino_t *inum); -typedef int (*xfs_dir_removename_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name, - int namelen, - xfs_ino_t ino, - xfs_fsblock_t *first, - struct xfs_bmap_free *flist, - xfs_extlen_t total); -typedef int (*xfs_dir_getdents_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - struct uio *uio, - int *eofp); -typedef int (*xfs_dir_replace_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name, - int namelen, - xfs_ino_t inum, - xfs_fsblock_t *first, - struct xfs_bmap_free *flist, - xfs_extlen_t total); -typedef int (*xfs_dir_canenter_t)(struct xfs_trans *tp, - struct xfs_inode *dp, - char *name, - int namelen); -typedef int (*xfs_dir_shortform_validate_ondisk_t)(struct xfs_mount *mp, - struct xfs_dinode *dip); -typedef int (*xfs_dir_shortform_to_single_t)(struct xfs_da_args *args); - -typedef struct xfs_dirops { - xfs_dir_mount_t xd_mount; - xfs_dir_isempty_t xd_isempty; - xfs_dir_init_t xd_init; - xfs_dir_createname_t xd_createname; - xfs_dir_lookup_t xd_lookup; - xfs_dir_removename_t xd_removename; - xfs_dir_getdents_t xd_getdents; - xfs_dir_replace_t xd_replace; - xfs_dir_canenter_t xd_canenter; - xfs_dir_shortform_validate_ondisk_t xd_shortform_validate_ondisk; - xfs_dir_shortform_to_single_t xd_shortform_to_single; -} xfs_dirops_t; - -/* - * Overall external interface routines. - */ -void xfs_dir_startup(void); /* called exactly once */ - -#define XFS_DIR_MOUNT(mp) \ - ((mp)->m_dirops.xd_mount(mp)) -#define XFS_DIR_ISEMPTY(mp,dp) \ - ((mp)->m_dirops.xd_isempty(dp)) -#define XFS_DIR_INIT(mp,tp,dp,pdp) \ - ((mp)->m_dirops.xd_init(tp,dp,pdp)) -#define XFS_DIR_CREATENAME(mp,tp,dp,name,namelen,inum,first,flist,total) \ - ((mp)->m_dirops.xd_createname(tp,dp,name,namelen,inum,first,flist,\ - total)) -#define XFS_DIR_LOOKUP(mp,tp,dp,name,namelen,inum) \ - ((mp)->m_dirops.xd_lookup(tp,dp,name,namelen,inum)) -#define XFS_DIR_REMOVENAME(mp,tp,dp,name,namelen,ino,first,flist,total) \ - ((mp)->m_dirops.xd_removename(tp,dp,name,namelen,ino,first,flist,total)) -#define XFS_DIR_GETDENTS(mp,tp,dp,uio,eofp) \ - ((mp)->m_dirops.xd_getdents(tp,dp,uio,eofp)) -#define XFS_DIR_REPLACE(mp,tp,dp,name,namelen,inum,first,flist,total) \ - ((mp)->m_dirops.xd_replace(tp,dp,name,namelen,inum,first,flist,total)) -#define XFS_DIR_CANENTER(mp,tp,dp,name,namelen) \ - ((mp)->m_dirops.xd_canenter(tp,dp,name,namelen)) -#define XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp,dip) \ - ((mp)->m_dirops.xd_shortform_validate_ondisk(mp,dip)) -#define XFS_DIR_SHORTFORM_TO_SINGLE(mp,args) \ - ((mp)->m_dirops.xd_shortform_to_single(args)) - -#define XFS_DIR_IS_V1(mp) ((mp)->m_dirversion == 1) -#define XFS_DIR_IS_V2(mp) ((mp)->m_dirversion == 2) -extern xfs_dirops_t xfsv1_dirops; -extern xfs_dirops_t xfsv2_dirops; - -#endif /* __XFS_DIR_H__ */ diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c index 022c839..8edbe1a 100644 --- a/fs/xfs/xfs_dir2.c +++ b/fs/xfs/xfs_dir2.c @@ -24,21 +24,18 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_bmap.h" -#include "xfs_dir_leaf.h" #include "xfs_dir2_data.h" #include "xfs_dir2_leaf.h" #include "xfs_dir2_block.h" @@ -46,69 +43,14 @@ #include "xfs_dir2_node.h" #include "xfs_dir2_trace.h" #include "xfs_error.h" -/* - * Declarations for interface routines. - */ -static void xfs_dir2_mount(xfs_mount_t *mp); -static int xfs_dir2_isempty(xfs_inode_t *dp); -static int xfs_dir2_init(xfs_trans_t *tp, xfs_inode_t *dp, - xfs_inode_t *pdp); -static int xfs_dir2_createname(xfs_trans_t *tp, xfs_inode_t *dp, - char *name, int namelen, xfs_ino_t inum, - xfs_fsblock_t *first, - xfs_bmap_free_t *flist, xfs_extlen_t total); -static int xfs_dir2_lookup(xfs_trans_t *tp, xfs_inode_t *dp, char *name, - int namelen, xfs_ino_t *inum); -static int xfs_dir2_removename(xfs_trans_t *tp, xfs_inode_t *dp, - char *name, int namelen, xfs_ino_t ino, - xfs_fsblock_t *first, - xfs_bmap_free_t *flist, xfs_extlen_t total); -static int xfs_dir2_getdents(xfs_trans_t *tp, xfs_inode_t *dp, uio_t *uio, - int *eofp); -static int xfs_dir2_replace(xfs_trans_t *tp, xfs_inode_t *dp, char *name, - int namelen, xfs_ino_t inum, - xfs_fsblock_t *first, xfs_bmap_free_t *flist, - xfs_extlen_t total); -static int xfs_dir2_canenter(xfs_trans_t *tp, xfs_inode_t *dp, char *name, - int namelen); -static int xfs_dir2_shortform_validate_ondisk(xfs_mount_t *mp, - xfs_dinode_t *dip); - -/* - * Utility routine declarations. - */ static int xfs_dir2_put_dirent64_direct(xfs_dir2_put_args_t *pa); static int xfs_dir2_put_dirent64_uio(xfs_dir2_put_args_t *pa); -/* - * Directory operations vector. - */ -xfs_dirops_t xfsv2_dirops = { - .xd_mount = xfs_dir2_mount, - .xd_isempty = xfs_dir2_isempty, - .xd_init = xfs_dir2_init, - .xd_createname = xfs_dir2_createname, - .xd_lookup = xfs_dir2_lookup, - .xd_removename = xfs_dir2_removename, - .xd_getdents = xfs_dir2_getdents, - .xd_replace = xfs_dir2_replace, - .xd_canenter = xfs_dir2_canenter, - .xd_shortform_validate_ondisk = xfs_dir2_shortform_validate_ondisk, - .xd_shortform_to_single = xfs_dir2_sf_to_block, -}; - -/* - * Interface routines. - */ - -/* - * Initialize directory-related fields in the mount structure. - */ -static void -xfs_dir2_mount( - xfs_mount_t *mp) /* filesystem mount point */ +void +xfs_dir_mount( + xfs_mount_t *mp) { - mp->m_dirversion = 2; + ASSERT(XFS_SB_VERSION_HASDIRV2(&mp->m_sb)); ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <= XFS_MAX_BLOCKSIZE); mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog); @@ -128,19 +70,15 @@ xfs_dir2_mount( /* * Return 1 if directory contains only "." and "..". */ -static int /* return code */ -xfs_dir2_isempty( - xfs_inode_t *dp) /* incore inode structure */ +int +xfs_dir_isempty( + xfs_inode_t *dp) { - xfs_dir2_sf_t *sfp; /* shortform directory structure */ + xfs_dir2_sf_t *sfp; ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - /* - * Might happen during shutdown. - */ - if (dp->i_d.di_size == 0) { + if (dp->i_d.di_size == 0) /* might happen during shutdown. */ return 1; - } if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp)) return 0; sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; @@ -148,53 +86,83 @@ xfs_dir2_isempty( } /* + * Validate a given inode number. + */ +int +xfs_dir_ino_validate( + xfs_mount_t *mp, + xfs_ino_t ino) +{ + xfs_agblock_t agblkno; + xfs_agino_t agino; + xfs_agnumber_t agno; + int ino_ok; + int ioff; + + agno = XFS_INO_TO_AGNO(mp, ino); + agblkno = XFS_INO_TO_AGBNO(mp, ino); + ioff = XFS_INO_TO_OFFSET(mp, ino); + agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff); + ino_ok = + agno < mp->m_sb.sb_agcount && + agblkno < mp->m_sb.sb_agblocks && + agblkno != 0 && + ioff < (1 << mp->m_sb.sb_inopblog) && + XFS_AGINO_TO_INO(mp, agno, agino) == ino; + if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE, + XFS_RANDOM_DIR_INO_VALIDATE))) { + xfs_fs_cmn_err(CE_WARN, mp, "Invalid inode number 0x%Lx", + (unsigned long long) ino); + XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp); + return XFS_ERROR(EFSCORRUPTED); + } + return 0; +} + +/* * Initialize a directory with its "." and ".." entries. */ -static int /* error */ -xfs_dir2_init( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ - xfs_inode_t *pdp) /* incore parent directory inode */ +int +xfs_dir_init( + xfs_trans_t *tp, + xfs_inode_t *dp, + xfs_inode_t *pdp) { - xfs_da_args_t args; /* operation arguments */ - int error; /* error return value */ + xfs_da_args_t args; + int error; memset((char *)&args, 0, sizeof(args)); args.dp = dp; args.trans = tp; ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino))) { + if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino))) return error; - } return xfs_dir2_sf_create(&args, pdp->i_ino); } /* Enter a name in a directory. */ -static int /* error */ -xfs_dir2_createname( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ - char *name, /* new entry name */ - int namelen, /* new entry name length */ +int +xfs_dir_createname( + xfs_trans_t *tp, + xfs_inode_t *dp, + char *name, + int namelen, xfs_ino_t inum, /* new entry inode number */ xfs_fsblock_t *first, /* bmap's firstblock */ xfs_bmap_free_t *flist, /* bmap's freeblock list */ xfs_extlen_t total) /* bmap's total block count */ { - xfs_da_args_t args; /* operation arguments */ - int rval; /* return value */ + xfs_da_args_t args; + int rval; int v; /* type-checking value */ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) { + if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) return rval; - } XFS_STATS_INC(xs_dir_create); - /* - * Fill in the arg structure for this request. - */ + args.name = name; args.namelen = namelen; args.hashval = xfs_da_hashname(name, namelen); @@ -207,18 +175,16 @@ xfs_dir2_createname( args.trans = tp; args.justcheck = 0; args.addname = args.oknoent = 1; - /* - * Decide on what work routines to call based on the inode size. - */ + if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_addname(&args); - else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { + else if ((rval = xfs_dir2_isblock(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_block_addname(&args); - else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { + else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_leaf_addname(&args); else rval = xfs_dir2_node_addname(&args); @@ -228,24 +194,21 @@ xfs_dir2_createname( /* * Lookup a name in a directory, give back the inode number. */ -static int /* error */ -xfs_dir2_lookup( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ - char *name, /* lookup name */ - int namelen, /* lookup name length */ +int +xfs_dir_lookup( + xfs_trans_t *tp, + xfs_inode_t *dp, + char *name, + int namelen, xfs_ino_t *inum) /* out: inode number */ { - xfs_da_args_t args; /* operation arguments */ - int rval; /* return value */ + xfs_da_args_t args; + int rval; int v; /* type-checking value */ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); XFS_STATS_INC(xs_dir_lookup); - /* - * Fill in the arg structure for this request. - */ args.name = name; args.namelen = namelen; args.hashval = xfs_da_hashname(name, namelen); @@ -258,18 +221,16 @@ xfs_dir2_lookup( args.trans = tp; args.justcheck = args.addname = 0; args.oknoent = 1; - /* - * Decide on what work routines to call based on the inode size. - */ + if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_lookup(&args); - else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { + else if ((rval = xfs_dir2_isblock(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_block_lookup(&args); - else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { + else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_leaf_lookup(&args); else rval = xfs_dir2_node_lookup(&args); @@ -283,26 +244,24 @@ xfs_dir2_lookup( /* * Remove an entry from a directory. */ -static int /* error */ -xfs_dir2_removename( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ - char *name, /* name of entry to remove */ - int namelen, /* name length of entry to remove */ - xfs_ino_t ino, /* inode number of entry to remove */ +int +xfs_dir_removename( + xfs_trans_t *tp, + xfs_inode_t *dp, + char *name, + int namelen, + xfs_ino_t ino, xfs_fsblock_t *first, /* bmap's firstblock */ xfs_bmap_free_t *flist, /* bmap's freeblock list */ xfs_extlen_t total) /* bmap's total block count */ { - xfs_da_args_t args; /* operation arguments */ - int rval; /* return value */ + xfs_da_args_t args; + int rval; int v; /* type-checking value */ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); XFS_STATS_INC(xs_dir_remove); - /* - * Fill in the arg structure for this request. - */ + args.name = name; args.namelen = namelen; args.hashval = xfs_da_hashname(name, namelen); @@ -314,18 +273,16 @@ xfs_dir2_removename( args.whichfork = XFS_DATA_FORK; args.trans = tp; args.justcheck = args.addname = args.oknoent = 0; - /* - * Decide on what work routines to call based on the inode size. - */ + if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_removename(&args); - else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { + else if ((rval = xfs_dir2_isblock(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_block_removename(&args); - else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { + else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_leaf_removename(&args); else rval = xfs_dir2_node_removename(&args); @@ -335,10 +292,10 @@ xfs_dir2_removename( /* * Read a directory. */ -static int /* error */ -xfs_dir2_getdents( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ +int +xfs_dir_getdents( + xfs_trans_t *tp, + xfs_inode_t *dp, uio_t *uio, /* caller's buffer control */ int *eofp) /* out: eof reached */ { @@ -367,14 +324,11 @@ xfs_dir2_getdents( } *eofp = 0; - /* - * Decide on what work routines to call based on the inode size. - */ if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_getdents(dp, uio, eofp, dbp, put); - else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { + else if ((rval = xfs_dir2_isblock(tp, dp, &v))) ; - } else if (v) + else if (v) rval = xfs_dir2_block_getdents(tp, dp, uio, eofp, dbp, put); else rval = xfs_dir2_leaf_getdents(tp, dp, uio, eofp, dbp, put); @@ -386,29 +340,26 @@ xfs_dir2_getdents( /* * Replace the inode number of a directory entry. */ -static int /* error */ -xfs_dir2_replace( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ +int +xfs_dir_replace( + xfs_trans_t *tp, + xfs_inode_t *dp, char *name, /* name of entry to replace */ - int namelen, /* name length of entry to replace */ + int namelen, xfs_ino_t inum, /* new inode number */ xfs_fsblock_t *first, /* bmap's firstblock */ xfs_bmap_free_t *flist, /* bmap's freeblock list */ xfs_extlen_t total) /* bmap's total block count */ { - xfs_da_args_t args; /* operation arguments */ - int rval; /* return value */ + xfs_da_args_t args; + int rval; int v; /* type-checking value */ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) { + if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) return rval; - } - /* - * Fill in the arg structure for this request. - */ + args.name = name; args.namelen = namelen; args.hashval = xfs_da_hashname(name, namelen); @@ -420,18 +371,16 @@ xfs_dir2_replace( args.whichfork = XFS_DATA_FORK; args.trans = tp; args.justcheck = args.addname = args.oknoent = 0; - /* - * Decide on what work routines to call based on the inode size. - */ + if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_replace(&args); - else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { + else if ((rval = xfs_dir2_isblock(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_block_replace(&args); - else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { + else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_leaf_replace(&args); else rval = xfs_dir2_node_replace(&args); @@ -441,21 +390,19 @@ xfs_dir2_replace( /* * See if this entry can be added to the directory without allocating space. */ -static int /* error */ -xfs_dir2_canenter( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ +int +xfs_dir_canenter( + xfs_trans_t *tp, + xfs_inode_t *dp, char *name, /* name of entry to add */ - int namelen) /* name length of entry to add */ + int namelen) { - xfs_da_args_t args; /* operation arguments */ - int rval; /* return value */ + xfs_da_args_t args; + int rval; int v; /* type-checking value */ ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); - /* - * Fill in the arg structure for this request. - */ + args.name = name; args.namelen = namelen; args.hashval = xfs_da_hashname(name, namelen); @@ -467,18 +414,16 @@ xfs_dir2_canenter( args.whichfork = XFS_DATA_FORK; args.trans = tp; args.justcheck = args.addname = args.oknoent = 1; - /* - * Decide on what work routines to call based on the inode size. - */ + if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) rval = xfs_dir2_sf_addname(&args); - else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { + else if ((rval = xfs_dir2_isblock(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_block_addname(&args); - else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { + else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) return rval; - } else if (v) + else if (v) rval = xfs_dir2_leaf_addname(&args); else rval = xfs_dir2_node_addname(&args); @@ -486,19 +431,6 @@ xfs_dir2_canenter( } /* - * Dummy routine for shortform inode validation. - * Can't really do this. - */ -/* ARGSUSED */ -static int /* error */ -xfs_dir2_shortform_validate_ondisk( - xfs_mount_t *mp, /* filesystem mount point */ - xfs_dinode_t *dip) /* ondisk inode */ -{ - return 0; -} - -/* * Utility routines. */ @@ -507,24 +439,24 @@ xfs_dir2_shortform_validate_ondisk( * This routine is for data and free blocks, not leaf/node blocks * which are handled by xfs_da_grow_inode. */ -int /* error */ +int xfs_dir2_grow_inode( - xfs_da_args_t *args, /* operation arguments */ + xfs_da_args_t *args, int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */ xfs_dir2_db_t *dbp) /* out: block number added */ { xfs_fileoff_t bno; /* directory offset of new block */ int count; /* count of filesystem blocks */ xfs_inode_t *dp; /* incore directory inode */ - int error; /* error return value */ + int error; int got; /* blocks actually mapped */ - int i; /* temp mapping index */ + int i; xfs_bmbt_irec_t map; /* single structure for bmap */ int mapi; /* mapping index */ xfs_bmbt_irec_t *mapp; /* bmap mapping structure(s) */ - xfs_mount_t *mp; /* filesystem mount point */ + xfs_mount_t *mp; int nmap; /* number of bmap entries */ - xfs_trans_t *tp; /* transaction pointer */ + xfs_trans_t *tp; xfs_dir2_trace_args_s("grow_inode", args, space); dp = args->dp; @@ -538,9 +470,8 @@ xfs_dir2_grow_inode( /* * Find the first hole for our block. */ - if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, XFS_DATA_FORK))) { + if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, XFS_DATA_FORK))) return error; - } nmap = 1; ASSERT(args->firstblock != NULL); /* @@ -549,13 +480,9 @@ xfs_dir2_grow_inode( if ((error = xfs_bmapi(tp, dp, bno, count, XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG, args->firstblock, args->total, &map, &nmap, - args->flist))) { + args->flist, NULL))) return error; - } ASSERT(nmap <= 1); - /* - * Got it in 1. - */ if (nmap == 1) { mapp = ↦ mapi = 1; @@ -585,7 +512,8 @@ xfs_dir2_grow_inode( if ((error = xfs_bmapi(tp, dp, b, c, XFS_BMAPI_WRITE|XFS_BMAPI_METADATA, args->firstblock, args->total, - &mapp[mapi], &nmap, args->flist))) { + &mapp[mapi], &nmap, args->flist, + NULL))) { kmem_free(mapp, sizeof(*mapp) * count); return error; } @@ -645,20 +573,19 @@ xfs_dir2_grow_inode( /* * See if the directory is a single-block form directory. */ -int /* error */ +int xfs_dir2_isblock( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ + xfs_trans_t *tp, + xfs_inode_t *dp, int *vp) /* out: 1 is block, 0 is not block */ { xfs_fileoff_t last; /* last file offset */ - xfs_mount_t *mp; /* filesystem mount point */ - int rval; /* return value */ + xfs_mount_t *mp; + int rval; mp = dp->i_mount; - if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) { + if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) return rval; - } rval = XFS_FSB_TO_B(mp, last) == mp->m_dirblksize; ASSERT(rval == 0 || dp->i_d.di_size == mp->m_dirblksize); *vp = rval; @@ -668,20 +595,19 @@ xfs_dir2_isblock( /* * See if the directory is a single-leaf form directory. */ -int /* error */ +int xfs_dir2_isleaf( - xfs_trans_t *tp, /* transaction pointer */ - xfs_inode_t *dp, /* incore directory inode */ + xfs_trans_t *tp, + xfs_inode_t *dp, int *vp) /* out: 1 is leaf, 0 is not leaf */ { xfs_fileoff_t last; /* last file offset */ - xfs_mount_t *mp; /* filesystem mount point */ - int rval; /* return value */ + xfs_mount_t *mp; + int rval; mp = dp->i_mount; - if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) { + if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) return rval; - } *vp = last == mp->m_dirleafblk + (1 << mp->m_sb.sb_dirblklog); return 0; } @@ -689,9 +615,9 @@ xfs_dir2_isleaf( /* * Getdents put routine for 64-bit ABI, direct form. */ -static int /* error */ +static int xfs_dir2_put_dirent64_direct( - xfs_dir2_put_args_t *pa) /* argument bundle */ + xfs_dir2_put_args_t *pa) { xfs_dirent_t *idbp; /* dirent pointer */ iovec_t *iovp; /* io vector */ @@ -726,9 +652,9 @@ xfs_dir2_put_dirent64_direct( /* * Getdents put routine for 64-bit ABI, uio form. */ -static int /* error */ +static int xfs_dir2_put_dirent64_uio( - xfs_dir2_put_args_t *pa) /* argument bundle */ + xfs_dir2_put_args_t *pa) { xfs_dirent_t *idbp; /* dirent pointer */ int namelen; /* entry name length */ @@ -764,17 +690,17 @@ xfs_dir2_put_dirent64_uio( */ int xfs_dir2_shrink_inode( - xfs_da_args_t *args, /* operation arguments */ - xfs_dir2_db_t db, /* directory block number */ - xfs_dabuf_t *bp) /* block's buffer */ + xfs_da_args_t *args, + xfs_dir2_db_t db, + xfs_dabuf_t *bp) { xfs_fileoff_t bno; /* directory file offset */ xfs_dablk_t da; /* directory file offset */ int done; /* bunmap is finished */ - xfs_inode_t *dp; /* incore directory inode */ - int error; /* error return value */ - xfs_mount_t *mp; /* filesystem mount point */ - xfs_trans_t *tp; /* transaction pointer */ + xfs_inode_t *dp; + int error; + xfs_mount_t *mp; + xfs_trans_t *tp; xfs_dir2_trace_args_db("shrink_inode", args, db, bp); dp = args->dp; @@ -786,7 +712,7 @@ xfs_dir2_shrink_inode( */ if ((error = xfs_bunmapi(tp, dp, da, mp->m_dirblkfsbs, XFS_BMAPI_METADATA, 0, args->firstblock, args->flist, - &done))) { + NULL, &done))) { /* * ENOSPC actually can happen if we're in a removename with * no space reservation, and the resulting block removal diff --git a/fs/xfs/xfs_dir2.h b/fs/xfs/xfs_dir2.h index 7dd364b..86560b6 100644 --- a/fs/xfs/xfs_dir2.h +++ b/fs/xfs/xfs_dir2.h @@ -22,7 +22,9 @@ struct uio; struct xfs_dabuf; struct xfs_da_args; struct xfs_dir2_put_args; +struct xfs_bmap_free; struct xfs_inode; +struct xfs_mount; struct xfs_trans; /* @@ -73,7 +75,35 @@ typedef struct xfs_dir2_put_args { } xfs_dir2_put_args_t; /* - * Other interfaces used by the rest of the dir v2 code. + * Generic directory interface routines + */ +extern void xfs_dir_startup(void); +extern void xfs_dir_mount(struct xfs_mount *mp); +extern int xfs_dir_isempty(struct xfs_inode *dp); +extern int xfs_dir_init(struct xfs_trans *tp, struct xfs_inode *dp, + struct xfs_inode *pdp); +extern int xfs_dir_createname(struct xfs_trans *tp, struct xfs_inode *dp, + char *name, int namelen, xfs_ino_t inum, + xfs_fsblock_t *first, + struct xfs_bmap_free *flist, xfs_extlen_t tot); +extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp, + char *name, int namelen, xfs_ino_t *inum); +extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp, + char *name, int namelen, xfs_ino_t ino, + xfs_fsblock_t *first, + struct xfs_bmap_free *flist, xfs_extlen_t tot); +extern int xfs_dir_getdents(struct xfs_trans *tp, struct xfs_inode *dp, + uio_t *uio, int *eofp); +extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp, + char *name, int namelen, xfs_ino_t inum, + xfs_fsblock_t *first, + struct xfs_bmap_free *flist, xfs_extlen_t tot); +extern int xfs_dir_canenter(struct xfs_trans *tp, struct xfs_inode *dp, + char *name, int namelen); +extern int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino); + +/* + * Utility routines for v2 directories. */ extern int xfs_dir2_grow_inode(struct xfs_da_args *args, int space, xfs_dir2_db_t *dbp); diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c index 972ded5..9d7438b 100644 --- a/fs/xfs/xfs_dir2_block.c +++ b/fs/xfs/xfs_dir2_block.c @@ -22,19 +22,16 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" -#include "xfs_dir_leaf.h" #include "xfs_dir2_data.h" #include "xfs_dir2_leaf.h" #include "xfs_dir2_block.h" @@ -51,6 +48,18 @@ static int xfs_dir2_block_lookup_int(xfs int *entno); static int xfs_dir2_block_sort(const void *a, const void *b); +static xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot; + +/* + * One-time startup routine called from xfs_init(). + */ +void +xfs_dir_startup(void) +{ + xfs_dir_hash_dot = xfs_da_hashname(".", 1); + xfs_dir_hash_dotdot = xfs_da_hashname("..", 2); +} + /* * Add an entry to a block directory. */ @@ -400,7 +409,7 @@ xfs_dir2_block_addname( /* * Create the new data entry. */ - INT_SET(dep->inumber, ARCH_CONVERT, args->inumber); + dep->inumber = cpu_to_be64(args->inumber); dep->namelen = args->namelen; memcpy(dep->name, args->name, args->namelen); tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); @@ -508,7 +517,7 @@ xfs_dir2_block_getdents( p.cook = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, mp->m_dirdatablk, ptr - (char *)block); - p.ino = INT_GET(dep->inumber, ARCH_CONVERT); + p.ino = be64_to_cpu(dep->inumber); #if XFS_BIG_INUMS p.ino += mp->m_inoadd; #endif @@ -626,7 +635,7 @@ xfs_dir2_block_lookup( /* * Fill in inode number, release the block. */ - args->inumber = INT_GET(dep->inumber, ARCH_CONVERT); + args->inumber = be64_to_cpu(dep->inumber); xfs_da_brelse(args->trans, bp); return XFS_ERROR(EEXIST); } @@ -844,11 +853,11 @@ xfs_dir2_block_replace( */ dep = (xfs_dir2_data_entry_t *) ((char *)block + XFS_DIR2_DATAPTR_TO_OFF(mp, be32_to_cpu(blp[ent].address))); - ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) != args->inumber); + ASSERT(be64_to_cpu(dep->inumber) != args->inumber); /* * Change the inode number to the new value. */ - INT_SET(dep->inumber, ARCH_CONVERT, args->inumber); + dep->inumber = cpu_to_be64(args->inumber); xfs_dir2_data_log_entry(args->trans, bp, dep); xfs_dir2_data_check(dp, bp); xfs_da_buf_done(bp); @@ -1130,7 +1139,7 @@ xfs_dir2_sf_to_block( */ dep = (xfs_dir2_data_entry_t *) ((char *)block + XFS_DIR2_DATA_DOT_OFFSET); - INT_SET(dep->inumber, ARCH_CONVERT, dp->i_ino); + dep->inumber = cpu_to_be64(dp->i_ino); dep->namelen = 1; dep->name[0] = '.'; tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); @@ -1144,7 +1153,7 @@ xfs_dir2_sf_to_block( */ dep = (xfs_dir2_data_entry_t *) ((char *)block + XFS_DIR2_DATA_DOTDOT_OFFSET); - INT_SET(dep->inumber, ARCH_CONVERT, XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent)); + dep->inumber = cpu_to_be64(XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent)); dep->namelen = 2; dep->name[0] = dep->name[1] = '.'; tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); @@ -1193,7 +1202,7 @@ xfs_dir2_sf_to_block( * Copy a real entry. */ dep = (xfs_dir2_data_entry_t *)((char *)block + newoffset); - INT_SET(dep->inumber, ARCH_CONVERT, XFS_DIR2_SF_GET_INUMBER(sfp, + dep->inumber = cpu_to_be64(XFS_DIR2_SF_GET_INUMBER(sfp, XFS_DIR2_SF_INUMBERP(sfep))); dep->namelen = sfep->namelen; memcpy(dep->name, sfep->name, dep->namelen); diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c index bb3d03f..f7c7992 100644 --- a/fs/xfs/xfs_dir2_data.c +++ b/fs/xfs/xfs_dir2_data.c @@ -22,18 +22,15 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_dir_leaf.h" #include "xfs_dir2_data.h" #include "xfs_dir2_leaf.h" #include "xfs_dir2_block.h" @@ -133,7 +130,7 @@ xfs_dir2_data_check( */ dep = (xfs_dir2_data_entry_t *)p; ASSERT(dep->namelen != 0); - ASSERT(xfs_dir_ino_validate(mp, INT_GET(dep->inumber, ARCH_CONVERT)) == 0); + ASSERT(xfs_dir_ino_validate(mp, be64_to_cpu(dep->inumber)) == 0); ASSERT(be16_to_cpu(*XFS_DIR2_DATA_ENTRY_TAG_P(dep)) == (char *)dep - (char *)d); count++; diff --git a/fs/xfs/xfs_dir2_data.h b/fs/xfs/xfs_dir2_data.h index 0847cbb..a6ae2d2 100644 --- a/fs/xfs/xfs_dir2_data.h +++ b/fs/xfs/xfs_dir2_data.h @@ -85,11 +85,11 @@ typedef struct xfs_dir2_data_hdr { * Tag appears as the last 2 bytes. */ typedef struct xfs_dir2_data_entry { - xfs_ino_t inumber; /* inode number */ - __uint8_t namelen; /* name length */ - __uint8_t name[1]; /* name bytes, no null */ + __be64 inumber; /* inode number */ + __u8 namelen; /* name length */ + __u8 name[1]; /* name bytes, no null */ /* variable offset */ - xfs_dir2_data_off_t tag; /* starting offset of us */ + __be16 tag; /* starting offset of us */ } xfs_dir2_data_entry_t; /* diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index 0f5e2f2..b1cf1fb 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_attr_sf.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" @@ -407,7 +405,7 @@ xfs_dir2_leaf_addname( * Initialize our new entry (at last). */ dep = (xfs_dir2_data_entry_t *)dup; - INT_SET(dep->inumber, ARCH_CONVERT, args->inumber); + dep->inumber = cpu_to_be64(args->inumber); dep->namelen = args->namelen; memcpy(dep->name, args->name, dep->namelen); tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); @@ -884,7 +882,7 @@ xfs_dir2_leaf_getdents( XFS_DIR2_BYTE_TO_DA(mp, XFS_DIR2_LEAF_OFFSET) - map_off, XFS_BMAPI_METADATA, NULL, 0, - &map[map_valid], &nmap, NULL); + &map[map_valid], &nmap, NULL, NULL); /* * Don't know if we should ignore this or * try to return an error. @@ -1098,7 +1096,7 @@ xfs_dir2_leaf_getdents( p->cook = XFS_DIR2_BYTE_TO_DATAPTR(mp, curoff + length); - p->ino = INT_GET(dep->inumber, ARCH_CONVERT); + p->ino = be64_to_cpu(dep->inumber); #if XFS_BIG_INUMS p->ino += mp->m_inoadd; #endif @@ -1319,7 +1317,7 @@ xfs_dir2_leaf_lookup( /* * Return the found inode number. */ - args->inumber = INT_GET(dep->inumber, ARCH_CONVERT); + args->inumber = be64_to_cpu(dep->inumber); xfs_da_brelse(tp, dbp); xfs_da_brelse(tp, lbp); return XFS_ERROR(EEXIST); @@ -1606,11 +1604,11 @@ xfs_dir2_leaf_replace( dep = (xfs_dir2_data_entry_t *) ((char *)dbp->data + XFS_DIR2_DATAPTR_TO_OFF(dp->i_mount, be32_to_cpu(lep->address))); - ASSERT(args->inumber != INT_GET(dep->inumber, ARCH_CONVERT)); + ASSERT(args->inumber != be64_to_cpu(dep->inumber)); /* * Put the new inode number in, log it. */ - INT_SET(dep->inumber, ARCH_CONVERT, args->inumber); + dep->inumber = cpu_to_be64(args->inumber); tp = args->trans; xfs_dir2_data_log_entry(tp, dbp, dep); xfs_da_buf_done(dbp); diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index ac511ab..9ca7171 100644 --- a/fs/xfs/xfs_dir2_node.c +++ b/fs/xfs/xfs_dir2_node.c @@ -22,13 +22,11 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -505,7 +503,6 @@ #endif XFS_DATA_FORK))) { return error; } - curfdb = newfdb; free = curbp->data; ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC); @@ -527,8 +524,11 @@ #endif if (unlikely(be16_to_cpu(free->bests[fi]) == NULLDATAOFF)) { XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int", XFS_ERRLEVEL_LOW, mp); + if (curfdb != newfdb) + xfs_da_brelse(tp, curbp); return XFS_ERROR(EFSCORRUPTED); } + curfdb = newfdb; if (be16_to_cpu(free->bests[fi]) >= length) { *indexp = index; state->extravalid = 1; @@ -580,7 +580,7 @@ #endif if (dep->namelen == args->namelen && dep->name[0] == args->name[0] && memcmp(dep->name, args->name, args->namelen) == 0) { - args->inumber = INT_GET(dep->inumber, ARCH_CONVERT); + args->inumber = be64_to_cpu(dep->inumber); *indexp = index; state->extravalid = 1; state->extrablk.bp = curbp; @@ -970,7 +970,7 @@ xfs_dir2_leafn_remove( /* * One less used entry in the free table. */ - free->hdr.nused = cpu_to_be32(-1); + be32_add(&free->hdr.nused, -1); xfs_dir2_free_log_header(tp, fbp); /* * If this was the last entry in the table, we can @@ -1695,7 +1695,7 @@ xfs_dir2_node_addname_int( * Fill in the new entry and log it. */ dep = (xfs_dir2_data_entry_t *)dup; - INT_SET(dep->inumber, ARCH_CONVERT, args->inumber); + dep->inumber = cpu_to_be64(args->inumber); dep->namelen = args->namelen; memcpy(dep->name, args->name, dep->namelen); tagp = XFS_DIR2_DATA_ENTRY_TAG_P(dep); @@ -1905,11 +1905,11 @@ xfs_dir2_node_replace( dep = (xfs_dir2_data_entry_t *) ((char *)data + XFS_DIR2_DATAPTR_TO_OFF(state->mp, be32_to_cpu(lep->address))); - ASSERT(inum != INT_GET(dep->inumber, ARCH_CONVERT)); + ASSERT(inum != be64_to_cpu(dep->inumber)); /* * Fill in the new inode number and log the entry. */ - INT_SET(dep->inumber, ARCH_CONVERT, inum); + dep->inumber = cpu_to_be64(inum); xfs_dir2_data_log_entry(args->trans, state->extrablk.bp, dep); rval = 0; } diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c index d98a41d..0cd77b1 100644 --- a/fs/xfs/xfs_dir2_sf.c +++ b/fs/xfs/xfs_dir2_sf.c @@ -22,19 +22,16 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" -#include "xfs_dir_leaf.h" #include "xfs_error.h" #include "xfs_dir2_data.h" #include "xfs_dir2_leaf.h" @@ -117,13 +114,13 @@ xfs_dir2_block_sfsize( dep->name[0] == '.' && dep->name[1] == '.'; #if XFS_BIG_INUMS if (!isdot) - i8count += INT_GET(dep->inumber, ARCH_CONVERT) > XFS_DIR2_MAX_SHORT_INUM; + i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM; #endif if (!isdot && !isdotdot) { count++; namelen += dep->namelen; } else if (isdotdot) - parent = INT_GET(dep->inumber, ARCH_CONVERT); + parent = be64_to_cpu(dep->inumber); /* * Calculate the new size, see if we should give up yet. */ @@ -229,13 +226,13 @@ xfs_dir2_block_to_sf( * Skip . */ if (dep->namelen == 1 && dep->name[0] == '.') - ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == dp->i_ino); + ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino); /* * Skip .., but make sure the inode number is right. */ else if (dep->namelen == 2 && dep->name[0] == '.' && dep->name[1] == '.') - ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) == + ASSERT(be64_to_cpu(dep->inumber) == XFS_DIR2_SF_GET_INUMBER(sfp, &sfp->hdr.parent)); /* * Normal entry, copy it into shortform. @@ -246,7 +243,7 @@ xfs_dir2_block_to_sf( (xfs_dir2_data_aoff_t) ((char *)dep - (char *)block)); memcpy(sfep->name, dep->name, dep->namelen); - temp=INT_GET(dep->inumber, ARCH_CONVERT); + temp = be64_to_cpu(dep->inumber); XFS_DIR2_SF_PUT_INUMBER(sfp, &temp, XFS_DIR2_SF_INUMBERP(sfep)); sfep = XFS_DIR2_SF_NEXTENTRY(sfp, sfep); diff --git a/fs/xfs/xfs_dir2_trace.c b/fs/xfs/xfs_dir2_trace.c index c626943..f3fb2ff 100644 --- a/fs/xfs/xfs_dir2_trace.c +++ b/fs/xfs/xfs_dir2_trace.c @@ -19,11 +19,9 @@ #include "xfs.h" #include "xfs_fs.h" #include "xfs_types.h" #include "xfs_inum.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/xfs_dir_leaf.c b/fs/xfs/xfs_dir_leaf.c deleted file mode 100644 index 6d71186..0000000 --- a/fs/xfs/xfs_dir_leaf.c +++ /dev/null @@ -1,2213 +0,0 @@ -/* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "xfs.h" -#include "xfs_fs.h" -#include "xfs_types.h" -#include "xfs_log.h" -#include "xfs_inum.h" -#include "xfs_trans.h" -#include "xfs_sb.h" -#include "xfs_dir.h" -#include "xfs_dir2.h" -#include "xfs_dmapi.h" -#include "xfs_mount.h" -#include "xfs_da_btree.h" -#include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" -#include "xfs_inode.h" -#include "xfs_inode_item.h" -#include "xfs_alloc.h" -#include "xfs_btree.h" -#include "xfs_bmap.h" -#include "xfs_dir_leaf.h" -#include "xfs_error.h" - -/* - * xfs_dir_leaf.c - * - * Routines to implement leaf blocks of directories as Btrees of hashed names. - */ - -/*======================================================================== - * Function prototypes for the kernel. - *========================================================================*/ - -/* - * Routines used for growing the Btree. - */ -STATIC void xfs_dir_leaf_add_work(xfs_dabuf_t *leaf_buffer, xfs_da_args_t *args, - int insertion_index, - int freemap_index); -STATIC int xfs_dir_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *leaf_buffer, - int musthave, int justcheck); -STATIC void xfs_dir_leaf_rebalance(xfs_da_state_t *state, - xfs_da_state_blk_t *blk1, - xfs_da_state_blk_t *blk2); -STATIC int xfs_dir_leaf_figure_balance(xfs_da_state_t *state, - xfs_da_state_blk_t *leaf_blk_1, - xfs_da_state_blk_t *leaf_blk_2, - int *number_entries_in_blk1, - int *number_namebytes_in_blk1); - -STATIC int xfs_dir_leaf_create(struct xfs_da_args *args, - xfs_dablk_t which_block, - struct xfs_dabuf **bpp); - -/* - * Utility routines. - */ -STATIC void xfs_dir_leaf_moveents(xfs_dir_leafblock_t *src_leaf, - int src_start, - xfs_dir_leafblock_t *dst_leaf, - int dst_start, int move_count, - xfs_mount_t *mp); - - -/*======================================================================== - * External routines when dirsize < XFS_IFORK_DSIZE(dp). - *========================================================================*/ - - -/* - * Validate a given inode number. - */ -int -xfs_dir_ino_validate(xfs_mount_t *mp, xfs_ino_t ino) -{ - xfs_agblock_t agblkno; - xfs_agino_t agino; - xfs_agnumber_t agno; - int ino_ok; - int ioff; - - agno = XFS_INO_TO_AGNO(mp, ino); - agblkno = XFS_INO_TO_AGBNO(mp, ino); - ioff = XFS_INO_TO_OFFSET(mp, ino); - agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff); - ino_ok = - agno < mp->m_sb.sb_agcount && - agblkno < mp->m_sb.sb_agblocks && - agblkno != 0 && - ioff < (1 << mp->m_sb.sb_inopblog) && - XFS_AGINO_TO_INO(mp, agno, agino) == ino; - if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE, - XFS_RANDOM_DIR_INO_VALIDATE))) { - xfs_fs_cmn_err(CE_WARN, mp, "Invalid inode number 0x%Lx", - (unsigned long long) ino); - XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp); - return XFS_ERROR(EFSCORRUPTED); - } - return 0; -} - -/* - * Create the initial contents of a shortform directory. - */ -int -xfs_dir_shortform_create(xfs_da_args_t *args, xfs_ino_t parent) -{ - xfs_dir_sf_hdr_t *hdr; - xfs_inode_t *dp; - - dp = args->dp; - ASSERT(dp != NULL); - ASSERT(dp->i_d.di_size == 0); - if (dp->i_d.di_format == XFS_DINODE_FMT_EXTENTS) { - dp->i_df.if_flags &= ~XFS_IFEXTENTS; /* just in case */ - dp->i_d.di_format = XFS_DINODE_FMT_LOCAL; - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); - dp->i_df.if_flags |= XFS_IFINLINE; - } - ASSERT(dp->i_df.if_flags & XFS_IFINLINE); - ASSERT(dp->i_df.if_bytes == 0); - xfs_idata_realloc(dp, sizeof(*hdr), XFS_DATA_FORK); - hdr = (xfs_dir_sf_hdr_t *)dp->i_df.if_u1.if_data; - XFS_DIR_SF_PUT_DIRINO(&parent, &hdr->parent); - - hdr->count = 0; - dp->i_d.di_size = sizeof(*hdr); - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); - return 0; -} - -/* - * Add a name to the shortform directory structure. - * Overflow from the inode has already been checked for. - */ -int -xfs_dir_shortform_addname(xfs_da_args_t *args) -{ - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - int i, offset, size; - xfs_inode_t *dp; - - dp = args->dp; - ASSERT(dp->i_df.if_flags & XFS_IFINLINE); - /* - * Catch the case where the conversion from shortform to leaf - * failed part way through. - */ - if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) { - ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); - return XFS_ERROR(EIO); - } - ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); - ASSERT(dp->i_df.if_u1.if_data != NULL); - sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data; - sfe = &sf->list[0]; - for (i = sf->hdr.count-1; i >= 0; i--) { - if (sfe->namelen == args->namelen && - args->name[0] == sfe->name[0] && - memcmp(args->name, sfe->name, args->namelen) == 0) - return XFS_ERROR(EEXIST); - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - } - - offset = (int)((char *)sfe - (char *)sf); - size = XFS_DIR_SF_ENTSIZE_BYNAME(args->namelen); - xfs_idata_realloc(dp, size, XFS_DATA_FORK); - sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data; - sfe = (xfs_dir_sf_entry_t *)((char *)sf + offset); - - XFS_DIR_SF_PUT_DIRINO(&args->inumber, &sfe->inumber); - sfe->namelen = args->namelen; - memcpy(sfe->name, args->name, sfe->namelen); - sf->hdr.count++; - - dp->i_d.di_size += size; - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); - - return 0; -} - -/* - * Remove a name from the shortform directory structure. - */ -int -xfs_dir_shortform_removename(xfs_da_args_t *args) -{ - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - int base, size = 0, i; - xfs_inode_t *dp; - - dp = args->dp; - ASSERT(dp->i_df.if_flags & XFS_IFINLINE); - /* - * Catch the case where the conversion from shortform to leaf - * failed part way through. - */ - if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) { - ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); - return XFS_ERROR(EIO); - } - ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); - ASSERT(dp->i_df.if_u1.if_data != NULL); - base = sizeof(xfs_dir_sf_hdr_t); - sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data; - sfe = &sf->list[0]; - for (i = sf->hdr.count-1; i >= 0; i--) { - size = XFS_DIR_SF_ENTSIZE_BYENTRY(sfe); - if (sfe->namelen == args->namelen && - sfe->name[0] == args->name[0] && - memcmp(sfe->name, args->name, args->namelen) == 0) - break; - base += size; - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - } - if (i < 0) { - ASSERT(args->oknoent); - return XFS_ERROR(ENOENT); - } - - if ((base + size) != dp->i_d.di_size) { - memmove(&((char *)sf)[base], &((char *)sf)[base+size], - dp->i_d.di_size - (base+size)); - } - sf->hdr.count--; - - xfs_idata_realloc(dp, -size, XFS_DATA_FORK); - dp->i_d.di_size -= size; - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); - - return 0; -} - -/* - * Look up a name in a shortform directory structure. - */ -int -xfs_dir_shortform_lookup(xfs_da_args_t *args) -{ - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - int i; - xfs_inode_t *dp; - - dp = args->dp; - ASSERT(dp->i_df.if_flags & XFS_IFINLINE); - /* - * Catch the case where the conversion from shortform to leaf - * failed part way through. - */ - if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) { - ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); - return XFS_ERROR(EIO); - } - ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); - ASSERT(dp->i_df.if_u1.if_data != NULL); - sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data; - if (args->namelen == 2 && - args->name[0] == '.' && args->name[1] == '.') { - XFS_DIR_SF_GET_DIRINO(&sf->hdr.parent, &args->inumber); - return(XFS_ERROR(EEXIST)); - } - if (args->namelen == 1 && args->name[0] == '.') { - args->inumber = dp->i_ino; - return(XFS_ERROR(EEXIST)); - } - sfe = &sf->list[0]; - for (i = sf->hdr.count-1; i >= 0; i--) { - if (sfe->namelen == args->namelen && - sfe->name[0] == args->name[0] && - memcmp(args->name, sfe->name, args->namelen) == 0) { - XFS_DIR_SF_GET_DIRINO(&sfe->inumber, &args->inumber); - return(XFS_ERROR(EEXIST)); - } - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - } - ASSERT(args->oknoent); - return(XFS_ERROR(ENOENT)); -} - -/* - * Convert from using the shortform to the leaf. - */ -int -xfs_dir_shortform_to_leaf(xfs_da_args_t *iargs) -{ - xfs_inode_t *dp; - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - xfs_da_args_t args; - xfs_ino_t inumber; - char *tmpbuffer; - int retval, i, size; - xfs_dablk_t blkno; - xfs_dabuf_t *bp; - - dp = iargs->dp; - /* - * Catch the case where the conversion from shortform to leaf - * failed part way through. - */ - if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) { - ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); - return XFS_ERROR(EIO); - } - ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); - ASSERT(dp->i_df.if_u1.if_data != NULL); - size = dp->i_df.if_bytes; - tmpbuffer = kmem_alloc(size, KM_SLEEP); - ASSERT(tmpbuffer != NULL); - - memcpy(tmpbuffer, dp->i_df.if_u1.if_data, size); - - sf = (xfs_dir_shortform_t *)tmpbuffer; - XFS_DIR_SF_GET_DIRINO(&sf->hdr.parent, &inumber); - - xfs_idata_realloc(dp, -size, XFS_DATA_FORK); - dp->i_d.di_size = 0; - xfs_trans_log_inode(iargs->trans, dp, XFS_ILOG_CORE); - retval = xfs_da_grow_inode(iargs, &blkno); - if (retval) - goto out; - - ASSERT(blkno == 0); - retval = xfs_dir_leaf_create(iargs, blkno, &bp); - if (retval) - goto out; - xfs_da_buf_done(bp); - - args.name = "."; - args.namelen = 1; - args.hashval = xfs_dir_hash_dot; - args.inumber = dp->i_ino; - args.dp = dp; - args.firstblock = iargs->firstblock; - args.flist = iargs->flist; - args.total = iargs->total; - args.whichfork = XFS_DATA_FORK; - args.trans = iargs->trans; - args.justcheck = 0; - args.addname = args.oknoent = 1; - retval = xfs_dir_leaf_addname(&args); - if (retval) - goto out; - - args.name = ".."; - args.namelen = 2; - args.hashval = xfs_dir_hash_dotdot; - args.inumber = inumber; - retval = xfs_dir_leaf_addname(&args); - if (retval) - goto out; - - sfe = &sf->list[0]; - for (i = 0; i < sf->hdr.count; i++) { - args.name = (char *)(sfe->name); - args.namelen = sfe->namelen; - args.hashval = xfs_da_hashname((char *)(sfe->name), - sfe->namelen); - XFS_DIR_SF_GET_DIRINO(&sfe->inumber, &args.inumber); - retval = xfs_dir_leaf_addname(&args); - if (retval) - goto out; - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - } - retval = 0; - -out: - kmem_free(tmpbuffer, size); - return retval; -} - -STATIC int -xfs_dir_shortform_compare(const void *a, const void *b) -{ - xfs_dir_sf_sort_t *sa, *sb; - - sa = (xfs_dir_sf_sort_t *)a; - sb = (xfs_dir_sf_sort_t *)b; - if (sa->hash < sb->hash) - return -1; - else if (sa->hash > sb->hash) - return 1; - else - return sa->entno - sb->entno; -} - -/* - * Copy out directory entries for getdents(), for shortform directories. - */ -/*ARGSUSED*/ -int -xfs_dir_shortform_getdents(xfs_inode_t *dp, uio_t *uio, int *eofp, - xfs_dirent_t *dbp, xfs_dir_put_t put) -{ - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - int retval, i, sbsize, nsbuf, lastresid=0, want_entno; - xfs_mount_t *mp; - xfs_dahash_t cookhash, hash; - xfs_dir_put_args_t p; - xfs_dir_sf_sort_t *sbuf, *sbp; - - mp = dp->i_mount; - sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data; - cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset); - want_entno = XFS_DA_COOKIE_ENTRY(mp, uio->uio_offset); - nsbuf = sf->hdr.count + 2; - sbsize = (nsbuf + 1) * sizeof(*sbuf); - sbp = sbuf = kmem_alloc(sbsize, KM_SLEEP); - - xfs_dir_trace_g_du("sf: start", dp, uio); - - /* - * Collect all the entries into the buffer. - * Entry 0 is . - */ - sbp->entno = 0; - sbp->seqno = 0; - sbp->hash = xfs_dir_hash_dot; - sbp->ino = dp->i_ino; - sbp->name = "."; - sbp->namelen = 1; - sbp++; - - /* - * Entry 1 is .. - */ - sbp->entno = 1; - sbp->seqno = 0; - sbp->hash = xfs_dir_hash_dotdot; - sbp->ino = XFS_GET_DIR_INO8(sf->hdr.parent); - sbp->name = ".."; - sbp->namelen = 2; - sbp++; - - /* - * Scan the directory data for the rest of the entries. - */ - for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) { - - if (unlikely( - ((char *)sfe < (char *)sf) || - ((char *)sfe >= ((char *)sf + dp->i_df.if_bytes)))) { - xfs_dir_trace_g_du("sf: corrupted", dp, uio); - XFS_CORRUPTION_ERROR("xfs_dir_shortform_getdents", - XFS_ERRLEVEL_LOW, mp, sfe); - kmem_free(sbuf, sbsize); - return XFS_ERROR(EFSCORRUPTED); - } - - sbp->entno = i + 2; - sbp->seqno = 0; - sbp->hash = xfs_da_hashname((char *)sfe->name, sfe->namelen); - sbp->ino = XFS_GET_DIR_INO8(sfe->inumber); - sbp->name = (char *)sfe->name; - sbp->namelen = sfe->namelen; - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - sbp++; - } - - /* - * Sort the entries on hash then entno. - */ - xfs_sort(sbuf, nsbuf, sizeof(*sbuf), xfs_dir_shortform_compare); - /* - * Stuff in last entry. - */ - sbp->entno = nsbuf; - sbp->hash = XFS_DA_MAXHASH; - sbp->seqno = 0; - /* - * Figure out the sequence numbers in case there's a hash duplicate. - */ - for (hash = sbuf->hash, sbp = sbuf + 1; - sbp < &sbuf[nsbuf + 1]; sbp++) { - if (sbp->hash == hash) - sbp->seqno = sbp[-1].seqno + 1; - else - hash = sbp->hash; - } - - /* - * Set up put routine. - */ - p.dbp = dbp; - p.put = put; - p.uio = uio; - - /* - * Find our place. - */ - for (sbp = sbuf; sbp < &sbuf[nsbuf + 1]; sbp++) { - if (sbp->hash > cookhash || - (sbp->hash == cookhash && sbp->seqno >= want_entno)) - break; - } - - /* - * Did we fail to find anything? We stop at the last entry, - * the one we put maxhash into. - */ - if (sbp == &sbuf[nsbuf]) { - kmem_free(sbuf, sbsize); - xfs_dir_trace_g_du("sf: hash beyond end", dp, uio); - uio->uio_offset = XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH); - *eofp = 1; - return 0; - } - - /* - * Loop putting entries into the user buffer. - */ - while (sbp < &sbuf[nsbuf]) { - /* - * Save the first resid in a run of equal-hashval entries - * so that we can back them out if they don't all fit. - */ - if (sbp->seqno == 0 || sbp == sbuf) - lastresid = uio->uio_resid; - XFS_PUT_COOKIE(p.cook, mp, 0, sbp[1].seqno, sbp[1].hash); - p.ino = sbp->ino; -#if XFS_BIG_INUMS - p.ino += mp->m_inoadd; -#endif - p.name = sbp->name; - p.namelen = sbp->namelen; - retval = p.put(&p); - if (!p.done) { - uio->uio_offset = - XFS_DA_MAKE_COOKIE(mp, 0, 0, sbp->hash); - kmem_free(sbuf, sbsize); - uio->uio_resid = lastresid; - xfs_dir_trace_g_du("sf: E-O-B", dp, uio); - return retval; - } - sbp++; - } - kmem_free(sbuf, sbsize); - uio->uio_offset = p.cook.o; - *eofp = 1; - xfs_dir_trace_g_du("sf: E-O-F", dp, uio); - return 0; -} - -/* - * Look up a name in a shortform directory structure, replace the inode number. - */ -int -xfs_dir_shortform_replace(xfs_da_args_t *args) -{ - xfs_dir_shortform_t *sf; - xfs_dir_sf_entry_t *sfe; - xfs_inode_t *dp; - int i; - - dp = args->dp; - ASSERT(dp->i_df.if_flags & XFS_IFINLINE); - /* - * Catch the case where the conversion from shortform to leaf - * failed part way through. - */ - if (dp->i_d.di_size < sizeof(xfs_dir_sf_hdr_t)) { - ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); - return XFS_ERROR(EIO); - } - ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); - ASSERT(dp->i_df.if_u1.if_data != NULL); - sf = (xfs_dir_shortform_t *)dp->i_df.if_u1.if_data; - if (args->namelen == 2 && - args->name[0] == '.' && args->name[1] == '.') { - /* XXX - replace assert? */ - XFS_DIR_SF_PUT_DIRINO(&args->inumber, &sf->hdr.parent); - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA); - return 0; - } - ASSERT(args->namelen != 1 || args->name[0] != '.'); - sfe = &sf->list[0]; - for (i = sf->hdr.count-1; i >= 0; i--) { - if (sfe->namelen == args->namelen && - sfe->name[0] == args->name[0] && - memcmp(args->name, sfe->name, args->namelen) == 0) { - ASSERT(memcmp((char *)&args->inumber, - (char *)&sfe->inumber, sizeof(xfs_ino_t))); - XFS_DIR_SF_PUT_DIRINO(&args->inumber, &sfe->inumber); - xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA); - return 0; - } - sfe = XFS_DIR_SF_NEXTENTRY(sfe); - } - ASSERT(args->oknoent); - return XFS_ERROR(ENOENT); -} - -/* - * Convert a leaf directory to shortform structure - */ -int -xfs_dir_leaf_to_shortform(xfs_da_args_t *iargs) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_hdr_t *hdr; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - xfs_da_args_t args; - xfs_inode_t *dp; - xfs_ino_t parent = 0; - char *tmpbuffer; - int retval, i; - xfs_dabuf_t *bp; - - dp = iargs->dp; - tmpbuffer = kmem_alloc(XFS_LBSIZE(dp->i_mount), KM_SLEEP); - ASSERT(tmpbuffer != NULL); - - retval = xfs_da_read_buf(iargs->trans, iargs->dp, 0, -1, &bp, - XFS_DATA_FORK); - if (retval) - goto out; - ASSERT(bp != NULL); - memcpy(tmpbuffer, bp->data, XFS_LBSIZE(dp->i_mount)); - leaf = (xfs_dir_leafblock_t *)tmpbuffer; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - memset(bp->data, 0, XFS_LBSIZE(dp->i_mount)); - - /* - * Find and special case the parent inode number - */ - hdr = &leaf->hdr; - entry = &leaf->entries[0]; - for (i = INT_GET(hdr->count, ARCH_CONVERT)-1; i >= 0; entry++, i--) { - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - if ((entry->namelen == 2) && - (namest->name[0] == '.') && - (namest->name[1] == '.')) { - XFS_DIR_SF_GET_DIRINO(&namest->inumber, &parent); - entry->nameidx = 0; - } else if ((entry->namelen == 1) && (namest->name[0] == '.')) { - entry->nameidx = 0; - } - } - retval = xfs_da_shrink_inode(iargs, 0, bp); - if (retval) - goto out; - retval = xfs_dir_shortform_create(iargs, parent); - if (retval) - goto out; - - /* - * Copy the rest of the filenames - */ - entry = &leaf->entries[0]; - args.dp = dp; - args.firstblock = iargs->firstblock; - args.flist = iargs->flist; - args.total = iargs->total; - args.whichfork = XFS_DATA_FORK; - args.trans = iargs->trans; - args.justcheck = 0; - args.addname = args.oknoent = 1; - for (i = 0; i < INT_GET(hdr->count, ARCH_CONVERT); entry++, i++) { - if (!entry->nameidx) - continue; - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - args.name = (char *)(namest->name); - args.namelen = entry->namelen; - args.hashval = INT_GET(entry->hashval, ARCH_CONVERT); - XFS_DIR_SF_GET_DIRINO(&namest->inumber, &args.inumber); - xfs_dir_shortform_addname(&args); - } - -out: - kmem_free(tmpbuffer, XFS_LBSIZE(dp->i_mount)); - return retval; -} - -/* - * Convert from using a single leaf to a root node and a leaf. - */ -int -xfs_dir_leaf_to_node(xfs_da_args_t *args) -{ - xfs_dir_leafblock_t *leaf; - xfs_da_intnode_t *node; - xfs_inode_t *dp; - xfs_dabuf_t *bp1, *bp2; - xfs_dablk_t blkno; - int retval; - - dp = args->dp; - retval = xfs_da_grow_inode(args, &blkno); - ASSERT(blkno == 1); - if (retval) - return retval; - retval = xfs_da_read_buf(args->trans, args->dp, 0, -1, &bp1, - XFS_DATA_FORK); - if (retval) - return retval; - ASSERT(bp1 != NULL); - retval = xfs_da_get_buf(args->trans, args->dp, 1, -1, &bp2, - XFS_DATA_FORK); - if (retval) { - xfs_da_buf_done(bp1); - return retval; - } - ASSERT(bp2 != NULL); - memcpy(bp2->data, bp1->data, XFS_LBSIZE(dp->i_mount)); - xfs_da_buf_done(bp1); - xfs_da_log_buf(args->trans, bp2, 0, XFS_LBSIZE(dp->i_mount) - 1); - - /* - * Set up the new root node. - */ - retval = xfs_da_node_create(args, 0, 1, &bp1, XFS_DATA_FORK); - if (retval) { - xfs_da_buf_done(bp2); - return retval; - } - node = bp1->data; - leaf = bp2->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - node->btree[0].hashval = cpu_to_be32( - INT_GET(leaf->entries[ - INT_GET(leaf->hdr.count, ARCH_CONVERT)-1].hashval, ARCH_CONVERT)); - xfs_da_buf_done(bp2); - node->btree[0].before = cpu_to_be32(blkno); - node->hdr.count = cpu_to_be16(1); - xfs_da_log_buf(args->trans, bp1, - XFS_DA_LOGRANGE(node, &node->btree[0], sizeof(node->btree[0]))); - xfs_da_buf_done(bp1); - - return retval; -} - - -/*======================================================================== - * Routines used for growing the Btree. - *========================================================================*/ - -/* - * Create the initial contents of a leaf directory - * or a leaf in a node directory. - */ -STATIC int -xfs_dir_leaf_create(xfs_da_args_t *args, xfs_dablk_t blkno, xfs_dabuf_t **bpp) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_hdr_t *hdr; - xfs_inode_t *dp; - xfs_dabuf_t *bp; - int retval; - - dp = args->dp; - ASSERT(dp != NULL); - retval = xfs_da_get_buf(args->trans, dp, blkno, -1, &bp, XFS_DATA_FORK); - if (retval) - return retval; - ASSERT(bp != NULL); - leaf = bp->data; - memset((char *)leaf, 0, XFS_LBSIZE(dp->i_mount)); - hdr = &leaf->hdr; - hdr->info.magic = cpu_to_be16(XFS_DIR_LEAF_MAGIC); - INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount)); - if (!hdr->firstused) - INT_SET(hdr->firstused, ARCH_CONVERT, XFS_LBSIZE(dp->i_mount) - 1); - INT_SET(hdr->freemap[0].base, ARCH_CONVERT, sizeof(xfs_dir_leaf_hdr_t)); - INT_SET(hdr->freemap[0].size, ARCH_CONVERT, INT_GET(hdr->firstused, ARCH_CONVERT) - INT_GET(hdr->freemap[0].base, ARCH_CONVERT)); - - xfs_da_log_buf(args->trans, bp, 0, XFS_LBSIZE(dp->i_mount) - 1); - - *bpp = bp; - return 0; -} - -/* - * Split the leaf node, rebalance, then add the new entry. - */ -int -xfs_dir_leaf_split(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk, - xfs_da_state_blk_t *newblk) -{ - xfs_dablk_t blkno; - xfs_da_args_t *args; - int error; - - /* - * Allocate space for a new leaf node. - */ - args = state->args; - ASSERT(args != NULL); - ASSERT(oldblk->magic == XFS_DIR_LEAF_MAGIC); - error = xfs_da_grow_inode(args, &blkno); - if (error) - return error; - error = xfs_dir_leaf_create(args, blkno, &newblk->bp); - if (error) - return error; - newblk->blkno = blkno; - newblk->magic = XFS_DIR_LEAF_MAGIC; - - /* - * Rebalance the entries across the two leaves. - */ - xfs_dir_leaf_rebalance(state, oldblk, newblk); - error = xfs_da_blk_link(state, oldblk, newblk); - if (error) - return error; - - /* - * Insert the new entry in the correct block. - */ - if (state->inleaf) { - error = xfs_dir_leaf_add(oldblk->bp, args, oldblk->index); - } else { - error = xfs_dir_leaf_add(newblk->bp, args, newblk->index); - } - - /* - * Update last hashval in each block since we added the name. - */ - oldblk->hashval = xfs_dir_leaf_lasthash(oldblk->bp, NULL); - newblk->hashval = xfs_dir_leaf_lasthash(newblk->bp, NULL); - return error; -} - -/* - * Add a name to the leaf directory structure. - * - * Must take into account fragmented leaves and leaves where spacemap has - * lost some freespace information (ie: holes). - */ -int -xfs_dir_leaf_add(xfs_dabuf_t *bp, xfs_da_args_t *args, int index) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_hdr_t *hdr; - xfs_dir_leaf_map_t *map; - int tablesize, entsize, sum, i, tmp, error; - - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - ASSERT((index >= 0) && (index <= INT_GET(leaf->hdr.count, ARCH_CONVERT))); - hdr = &leaf->hdr; - entsize = XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen); - - /* - * Search through freemap for first-fit on new name length. - * (may need to figure in size of entry struct too) - */ - tablesize = (INT_GET(hdr->count, ARCH_CONVERT) + 1) * (uint)sizeof(xfs_dir_leaf_entry_t) - + (uint)sizeof(xfs_dir_leaf_hdr_t); - map = &hdr->freemap[XFS_DIR_LEAF_MAPSIZE-1]; - for (sum = 0, i = XFS_DIR_LEAF_MAPSIZE-1; i >= 0; map--, i--) { - if (tablesize > INT_GET(hdr->firstused, ARCH_CONVERT)) { - sum += INT_GET(map->size, ARCH_CONVERT); - continue; - } - if (!map->size) - continue; /* no space in this map */ - tmp = entsize; - if (INT_GET(map->base, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT)) - tmp += (uint)sizeof(xfs_dir_leaf_entry_t); - if (INT_GET(map->size, ARCH_CONVERT) >= tmp) { - if (!args->justcheck) - xfs_dir_leaf_add_work(bp, args, index, i); - return 0; - } - sum += INT_GET(map->size, ARCH_CONVERT); - } - - /* - * If there are no holes in the address space of the block, - * and we don't have enough freespace, then compaction will do us - * no good and we should just give up. - */ - if (!hdr->holes && (sum < entsize)) - return XFS_ERROR(ENOSPC); - - /* - * Compact the entries to coalesce free space. - * Pass the justcheck flag so the checking pass can return - * an error, without changing anything, if it won't fit. - */ - error = xfs_dir_leaf_compact(args->trans, bp, - args->total == 0 ? - entsize + - (uint)sizeof(xfs_dir_leaf_entry_t) : 0, - args->justcheck); - if (error) - return error; - /* - * After compaction, the block is guaranteed to have only one - * free region, in freemap[0]. If it is not big enough, give up. - */ - if (INT_GET(hdr->freemap[0].size, ARCH_CONVERT) < - (entsize + (uint)sizeof(xfs_dir_leaf_entry_t))) - return XFS_ERROR(ENOSPC); - - if (!args->justcheck) - xfs_dir_leaf_add_work(bp, args, index, 0); - return 0; -} - -/* - * Add a name to a leaf directory structure. - */ -STATIC void -xfs_dir_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int index, - int mapindex) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_hdr_t *hdr; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - xfs_dir_leaf_map_t *map; - /* REFERENCED */ - xfs_mount_t *mp; - int tmp, i; - - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - hdr = &leaf->hdr; - ASSERT((mapindex >= 0) && (mapindex < XFS_DIR_LEAF_MAPSIZE)); - ASSERT((index >= 0) && (index <= INT_GET(hdr->count, ARCH_CONVERT))); - - /* - * Force open some space in the entry array and fill it in. - */ - entry = &leaf->entries[index]; - if (index < INT_GET(hdr->count, ARCH_CONVERT)) { - tmp = INT_GET(hdr->count, ARCH_CONVERT) - index; - tmp *= (uint)sizeof(xfs_dir_leaf_entry_t); - memmove(entry + 1, entry, tmp); - xfs_da_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, tmp + (uint)sizeof(*entry))); - } - INT_MOD(hdr->count, ARCH_CONVERT, +1); - - /* - * Allocate space for the new string (at the end of the run). - */ - map = &hdr->freemap[mapindex]; - mp = args->trans->t_mountp; - ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp)); - ASSERT(INT_GET(map->size, ARCH_CONVERT) >= XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen)); - ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp)); - INT_MOD(map->size, ARCH_CONVERT, -(XFS_DIR_LEAF_ENTSIZE_BYNAME(args->namelen))); - INT_SET(entry->nameidx, ARCH_CONVERT, INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT)); - INT_SET(entry->hashval, ARCH_CONVERT, args->hashval); - entry->namelen = args->namelen; - xfs_da_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, entry, sizeof(*entry))); - - /* - * Copy the string and inode number into the new space. - */ - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - XFS_DIR_SF_PUT_DIRINO(&args->inumber, &namest->inumber); - memcpy(namest->name, args->name, args->namelen); - xfs_da_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, namest, XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry))); - - /* - * Update the control info for this leaf node - */ - if (INT_GET(entry->nameidx, ARCH_CONVERT) < INT_GET(hdr->firstused, ARCH_CONVERT)) - INT_COPY(hdr->firstused, entry->nameidx, ARCH_CONVERT); - ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT) >= ((INT_GET(hdr->count, ARCH_CONVERT)*sizeof(*entry))+sizeof(*hdr))); - tmp = (INT_GET(hdr->count, ARCH_CONVERT)-1) * (uint)sizeof(xfs_dir_leaf_entry_t) - + (uint)sizeof(xfs_dir_leaf_hdr_t); - map = &hdr->freemap[0]; - for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; map++, i++) { - if (INT_GET(map->base, ARCH_CONVERT) == tmp) { - INT_MOD(map->base, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_entry_t)); - INT_MOD(map->size, ARCH_CONVERT, -((uint)sizeof(xfs_dir_leaf_entry_t))); - } - } - INT_MOD(hdr->namebytes, ARCH_CONVERT, args->namelen); - xfs_da_log_buf(args->trans, bp, - XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr))); -} - -/* - * Garbage collect a leaf directory block by copying it to a new buffer. - */ -STATIC int -xfs_dir_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp, int musthave, - int justcheck) -{ - xfs_dir_leafblock_t *leaf_s, *leaf_d; - xfs_dir_leaf_hdr_t *hdr_s, *hdr_d; - xfs_mount_t *mp; - char *tmpbuffer; - char *tmpbuffer2=NULL; - int rval; - int lbsize; - - mp = trans->t_mountp; - lbsize = XFS_LBSIZE(mp); - tmpbuffer = kmem_alloc(lbsize, KM_SLEEP); - ASSERT(tmpbuffer != NULL); - memcpy(tmpbuffer, bp->data, lbsize); - - /* - * Make a second copy in case xfs_dir_leaf_moveents() - * below destroys the original. - */ - if (musthave || justcheck) { - tmpbuffer2 = kmem_alloc(lbsize, KM_SLEEP); - memcpy(tmpbuffer2, bp->data, lbsize); - } - memset(bp->data, 0, lbsize); - - /* - * Copy basic information - */ - leaf_s = (xfs_dir_leafblock_t *)tmpbuffer; - leaf_d = bp->data; - hdr_s = &leaf_s->hdr; - hdr_d = &leaf_d->hdr; - hdr_d->info = hdr_s->info; /* struct copy */ - INT_SET(hdr_d->firstused, ARCH_CONVERT, lbsize); - if (!hdr_d->firstused) - INT_SET(hdr_d->firstused, ARCH_CONVERT, lbsize - 1); - hdr_d->namebytes = 0; - hdr_d->count = 0; - hdr_d->holes = 0; - INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT, sizeof(xfs_dir_leaf_hdr_t)); - INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT, INT_GET(hdr_d->firstused, ARCH_CONVERT) - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT)); - - /* - * Copy all entry's in the same (sorted) order, - * but allocate filenames packed and in sequence. - * This changes the source (leaf_s) as well. - */ - xfs_dir_leaf_moveents(leaf_s, 0, leaf_d, 0, (int)INT_GET(hdr_s->count, ARCH_CONVERT), mp); - - if (musthave && INT_GET(hdr_d->freemap[0].size, ARCH_CONVERT) < musthave) - rval = XFS_ERROR(ENOSPC); - else - rval = 0; - - if (justcheck || rval == ENOSPC) { - ASSERT(tmpbuffer2); - memcpy(bp->data, tmpbuffer2, lbsize); - } else { - xfs_da_log_buf(trans, bp, 0, lbsize - 1); - } - - kmem_free(tmpbuffer, lbsize); - if (musthave || justcheck) - kmem_free(tmpbuffer2, lbsize); - return rval; -} - -/* - * Redistribute the directory entries between two leaf nodes, - * taking into account the size of the new entry. - * - * NOTE: if new block is empty, then it will get the upper half of old block. - */ -STATIC void -xfs_dir_leaf_rebalance(xfs_da_state_t *state, xfs_da_state_blk_t *blk1, - xfs_da_state_blk_t *blk2) -{ - xfs_da_state_blk_t *tmp_blk; - xfs_dir_leafblock_t *leaf1, *leaf2; - xfs_dir_leaf_hdr_t *hdr1, *hdr2; - int count, totallen, max, space, swap; - - /* - * Set up environment. - */ - ASSERT(blk1->magic == XFS_DIR_LEAF_MAGIC); - ASSERT(blk2->magic == XFS_DIR_LEAF_MAGIC); - leaf1 = blk1->bp->data; - leaf2 = blk2->bp->data; - ASSERT(be16_to_cpu(leaf1->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - ASSERT(be16_to_cpu(leaf2->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - - /* - * Check ordering of blocks, reverse if it makes things simpler. - */ - swap = 0; - if (xfs_dir_leaf_order(blk1->bp, blk2->bp)) { - tmp_blk = blk1; - blk1 = blk2; - blk2 = tmp_blk; - leaf1 = blk1->bp->data; - leaf2 = blk2->bp->data; - swap = 1; - } - hdr1 = &leaf1->hdr; - hdr2 = &leaf2->hdr; - - /* - * Examine entries until we reduce the absolute difference in - * byte usage between the two blocks to a minimum. Then get - * the direction to copy and the number of elements to move. - */ - state->inleaf = xfs_dir_leaf_figure_balance(state, blk1, blk2, - &count, &totallen); - if (swap) - state->inleaf = !state->inleaf; - - /* - * Move any entries required from leaf to leaf: - */ - if (count < INT_GET(hdr1->count, ARCH_CONVERT)) { - /* - * Figure the total bytes to be added to the destination leaf. - */ - count = INT_GET(hdr1->count, ARCH_CONVERT) - count; /* number entries being moved */ - space = INT_GET(hdr1->namebytes, ARCH_CONVERT) - totallen; - space += count * ((uint)sizeof(xfs_dir_leaf_name_t)-1); - space += count * (uint)sizeof(xfs_dir_leaf_entry_t); - - /* - * leaf2 is the destination, compact it if it looks tight. - */ - max = INT_GET(hdr2->firstused, ARCH_CONVERT) - (uint)sizeof(xfs_dir_leaf_hdr_t); - max -= INT_GET(hdr2->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t); - if (space > max) { - xfs_dir_leaf_compact(state->args->trans, blk2->bp, - 0, 0); - } - - /* - * Move high entries from leaf1 to low end of leaf2. - */ - xfs_dir_leaf_moveents(leaf1, INT_GET(hdr1->count, ARCH_CONVERT) - count, - leaf2, 0, count, state->mp); - - xfs_da_log_buf(state->args->trans, blk1->bp, 0, - state->blocksize-1); - xfs_da_log_buf(state->args->trans, blk2->bp, 0, - state->blocksize-1); - - } else if (count > INT_GET(hdr1->count, ARCH_CONVERT)) { - /* - * Figure the total bytes to be added to the destination leaf. - */ - count -= INT_GET(hdr1->count, ARCH_CONVERT); /* number entries being moved */ - space = totallen - INT_GET(hdr1->namebytes, ARCH_CONVERT); - space += count * ((uint)sizeof(xfs_dir_leaf_name_t)-1); - space += count * (uint)sizeof(xfs_dir_leaf_entry_t); - - /* - * leaf1 is the destination, compact it if it looks tight. - */ - max = INT_GET(hdr1->firstused, ARCH_CONVERT) - (uint)sizeof(xfs_dir_leaf_hdr_t); - max -= INT_GET(hdr1->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t); - if (space > max) { - xfs_dir_leaf_compact(state->args->trans, blk1->bp, - 0, 0); - } - - /* - * Move low entries from leaf2 to high end of leaf1. - */ - xfs_dir_leaf_moveents(leaf2, 0, leaf1, (int)INT_GET(hdr1->count, ARCH_CONVERT), - count, state->mp); - - xfs_da_log_buf(state->args->trans, blk1->bp, 0, - state->blocksize-1); - xfs_da_log_buf(state->args->trans, blk2->bp, 0, - state->blocksize-1); - } - - /* - * Copy out last hashval in each block for B-tree code. - */ - blk1->hashval = INT_GET(leaf1->entries[ INT_GET(leaf1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT); - blk2->hashval = INT_GET(leaf2->entries[ INT_GET(leaf2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT); - - /* - * Adjust the expected index for insertion. - * GROT: this doesn't work unless blk2 was originally empty. - */ - if (!state->inleaf) { - blk2->index = blk1->index - INT_GET(leaf1->hdr.count, ARCH_CONVERT); - } -} - -/* - * Examine entries until we reduce the absolute difference in - * byte usage between the two blocks to a minimum. - * GROT: Is this really necessary? With other than a 512 byte blocksize, - * GROT: there will always be enough room in either block for a new entry. - * GROT: Do a double-split for this case? - */ -STATIC int -xfs_dir_leaf_figure_balance(xfs_da_state_t *state, - xfs_da_state_blk_t *blk1, - xfs_da_state_blk_t *blk2, - int *countarg, int *namebytesarg) -{ - xfs_dir_leafblock_t *leaf1, *leaf2; - xfs_dir_leaf_hdr_t *hdr1, *hdr2; - xfs_dir_leaf_entry_t *entry; - int count, max, totallen, half; - int lastdelta, foundit, tmp; - - /* - * Set up environment. - */ - leaf1 = blk1->bp->data; - leaf2 = blk2->bp->data; - hdr1 = &leaf1->hdr; - hdr2 = &leaf2->hdr; - foundit = 0; - totallen = 0; - - /* - * Examine entries until we reduce the absolute difference in - * byte usage between the two blocks to a minimum. - */ - max = INT_GET(hdr1->count, ARCH_CONVERT) + INT_GET(hdr2->count, ARCH_CONVERT); - half = (max+1) * (uint)(sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1); - half += INT_GET(hdr1->namebytes, ARCH_CONVERT) + INT_GET(hdr2->namebytes, ARCH_CONVERT) + state->args->namelen; - half /= 2; - lastdelta = state->blocksize; - entry = &leaf1->entries[0]; - for (count = 0; count < max; entry++, count++) { - -#define XFS_DIR_ABS(A) (((A) < 0) ? -(A) : (A)) - /* - * The new entry is in the first block, account for it. - */ - if (count == blk1->index) { - tmp = totallen + (uint)sizeof(*entry) - + XFS_DIR_LEAF_ENTSIZE_BYNAME(state->args->namelen); - if (XFS_DIR_ABS(half - tmp) > lastdelta) - break; - lastdelta = XFS_DIR_ABS(half - tmp); - totallen = tmp; - foundit = 1; - } - - /* - * Wrap around into the second block if necessary. - */ - if (count == INT_GET(hdr1->count, ARCH_CONVERT)) { - leaf1 = leaf2; - entry = &leaf1->entries[0]; - } - - /* - * Figure out if next leaf entry would be too much. - */ - tmp = totallen + (uint)sizeof(*entry) - + XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry); - if (XFS_DIR_ABS(half - tmp) > lastdelta) - break; - lastdelta = XFS_DIR_ABS(half - tmp); - totallen = tmp; -#undef XFS_DIR_ABS - } - - /* - * Calculate the number of namebytes that will end up in lower block. - * If new entry not in lower block, fix up the count. - */ - totallen -= - count * (uint)(sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1); - if (foundit) { - totallen -= (sizeof(*entry)+sizeof(xfs_dir_leaf_entry_t)-1) + - state->args->namelen; - } - - *countarg = count; - *namebytesarg = totallen; - return foundit; -} - -/*======================================================================== - * Routines used for shrinking the Btree. - *========================================================================*/ - -/* - * Check a leaf block and its neighbors to see if the block should be - * collapsed into one or the other neighbor. Always keep the block - * with the smaller block number. - * If the current block is over 50% full, don't try to join it, return 0. - * If the block is empty, fill in the state structure and return 2. - * If it can be collapsed, fill in the state structure and return 1. - * If nothing can be done, return 0. - */ -int -xfs_dir_leaf_toosmall(xfs_da_state_t *state, int *action) -{ - xfs_dir_leafblock_t *leaf; - xfs_da_state_blk_t *blk; - xfs_da_blkinfo_t *info; - int count, bytes, forward, error, retval, i; - xfs_dablk_t blkno; - xfs_dabuf_t *bp; - - /* - * Check for the degenerate case of the block being over 50% full. - * If so, it's not worth even looking to see if we might be able - * to coalesce with a sibling. - */ - blk = &state->path.blk[ state->path.active-1 ]; - info = blk->bp->data; - ASSERT(be16_to_cpu(info->magic) == XFS_DIR_LEAF_MAGIC); - leaf = (xfs_dir_leafblock_t *)info; - count = INT_GET(leaf->hdr.count, ARCH_CONVERT); - bytes = (uint)sizeof(xfs_dir_leaf_hdr_t) + - count * (uint)sizeof(xfs_dir_leaf_entry_t) + - count * ((uint)sizeof(xfs_dir_leaf_name_t)-1) + - INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); - if (bytes > (state->blocksize >> 1)) { - *action = 0; /* blk over 50%, don't try to join */ - return 0; - } - - /* - * Check for the degenerate case of the block being empty. - * If the block is empty, we'll simply delete it, no need to - * coalesce it with a sibling block. We choose (arbitrarily) - * to merge with the forward block unless it is NULL. - */ - if (count == 0) { - /* - * Make altpath point to the block we want to keep and - * path point to the block we want to drop (this one). - */ - forward = (info->forw != 0); - memcpy(&state->altpath, &state->path, sizeof(state->path)); - error = xfs_da_path_shift(state, &state->altpath, forward, - 0, &retval); - if (error) - return error; - if (retval) { - *action = 0; - } else { - *action = 2; - } - return 0; - } - - /* - * Examine each sibling block to see if we can coalesce with - * at least 25% free space to spare. We need to figure out - * whether to merge with the forward or the backward block. - * We prefer coalescing with the lower numbered sibling so as - * to shrink a directory over time. - */ - forward = (be32_to_cpu(info->forw) < be32_to_cpu(info->back)); /* start with smaller blk num */ - for (i = 0; i < 2; forward = !forward, i++) { - if (forward) - blkno = be32_to_cpu(info->forw); - else - blkno = be32_to_cpu(info->back); - if (blkno == 0) - continue; - error = xfs_da_read_buf(state->args->trans, state->args->dp, - blkno, -1, &bp, - XFS_DATA_FORK); - if (error) - return error; - ASSERT(bp != NULL); - - leaf = (xfs_dir_leafblock_t *)info; - count = INT_GET(leaf->hdr.count, ARCH_CONVERT); - bytes = state->blocksize - (state->blocksize>>2); - bytes -= INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - count += INT_GET(leaf->hdr.count, ARCH_CONVERT); - bytes -= INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); - bytes -= count * ((uint)sizeof(xfs_dir_leaf_name_t) - 1); - bytes -= count * (uint)sizeof(xfs_dir_leaf_entry_t); - bytes -= (uint)sizeof(xfs_dir_leaf_hdr_t); - if (bytes >= 0) - break; /* fits with at least 25% to spare */ - - xfs_da_brelse(state->args->trans, bp); - } - if (i >= 2) { - *action = 0; - return 0; - } - xfs_da_buf_done(bp); - - /* - * Make altpath point to the block we want to keep (the lower - * numbered block) and path point to the block we want to drop. - */ - memcpy(&state->altpath, &state->path, sizeof(state->path)); - if (blkno < blk->blkno) { - error = xfs_da_path_shift(state, &state->altpath, forward, - 0, &retval); - } else { - error = xfs_da_path_shift(state, &state->path, forward, - 0, &retval); - } - if (error) - return error; - if (retval) { - *action = 0; - } else { - *action = 1; - } - return 0; -} - -/* - * Remove a name from the leaf directory structure. - * - * Return 1 if leaf is less than 37% full, 0 if >= 37% full. - * If two leaves are 37% full, when combined they will leave 25% free. - */ -int -xfs_dir_leaf_remove(xfs_trans_t *trans, xfs_dabuf_t *bp, int index) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_hdr_t *hdr; - xfs_dir_leaf_map_t *map; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - int before, after, smallest, entsize; - int tablesize, tmp, i; - xfs_mount_t *mp; - - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - hdr = &leaf->hdr; - mp = trans->t_mountp; - ASSERT((INT_GET(hdr->count, ARCH_CONVERT) > 0) && (INT_GET(hdr->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8))); - ASSERT((index >= 0) && (index < INT_GET(hdr->count, ARCH_CONVERT))); - ASSERT(INT_GET(hdr->firstused, ARCH_CONVERT) >= ((INT_GET(hdr->count, ARCH_CONVERT)*sizeof(*entry))+sizeof(*hdr))); - entry = &leaf->entries[index]; - ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) >= INT_GET(hdr->firstused, ARCH_CONVERT)); - ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp)); - - /* - * Scan through free region table: - * check for adjacency of free'd entry with an existing one, - * find smallest free region in case we need to replace it, - * adjust any map that borders the entry table, - */ - tablesize = INT_GET(hdr->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t) - + (uint)sizeof(xfs_dir_leaf_hdr_t); - map = &hdr->freemap[0]; - tmp = INT_GET(map->size, ARCH_CONVERT); - before = after = -1; - smallest = XFS_DIR_LEAF_MAPSIZE - 1; - entsize = XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry); - for (i = 0; i < XFS_DIR_LEAF_MAPSIZE; map++, i++) { - ASSERT(INT_GET(map->base, ARCH_CONVERT) < XFS_LBSIZE(mp)); - ASSERT(INT_GET(map->size, ARCH_CONVERT) < XFS_LBSIZE(mp)); - if (INT_GET(map->base, ARCH_CONVERT) == tablesize) { - INT_MOD(map->base, ARCH_CONVERT, -((uint)sizeof(xfs_dir_leaf_entry_t))); - INT_MOD(map->size, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_entry_t)); - } - - if ((INT_GET(map->base, ARCH_CONVERT) + INT_GET(map->size, ARCH_CONVERT)) == INT_GET(entry->nameidx, ARCH_CONVERT)) { - before = i; - } else if (INT_GET(map->base, ARCH_CONVERT) == (INT_GET(entry->nameidx, ARCH_CONVERT) + entsize)) { - after = i; - } else if (INT_GET(map->size, ARCH_CONVERT) < tmp) { - tmp = INT_GET(map->size, ARCH_CONVERT); - smallest = i; - } - } - - /* - * Coalesce adjacent freemap regions, - * or replace the smallest region. - */ - if ((before >= 0) || (after >= 0)) { - if ((before >= 0) && (after >= 0)) { - map = &hdr->freemap[before]; - INT_MOD(map->size, ARCH_CONVERT, entsize); - INT_MOD(map->size, ARCH_CONVERT, INT_GET(hdr->freemap[after].size, ARCH_CONVERT)); - hdr->freemap[after].base = 0; - hdr->freemap[after].size = 0; - } else if (before >= 0) { - map = &hdr->freemap[before]; - INT_MOD(map->size, ARCH_CONVERT, entsize); - } else { - map = &hdr->freemap[after]; - INT_COPY(map->base, entry->nameidx, ARCH_CONVERT); - INT_MOD(map->size, ARCH_CONVERT, entsize); - } - } else { - /* - * Replace smallest region (if it is smaller than free'd entry) - */ - map = &hdr->freemap[smallest]; - if (INT_GET(map->size, ARCH_CONVERT) < entsize) { - INT_COPY(map->base, entry->nameidx, ARCH_CONVERT); - INT_SET(map->size, ARCH_CONVERT, entsize); - } - } - - /* - * Did we remove the first entry? - */ - if (INT_GET(entry->nameidx, ARCH_CONVERT) == INT_GET(hdr->firstused, ARCH_CONVERT)) - smallest = 1; - else - smallest = 0; - - /* - * Compress the remaining entries and zero out the removed stuff. - */ - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - memset((char *)namest, 0, entsize); - xfs_da_log_buf(trans, bp, XFS_DA_LOGRANGE(leaf, namest, entsize)); - - INT_MOD(hdr->namebytes, ARCH_CONVERT, -(entry->namelen)); - tmp = (INT_GET(hdr->count, ARCH_CONVERT) - index) * (uint)sizeof(xfs_dir_leaf_entry_t); - memmove(entry, entry + 1, tmp); - INT_MOD(hdr->count, ARCH_CONVERT, -1); - xfs_da_log_buf(trans, bp, - XFS_DA_LOGRANGE(leaf, entry, tmp + (uint)sizeof(*entry))); - entry = &leaf->entries[INT_GET(hdr->count, ARCH_CONVERT)]; - memset((char *)entry, 0, sizeof(xfs_dir_leaf_entry_t)); - - /* - * If we removed the first entry, re-find the first used byte - * in the name area. Note that if the entry was the "firstused", - * then we don't have a "hole" in our block resulting from - * removing the name. - */ - if (smallest) { - tmp = XFS_LBSIZE(mp); - entry = &leaf->entries[0]; - for (i = INT_GET(hdr->count, ARCH_CONVERT)-1; i >= 0; entry++, i--) { - ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) >= INT_GET(hdr->firstused, ARCH_CONVERT)); - ASSERT(INT_GET(entry->nameidx, ARCH_CONVERT) < XFS_LBSIZE(mp)); - if (INT_GET(entry->nameidx, ARCH_CONVERT) < tmp) - tmp = INT_GET(entry->nameidx, ARCH_CONVERT); - } - INT_SET(hdr->firstused, ARCH_CONVERT, tmp); - if (!hdr->firstused) - INT_SET(hdr->firstused, ARCH_CONVERT, tmp - 1); - } else { - hdr->holes = 1; /* mark as needing compaction */ - } - - xfs_da_log_buf(trans, bp, XFS_DA_LOGRANGE(leaf, hdr, sizeof(*hdr))); - - /* - * Check if leaf is less than 50% full, caller may want to - * "join" the leaf with a sibling if so. - */ - tmp = (uint)sizeof(xfs_dir_leaf_hdr_t); - tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t); - tmp += INT_GET(leaf->hdr.count, ARCH_CONVERT) * ((uint)sizeof(xfs_dir_leaf_name_t) - 1); - tmp += INT_GET(leaf->hdr.namebytes, ARCH_CONVERT); - if (tmp < mp->m_dir_magicpct) - return 1; /* leaf is < 37% full */ - return 0; -} - -/* - * Move all the directory entries from drop_leaf into save_leaf. - */ -void -xfs_dir_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk, - xfs_da_state_blk_t *save_blk) -{ - xfs_dir_leafblock_t *drop_leaf, *save_leaf, *tmp_leaf; - xfs_dir_leaf_hdr_t *drop_hdr, *save_hdr, *tmp_hdr; - xfs_mount_t *mp; - char *tmpbuffer; - - /* - * Set up environment. - */ - mp = state->mp; - ASSERT(drop_blk->magic == XFS_DIR_LEAF_MAGIC); - ASSERT(save_blk->magic == XFS_DIR_LEAF_MAGIC); - drop_leaf = drop_blk->bp->data; - save_leaf = save_blk->bp->data; - ASSERT(be16_to_cpu(drop_leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - ASSERT(be16_to_cpu(save_leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - drop_hdr = &drop_leaf->hdr; - save_hdr = &save_leaf->hdr; - - /* - * Save last hashval from dying block for later Btree fixup. - */ - drop_blk->hashval = INT_GET(drop_leaf->entries[ drop_leaf->hdr.count-1 ].hashval, ARCH_CONVERT); - - /* - * Check if we need a temp buffer, or can we do it in place. - * Note that we don't check "leaf" for holes because we will - * always be dropping it, toosmall() decided that for us already. - */ - if (save_hdr->holes == 0) { - /* - * dest leaf has no holes, so we add there. May need - * to make some room in the entry array. - */ - if (xfs_dir_leaf_order(save_blk->bp, drop_blk->bp)) { - xfs_dir_leaf_moveents(drop_leaf, 0, save_leaf, 0, - (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp); - } else { - xfs_dir_leaf_moveents(drop_leaf, 0, - save_leaf, INT_GET(save_hdr->count, ARCH_CONVERT), - (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp); - } - } else { - /* - * Destination has holes, so we make a temporary copy - * of the leaf and add them both to that. - */ - tmpbuffer = kmem_alloc(state->blocksize, KM_SLEEP); - ASSERT(tmpbuffer != NULL); - memset(tmpbuffer, 0, state->blocksize); - tmp_leaf = (xfs_dir_leafblock_t *)tmpbuffer; - tmp_hdr = &tmp_leaf->hdr; - tmp_hdr->info = save_hdr->info; /* struct copy */ - tmp_hdr->count = 0; - INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize); - if (!tmp_hdr->firstused) - INT_SET(tmp_hdr->firstused, ARCH_CONVERT, state->blocksize - 1); - tmp_hdr->namebytes = 0; - if (xfs_dir_leaf_order(save_blk->bp, drop_blk->bp)) { - xfs_dir_leaf_moveents(drop_leaf, 0, tmp_leaf, 0, - (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp); - xfs_dir_leaf_moveents(save_leaf, 0, - tmp_leaf, INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT), - (int)INT_GET(save_hdr->count, ARCH_CONVERT), mp); - } else { - xfs_dir_leaf_moveents(save_leaf, 0, tmp_leaf, 0, - (int)INT_GET(save_hdr->count, ARCH_CONVERT), mp); - xfs_dir_leaf_moveents(drop_leaf, 0, - tmp_leaf, INT_GET(tmp_leaf->hdr.count, ARCH_CONVERT), - (int)INT_GET(drop_hdr->count, ARCH_CONVERT), mp); - } - memcpy(save_leaf, tmp_leaf, state->blocksize); - kmem_free(tmpbuffer, state->blocksize); - } - - xfs_da_log_buf(state->args->trans, save_blk->bp, 0, - state->blocksize - 1); - - /* - * Copy out last hashval in each block for B-tree code. - */ - save_blk->hashval = INT_GET(save_leaf->entries[ INT_GET(save_leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT); -} - -/*======================================================================== - * Routines used for finding things in the Btree. - *========================================================================*/ - -/* - * Look up a name in a leaf directory structure. - * This is the internal routine, it uses the caller's buffer. - * - * Note that duplicate keys are allowed, but only check within the - * current leaf node. The Btree code must check in adjacent leaf nodes. - * - * Return in *index the index into the entry[] array of either the found - * entry, or where the entry should have been (insert before that entry). - * - * Don't change the args->inumber unless we find the filename. - */ -int -xfs_dir_leaf_lookup_int(xfs_dabuf_t *bp, xfs_da_args_t *args, int *index) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - int probe, span; - xfs_dahash_t hashval; - - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - ASSERT(INT_GET(leaf->hdr.count, ARCH_CONVERT) < (XFS_LBSIZE(args->dp->i_mount)/8)); - - /* - * Binary search. (note: small blocks will skip this loop) - */ - hashval = args->hashval; - probe = span = INT_GET(leaf->hdr.count, ARCH_CONVERT) / 2; - for (entry = &leaf->entries[probe]; span > 4; - entry = &leaf->entries[probe]) { - span /= 2; - if (INT_GET(entry->hashval, ARCH_CONVERT) < hashval) - probe += span; - else if (INT_GET(entry->hashval, ARCH_CONVERT) > hashval) - probe -= span; - else - break; - } - ASSERT((probe >= 0) && \ - ((!leaf->hdr.count) || (probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)))); - ASSERT((span <= 4) || (INT_GET(entry->hashval, ARCH_CONVERT) == hashval)); - - /* - * Since we may have duplicate hashval's, find the first matching - * hashval in the leaf. - */ - while ((probe > 0) && (INT_GET(entry->hashval, ARCH_CONVERT) >= hashval)) { - entry--; - probe--; - } - while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)) && (INT_GET(entry->hashval, ARCH_CONVERT) < hashval)) { - entry++; - probe++; - } - if ((probe == INT_GET(leaf->hdr.count, ARCH_CONVERT)) || (INT_GET(entry->hashval, ARCH_CONVERT) != hashval)) { - *index = probe; - ASSERT(args->oknoent); - return XFS_ERROR(ENOENT); - } - - /* - * Duplicate keys may be present, so search all of them for a match. - */ - while ((probe < INT_GET(leaf->hdr.count, ARCH_CONVERT)) && (INT_GET(entry->hashval, ARCH_CONVERT) == hashval)) { - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(entry->nameidx, ARCH_CONVERT)); - if (entry->namelen == args->namelen && - namest->name[0] == args->name[0] && - memcmp(args->name, namest->name, args->namelen) == 0) { - XFS_DIR_SF_GET_DIRINO(&namest->inumber, &args->inumber); - *index = probe; - return XFS_ERROR(EEXIST); - } - entry++; - probe++; - } - *index = probe; - ASSERT(probe == INT_GET(leaf->hdr.count, ARCH_CONVERT) || args->oknoent); - return XFS_ERROR(ENOENT); -} - -/*======================================================================== - * Utility routines. - *========================================================================*/ - -/* - * Move the indicated entries from one leaf to another. - * NOTE: this routine modifies both source and destination leaves. - */ -/* ARGSUSED */ -STATIC void -xfs_dir_leaf_moveents(xfs_dir_leafblock_t *leaf_s, int start_s, - xfs_dir_leafblock_t *leaf_d, int start_d, - int count, xfs_mount_t *mp) -{ - xfs_dir_leaf_hdr_t *hdr_s, *hdr_d; - xfs_dir_leaf_entry_t *entry_s, *entry_d; - int tmp, i; - - /* - * Check for nothing to do. - */ - if (count == 0) - return; - - /* - * Set up environment. - */ - ASSERT(be16_to_cpu(leaf_s->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - ASSERT(be16_to_cpu(leaf_d->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - hdr_s = &leaf_s->hdr; - hdr_d = &leaf_d->hdr; - ASSERT((INT_GET(hdr_s->count, ARCH_CONVERT) > 0) && (INT_GET(hdr_s->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8))); - ASSERT(INT_GET(hdr_s->firstused, ARCH_CONVERT) >= - ((INT_GET(hdr_s->count, ARCH_CONVERT)*sizeof(*entry_s))+sizeof(*hdr_s))); - ASSERT(INT_GET(hdr_d->count, ARCH_CONVERT) < (XFS_LBSIZE(mp)/8)); - ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >= - ((INT_GET(hdr_d->count, ARCH_CONVERT)*sizeof(*entry_d))+sizeof(*hdr_d))); - - ASSERT(start_s < INT_GET(hdr_s->count, ARCH_CONVERT)); - ASSERT(start_d <= INT_GET(hdr_d->count, ARCH_CONVERT)); - ASSERT(count <= INT_GET(hdr_s->count, ARCH_CONVERT)); - - /* - * Move the entries in the destination leaf up to make a hole? - */ - if (start_d < INT_GET(hdr_d->count, ARCH_CONVERT)) { - tmp = INT_GET(hdr_d->count, ARCH_CONVERT) - start_d; - tmp *= (uint)sizeof(xfs_dir_leaf_entry_t); - entry_s = &leaf_d->entries[start_d]; - entry_d = &leaf_d->entries[start_d + count]; - memcpy(entry_d, entry_s, tmp); - } - - /* - * Copy all entry's in the same (sorted) order, - * but allocate filenames packed and in sequence. - */ - entry_s = &leaf_s->entries[start_s]; - entry_d = &leaf_d->entries[start_d]; - for (i = 0; i < count; entry_s++, entry_d++, i++) { - ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) >= INT_GET(hdr_s->firstused, ARCH_CONVERT)); - tmp = XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry_s); - INT_MOD(hdr_d->firstused, ARCH_CONVERT, -(tmp)); - entry_d->hashval = entry_s->hashval; /* INT_: direct copy */ - INT_COPY(entry_d->nameidx, hdr_d->firstused, ARCH_CONVERT); - entry_d->namelen = entry_s->namelen; - ASSERT(INT_GET(entry_d->nameidx, ARCH_CONVERT) + tmp <= XFS_LBSIZE(mp)); - memcpy(XFS_DIR_LEAF_NAMESTRUCT(leaf_d, INT_GET(entry_d->nameidx, ARCH_CONVERT)), - XFS_DIR_LEAF_NAMESTRUCT(leaf_s, INT_GET(entry_s->nameidx, ARCH_CONVERT)), tmp); - ASSERT(INT_GET(entry_s->nameidx, ARCH_CONVERT) + tmp <= XFS_LBSIZE(mp)); - memset((char *)XFS_DIR_LEAF_NAMESTRUCT(leaf_s, INT_GET(entry_s->nameidx, ARCH_CONVERT)), - 0, tmp); - INT_MOD(hdr_s->namebytes, ARCH_CONVERT, -(entry_d->namelen)); - INT_MOD(hdr_d->namebytes, ARCH_CONVERT, entry_d->namelen); - INT_MOD(hdr_s->count, ARCH_CONVERT, -1); - INT_MOD(hdr_d->count, ARCH_CONVERT, +1); - tmp = INT_GET(hdr_d->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t) - + (uint)sizeof(xfs_dir_leaf_hdr_t); - ASSERT(INT_GET(hdr_d->firstused, ARCH_CONVERT) >= tmp); - - } - - /* - * Zero out the entries we just copied. - */ - if (start_s == INT_GET(hdr_s->count, ARCH_CONVERT)) { - tmp = count * (uint)sizeof(xfs_dir_leaf_entry_t); - entry_s = &leaf_s->entries[start_s]; - ASSERT((char *)entry_s + tmp <= (char *)leaf_s + XFS_LBSIZE(mp)); - memset((char *)entry_s, 0, tmp); - } else { - /* - * Move the remaining entries down to fill the hole, - * then zero the entries at the top. - */ - tmp = INT_GET(hdr_s->count, ARCH_CONVERT) - count; - tmp *= (uint)sizeof(xfs_dir_leaf_entry_t); - entry_s = &leaf_s->entries[start_s + count]; - entry_d = &leaf_s->entries[start_s]; - memcpy(entry_d, entry_s, tmp); - - tmp = count * (uint)sizeof(xfs_dir_leaf_entry_t); - entry_s = &leaf_s->entries[INT_GET(hdr_s->count, ARCH_CONVERT)]; - ASSERT((char *)entry_s + tmp <= (char *)leaf_s + XFS_LBSIZE(mp)); - memset((char *)entry_s, 0, tmp); - } - - /* - * Fill in the freemap information - */ - INT_SET(hdr_d->freemap[0].base, ARCH_CONVERT, (uint)sizeof(xfs_dir_leaf_hdr_t)); - INT_MOD(hdr_d->freemap[0].base, ARCH_CONVERT, INT_GET(hdr_d->count, ARCH_CONVERT) * (uint)sizeof(xfs_dir_leaf_entry_t)); - INT_SET(hdr_d->freemap[0].size, ARCH_CONVERT, INT_GET(hdr_d->firstused, ARCH_CONVERT) - INT_GET(hdr_d->freemap[0].base, ARCH_CONVERT)); - INT_SET(hdr_d->freemap[1].base, ARCH_CONVERT, (hdr_d->freemap[2].base = 0)); - INT_SET(hdr_d->freemap[1].size, ARCH_CONVERT, (hdr_d->freemap[2].size = 0)); - hdr_s->holes = 1; /* leaf may not be compact */ -} - -/* - * Compare two leaf blocks "order". - */ -int -xfs_dir_leaf_order(xfs_dabuf_t *leaf1_bp, xfs_dabuf_t *leaf2_bp) -{ - xfs_dir_leafblock_t *leaf1, *leaf2; - - leaf1 = leaf1_bp->data; - leaf2 = leaf2_bp->data; - ASSERT((be16_to_cpu(leaf1->hdr.info.magic) == XFS_DIR_LEAF_MAGIC) && - (be16_to_cpu(leaf2->hdr.info.magic) == XFS_DIR_LEAF_MAGIC)); - if ((INT_GET(leaf1->hdr.count, ARCH_CONVERT) > 0) && (INT_GET(leaf2->hdr.count, ARCH_CONVERT) > 0) && - ((INT_GET(leaf2->entries[ 0 ].hashval, ARCH_CONVERT) < - INT_GET(leaf1->entries[ 0 ].hashval, ARCH_CONVERT)) || - (INT_GET(leaf2->entries[ INT_GET(leaf2->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT) < - INT_GET(leaf1->entries[ INT_GET(leaf1->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)))) { - return 1; - } - return 0; -} - -/* - * Pick up the last hashvalue from a leaf block. - */ -xfs_dahash_t -xfs_dir_leaf_lasthash(xfs_dabuf_t *bp, int *count) -{ - xfs_dir_leafblock_t *leaf; - - leaf = bp->data; - ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR_LEAF_MAGIC); - if (count) - *count = INT_GET(leaf->hdr.count, ARCH_CONVERT); - if (!leaf->hdr.count) - return(0); - return(INT_GET(leaf->entries[ INT_GET(leaf->hdr.count, ARCH_CONVERT)-1 ].hashval, ARCH_CONVERT)); -} - -/* - * Copy out directory entries for getdents(), for leaf directories. - */ -int -xfs_dir_leaf_getdents_int( - xfs_dabuf_t *bp, - xfs_inode_t *dp, - xfs_dablk_t bno, - uio_t *uio, - int *eobp, - xfs_dirent_t *dbp, - xfs_dir_put_t put, - xfs_daddr_t nextda) -{ - xfs_dir_leafblock_t *leaf; - xfs_dir_leaf_entry_t *entry; - xfs_dir_leaf_name_t *namest; - int entno, want_entno, i, nextentno; - xfs_mount_t *mp; - xfs_dahash_t cookhash; - xfs_dahash_t nexthash = 0; -#if (BITS_PER_LONG == 32) - xfs_dahash_t lasthash = XFS_DA_MAXHASH; -#endif - xfs_dir_put_args_t p; - - mp = dp->i_mount; - leaf = bp->data; - if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR_LEAF_MAGIC) { - *eobp = 1; - return XFS_ERROR(ENOENT); /* XXX wrong code */ - } - - want_entno = XFS_DA_COOKIE_ENTRY(mp, uio->uio_offset); - - cookhash = XFS_DA_COOKIE_HASH(mp, uio->uio_offset); - - xfs_dir_trace_g_dul("leaf: start", dp, uio, leaf); - - /* - * Re-find our place. - */ - for (i = entno = 0, entry = &leaf->entries[0]; - i < INT_GET(leaf->hdr.count, ARCH_CONVERT); - entry++, i++) { - - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, - INT_GET(entry->nameidx, ARCH_CONVERT)); - - if (unlikely( - ((char *)namest < (char *)leaf) || - ((char *)namest >= (char *)leaf + XFS_LBSIZE(mp)))) { - XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(1)", - XFS_ERRLEVEL_LOW, mp, leaf); - xfs_dir_trace_g_du("leaf: corrupted", dp, uio); - return XFS_ERROR(EFSCORRUPTED); - } - if (INT_GET(entry->hashval, ARCH_CONVERT) >= cookhash) { - if ( entno < want_entno - && INT_GET(entry->hashval, ARCH_CONVERT) - == cookhash) { - /* - * Trying to get to a particular offset in a - * run of equal-hashval entries. - */ - entno++; - } else if ( want_entno > 0 - && entno == want_entno - && INT_GET(entry->hashval, ARCH_CONVERT) - == cookhash) { - break; - } else { - entno = 0; - break; - } - } - } - - if (i == INT_GET(leaf->hdr.count, ARCH_CONVERT)) { - xfs_dir_trace_g_du("leaf: hash not found", dp, uio); - if (!leaf->hdr.info.forw) - uio->uio_offset = - XFS_DA_MAKE_COOKIE(mp, 0, 0, XFS_DA_MAXHASH); - /* - * Don't set uio_offset if there's another block: - * the node code will be setting uio_offset anyway. - */ - *eobp = 0; - return 0; - } - xfs_dir_trace_g_due("leaf: hash found", dp, uio, entry); - - p.dbp = dbp; - p.put = put; - p.uio = uio; - - /* - * We're synchronized, start copying entries out to the user. - */ - for (; entno >= 0 && i < INT_GET(leaf->hdr.count, ARCH_CONVERT); - entry++, i++, (entno = nextentno)) { - int lastresid=0, retval; - xfs_dircook_t lastoffset; - xfs_dahash_t thishash; - - /* - * Check for a damaged directory leaf block and pick up - * the inode number from this entry. - */ - namest = XFS_DIR_LEAF_NAMESTRUCT(leaf, - INT_GET(entry->nameidx, ARCH_CONVERT)); - - if (unlikely( - ((char *)namest < (char *)leaf) || - ((char *)namest >= (char *)leaf + XFS_LBSIZE(mp)))) { - XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(2)", - XFS_ERRLEVEL_LOW, mp, leaf); - xfs_dir_trace_g_du("leaf: corrupted", dp, uio); - return XFS_ERROR(EFSCORRUPTED); - } - - xfs_dir_trace_g_duc("leaf: middle cookie ", - dp, uio, p.cook.o); - - if (i < (INT_GET(leaf->hdr.count, ARCH_CONVERT) - 1)) { - nexthash = INT_GET(entry[1].hashval, ARCH_CONVERT); - - if (nexthash == INT_GET(entry->hashval, ARCH_CONVERT)) - nextentno = entno + 1; - else - nextentno = 0; - XFS_PUT_COOKIE(p.cook, mp, bno, nextentno, nexthash); - xfs_dir_trace_g_duc("leaf: middle cookie ", - dp, uio, p.cook.o); - - } else if ((thishash = be32_to_cpu(leaf->hdr.info.forw))) { - xfs_dabuf_t *bp2; - xfs_dir_leafblock_t *leaf2; - - ASSERT(nextda != -1); - - retval = xfs_da_read_buf(dp->i_transp, dp, thishash, - nextda, &bp2, XFS_DATA_FORK); - if (retval) - return retval; - - ASSERT(bp2 != NULL); - - leaf2 = bp2->data; - - if (unlikely( - (be16_to_cpu(leaf2->hdr.info.magic) - != XFS_DIR_LEAF_MAGIC) - || (be32_to_cpu(leaf2->hdr.info.back) - != bno))) { /* GROT */ - XFS_CORRUPTION_ERROR("xfs_dir_leaf_getdents_int(3)", - XFS_ERRLEVEL_LOW, mp, - leaf2); - xfs_da_brelse(dp->i_transp, bp2); - - return XFS_ERROR(EFSCORRUPTED); - } - - nexthash = INT_GET(leaf2->entries[0].hashval, - ARCH_CONVERT); - nextentno = -1; - XFS_PUT_COOKIE(p.cook, mp, thishash, 0, nexthash); - xfs_da_brelse(dp->i_transp, bp2); - xfs_dir_trace_g_duc("leaf: next blk cookie", - dp, uio, p.cook.o); - } else { - nextentno = -1; - XFS_PUT_COOKIE(p.cook, mp, 0, 0, XFS_DA_MAXHASH); - } - - /* - * Save off the cookie so we can fall back should the - * 'put' into the outgoing buffer fails. To handle a run - * of equal-hashvals, the off_t structure on 64bit - * builds has entno built into the cookie to ID the - * entry. On 32bit builds, we only have space for the - * hashval so we can't ID specific entries within a group - * of same hashval entries. For this, lastoffset is set - * to the first in the run of equal hashvals so we don't - * include any entries unless we can include all entries - * that share the same hashval. Hopefully the buffer - * provided is big enough to handle it (see pv763517). - */ -#if (BITS_PER_LONG == 32) - if ((thishash = INT_GET(entry->hashval, ARCH_CONVERT)) - != lasthash) { - XFS_PUT_COOKIE(lastoffset, mp, bno, entno, thishash); - lastresid = uio->uio_resid; - lasthash = thishash; - } else { - xfs_dir_trace_g_duc("leaf: DUP COOKIES, skipped", - dp, uio, p.cook.o); - } -#else - thishash = INT_GET(entry->hashval, ARCH_CONVERT); - XFS_PUT_COOKIE(lastoffset, mp, bno, entno, thishash); - lastresid = uio->uio_resid; -#endif /* BITS_PER_LONG == 32 */ - - /* - * Put the current entry into the outgoing buffer. If we fail - * then restore the UIO to the first entry in the current - * run of equal-hashval entries (probably one 1 entry long). - */ - p.ino = XFS_GET_DIR_INO8(namest->inumber); -#if XFS_BIG_INUMS - p.ino += mp->m_inoadd; -#endif - p.name = (char *)namest->name; - p.namelen = entry->namelen; - - retval = p.put(&p); - - if (!p.done) { - uio->uio_offset = lastoffset.o; - uio->uio_resid = lastresid; - - *eobp = 1; - - xfs_dir_trace_g_du("leaf: E-O-B", dp, uio); - - return retval; - } - } - - uio->uio_offset = p.cook.o; - - *eobp = 0; - - xfs_dir_trace_g_du("leaf: E-O-F", dp, uio); - - return 0; -} - -/* - * Format a dirent64 structure and copy it out the the user's buffer. - */ -int -xfs_dir_put_dirent64_direct(xfs_dir_put_args_t *pa) -{ - iovec_t *iovp; - int reclen, namelen; - xfs_dirent_t *idbp; - uio_t *uio; - - namelen = pa->namelen; - reclen = DIRENTSIZE(namelen); - uio = pa->uio; - if (reclen > uio->uio_resid) { - pa->done = 0; - return 0; - } - iovp = uio->uio_iov; - idbp = (xfs_dirent_t *)iovp->iov_base; - iovp->iov_base = (char *)idbp + reclen; - iovp->iov_len -= reclen; - uio->uio_resid -= reclen; - idbp->d_reclen = reclen; - idbp->d_ino = pa->ino; - idbp->d_off = pa->cook.o; - idbp->d_name[namelen] = '\0'; - pa->done = 1; - memcpy(idbp->d_name, pa->name, namelen); - return 0; -} - -/* - * Format a dirent64 structure and copy it out the the user's buffer. - */ -int -xfs_dir_put_dirent64_uio(xfs_dir_put_args_t *pa) -{ - int retval, reclen, namelen; - xfs_dirent_t *idbp; - uio_t *uio; - - namelen = pa->namelen; - reclen = DIRENTSIZE(namelen); - uio = pa->uio; - if (reclen > uio->uio_resid) { - pa->done = 0; - return 0; - } - idbp = pa->dbp; - idbp->d_reclen = reclen; - idbp->d_ino = pa->ino; - idbp->d_off = pa->cook.o; - idbp->d_name[namelen] = '\0'; - memcpy(idbp->d_name, pa->name, namelen); - retval = uio_read((caddr_t)idbp, reclen, uio); - pa->done = (retval == 0); - return retval; -} diff --git a/fs/xfs/xfs_dir_leaf.h b/fs/xfs/xfs_dir_leaf.h deleted file mode 100644 index eb8cd9a..0000000 --- a/fs/xfs/xfs_dir_leaf.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_DIR_LEAF_H__ -#define __XFS_DIR_LEAF_H__ - -/* - * Directory layout, internal structure, access macros, etc. - * - * Large directories are structured around Btrees where all the data - * elements are in the leaf nodes. Filenames are hashed into an int, - * then that int is used as the index into the Btree. Since the hashval - * of a filename may not be unique, we may have duplicate keys. The - * internal links in the Btree are logical block offsets into the file. - */ - -struct uio; -struct xfs_bmap_free; -struct xfs_dabuf; -struct xfs_da_args; -struct xfs_da_state; -struct xfs_da_state_blk; -struct xfs_dir_put_args; -struct xfs_inode; -struct xfs_mount; -struct xfs_trans; - -/*======================================================================== - * Directory Structure when equal to XFS_LBSIZE(mp) bytes. - *========================================================================*/ - -/* - * This is the structure of the leaf nodes in the Btree. - * - * Struct leaf_entry's are packed from the top. Names grow from the bottom - * but are not packed. The freemap contains run-length-encoded entries - * for the free bytes after the leaf_entry's, but only the N largest such, - * smaller runs are dropped. When the freemap doesn't show enough space - * for an allocation, we compact the namelist area and try again. If we - * still don't have enough space, then we have to split the block. - * - * Since we have duplicate hash keys, for each key that matches, compare - * the actual string. The root and intermediate node search always takes - * the first-in-the-block key match found, so we should only have to work - * "forw"ard. If none matches, continue with the "forw"ard leaf nodes - * until the hash key changes or the filename is found. - * - * The parent directory and the self-pointer are explicitly represented - * (ie: there are entries for "." and ".."). - * - * Note that the count being a __uint16_t limits us to something like a - * blocksize of 1.3MB in the face of worst case (short) filenames. - */ -#define XFS_DIR_LEAF_MAPSIZE 3 /* how many freespace slots */ - -typedef struct xfs_dir_leaf_map { /* RLE map of free bytes */ - __uint16_t base; /* base of free region */ - __uint16_t size; /* run length of free region */ -} xfs_dir_leaf_map_t; - -typedef struct xfs_dir_leaf_hdr { /* constant-structure header block */ - xfs_da_blkinfo_t info; /* block type, links, etc. */ - __uint16_t count; /* count of active leaf_entry's */ - __uint16_t namebytes; /* num bytes of name strings stored */ - __uint16_t firstused; /* first used byte in name area */ - __uint8_t holes; /* != 0 if blk needs compaction */ - __uint8_t pad1; - xfs_dir_leaf_map_t freemap[XFS_DIR_LEAF_MAPSIZE]; -} xfs_dir_leaf_hdr_t; - -typedef struct xfs_dir_leaf_entry { /* sorted on key, not name */ - xfs_dahash_t hashval; /* hash value of name */ - __uint16_t nameidx; /* index into buffer of name */ - __uint8_t namelen; /* length of name string */ - __uint8_t pad2; -} xfs_dir_leaf_entry_t; - -typedef struct xfs_dir_leaf_name { - xfs_dir_ino_t inumber; /* inode number for this key */ - __uint8_t name[1]; /* name string itself */ -} xfs_dir_leaf_name_t; - -typedef struct xfs_dir_leafblock { - xfs_dir_leaf_hdr_t hdr; /* constant-structure header block */ - xfs_dir_leaf_entry_t entries[1]; /* var sized array */ - xfs_dir_leaf_name_t namelist[1]; /* grows from bottom of buf */ -} xfs_dir_leafblock_t; - -/* - * Length of name for which a 512-byte block filesystem - * can get a double split. - */ -#define XFS_DIR_LEAF_CAN_DOUBLE_SPLIT_LEN \ - (512 - (uint)sizeof(xfs_dir_leaf_hdr_t) - \ - (uint)sizeof(xfs_dir_leaf_entry_t) * 2 - \ - (uint)sizeof(xfs_dir_leaf_name_t) * 2 - (MAXNAMELEN - 2) + 1 + 1) - -typedef int (*xfs_dir_put_t)(struct xfs_dir_put_args *pa); - -typedef union { - xfs_off_t o; /* offset (cookie) */ - /* - * Watch the order here (endian-ness dependent). - */ - struct { -#ifndef XFS_NATIVE_HOST - xfs_dahash_t h; /* hash value */ - __uint32_t be; /* block and entry */ -#else - __uint32_t be; /* block and entry */ - xfs_dahash_t h; /* hash value */ -#endif /* XFS_NATIVE_HOST */ - } s; -} xfs_dircook_t; - -#define XFS_PUT_COOKIE(c,mp,bno,entry,hash) \ - ((c).s.be = XFS_DA_MAKE_BNOENTRY(mp, bno, entry), (c).s.h = (hash)) - -typedef struct xfs_dir_put_args { - xfs_dircook_t cook; /* cookie of (next) entry */ - xfs_intino_t ino; /* inode number */ - struct xfs_dirent *dbp; /* buffer pointer */ - char *name; /* directory entry name */ - int namelen; /* length of name */ - int done; /* output: set if value was stored */ - xfs_dir_put_t put; /* put function ptr (i/o) */ - struct uio *uio; /* uio control structure */ -} xfs_dir_put_args_t; - -#define XFS_DIR_LEAF_ENTSIZE_BYNAME(len) \ - xfs_dir_leaf_entsize_byname(len) -static inline int xfs_dir_leaf_entsize_byname(int len) -{ - return (uint)sizeof(xfs_dir_leaf_name_t)-1 + len; -} - -#define XFS_DIR_LEAF_ENTSIZE_BYENTRY(entry) \ - xfs_dir_leaf_entsize_byentry(entry) -static inline int xfs_dir_leaf_entsize_byentry(xfs_dir_leaf_entry_t *entry) -{ - return (uint)sizeof(xfs_dir_leaf_name_t)-1 + (entry)->namelen; -} - -#define XFS_DIR_LEAF_NAMESTRUCT(leafp,offset) \ - xfs_dir_leaf_namestruct(leafp,offset) -static inline xfs_dir_leaf_name_t * -xfs_dir_leaf_namestruct(xfs_dir_leafblock_t *leafp, int offset) -{ - return (xfs_dir_leaf_name_t *)&((char *)(leafp))[offset]; -} - -/*======================================================================== - * Function prototypes for the kernel. - *========================================================================*/ - -/* - * Internal routines when dirsize < XFS_LITINO(mp). - */ -int xfs_dir_shortform_create(struct xfs_da_args *args, xfs_ino_t parent); -int xfs_dir_shortform_addname(struct xfs_da_args *args); -int xfs_dir_shortform_lookup(struct xfs_da_args *args); -int xfs_dir_shortform_to_leaf(struct xfs_da_args *args); -int xfs_dir_shortform_removename(struct xfs_da_args *args); -int xfs_dir_shortform_getdents(struct xfs_inode *dp, struct uio *uio, int *eofp, - struct xfs_dirent *dbp, xfs_dir_put_t put); -int xfs_dir_shortform_replace(struct xfs_da_args *args); - -/* - * Internal routines when dirsize == XFS_LBSIZE(mp). - */ -int xfs_dir_leaf_to_node(struct xfs_da_args *args); -int xfs_dir_leaf_to_shortform(struct xfs_da_args *args); - -/* - * Routines used for growing the Btree. - */ -int xfs_dir_leaf_split(struct xfs_da_state *state, - struct xfs_da_state_blk *oldblk, - struct xfs_da_state_blk *newblk); -int xfs_dir_leaf_add(struct xfs_dabuf *leaf_buffer, - struct xfs_da_args *args, int insertion_index); -int xfs_dir_leaf_addname(struct xfs_da_args *args); -int xfs_dir_leaf_lookup_int(struct xfs_dabuf *leaf_buffer, - struct xfs_da_args *args, - int *index_found_at); -int xfs_dir_leaf_remove(struct xfs_trans *trans, - struct xfs_dabuf *leaf_buffer, - int index_to_remove); -int xfs_dir_leaf_getdents_int(struct xfs_dabuf *bp, struct xfs_inode *dp, - xfs_dablk_t bno, struct uio *uio, - int *eobp, struct xfs_dirent *dbp, - xfs_dir_put_t put, xfs_daddr_t nextda); - -/* - * Routines used for shrinking the Btree. - */ -int xfs_dir_leaf_toosmall(struct xfs_da_state *state, int *retval); -void xfs_dir_leaf_unbalance(struct xfs_da_state *state, - struct xfs_da_state_blk *drop_blk, - struct xfs_da_state_blk *save_blk); - -/* - * Utility routines. - */ -uint xfs_dir_leaf_lasthash(struct xfs_dabuf *bp, int *count); -int xfs_dir_leaf_order(struct xfs_dabuf *leaf1_bp, - struct xfs_dabuf *leaf2_bp); -int xfs_dir_put_dirent64_direct(xfs_dir_put_args_t *pa); -int xfs_dir_put_dirent64_uio(xfs_dir_put_args_t *pa); -int xfs_dir_ino_validate(struct xfs_mount *mp, xfs_ino_t ino); - -/* - * Global data. - */ -extern xfs_dahash_t xfs_dir_hash_dot, xfs_dir_hash_dotdot; - -#endif /* __XFS_DIR_LEAF_H__ */ diff --git a/fs/xfs/xfs_dir_sf.h b/fs/xfs/xfs_dir_sf.h deleted file mode 100644 index 5b20b4d..0000000 --- a/fs/xfs/xfs_dir_sf.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2000,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_DIR_SF_H__ -#define __XFS_DIR_SF_H__ - -/* - * Directory layout when stored internal to an inode. - * - * Small directories are packed as tightly as possible so as to - * fit into the literal area of the inode. - */ - -typedef struct { __uint8_t i[sizeof(xfs_ino_t)]; } xfs_dir_ino_t; - -/* - * The parent directory has a dedicated field, and the self-pointer must - * be calculated on the fly. - * - * Entries are packed toward the top as tight as possible. The header - * and the elements much be memcpy'd out into a work area to get correct - * alignment for the inode number fields. - */ -typedef struct xfs_dir_sf_hdr { /* constant-structure header block */ - xfs_dir_ino_t parent; /* parent dir inode number */ - __uint8_t count; /* count of active entries */ -} xfs_dir_sf_hdr_t; - -typedef struct xfs_dir_sf_entry { - xfs_dir_ino_t inumber; /* referenced inode number */ - __uint8_t namelen; /* actual length of name (no NULL) */ - __uint8_t name[1]; /* name */ -} xfs_dir_sf_entry_t; - -typedef struct xfs_dir_shortform { - xfs_dir_sf_hdr_t hdr; - xfs_dir_sf_entry_t list[1]; /* variable sized array */ -} xfs_dir_shortform_t; - -/* - * We generate this then sort it, so that readdirs are returned in - * hash-order. Else seekdir won't work. - */ -typedef struct xfs_dir_sf_sort { - __uint8_t entno; /* .=0, ..=1, else entry# + 2 */ - __uint8_t seqno; /* sequence # with same hash value */ - __uint8_t namelen; /* length of name value (no null) */ - xfs_dahash_t hash; /* this entry's hash value */ - xfs_intino_t ino; /* this entry's inode number */ - char *name; /* name value, pointer into buffer */ -} xfs_dir_sf_sort_t; - -#define XFS_DIR_SF_GET_DIRINO(from,to) xfs_dir_sf_get_dirino(from, to) -static inline void xfs_dir_sf_get_dirino(xfs_dir_ino_t *from, xfs_ino_t *to) -{ - *(to) = XFS_GET_DIR_INO8(*from); -} - -#define XFS_DIR_SF_PUT_DIRINO(from,to) xfs_dir_sf_put_dirino(from, to) -static inline void xfs_dir_sf_put_dirino(xfs_ino_t *from, xfs_dir_ino_t *to) -{ - XFS_PUT_DIR_INO8(*(from), *(to)); -} - -#define XFS_DIR_SF_ENTSIZE_BYNAME(len) xfs_dir_sf_entsize_byname(len) -static inline int xfs_dir_sf_entsize_byname(int len) -{ - return (uint)sizeof(xfs_dir_sf_entry_t)-1 + (len); -} - -#define XFS_DIR_SF_ENTSIZE_BYENTRY(sfep) xfs_dir_sf_entsize_byentry(sfep) -static inline int xfs_dir_sf_entsize_byentry(xfs_dir_sf_entry_t *sfep) -{ - return (uint)sizeof(xfs_dir_sf_entry_t)-1 + (sfep)->namelen; -} - -#define XFS_DIR_SF_NEXTENTRY(sfep) xfs_dir_sf_nextentry(sfep) -static inline xfs_dir_sf_entry_t *xfs_dir_sf_nextentry(xfs_dir_sf_entry_t *sfep) -{ - return (xfs_dir_sf_entry_t *) \ - ((char *)(sfep) + XFS_DIR_SF_ENTSIZE_BYENTRY(sfep)); -} - -#define XFS_DIR_SF_ALLFIT(count,totallen) \ - xfs_dir_sf_allfit(count,totallen) -static inline int xfs_dir_sf_allfit(int count, int totallen) -{ - return ((uint)sizeof(xfs_dir_sf_hdr_t) + \ - ((uint)sizeof(xfs_dir_sf_entry_t)-1)*(count) + (totallen)); -} - -#if defined(XFS_DIR_TRACE) - -/* - * Kernel tracing support for directories. - */ -struct uio; -struct xfs_inode; -struct xfs_da_intnode; -struct xfs_dinode; -struct xfs_dir_leafblock; -struct xfs_dir_leaf_entry; - -#define XFS_DIR_TRACE_SIZE 4096 /* size of global trace buffer */ -extern ktrace_t *xfs_dir_trace_buf; - -/* - * Trace record types. - */ -#define XFS_DIR_KTRACE_G_DU 1 /* dp, uio */ -#define XFS_DIR_KTRACE_G_DUB 2 /* dp, uio, bno */ -#define XFS_DIR_KTRACE_G_DUN 3 /* dp, uio, node */ -#define XFS_DIR_KTRACE_G_DUL 4 /* dp, uio, leaf */ -#define XFS_DIR_KTRACE_G_DUE 5 /* dp, uio, leaf entry */ -#define XFS_DIR_KTRACE_G_DUC 6 /* dp, uio, cookie */ - -void xfs_dir_trace_g_du(char *where, struct xfs_inode *dp, struct uio *uio); -void xfs_dir_trace_g_dub(char *where, struct xfs_inode *dp, struct uio *uio, - xfs_dablk_t bno); -void xfs_dir_trace_g_dun(char *where, struct xfs_inode *dp, struct uio *uio, - struct xfs_da_intnode *node); -void xfs_dir_trace_g_dul(char *where, struct xfs_inode *dp, struct uio *uio, - struct xfs_dir_leafblock *leaf); -void xfs_dir_trace_g_due(char *where, struct xfs_inode *dp, struct uio *uio, - struct xfs_dir_leaf_entry *entry); -void xfs_dir_trace_g_duc(char *where, struct xfs_inode *dp, struct uio *uio, - xfs_off_t cookie); -void xfs_dir_trace_enter(int type, char *where, - void *a0, void *a1, void *a2, void *a3, - void *a4, void *a5, void *a6, void *a7, - void *a8, void *a9, void *a10, void *a11); -#else -#define xfs_dir_trace_g_du(w,d,u) -#define xfs_dir_trace_g_dub(w,d,u,b) -#define xfs_dir_trace_g_dun(w,d,u,n) -#define xfs_dir_trace_g_dul(w,d,u,l) -#define xfs_dir_trace_g_due(w,d,u,e) -#define xfs_dir_trace_g_duc(w,d,u,c) -#endif /* DEBUG */ - -#endif /* __XFS_DIR_SF_H__ */ diff --git a/fs/xfs/xfs_dmapi.h b/fs/xfs/xfs_dmapi.h index 00b1540..4e7865a 100644 --- a/fs/xfs/xfs_dmapi.h +++ b/fs/xfs/xfs_dmapi.h @@ -189,6 +189,6 @@ #define FILP_DELAY_FLAG(filp) ((filp->f_ #define AT_DELAY_FLAG(f) ((f&ATTR_NONBLOCK) ? DM_FLAGS_NDELAY : 0) -extern struct bhv_vfsops xfs_dmops; +extern struct bhv_module_vfsops xfs_dmops; #endif /* __XFS_DMAPI_H__ */ diff --git a/fs/xfs/xfs_dmops.c b/fs/xfs/xfs_dmops.c index 629795b..1e4a35d 100644 --- a/fs/xfs/xfs_dmops.c +++ b/fs/xfs/xfs_dmops.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 2a21c50..b95681b 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -22,12 +22,10 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index f19282e..6cf6d87 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_buf_item.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" @@ -294,6 +293,62 @@ xfs_efi_init(xfs_mount_t *mp, } /* + * Copy an EFI format buffer from the given buf, and into the destination + * EFI format structure. + * The given buffer can be in 32 bit or 64 bit form (which has different padding), + * one of which will be the native format for this kernel. + * It will handle the conversion of formats if necessary. + */ +int +xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) +{ + xfs_efi_log_format_t *src_efi_fmt = (xfs_efi_log_format_t *)buf->i_addr; + uint i; + uint len = sizeof(xfs_efi_log_format_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t); + uint len32 = sizeof(xfs_efi_log_format_32_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t); + uint len64 = sizeof(xfs_efi_log_format_64_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t); + + if (buf->i_len == len) { + memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len); + return 0; + } else if (buf->i_len == len32) { + xfs_efi_log_format_32_t *src_efi_fmt_32 = + (xfs_efi_log_format_32_t *)buf->i_addr; + + dst_efi_fmt->efi_type = src_efi_fmt_32->efi_type; + dst_efi_fmt->efi_size = src_efi_fmt_32->efi_size; + dst_efi_fmt->efi_nextents = src_efi_fmt_32->efi_nextents; + dst_efi_fmt->efi_id = src_efi_fmt_32->efi_id; + for (i = 0; i < dst_efi_fmt->efi_nextents; i++) { + dst_efi_fmt->efi_extents[i].ext_start = + src_efi_fmt_32->efi_extents[i].ext_start; + dst_efi_fmt->efi_extents[i].ext_len = + src_efi_fmt_32->efi_extents[i].ext_len; + } + return 0; + } else if (buf->i_len == len64) { + xfs_efi_log_format_64_t *src_efi_fmt_64 = + (xfs_efi_log_format_64_t *)buf->i_addr; + + dst_efi_fmt->efi_type = src_efi_fmt_64->efi_type; + dst_efi_fmt->efi_size = src_efi_fmt_64->efi_size; + dst_efi_fmt->efi_nextents = src_efi_fmt_64->efi_nextents; + dst_efi_fmt->efi_id = src_efi_fmt_64->efi_id; + for (i = 0; i < dst_efi_fmt->efi_nextents; i++) { + dst_efi_fmt->efi_extents[i].ext_start = + src_efi_fmt_64->efi_extents[i].ext_start; + dst_efi_fmt->efi_extents[i].ext_len = + src_efi_fmt_64->efi_extents[i].ext_len; + } + return 0; + } + return EFSCORRUPTED; +} + +/* * This is called by the efd item code below to release references to * the given efi item. Each efd calls this with the number of * extents that it has logged, and when the sum of these reaches diff --git a/fs/xfs/xfs_extfree_item.h b/fs/xfs/xfs_extfree_item.h index 5bf6817..0ea45ed 100644 --- a/fs/xfs/xfs_extfree_item.h +++ b/fs/xfs/xfs_extfree_item.h @@ -27,6 +27,24 @@ typedef struct xfs_extent { } xfs_extent_t; /* + * Since an xfs_extent_t has types (start:64, len: 32) + * there are different alignments on 32 bit and 64 bit kernels. + * So we provide the different variants for use by a + * conversion routine. + */ + +typedef struct xfs_extent_32 { + xfs_dfsbno_t ext_start; + xfs_extlen_t ext_len; +} __attribute__((packed)) xfs_extent_32_t; + +typedef struct xfs_extent_64 { + xfs_dfsbno_t ext_start; + xfs_extlen_t ext_len; + __uint32_t ext_pad; +} xfs_extent_64_t; + +/* * This is the structure used to lay out an efi log item in the * log. The efi_extents field is a variable size array whose * size is given by efi_nextents. @@ -39,6 +57,22 @@ typedef struct xfs_efi_log_format { xfs_extent_t efi_extents[1]; /* array of extents to free */ } xfs_efi_log_format_t; +typedef struct xfs_efi_log_format_32 { + unsigned short efi_type; /* efi log item type */ + unsigned short efi_size; /* size of this item */ + uint efi_nextents; /* # extents to free */ + __uint64_t efi_id; /* efi identifier */ + xfs_extent_32_t efi_extents[1]; /* array of extents to free */ +} __attribute__((packed)) xfs_efi_log_format_32_t; + +typedef struct xfs_efi_log_format_64 { + unsigned short efi_type; /* efi log item type */ + unsigned short efi_size; /* size of this item */ + uint efi_nextents; /* # extents to free */ + __uint64_t efi_id; /* efi identifier */ + xfs_extent_64_t efi_extents[1]; /* array of extents to free */ +} xfs_efi_log_format_64_t; + /* * This is the structure used to lay out an efd log item in the * log. The efd_extents array is a variable size array whose @@ -52,6 +86,22 @@ typedef struct xfs_efd_log_format { xfs_extent_t efd_extents[1]; /* array of extents freed */ } xfs_efd_log_format_t; +typedef struct xfs_efd_log_format_32 { + unsigned short efd_type; /* efd log item type */ + unsigned short efd_size; /* size of this item */ + uint efd_nextents; /* # of extents freed */ + __uint64_t efd_efi_id; /* id of corresponding efi */ + xfs_extent_32_t efd_extents[1]; /* array of extents freed */ +} __attribute__((packed)) xfs_efd_log_format_32_t; + +typedef struct xfs_efd_log_format_64 { + unsigned short efd_type; /* efd log item type */ + unsigned short efd_size; /* size of this item */ + uint efd_nextents; /* # of extents freed */ + __uint64_t efd_efi_id; /* id of corresponding efi */ + xfs_extent_64_t efd_extents[1]; /* array of extents freed */ +} xfs_efd_log_format_64_t; + #ifdef __KERNEL__ @@ -103,7 +153,8 @@ extern struct kmem_zone *xfs_efd_zone; xfs_efi_log_item_t *xfs_efi_init(struct xfs_mount *, uint); xfs_efd_log_item_t *xfs_efd_init(struct xfs_mount *, xfs_efi_log_item_t *, uint); - +int xfs_efi_copy_format(xfs_log_iovec_t *buf, + xfs_efi_log_format_t *dst_efi_fmt); void xfs_efi_item_free(xfs_efi_log_item_t *); #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h index 14010f1..0f0ad15 100644 --- a/fs/xfs/xfs_fs.h +++ b/fs/xfs/xfs_fs.h @@ -67,14 +67,15 @@ #define XFS_XFLAG_PROJINHERIT 0x00000200 #define XFS_XFLAG_NOSYMLINKS 0x00000400 /* disallow symlink creation */ #define XFS_XFLAG_EXTSIZE 0x00000800 /* extent size allocator hint */ #define XFS_XFLAG_EXTSZINHERIT 0x00001000 /* inherit inode extent size */ +#define XFS_XFLAG_NODEFRAG 0x00002000 /* do not defragment */ #define XFS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ /* * Structure for XFS_IOC_GETBMAP. * On input, fill in bmv_offset and bmv_length of the first structure - * to indicate the area of interest in the file, and bmv_entry with the - * number of array elements given. The first structure is updated on - * return to give the offset and length for the next call. + * to indicate the area of interest in the file, and bmv_entries with + * the number of array elements given back. The first structure is + * updated on return to give the offset and length for the next call. */ #ifndef HAVE_GETBMAP struct getbmap { diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index dfa3527..077629b 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -24,14 +24,12 @@ #include "xfs_log.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -542,14 +540,13 @@ xfs_reserve_blocks( } void -xfs_fs_log_dummy(xfs_mount_t *mp) +xfs_fs_log_dummy( + xfs_mount_t *mp) { - xfs_trans_t *tp; - xfs_inode_t *ip; - + xfs_trans_t *tp; + xfs_inode_t *ip; tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1); - atomic_inc(&mp->m_active_trans); if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) { xfs_trans_cancel(tp, 0); return; @@ -574,21 +571,22 @@ xfs_fs_goingdown( { switch (inflags) { case XFS_FSOP_GOING_FLAGS_DEFAULT: { - struct vfs *vfsp = XFS_MTOVFS(mp); + struct bhv_vfs *vfsp = XFS_MTOVFS(mp); struct super_block *sb = freeze_bdev(vfsp->vfs_super->s_bdev); if (sb && !IS_ERR(sb)) { - xfs_force_shutdown(mp, XFS_FORCE_UMOUNT); + xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); thaw_bdev(sb->s_bdev, sb); } break; } case XFS_FSOP_GOING_FLAGS_LOGFLUSH: - xfs_force_shutdown(mp, XFS_FORCE_UMOUNT); + xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT); break; case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH: - xfs_force_shutdown(mp, XFS_FORCE_UMOUNT|XFS_LOG_IO_ERROR); + xfs_force_shutdown(mp, + SHUTDOWN_FORCE_UMOUNT | SHUTDOWN_LOG_IO_ERROR); break; default: return XFS_ERROR(EINVAL); diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index deddbd0..33164a8 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -1174,6 +1172,9 @@ xfs_dilocate( if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || ino != XFS_AGINO_TO_INO(mp, agno, agino)) { #ifdef DEBUG + /* no diagnostics for bulkstat, ino comes from userspace */ + if (flags & XFS_IMAP_BULKSTAT) + return XFS_ERROR(EINVAL); if (agno >= mp->m_sb.sb_agcount) { xfs_fs_cmn_err(CE_ALERT, mp, "xfs_dilocate: agno (%d) >= " diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c index 60c6568..616eeeb 100644 --- a/fs/xfs/xfs_ialloc_btree.c +++ b/fs/xfs/xfs_ialloc_btree.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index b538543..0724df7 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -186,7 +184,7 @@ xfs_ihash_promote( */ STATIC int xfs_iget_core( - vnode_t *vp, + bhv_vnode_t *vp, xfs_mount_t *mp, xfs_trans_t *tp, xfs_ino_t ino, @@ -198,7 +196,7 @@ xfs_iget_core( xfs_ihash_t *ih; xfs_inode_t *ip; xfs_inode_t *iq; - vnode_t *inode_vp; + bhv_vnode_t *inode_vp; ulong version; int error; /* REFERENCED */ @@ -468,7 +466,7 @@ finish_inode: * If we have a real type for an on-disk inode, we can set ops(&unlock) * now. If it's a new inode being created, xfs_ialloc will handle it. */ - VFS_INIT_VNODE(XFS_MTOVFS(mp), vp, XFS_ITOBHV(ip), 1); + bhv_vfs_init_vnode(XFS_MTOVFS(mp), vp, XFS_ITOBHV(ip), 1); return 0; } @@ -489,7 +487,7 @@ xfs_iget( xfs_daddr_t bno) { struct inode *inode; - vnode_t *vp = NULL; + bhv_vnode_t *vp = NULL; int error; XFS_STATS_INC(xs_ig_attempts); @@ -543,7 +541,7 @@ retry: void xfs_inode_lock_init( xfs_inode_t *ip, - vnode_t *vp) + bhv_vnode_t *vp) { mrlock_init(&ip->i_lock, MRLOCK_ALLOW_EQUAL_PRI|MRLOCK_BARRIER, "xfsino", (long)vp->v_number); @@ -603,12 +601,10 @@ void xfs_iput(xfs_inode_t *ip, uint lock_flags) { - vnode_t *vp = XFS_ITOV(ip); + bhv_vnode_t *vp = XFS_ITOV(ip); vn_trace_entry(vp, "xfs_iput", (inst_t *)__return_address); - xfs_iunlock(ip, lock_flags); - VN_RELE(vp); } @@ -619,7 +615,7 @@ void xfs_iput_new(xfs_inode_t *ip, uint lock_flags) { - vnode_t *vp = XFS_ITOV(ip); + bhv_vnode_t *vp = XFS_ITOV(ip); struct inode *inode = vn_to_inode(vp); vn_trace_entry(vp, "xfs_iput_new", (inst_t *)__return_address); @@ -645,7 +641,7 @@ xfs_iput_new(xfs_inode_t *ip, void xfs_ireclaim(xfs_inode_t *ip) { - vnode_t *vp; + bhv_vnode_t *vp; /* * Remove from old hash list and mount list. @@ -1033,6 +1029,6 @@ xfs_iflock_nowait(xfs_inode_t *ip) void xfs_ifunlock(xfs_inode_t *ip) { - ASSERT(valusema(&(ip->i_flock)) <= 0); + ASSERT(issemalocked(&(ip->i_flock))); vsema(&(ip->i_flock)); } diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 94b60dd..86c1bf0 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -26,14 +26,12 @@ #include "xfs_trans.h" #include "xfs_trans_priv.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -256,13 +254,11 @@ xfs_itobp( xfs_daddr_t bno, uint imap_flags) { + xfs_imap_t imap; xfs_buf_t *bp; int error; - xfs_imap_t imap; -#ifdef __KERNEL__ int i; int ni; -#endif if (ip->i_blkno == (xfs_daddr_t)0) { /* @@ -319,7 +315,6 @@ #endif /* DEBUG */ */ error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, imap.im_blkno, (int)imap.im_len, XFS_BUF_LOCK, &bp); - if (error) { #ifdef DEBUG xfs_fs_cmn_err(CE_ALERT, mp, "xfs_itobp: " @@ -330,17 +325,21 @@ #ifdef DEBUG #endif /* DEBUG */ return error; } -#ifdef __KERNEL__ + /* * Validate the magic number and version of every inode in the buffer * (if DEBUG kernel) or the first inode in the buffer, otherwise. + * No validation is done here in userspace (xfs_repair). */ -#ifdef DEBUG +#if !defined(__KERNEL__) + ni = 0; +#elif defined(DEBUG) ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 : (BBTOB(imap.im_len) >> mp->m_sb.sb_inodelog); -#else +#else /* usual case */ ni = (imap_flags & XFS_IMAP_BULKSTAT) ? 0 : 1; #endif + for (i = 0; i < ni; i++) { int di_ok; xfs_dinode_t *dip; @@ -352,8 +351,11 @@ #endif if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, XFS_RANDOM_ITOBP_INOTOBP))) { #ifdef DEBUG - prdev("bad inode magic/vsn daddr %lld #%d (magic=%x)", - mp->m_ddev_targp, + if (!(imap_flags & XFS_IMAP_BULKSTAT)) + cmn_err(CE_ALERT, + "Device %s - bad inode magic/vsn " + "daddr %lld #%d (magic=%x)", + XFS_BUFTARG_NAME(mp->m_ddev_targp), (unsigned long long)imap.im_blkno, i, INT_GET(dip->di_core.di_magic, ARCH_CONVERT)); #endif @@ -363,7 +365,6 @@ #endif return XFS_ERROR(EFSCORRUPTED); } } -#endif /* __KERNEL__ */ xfs_inobp_check(mp, bp); @@ -782,7 +783,6 @@ xfs_xlate_dinode_core( STATIC uint _xfs_dic2xflags( - xfs_dinode_core_t *dic, __uint16_t di_flags) { uint flags = 0; @@ -812,6 +812,8 @@ _xfs_dic2xflags( flags |= XFS_XFLAG_EXTSIZE; if (di_flags & XFS_DIFLAG_EXTSZINHERIT) flags |= XFS_XFLAG_EXTSZINHERIT; + if (di_flags & XFS_DIFLAG_NODEFRAG) + flags |= XFS_XFLAG_NODEFRAG; } return flags; @@ -823,16 +825,16 @@ xfs_ip2xflags( { xfs_dinode_core_t *dic = &ip->i_d; - return _xfs_dic2xflags(dic, dic->di_flags) | - (XFS_CFORK_Q(dic) ? XFS_XFLAG_HASATTR : 0); + return _xfs_dic2xflags(dic->di_flags) | + (XFS_CFORK_Q(dic) ? XFS_XFLAG_HASATTR : 0); } uint xfs_dic2xflags( xfs_dinode_core_t *dic) { - return _xfs_dic2xflags(dic, INT_GET(dic->di_flags, ARCH_CONVERT)) | - (XFS_CFORK_Q_DISK(dic) ? XFS_XFLAG_HASATTR : 0); + return _xfs_dic2xflags(INT_GET(dic->di_flags, ARCH_CONVERT)) | + (XFS_CFORK_Q_DISK(dic) ? XFS_XFLAG_HASATTR : 0); } /* @@ -1083,7 +1085,7 @@ xfs_ialloc( { xfs_ino_t ino; xfs_inode_t *ip; - vnode_t *vp; + bhv_vnode_t *vp; uint flags; int error; @@ -1221,6 +1223,9 @@ xfs_ialloc( di_flags |= XFS_DIFLAG_NOSYMLINKS; if (pip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) di_flags |= XFS_DIFLAG_PROJINHERIT; + if ((pip->i_d.di_flags & XFS_DIFLAG_NODEFRAG) && + xfs_inherit_nodefrag) + di_flags |= XFS_DIFLAG_NODEFRAG; ip->i_d.di_flags |= di_flags; } /* FALLTHROUGH */ @@ -1244,8 +1249,8 @@ xfs_ialloc( */ xfs_trans_log_inode(tp, ip, flags); - /* now that we have an i_mode we can set Linux inode ops (& unlock) */ - VFS_INIT_VNODE(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1); + /* now that we have an i_mode we can setup inode ops and unlock */ + bhv_vfs_init_vnode(XFS_MTOVFS(tp->t_mountp), vp, XFS_ITOBHV(ip), 1); *ipp = ip; return 0; @@ -1285,7 +1290,7 @@ xfs_isize_check( (xfs_ufsize_t)XFS_MAXIOFFSET(mp)) - map_first), XFS_BMAPI_ENTIRE, NULL, 0, imaps, &nimaps, - NULL)) + NULL, NULL)) return; ASSERT(nimaps == 1); ASSERT(imaps[0].br_startblock == HOLESTARTBLOCK); @@ -1421,7 +1426,7 @@ xfs_itruncate_start( xfs_fsize_t last_byte; xfs_off_t toss_start; xfs_mount_t *mp; - vnode_t *vp; + bhv_vnode_t *vp; ASSERT(ismrlocked(&ip->i_iolock, MR_UPDATE) != 0); ASSERT((new_size == 0) || (new_size <= ip->i_d.di_size)); @@ -1434,9 +1439,9 @@ xfs_itruncate_start( vn_iowait(vp); /* wait for the completion of any pending DIOs */ /* - * Call VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES() to get rid of pages and buffers + * Call toss_pages or flushinval_pages to get rid of pages * overlapping the region being removed. We have to use - * the less efficient VOP_FLUSHINVAL_PAGES() in the case that the + * the less efficient flushinval_pages in the case that the * caller may not be able to finish the truncate without * dropping the inode's I/O lock. Make sure * to catch any pages brought in by buffers overlapping @@ -1445,10 +1450,10 @@ xfs_itruncate_start( * so that we don't toss things on the same block as * new_size but before it. * - * Before calling VOP_TOSS_PAGES() or VOP_FLUSHINVAL_PAGES(), make sure to + * Before calling toss_page or flushinval_pages, make sure to * call remapf() over the same region if the file is mapped. * This frees up mapped file references to the pages in the - * given range and for the VOP_FLUSHINVAL_PAGES() case it ensures + * given range and for the flushinval_pages case it ensures * that we get the latest mapped changes flushed out. */ toss_start = XFS_B_TO_FSB(mp, (xfs_ufsize_t)new_size); @@ -1466,9 +1471,9 @@ xfs_itruncate_start( last_byte); if (last_byte > toss_start) { if (flags & XFS_ITRUNC_DEFINITE) { - VOP_TOSS_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED); + bhv_vop_toss_pages(vp, toss_start, -1, FI_REMAPF_LOCKED); } else { - VOP_FLUSHINVAL_PAGES(vp, toss_start, -1, FI_REMAPF_LOCKED); + bhv_vop_flushinval_pages(vp, toss_start, -1, FI_REMAPF_LOCKED); } } @@ -1666,12 +1671,13 @@ xfs_itruncate_finish( * runs. */ XFS_BMAP_INIT(&free_list, &first_block); - error = xfs_bunmapi(ntp, ip, first_unmap_block, - unmap_len, + error = XFS_BUNMAPI(mp, ntp, &ip->i_iocore, + first_unmap_block, unmap_len, XFS_BMAPI_AFLAG(fork) | (sync ? 0 : XFS_BMAPI_ASYNC), XFS_ITRUNC_MAX_EXTENTS, - &first_block, &free_list, &done); + &first_block, &free_list, + NULL, &done); if (error) { /* * If the bunmapi call encounters an error, @@ -1955,9 +1961,9 @@ xfs_iunlink_remove( xfs_agino_t agino; xfs_agino_t next_agino; xfs_buf_t *last_ibp; - xfs_dinode_t *last_dip; + xfs_dinode_t *last_dip = NULL; short bucket_index; - int offset, last_offset; + int offset, last_offset = 0; int error; int agi_ok; @@ -2745,13 +2751,14 @@ xfs_iunpin( * the inode to become unpinned. */ if (!(ip->i_flags & (XFS_IRECLAIM|XFS_IRECLAIMABLE))) { - vnode_t *vp = XFS_ITOV_NULL(ip); + bhv_vnode_t *vp = XFS_ITOV_NULL(ip); /* make sync come back and flush this inode */ if (vp) { struct inode *inode = vn_to_inode(vp); - if (!(inode->i_state & I_NEW)) + if (!(inode->i_state & + (I_NEW|I_FREEING|I_CLEAR))) mark_inode_dirty_sync(inode); } } @@ -2916,13 +2923,6 @@ #endif ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork)); memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes); } - if (whichfork == XFS_DATA_FORK) { - if (unlikely(XFS_DIR_SHORTFORM_VALIDATE_ONDISK(mp, dip))) { - XFS_ERROR_REPORT("xfs_iflush_fork", - XFS_ERRLEVEL_LOW, mp); - return XFS_ERROR(EFSCORRUPTED); - } - } break; case XFS_DINODE_FMT_EXTENTS: @@ -3006,7 +3006,7 @@ xfs_iflush( XFS_STATS_INC(xs_iflush_count); ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS)); - ASSERT(valusema(&ip->i_flock) <= 0); + ASSERT(issemalocked(&(ip->i_flock))); ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE || ip->i_d.di_nextents > ip->i_df.if_ext_max); @@ -3199,7 +3199,7 @@ xfs_iflush( corrupt_out: xfs_buf_relse(bp); - xfs_force_shutdown(mp, XFS_CORRUPT_INCORE); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); xfs_iflush_abort(ip); /* * Unlocks the flush lock @@ -3221,7 +3221,7 @@ cluster_corrupt_out: xfs_buf_relse(bp); } - xfs_force_shutdown(mp, XFS_CORRUPT_INCORE); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); if(!bufwasdelwri) { /* @@ -3264,7 +3264,7 @@ #endif SPLDECL(s); ASSERT(ismrlocked(&ip->i_lock, MR_UPDATE|MR_ACCESS)); - ASSERT(valusema(&ip->i_flock) <= 0); + ASSERT(issemalocked(&(ip->i_flock))); ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE || ip->i_d.di_nextents > ip->i_df.if_ext_max); @@ -3504,7 +3504,7 @@ xfs_iflush_all( xfs_mount_t *mp) { xfs_inode_t *ip; - vnode_t *vp; + bhv_vnode_t *vp; again: XFS_MOUNT_ILOCK(mp); @@ -4180,7 +4180,7 @@ xfs_iext_direct_to_inline( */ memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents, nextents * sizeof(xfs_bmbt_rec_t)); - kmem_free(ifp->if_u1.if_extents, KM_SLEEP); + kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes); ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext; ifp->if_real_bytes = 0; } diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 3b544db..d10b76e 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -102,9 +102,9 @@ #define XFS_IMAP_BULKSTAT 0x2 #ifdef __KERNEL__ struct bhv_desc; +struct bhv_vnode; struct cred; struct ktrace; -struct vnode; struct xfs_buf; struct xfs_bmap_free; struct xfs_bmbt_irec; @@ -400,7 +400,7 @@ void xfs_chash_init(struct xfs_mount *) void xfs_chash_free(struct xfs_mount *); xfs_inode_t *xfs_inode_incore(struct xfs_mount *, xfs_ino_t, struct xfs_trans *); -void xfs_inode_lock_init(xfs_inode_t *, struct vnode *); +void xfs_inode_lock_init(xfs_inode_t *, struct bhv_vnode *); int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t, uint, uint, xfs_inode_t **, xfs_daddr_t); void xfs_iput(xfs_inode_t *, uint); @@ -461,7 +461,7 @@ void xfs_ichgtime(xfs_inode_t *, int); xfs_fsize_t xfs_file_last_byte(xfs_inode_t *); void xfs_lock_inodes(xfs_inode_t **, int, int, uint); -xfs_inode_t *xfs_vtoi(struct vnode *vp); +xfs_inode_t *xfs_vtoi(struct bhv_vnode *vp); void xfs_synchronize_atime(xfs_inode_t *); @@ -509,7 +509,6 @@ extern struct kmem_zone *xfs_chashlist_z extern struct kmem_zone *xfs_ifork_zone; extern struct kmem_zone *xfs_inode_zone; extern struct kmem_zone *xfs_ili_zone; -extern struct vnodeops xfs_vnodeops; #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 7497a48..f8e80d8 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -25,7 +25,6 @@ #include "xfs_trans.h" #include "xfs_buf_item.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -33,7 +32,6 @@ #include "xfs_trans_priv.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -794,7 +792,7 @@ xfs_inode_item_pushbuf( * inode flush completed and the inode was taken off the AIL. * So, just get out. */ - if ((valusema(&(ip->i_flock)) > 0) || + if (!issemalocked(&(ip->i_flock)) || ((iip->ili_item.li_flags & XFS_LI_IN_AIL) == 0)) { iip->ili_pushbuf_flag = 0; xfs_iunlock(ip, XFS_ILOCK_SHARED); @@ -816,7 +814,7 @@ xfs_inode_item_pushbuf( * If not, we can flush it async. */ dopush = ((iip->ili_item.li_flags & XFS_LI_IN_AIL) && - (valusema(&(ip->i_flock)) <= 0)); + issemalocked(&(ip->i_flock))); iip->ili_pushbuf_flag = 0; xfs_iunlock(ip, XFS_ILOCK_SHARED); xfs_buftrace("INODE ITEM PUSH", bp); @@ -864,7 +862,7 @@ xfs_inode_item_push( ip = iip->ili_inode; ASSERT(ismrlocked(&(ip->i_lock), MR_ACCESS)); - ASSERT(valusema(&(ip->i_flock)) <= 0); + ASSERT(issemalocked(&(ip->i_flock))); /* * Since we were able to lock the inode's flush lock and * we found it on the AIL, the inode must be dirty. This @@ -1084,3 +1082,52 @@ xfs_istale_done( { xfs_iflush_abort(iip->ili_inode); } + +/* + * convert an xfs_inode_log_format struct from either 32 or 64 bit versions + * (which can have different field alignments) to the native version + */ +int +xfs_inode_item_format_convert( + xfs_log_iovec_t *buf, + xfs_inode_log_format_t *in_f) +{ + if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) { + xfs_inode_log_format_32_t *in_f32; + + in_f32 = (xfs_inode_log_format_32_t *)buf->i_addr; + in_f->ilf_type = in_f32->ilf_type; + in_f->ilf_size = in_f32->ilf_size; + in_f->ilf_fields = in_f32->ilf_fields; + in_f->ilf_asize = in_f32->ilf_asize; + in_f->ilf_dsize = in_f32->ilf_dsize; + in_f->ilf_ino = in_f32->ilf_ino; + /* copy biggest field of ilf_u */ + memcpy(in_f->ilf_u.ilfu_uuid.__u_bits, + in_f32->ilf_u.ilfu_uuid.__u_bits, + sizeof(uuid_t)); + in_f->ilf_blkno = in_f32->ilf_blkno; + in_f->ilf_len = in_f32->ilf_len; + in_f->ilf_boffset = in_f32->ilf_boffset; + return 0; + } else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){ + xfs_inode_log_format_64_t *in_f64; + + in_f64 = (xfs_inode_log_format_64_t *)buf->i_addr; + in_f->ilf_type = in_f64->ilf_type; + in_f->ilf_size = in_f64->ilf_size; + in_f->ilf_fields = in_f64->ilf_fields; + in_f->ilf_asize = in_f64->ilf_asize; + in_f->ilf_dsize = in_f64->ilf_dsize; + in_f->ilf_ino = in_f64->ilf_ino; + /* copy biggest field of ilf_u */ + memcpy(in_f->ilf_u.ilfu_uuid.__u_bits, + in_f64->ilf_u.ilfu_uuid.__u_bits, + sizeof(uuid_t)); + in_f->ilf_blkno = in_f64->ilf_blkno; + in_f->ilf_len = in_f64->ilf_len; + in_f->ilf_boffset = in_f64->ilf_boffset; + return 0; + } + return EFSCORRUPTED; +} diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index c5dbf93..5db6cd1 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -23,25 +23,6 @@ #define __XFS_INODE_ITEM_H__ * log. The size of the inline data/extents/b-tree root to be logged * (if any) is indicated in the ilf_dsize field. Changes to this structure * must be added on to the end. - * - * Convention for naming inode log item versions : The current version - * is always named XFS_LI_INODE. When an inode log item gets superseded, - * add the latest version of IRIX that will generate logs with that item - * to the version name. - * - * -Version 1 of this structure (XFS_LI_5_3_INODE) included up to the first - * union (ilf_u) field. This was released with IRIX 5.3-XFS. - * -Version 2 of this structure (XFS_LI_6_1_INODE) is currently the entire - * structure. This was released with IRIX 6.0.1-XFS and IRIX 6.1. - * -Version 3 of this structure (XFS_LI_INODE) is the same as version 2 - * so a new structure definition wasn't necessary. However, we had - * to add a new type because the inode cluster size changed from 4K - * to 8K and the version number had to be rev'ved to keep older kernels - * from trying to recover logs with the 8K buffers in them. The logging - * code can handle recovery on different-sized clusters now so hopefully - * this'll be the last time we need to change the inode log item just - * for a change in the inode cluster size. This new version was - * released with IRIX 6.2. */ typedef struct xfs_inode_log_format { unsigned short ilf_type; /* inode log item type */ @@ -59,18 +40,38 @@ typedef struct xfs_inode_log_format { int ilf_boffset; /* off of inode in buffer */ } xfs_inode_log_format_t; -/* Initial version shipped with IRIX 5.3-XFS */ -typedef struct xfs_inode_log_format_v1 { - unsigned short ilf_type; /* inode log item type */ - unsigned short ilf_size; /* size of this item */ - uint ilf_fields; /* flags for fields logged */ - uint ilf_dsize; /* size of data/ext/root */ - xfs_ino_t ilf_ino; /* inode number */ +typedef struct xfs_inode_log_format_32 { + unsigned short ilf_type; /* 16: inode log item type */ + unsigned short ilf_size; /* 16: size of this item */ + uint ilf_fields; /* 32: flags for fields logged */ + ushort ilf_asize; /* 32: size of attr d/ext/root */ + ushort ilf_dsize; /* 32: size of data/ext/root */ + xfs_ino_t ilf_ino; /* 64: inode number */ union { - xfs_dev_t ilfu_rdev; /* rdev value for dev inode*/ - uuid_t ilfu_uuid; /* mount point value */ + xfs_dev_t ilfu_rdev; /* 32: rdev value for dev inode*/ + uuid_t ilfu_uuid; /* 128: mount point value */ + } ilf_u; + __int64_t ilf_blkno; /* 64: blkno of inode buffer */ + int ilf_len; /* 32: len of inode buffer */ + int ilf_boffset; /* 32: off of inode in buffer */ +} __attribute__((packed)) xfs_inode_log_format_32_t; + +typedef struct xfs_inode_log_format_64 { + unsigned short ilf_type; /* 16: inode log item type */ + unsigned short ilf_size; /* 16: size of this item */ + uint ilf_fields; /* 32: flags for fields logged */ + ushort ilf_asize; /* 32: size of attr d/ext/root */ + ushort ilf_dsize; /* 32: size of data/ext/root */ + __uint32_t ilf_pad; /* 32: pad for 64 bit boundary */ + xfs_ino_t ilf_ino; /* 64: inode number */ + union { + xfs_dev_t ilfu_rdev; /* 32: rdev value for dev inode*/ + uuid_t ilfu_uuid; /* 128: mount point value */ } ilf_u; -} xfs_inode_log_format_t_v1; + __int64_t ilf_blkno; /* 64: blkno of inode buffer */ + int ilf_len; /* 32: len of inode buffer */ + int ilf_boffset; /* 32: off of inode in buffer */ +} xfs_inode_log_format_64_t; /* * Flags for xfs_trans_log_inode flags field. @@ -172,6 +173,8 @@ extern void xfs_inode_item_destroy(struc extern void xfs_iflush_done(struct xfs_buf *, xfs_inode_log_item_t *); extern void xfs_istale_done(struct xfs_buf *, xfs_inode_log_item_t *); extern void xfs_iflush_abort(struct xfs_inode *); +extern int xfs_inode_item_format_convert(xfs_log_iovec_t *, + xfs_inode_log_format_t *); #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_iocore.c b/fs/xfs/xfs_iocore.c index a078156..06d710c 100644 --- a/fs/xfs/xfs_iocore.c +++ b/fs/xfs/xfs_iocore.c @@ -24,14 +24,13 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" +#include "xfs_dfrag.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -58,7 +57,7 @@ xfs_size_fn( STATIC int xfs_ioinit( - struct vfs *vfsp, + struct bhv_vfs *vfsp, struct xfs_mount_args *mntargs, int flags) { @@ -68,6 +67,7 @@ xfs_ioinit( xfs_ioops_t xfs_iocore_xfs = { .xfs_ioinit = (xfs_ioinit_t) xfs_ioinit, .xfs_bmapi_func = (xfs_bmapi_t) xfs_bmapi, + .xfs_bunmapi_func = (xfs_bunmapi_t) xfs_bunmapi, .xfs_bmap_eof_func = (xfs_bmap_eof_t) xfs_bmap_eof, .xfs_iomap_write_direct = (xfs_iomap_write_direct_t) xfs_iomap_write_direct, @@ -84,6 +84,7 @@ xfs_ioops_t xfs_iocore_xfs = { .xfs_unlock = (xfs_unlk_t) xfs_iunlock, .xfs_size_func = (xfs_size_t) xfs_size_fn, .xfs_iodone = (xfs_iodone_t) fs_noerr, + .xfs_swap_extents_func = (xfs_swap_extents_t) xfs_swap_extents, }; void diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index d5dfedc..f1949c1 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_dmapi.h" @@ -32,7 +31,6 @@ #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -252,7 +250,7 @@ xfs_iomap( error = XFS_BMAPI(mp, NULL, io, offset_fsb, (xfs_filblks_t)(end_fsb - offset_fsb), bmapi_flags, NULL, 0, &imap, - &nimaps, NULL); + &nimaps, NULL, NULL); if (error) goto out; @@ -519,8 +517,8 @@ xfs_iomap_write_direct( */ XFS_BMAP_INIT(&free_list, &firstfsb); nimaps = 1; - error = xfs_bmapi(tp, ip, offset_fsb, count_fsb, - bmapi_flag, &firstfsb, 0, &imap, &nimaps, &free_list); + error = XFS_BMAPI(mp, tp, io, offset_fsb, count_fsb, bmapi_flag, + &firstfsb, 0, &imap, &nimaps, &free_list, NULL); if (error) goto error0; @@ -610,8 +608,8 @@ xfs_iomap_eof_want_preallocate( while (count_fsb > 0) { imaps = nimaps; firstblock = NULLFSBLOCK; - error = XFS_BMAPI(mp, NULL, io, start_fsb, count_fsb, - 0, &firstblock, 0, imap, &imaps, NULL); + error = XFS_BMAPI(mp, NULL, io, start_fsb, count_fsb, 0, + &firstblock, 0, imap, &imaps, NULL, NULL); if (error) return error; for (n = 0; n < imaps; n++) { @@ -695,11 +693,11 @@ retry: nimaps = XFS_WRITE_IMAPS; firstblock = NULLFSBLOCK; - error = xfs_bmapi(NULL, ip, offset_fsb, + error = XFS_BMAPI(mp, NULL, io, offset_fsb, (xfs_filblks_t)(last_fsb - offset_fsb), XFS_BMAPI_DELAY | XFS_BMAPI_WRITE | XFS_BMAPI_ENTIRE, &firstblock, 1, imap, - &nimaps, NULL); + &nimaps, NULL, NULL); if (error && (error != ENOSPC)) return XFS_ERROR(error); @@ -832,9 +830,9 @@ xfs_iomap_write_allocate( } /* Go get the actual blocks */ - error = xfs_bmapi(tp, ip, map_start_fsb, count_fsb, + error = XFS_BMAPI(mp, tp, io, map_start_fsb, count_fsb, XFS_BMAPI_WRITE, &first_block, 1, - imap, &nimaps, &free_list); + imap, &nimaps, &free_list, NULL); if (error) goto trans_cancel; @@ -955,9 +953,9 @@ xfs_iomap_write_unwritten( */ XFS_BMAP_INIT(&free_list, &firstfsb); nimaps = 1; - error = xfs_bmapi(tp, ip, offset_fsb, count_fsb, + error = XFS_BMAPI(mp, tp, io, offset_fsb, count_fsb, XFS_BMAPI_WRITE|XFS_BMAPI_CONVERT, &firstfsb, - 1, &imap, &nimaps, &free_list); + 1, &imap, &nimaps, &free_list, NULL); if (error) goto error_on_bmapi_transaction; diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 94068d0..46249e4 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -41,11 +39,6 @@ #include "xfs_itable.h" #include "xfs_error.h" #include "xfs_btree.h" -#ifndef HAVE_USERACC -#define useracc(ubuffer, size, flags, foo) (0) -#define unuseracc(ubuffer, size, flags) -#endif - STATIC int xfs_bulkstat_one_iget( xfs_mount_t *mp, /* mount point for filesystem */ @@ -56,7 +49,7 @@ xfs_bulkstat_one_iget( { xfs_dinode_core_t *dic; /* dinode core info pointer */ xfs_inode_t *ip; /* incore inode pointer */ - vnode_t *vp; + bhv_vnode_t *vp; int error; error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, bno); @@ -336,15 +329,6 @@ xfs_bulkstat( nimask = ~(nicluster - 1); nbcluster = nicluster >> mp->m_sb.sb_inopblog; /* - * Lock down the user's buffer. If a buffer was not sent, as in the case - * disk quota code calls here, we skip this. - */ - if (ubuffer && - (error = useracc(ubuffer, ubcount * statstruct_size, - (B_READ|B_PHYS), NULL))) { - return error; - } - /* * Allocate a page-sized buffer for inode btree records. * We could try allocating something smaller, but for normal * calls we'll always (potentially) need the whole page. @@ -650,8 +634,6 @@ xfs_bulkstat( * Done, we're either out of filesystem or space to put the data. */ kmem_free(irbuf, NBPC); - if (ubuffer) - unuseracc(ubuffer, ubcount * statstruct_size, (B_READ|B_PHYS)); *ubcountp = ubelem; if (agno >= mp->m_sb.sb_agcount) { /* diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h index 11eb4e1..be5f12e 100644 --- a/fs/xfs/xfs_itable.h +++ b/fs/xfs/xfs_itable.h @@ -45,7 +45,6 @@ #define BULKSTAT_RV_GIVEUP 2 */ #define BULKSTAT_FG_IGET 0x1 /* Go through the buffer cache */ #define BULKSTAT_FG_QUICK 0x2 /* No iget, walk the dinode cluster */ -#define BULKSTAT_FG_VFSLOCKED 0x4 /* Already have vfs lock */ /* * Return stat information in bulk (by-inode) for the filesystem. diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 32e841d..e730328 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -24,7 +24,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -36,7 +35,6 @@ #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" #include "xfs_log_recover.h" #include "xfs_trans_priv.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -402,7 +400,7 @@ xfs_log_release_iclog(xfs_mount_t *mp, xlog_in_core_t *iclog = (xlog_in_core_t *)iclog_hndl; if (xlog_state_release_iclog(log, iclog)) { - xfs_force_shutdown(mp, XFS_LOG_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); return EIO; } @@ -498,9 +496,8 @@ xfs_log_mount(xfs_mount_t *mp, * just worked. */ if (!(mp->m_flags & XFS_MOUNT_NORECOVERY)) { - int error; - vfs_t *vfsp = XFS_MTOVFS(mp); - int readonly = (vfsp->vfs_flag & VFS_RDONLY); + bhv_vfs_t *vfsp = XFS_MTOVFS(mp); + int error, readonly = (vfsp->vfs_flag & VFS_RDONLY); if (readonly) vfsp->vfs_flag &= ~VFS_RDONLY; @@ -726,7 +723,7 @@ xfs_log_write(xfs_mount_t * mp, return XFS_ERROR(EIO); if ((error = xlog_write(mp, reg, nentries, tic, start_lsn, NULL, 0))) { - xfs_force_shutdown(mp, XFS_LOG_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); } return error; } /* xfs_log_write */ @@ -816,9 +813,9 @@ xfs_log_need_covered(xfs_mount_t *mp) SPLDECL(s); int needed = 0, gen; xlog_t *log = mp->m_log; - vfs_t *vfsp = XFS_MTOVFS(mp); + bhv_vfs_t *vfsp = XFS_MTOVFS(mp); - if (fs_frozen(vfsp) || XFS_FORCED_SHUTDOWN(mp) || + if (vfs_test_for_freeze(vfsp) || XFS_FORCED_SHUTDOWN(mp) || (vfsp->vfs_flag & VFS_RDONLY)) return 0; @@ -956,7 +953,7 @@ xlog_iodone(xfs_buf_t *bp) XFS_ERRTAG_IODONE_IOERR, XFS_RANDOM_IODONE_IOERR)) { xfs_ioerror_alert("xlog_iodone", l->l_mp, bp, XFS_BUF_ADDR(bp)); XFS_BUF_STALE(bp); - xfs_force_shutdown(l->l_mp, XFS_LOG_IO_ERROR); + xfs_force_shutdown(l->l_mp, SHUTDOWN_LOG_IO_ERROR); /* * This flag will be propagated to the trans-committed * callback routines to let them know that the log-commit @@ -1261,7 +1258,7 @@ xlog_commit_record(xfs_mount_t *mp, ASSERT_ALWAYS(iclog); if ((error = xlog_write(mp, reg, 1, ticket, commitlsnp, iclog, XLOG_COMMIT_TRANS))) { - xfs_force_shutdown(mp, XFS_LOG_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); } return error; } /* xlog_commit_record */ @@ -1743,10 +1740,10 @@ xlog_write(xfs_mount_t * mp, xlog_in_core_t **commit_iclog, uint flags) { - xlog_t *log = mp->m_log; + xlog_t *log = mp->m_log; xlog_ticket_t *ticket = (xlog_ticket_t *)tic; + xlog_in_core_t *iclog = NULL; /* ptr to current in-core log */ xlog_op_header_t *logop_head; /* ptr to log operation header */ - xlog_in_core_t *iclog; /* ptr to current in-core log */ __psint_t ptr; /* copy address into data region */ int len; /* # xlog_write() bytes 2 still copy */ int index; /* region index currently copying */ @@ -1790,7 +1787,7 @@ #else xfs_cmn_err(XFS_PTAG_LOGRES, CE_ALERT, mp, "xfs_log_write: reservation ran out. Need to up reservation"); /* If we did not panic, shutdown the filesystem */ - xfs_force_shutdown(mp, XFS_CORRUPT_INCORE); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); #endif } else ticket->t_curr_res -= len; diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 1f0016b..3cb678e 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -24,7 +24,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -32,7 +31,6 @@ #include "xfs_error.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -193,14 +191,14 @@ xlog_header_check_dump( { int b; - printk("%s: SB : uuid = ", __FUNCTION__); + cmn_err(CE_DEBUG, "%s: SB : uuid = ", __FUNCTION__); for (b = 0; b < 16; b++) - printk("%02x",((unsigned char *)&mp->m_sb.sb_uuid)[b]); - printk(", fmt = %d\n", XLOG_FMT); - printk(" log : uuid = "); + cmn_err(CE_DEBUG, "%02x", ((uchar_t *)&mp->m_sb.sb_uuid)[b]); + cmn_err(CE_DEBUG, ", fmt = %d\n", XLOG_FMT); + cmn_err(CE_DEBUG, " log : uuid = "); for (b = 0; b < 16; b++) - printk("%02x",((unsigned char *)&head->h_fs_uuid)[b]); - printk(", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT)); + cmn_err(CE_DEBUG, "%02x",((uchar_t *)&head->h_fs_uuid)[b]); + cmn_err(CE_DEBUG, ", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT)); } #else #define xlog_header_check_dump(mp, head) @@ -282,7 +280,7 @@ xlog_recover_iodone( mp = XFS_BUF_FSPRIVATE(bp, xfs_mount_t *); xfs_ioerror_alert("xlog_recover_iodone", mp, bp, XFS_BUF_ADDR(bp)); - xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); } XFS_BUF_SET_FSPRIVATE(bp, NULL); XFS_BUF_CLR_IODONE_FUNC(bp); @@ -992,6 +990,8 @@ xlog_find_zeroed( xfs_daddr_t num_scan_bblks; int error, log_bbnum = log->l_logBBsize; + *blk_no = 0; + /* check totally zeroed log */ bp = xlog_get_bp(log, 1); if (!bp) @@ -1889,7 +1889,7 @@ xlog_recover_do_inode_buffer( buffer_nextp = (xfs_agino_t *)xfs_buf_offset(bp, next_unlinked_offset); - INT_SET(*buffer_nextp, ARCH_CONVERT, *logged_nextp); + *buffer_nextp = *logged_nextp; } return 0; @@ -2292,12 +2292,22 @@ xlog_recover_do_inode_trans( int attr_index; uint fields; xfs_dinode_core_t *dicp; + int need_free = 0; if (pass == XLOG_RECOVER_PASS1) { return 0; } - in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr; + if (item->ri_buf[0].i_len == sizeof(xfs_inode_log_format_t)) { + in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr; + } else { + in_f = (xfs_inode_log_format_t *)kmem_alloc( + sizeof(xfs_inode_log_format_t), KM_SLEEP); + need_free = 1; + error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f); + if (error) + goto error; + } ino = in_f->ilf_ino; mp = log->l_mp; if (ITEM_TYPE(item) == XFS_LI_INODE) { @@ -2323,8 +2333,10 @@ xlog_recover_do_inode_trans( * Inode buffers can be freed, look out for it, * and do not replay the inode. */ - if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0)) - return 0; + if (xlog_check_buffer_cancelled(log, imap.im_blkno, imap.im_len, 0)) { + error = 0; + goto error; + } bp = xfs_buf_read_flags(mp->m_ddev_targp, imap.im_blkno, imap.im_len, XFS_BUF_LOCK); @@ -2333,7 +2345,7 @@ xlog_recover_do_inode_trans( bp, imap.im_blkno); error = XFS_BUF_GETERROR(bp); xfs_buf_relse(bp); - return error; + goto error; } error = 0; ASSERT(in_f->ilf_fields & XFS_ILOG_CORE); @@ -2350,7 +2362,8 @@ xlog_recover_do_inode_trans( dip, bp, ino); XFS_ERROR_REPORT("xlog_recover_do_inode_trans(1)", XFS_ERRLEVEL_LOW, mp); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } dicp = (xfs_dinode_core_t*)(item->ri_buf[1].i_addr); if (unlikely(dicp->di_magic != XFS_DINODE_MAGIC)) { @@ -2360,7 +2373,8 @@ xlog_recover_do_inode_trans( item, ino); XFS_ERROR_REPORT("xlog_recover_do_inode_trans(2)", XFS_ERRLEVEL_LOW, mp); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } /* Skip replay when the on disk inode is newer than the log one */ @@ -2376,7 +2390,8 @@ xlog_recover_do_inode_trans( /* do nothing */ } else { xfs_buf_relse(bp); - return 0; + error = 0; + goto error; } } /* Take the opportunity to reset the flush iteration count */ @@ -2391,7 +2406,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad regular inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld", item, dip, bp, ino); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } } else if (unlikely((dicp->di_mode & S_IFMT) == S_IFDIR)) { if ((dicp->di_format != XFS_DINODE_FMT_EXTENTS) && @@ -2403,7 +2419,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad dir inode log record, rec ptr 0x%p, ino ptr = 0x%p, ino bp = 0x%p, ino %Ld", item, dip, bp, ino); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } } if (unlikely(dicp->di_nextents + dicp->di_anextents > dicp->di_nblocks)){ @@ -2415,7 +2432,8 @@ xlog_recover_do_inode_trans( item, dip, bp, ino, dicp->di_nextents + dicp->di_anextents, dicp->di_nblocks); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } if (unlikely(dicp->di_forkoff > mp->m_sb.sb_inodesize)) { XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(6)", @@ -2424,7 +2442,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad inode log rec ptr 0x%p, dino ptr 0x%p, dino bp 0x%p, ino %Ld, forkoff 0x%x", item, dip, bp, ino, dicp->di_forkoff); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } if (unlikely(item->ri_buf[1].i_len > sizeof(xfs_dinode_core_t))) { XFS_CORRUPTION_ERROR("xlog_recover_do_inode_trans(7)", @@ -2433,7 +2452,8 @@ xlog_recover_do_inode_trans( xfs_fs_cmn_err(CE_ALERT, mp, "xfs_inode_recover: Bad inode log record length %d, rec ptr 0x%p", item->ri_buf[1].i_len, item); - return XFS_ERROR(EFSCORRUPTED); + error = EFSCORRUPTED; + goto error; } /* The core is in in-core format */ @@ -2521,7 +2541,8 @@ xlog_recover_do_inode_trans( xlog_warn("XFS: xlog_recover_do_inode_trans: Invalid flag"); ASSERT(0); xfs_buf_relse(bp); - return XFS_ERROR(EIO); + error = EIO; + goto error; } } @@ -2537,7 +2558,10 @@ write_inode_buffer: error = xfs_bwrite(mp, bp); } - return (error); +error: + if (need_free) + kmem_free(in_f, sizeof(*in_f)); + return XFS_ERROR(error); } /* @@ -2674,32 +2698,32 @@ xlog_recover_do_dquot_trans( * structure into it, and adds the efi to the AIL with the given * LSN. */ -STATIC void +STATIC int xlog_recover_do_efi_trans( xlog_t *log, xlog_recover_item_t *item, xfs_lsn_t lsn, int pass) { + int error; xfs_mount_t *mp; xfs_efi_log_item_t *efip; xfs_efi_log_format_t *efi_formatp; SPLDECL(s); if (pass == XLOG_RECOVER_PASS1) { - return; + return 0; } efi_formatp = (xfs_efi_log_format_t *)item->ri_buf[0].i_addr; - ASSERT(item->ri_buf[0].i_len == - (sizeof(xfs_efi_log_format_t) + - ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t)))); mp = log->l_mp; efip = xfs_efi_init(mp, efi_formatp->efi_nextents); - memcpy((char *)&(efip->efi_format), (char *)efi_formatp, - sizeof(xfs_efi_log_format_t) + - ((efi_formatp->efi_nextents - 1) * sizeof(xfs_extent_t))); + if ((error = xfs_efi_copy_format(&(item->ri_buf[0]), + &(efip->efi_format)))) { + xfs_efi_item_free(efip); + return error; + } efip->efi_next_extent = efi_formatp->efi_nextents; efip->efi_flags |= XFS_EFI_COMMITTED; @@ -2708,6 +2732,7 @@ xlog_recover_do_efi_trans( * xfs_trans_update_ail() drops the AIL lock. */ xfs_trans_update_ail(mp, (xfs_log_item_t *)efip, lsn, s); + return 0; } @@ -2738,9 +2763,10 @@ xlog_recover_do_efd_trans( } efd_formatp = (xfs_efd_log_format_t *)item->ri_buf[0].i_addr; - ASSERT(item->ri_buf[0].i_len == - (sizeof(xfs_efd_log_format_t) + - ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_t)))); + ASSERT((item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_32_t) + + ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_32_t)))) || + (item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_64_t) + + ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_64_t))))); efi_id = efd_formatp->efd_efi_id; /* @@ -2810,15 +2836,14 @@ xlog_recover_do_trans( if ((error = xlog_recover_do_buffer_trans(log, item, pass))) break; - } else if ((ITEM_TYPE(item) == XFS_LI_INODE) || - (ITEM_TYPE(item) == XFS_LI_6_1_INODE) || - (ITEM_TYPE(item) == XFS_LI_5_3_INODE)) { + } else if ((ITEM_TYPE(item) == XFS_LI_INODE)) { if ((error = xlog_recover_do_inode_trans(log, item, pass))) break; } else if (ITEM_TYPE(item) == XFS_LI_EFI) { - xlog_recover_do_efi_trans(log, item, trans->r_lsn, - pass); + if ((error = xlog_recover_do_efi_trans(log, item, trans->r_lsn, + pass))) + break; } else if (ITEM_TYPE(item) == XFS_LI_EFD) { xlog_recover_do_efd_trans(log, item, pass); } else if (ITEM_TYPE(item) == XFS_LI_DQUOT) { @@ -3419,13 +3444,13 @@ xlog_unpack_data_checksum( if (rhead->h_chksum || ((log->l_flags & XLOG_CHKSUM_MISMATCH) == 0)) { cmn_err(CE_DEBUG, - "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)", + "XFS: LogR chksum mismatch: was (0x%x) is (0x%x)\n", INT_GET(rhead->h_chksum, ARCH_CONVERT), chksum); cmn_err(CE_DEBUG, "XFS: Disregard message if filesystem was created with non-DEBUG kernel"); if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) { cmn_err(CE_DEBUG, - "XFS: LogR this is a LogV2 filesystem"); + "XFS: LogR this is a LogV2 filesystem\n"); } log->l_flags |= XLOG_CHKSUM_MISMATCH; } @@ -3798,7 +3823,7 @@ xlog_do_log_recovery( error = xlog_do_recovery_pass(log, head_blk, tail_blk, XLOG_RECOVER_PASS2); #ifdef DEBUG - { + if (!error) { int i; for (i = 0; i < XLOG_BC_TABLE_SIZE; i++) @@ -3974,7 +3999,7 @@ xlog_recover_finish( log->l_flags &= ~XLOG_RECOVERY_NEEDED; } else { cmn_err(CE_DEBUG, - "!Ending clean XFS mount for filesystem: %s", + "!Ending clean XFS mount for filesystem: %s\n", log->l_mp->m_fsname); } return 0; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index c0b1c29..4be5c0b 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -196,7 +194,7 @@ xfs_mount_free( kmem_free(mp->m_logname, strlen(mp->m_logname) + 1); if (remove_bhv) { - struct vfs *vfsp = XFS_MTOVFS(mp); + struct bhv_vfs *vfsp = XFS_MTOVFS(mp); bhv_remove_all_vfsops(vfsp, 0); VFS_REMOVEBHV(vfsp, &mp->m_bhv); @@ -337,7 +335,7 @@ #endif xfs_agnumber_t xfs_initialize_perag( - struct vfs *vfs, + bhv_vfs_t *vfs, xfs_mount_t *mp, xfs_agnumber_t agcount) { @@ -651,14 +649,14 @@ xfs_mount_common(xfs_mount_t *mp, xfs_sb */ int xfs_mountfs( - vfs_t *vfsp, + bhv_vfs_t *vfsp, xfs_mount_t *mp, int mfsi_flags) { xfs_buf_t *bp; xfs_sb_t *sbp = &(mp->m_sb); xfs_inode_t *rip; - vnode_t *rvp = NULL; + bhv_vnode_t *rvp = NULL; int readio_log, writeio_log; xfs_daddr_t d; __uint64_t ret64; @@ -934,18 +932,7 @@ xfs_mountfs( vfsp->vfs_altfsid = (xfs_fsid_t *)mp->m_fixedfsid; mp->m_dmevmask = 0; /* not persistent; set after each mount */ - /* - * Select the right directory manager. - */ - mp->m_dirops = - XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ? - xfsv2_dirops : - xfsv1_dirops; - - /* - * Initialize directory manager's entries. - */ - XFS_DIR_MOUNT(mp); + xfs_dir_mount(mp); /* * Initialize the attribute manager's entries. @@ -1006,8 +993,9 @@ xfs_mountfs( if (unlikely((rip->i_d.di_mode & S_IFMT) != S_IFDIR)) { cmn_err(CE_WARN, "XFS: corrupted root inode"); - prdev("Root inode %llu is not a directory", - mp->m_ddev_targp, (unsigned long long)rip->i_ino); + cmn_err(CE_WARN, "Device %s - root %llu is not a directory", + XFS_BUFTARG_NAME(mp->m_ddev_targp), + (unsigned long long)rip->i_ino); xfs_iunlock(rip, XFS_ILOCK_EXCL); XFS_ERROR_REPORT("xfs_mountfs_int(2)", XFS_ERRLEVEL_LOW, mp); @@ -1094,7 +1082,7 @@ xfs_mountfs( int xfs_unmountfs(xfs_mount_t *mp, struct cred *cr) { - struct vfs *vfsp = XFS_MTOVFS(mp); + struct bhv_vfs *vfsp = XFS_MTOVFS(mp); #if defined(DEBUG) || defined(INDUCE_IO_ERROR) int64_t fsid; #endif @@ -1254,6 +1242,26 @@ xfs_mod_sb(xfs_trans_t *tp, __int64_t fi xfs_trans_log_buf(tp, bp, first, last); } + +/* + * In order to avoid ENOSPC-related deadlock caused by + * out-of-order locking of AGF buffer (PV 947395), we place + * constraints on the relationship among actual allocations for + * data blocks, freelist blocks, and potential file data bmap + * btree blocks. However, these restrictions may result in no + * actual space allocated for a delayed extent, for example, a data + * block in a certain AG is allocated but there is no additional + * block for the additional bmap btree block due to a split of the + * bmap btree of the file. The result of this may lead to an + * infinite loop in xfssyncd when the file gets flushed to disk and + * all delayed extents need to be actually allocated. To get around + * this, we explicitly set aside a few blocks which will not be + * reserved in delayed allocation. Considering the minimum number of + * needed freelist blocks is 4 fsbs, a potential split of file's bmap + * btree requires 1 fsb, so we set the number of set-aside blocks to 8. +*/ +#define SET_ASIDE_BLOCKS 8 + /* * xfs_mod_incore_sb_unlocked() is a utility routine common used to apply * a delta to a specified field in the in-core superblock. Simply @@ -1298,7 +1306,7 @@ xfs_mod_incore_sb_unlocked(xfs_mount_t * return 0; case XFS_SBS_FDBLOCKS: - lcounter = (long long)mp->m_sb.sb_fdblocks; + lcounter = (long long)mp->m_sb.sb_fdblocks - SET_ASIDE_BLOCKS; res_used = (long long)(mp->m_resblks - mp->m_resblks_avail); if (delta > 0) { /* Putting blocks back */ @@ -1332,7 +1340,7 @@ xfs_mod_incore_sb_unlocked(xfs_mount_t * } } - mp->m_sb.sb_fdblocks = lcounter; + mp->m_sb.sb_fdblocks = lcounter + SET_ASIDE_BLOCKS; return 0; case XFS_SBS_FREXTENTS: lcounter = (long long)mp->m_sb.sb_frextents; @@ -1713,15 +1721,14 @@ #ifdef HAVE_PERCPU_SB * is present to prevent thrashing). */ +#ifdef CONFIG_HOTPLUG_CPU /* * hot-plug CPU notifier support. * - * We cannot use the hotcpu_register() function because it does - * not allow notifier instances. We need a notifier per filesystem - * as we need to be able to identify the filesystem to balance - * the counters out. This is achieved by having a notifier block - * embedded in the xfs_mount_t and doing pointer magic to get the - * mount pointer from the notifier block address. + * We need a notifier per filesystem as we need to be able to identify + * the filesystem to balance the counters out. This is achieved by + * having a notifier block embedded in the xfs_mount_t and doing pointer + * magic to get the mount pointer from the notifier block address. */ STATIC int xfs_icsb_cpu_notify( @@ -1771,6 +1778,7 @@ xfs_icsb_cpu_notify( return NOTIFY_OK; } +#endif /* CONFIG_HOTPLUG_CPU */ int xfs_icsb_init_counters( @@ -1783,9 +1791,11 @@ xfs_icsb_init_counters( if (mp->m_sb_cnts == NULL) return -ENOMEM; +#ifdef CONFIG_HOTPLUG_CPU mp->m_icsb_notifier.notifier_call = xfs_icsb_cpu_notify; mp->m_icsb_notifier.priority = 0; - register_cpu_notifier(&mp->m_icsb_notifier); + register_hotcpu_notifier(&mp->m_icsb_notifier); +#endif /* CONFIG_HOTPLUG_CPU */ for_each_online_cpu(i) { cntp = (xfs_icsb_cnts_t *)per_cpu_ptr(mp->m_sb_cnts, i); @@ -1804,7 +1814,7 @@ xfs_icsb_destroy_counters( xfs_mount_t *mp) { if (mp->m_sb_cnts) { - unregister_cpu_notifier(&mp->m_icsb_notifier); + unregister_hotcpu_notifier(&mp->m_icsb_notifier); free_percpu(mp->m_sb_cnts); } } @@ -2018,7 +2028,7 @@ xfs_icsb_balance_counter( xfs_sb_field_t field, int flags) { - uint64_t count, resid = 0; + uint64_t count, resid; int weight = num_online_cpus(); int s; @@ -2050,6 +2060,7 @@ xfs_icsb_balance_counter( break; default: BUG(); + count = resid = 0; /* quiet, gcc */ break; } diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 668ad23..b2bd4be 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -53,8 +53,8 @@ #define XFS_DADDR_TO_AGBNO(mp,d) \ #else struct cred; struct log; -struct vfs; -struct vnode; +struct bhv_vfs; +struct bhv_vnode; struct xfs_mount_args; struct xfs_ihash; struct xfs_chash; @@ -63,9 +63,11 @@ struct xfs_perag; struct xfs_iocore; struct xfs_bmbt_irec; struct xfs_bmap_free; +struct xfs_extdelta; +struct xfs_swapext; -extern struct vfsops xfs_vfsops; -extern struct vnodeops xfs_vnodeops; +extern struct bhv_vfsops xfs_vfsops; +extern struct bhv_vnodeops xfs_vnodeops; #define AIL_LOCK_T lock_t #define AIL_LOCKINIT(x,y) spinlock_init(x,y) @@ -78,15 +80,15 @@ #define AIL_UNLOCK(mp,s) mutex_spinunloc * Prototypes and functions for the Data Migration subsystem. */ -typedef int (*xfs_send_data_t)(int, struct vnode *, - xfs_off_t, size_t, int, vrwlock_t *); +typedef int (*xfs_send_data_t)(int, struct bhv_vnode *, + xfs_off_t, size_t, int, bhv_vrwlock_t *); typedef int (*xfs_send_mmap_t)(struct vm_area_struct *, uint); -typedef int (*xfs_send_destroy_t)(struct vnode *, dm_right_t); -typedef int (*xfs_send_namesp_t)(dm_eventtype_t, struct vfs *, - struct vnode *, - dm_right_t, struct vnode *, dm_right_t, +typedef int (*xfs_send_destroy_t)(struct bhv_vnode *, dm_right_t); +typedef int (*xfs_send_namesp_t)(dm_eventtype_t, struct bhv_vfs *, + struct bhv_vnode *, + dm_right_t, struct bhv_vnode *, dm_right_t, char *, char *, mode_t, int, int); -typedef void (*xfs_send_unmount_t)(struct vfs *, struct vnode *, +typedef void (*xfs_send_unmount_t)(struct bhv_vfs *, struct bhv_vnode *, dm_right_t, mode_t, int, int); typedef struct xfs_dmops { @@ -188,13 +190,18 @@ #define XFS_QM_DQVOPCHOWNRESV(mp, tp, ip * Prototypes and functions for I/O core modularization. */ -typedef int (*xfs_ioinit_t)(struct vfs *, +typedef int (*xfs_ioinit_t)(struct bhv_vfs *, struct xfs_mount_args *, int); typedef int (*xfs_bmapi_t)(struct xfs_trans *, void *, xfs_fileoff_t, xfs_filblks_t, int, xfs_fsblock_t *, xfs_extlen_t, struct xfs_bmbt_irec *, int *, - struct xfs_bmap_free *); + struct xfs_bmap_free *, struct xfs_extdelta *); +typedef int (*xfs_bunmapi_t)(struct xfs_trans *, + void *, xfs_fileoff_t, + xfs_filblks_t, int, xfs_extnum_t, + xfs_fsblock_t *, struct xfs_bmap_free *, + struct xfs_extdelta *, int *); typedef int (*xfs_bmap_eof_t)(void *, xfs_fileoff_t, int, int *); typedef int (*xfs_iomap_write_direct_t)( void *, xfs_off_t, size_t, int, @@ -213,11 +220,14 @@ typedef void (*xfs_lock_demote_t)(void typedef int (*xfs_lock_nowait_t)(void *, uint); typedef void (*xfs_unlk_t)(void *, unsigned int); typedef xfs_fsize_t (*xfs_size_t)(void *); -typedef xfs_fsize_t (*xfs_iodone_t)(struct vfs *); +typedef xfs_fsize_t (*xfs_iodone_t)(struct bhv_vfs *); +typedef int (*xfs_swap_extents_t)(void *, void *, + struct xfs_swapext*); typedef struct xfs_ioops { xfs_ioinit_t xfs_ioinit; xfs_bmapi_t xfs_bmapi_func; + xfs_bunmapi_t xfs_bunmapi_func; xfs_bmap_eof_t xfs_bmap_eof_func; xfs_iomap_write_direct_t xfs_iomap_write_direct; xfs_iomap_write_delay_t xfs_iomap_write_delay; @@ -230,13 +240,17 @@ typedef struct xfs_ioops { xfs_unlk_t xfs_unlock; xfs_size_t xfs_size_func; xfs_iodone_t xfs_iodone; + xfs_swap_extents_t xfs_swap_extents_func; } xfs_ioops_t; #define XFS_IOINIT(vfsp, args, flags) \ (*(mp)->m_io_ops.xfs_ioinit)(vfsp, args, flags) -#define XFS_BMAPI(mp, trans,io,bno,len,f,first,tot,mval,nmap,flist) \ +#define XFS_BMAPI(mp, trans,io,bno,len,f,first,tot,mval,nmap,flist,delta) \ (*(mp)->m_io_ops.xfs_bmapi_func) \ - (trans,(io)->io_obj,bno,len,f,first,tot,mval,nmap,flist) + (trans,(io)->io_obj,bno,len,f,first,tot,mval,nmap,flist,delta) +#define XFS_BUNMAPI(mp, trans,io,bno,len,f,nexts,first,flist,delta,done) \ + (*(mp)->m_io_ops.xfs_bunmapi_func) \ + (trans,(io)->io_obj,bno,len,f,nexts,first,flist,delta,done) #define XFS_BMAP_EOF(mp, io, endoff, whichfork, eof) \ (*(mp)->m_io_ops.xfs_bmap_eof_func) \ ((io)->io_obj, endoff, whichfork, eof) @@ -266,6 +280,9 @@ #define XFS_SIZE(mp, io) \ (*(mp)->m_io_ops.xfs_size_func)((io)->io_obj) #define XFS_IODONE(vfsp) \ (*(mp)->m_io_ops.xfs_iodone)(vfsp) +#define XFS_SWAP_EXTENTS(mp, io, tio, sxp) \ + (*(mp)->m_io_ops.xfs_swap_extents_func) \ + ((io)->io_obj, (tio)->io_obj, sxp) #ifdef HAVE_PERCPU_SB @@ -386,8 +403,6 @@ #endif __uint8_t m_inode_quiesce;/* call quiesce on new inodes. field governed by m_ilock */ __uint8_t m_sectbb_log; /* sectlog - BBSHIFT */ - __uint8_t m_dirversion; /* 1 or 2 */ - xfs_dirops_t m_dirops; /* table of dir funcs */ int m_dirblksize; /* directory block sz--bytes */ int m_dirblkfsbs; /* directory block sz--fsbs */ xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */ @@ -494,16 +509,7 @@ #define XFS_MAXIOFFSET(mp) ((mp)->m_maxi #define XFS_FORCED_SHUTDOWN(mp) ((mp)->m_flags & XFS_MOUNT_FS_SHUTDOWN) #define xfs_force_shutdown(m,f) \ - VFS_FORCE_SHUTDOWN((XFS_MTOVFS(m)), f, __FILE__, __LINE__) - -/* - * Flags sent to xfs_force_shutdown. - */ -#define XFS_METADATA_IO_ERROR 0x1 -#define XFS_LOG_IO_ERROR 0x2 -#define XFS_FORCE_UMOUNT 0x4 -#define XFS_CORRUPT_INCORE 0x8 /* Corrupt in-memory data structures */ -#define XFS_SHUTDOWN_REMOTE_REQ 0x10 /* Shutdown came from remote cell */ + bhv_vfs_force_shutdown((XFS_MTOVFS(m)), f, __FILE__, __LINE__) /* * Flags for xfs_mountfs @@ -521,7 +527,7 @@ #define XFS_MFSI_QUIET 0x40 /* Be silen * Macros for getting from mount to vfs and back. */ #define XFS_MTOVFS(mp) xfs_mtovfs(mp) -static inline struct vfs *xfs_mtovfs(xfs_mount_t *mp) +static inline struct bhv_vfs *xfs_mtovfs(xfs_mount_t *mp) { return bhvtovfs(&mp->m_bhv); } @@ -533,7 +539,7 @@ static inline xfs_mount_t *xfs_bhvtom(bh } #define XFS_VFSTOM(vfs) xfs_vfstom(vfs) -static inline xfs_mount_t *xfs_vfstom(vfs_t *vfs) +static inline xfs_mount_t *xfs_vfstom(bhv_vfs_t *vfs) { return XFS_BHVTOM(bhv_lookup(VFS_BHVHEAD(vfs), &xfs_vfsops)); } @@ -571,7 +577,7 @@ #define XFS_SB_UNLOCK(mp,s) mutex_spinun extern xfs_mount_t *xfs_mount_init(void); extern void xfs_mod_sb(xfs_trans_t *, __int64_t); extern void xfs_mount_free(xfs_mount_t *mp, int remove_bhv); -extern int xfs_mountfs(struct vfs *, xfs_mount_t *mp, int); +extern int xfs_mountfs(struct bhv_vfs *, xfs_mount_t *mp, int); extern void xfs_mountfs_check_barriers(xfs_mount_t *mp); extern int xfs_unmountfs(xfs_mount_t *, struct cred *); @@ -589,7 +595,7 @@ extern void xfs_freesb(xfs_mount_t *); extern void xfs_do_force_shutdown(bhv_desc_t *, int, char *, int); extern int xfs_syncsub(xfs_mount_t *, int, int, int *); extern int xfs_sync_inodes(xfs_mount_t *, int, int, int *); -extern xfs_agnumber_t xfs_initialize_perag(struct vfs *, xfs_mount_t *, +extern xfs_agnumber_t xfs_initialize_perag(struct bhv_vfs *, xfs_mount_t *, xfs_agnumber_t); extern void xfs_xlatesb(void *, struct xfs_sb *, int, __int64_t); diff --git a/fs/xfs/xfs_qmops.c b/fs/xfs/xfs_qmops.c index 1408a32..320d63f 100644 --- a/fs/xfs/xfs_qmops.c +++ b/fs/xfs/xfs_qmops.c @@ -23,7 +23,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" diff --git a/fs/xfs/xfs_quota.h b/fs/xfs/xfs_quota.h index 7fbef97..acb853b 100644 --- a/fs/xfs/xfs_quota.h +++ b/fs/xfs/xfs_quota.h @@ -365,7 +365,7 @@ #define XFS_TRANS_UNRESERVE_QUOTA(mp, tp extern int xfs_qm_dqcheck(xfs_disk_dquot_t *, xfs_dqid_t, uint, uint, char *); extern int xfs_mount_reset_sbqflags(struct xfs_mount *); -extern struct bhv_vfsops xfs_qmops; +extern struct bhv_module_vfsops xfs_qmops; #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index 1f14876..d98171d 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -22,13 +22,11 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -40,7 +38,6 @@ #include "xfs_quota.h" #include "xfs_refcache.h" #include "xfs_utils.h" #include "xfs_trans_space.h" -#include "xfs_dir_leaf.h" /* @@ -87,8 +84,8 @@ STATIC int xfs_lock_for_rename( xfs_inode_t *dp1, /* old (source) directory inode */ xfs_inode_t *dp2, /* new (target) directory inode */ - vname_t *vname1,/* old entry name */ - vname_t *vname2,/* new entry name */ + bhv_vname_t *vname1,/* old entry name */ + bhv_vname_t *vname2,/* new entry name */ xfs_inode_t **ipp1, /* inode of old entry */ xfs_inode_t **ipp2, /* inode of new entry, if it already exists, NULL otherwise. */ @@ -225,9 +222,9 @@ #endif int xfs_rename( bhv_desc_t *src_dir_bdp, - vname_t *src_vname, - vnode_t *target_dir_vp, - vname_t *target_vname, + bhv_vname_t *src_vname, + bhv_vnode_t *target_dir_vp, + bhv_vname_t *target_vname, cred_t *credp) { xfs_trans_t *tp; @@ -242,7 +239,7 @@ xfs_rename( int committed; xfs_inode_t *inodes[4]; int target_ip_dropped = 0; /* dropped target_ip link? */ - vnode_t *src_dir_vp; + bhv_vnode_t *src_dir_vp; int spaceres; int target_link_zero = 0; int num_inodes; @@ -398,34 +395,29 @@ xfs_rename( * fit before actually inserting it. */ if (spaceres == 0 && - (error = XFS_DIR_CANENTER(mp, tp, target_dp, target_name, - target_namelen))) { + (error = xfs_dir_canenter(tp, target_dp, target_name, + target_namelen))) goto error_return; - } /* * If target does not exist and the rename crosses * directories, adjust the target directory link count * to account for the ".." reference from the new entry. */ - error = XFS_DIR_CREATENAME(mp, tp, target_dp, target_name, + error = xfs_dir_createname(tp, target_dp, target_name, target_namelen, src_ip->i_ino, &first_block, &free_list, spaceres); - if (error == ENOSPC) { + if (error == ENOSPC) goto error_return; - } - if (error) { + if (error) goto abort_return; - } xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); if (new_parent && src_is_directory) { error = xfs_bumplink(tp, target_dp); - if (error) { + if (error) goto abort_return; - } } } else { /* target_ip != NULL */ - /* * If target exists and it's a directory, check that both * target and source are directories and that target can be @@ -435,7 +427,7 @@ xfs_rename( /* * Make sure target dir is empty. */ - if (!(XFS_DIR_ISEMPTY(target_ip->i_mount, target_ip)) || + if (!(xfs_dir_isempty(target_ip)) || (target_ip->i_d.di_nlink > 2)) { error = XFS_ERROR(EEXIST); goto error_return; @@ -451,12 +443,11 @@ xfs_rename( * In case there is already an entry with the same * name at the destination directory, remove it first. */ - error = XFS_DIR_REPLACE(mp, tp, target_dp, target_name, - target_namelen, src_ip->i_ino, &first_block, - &free_list, spaceres); - if (error) { + error = xfs_dir_replace(tp, target_dp, target_name, + target_namelen, src_ip->i_ino, + &first_block, &free_list, spaceres); + if (error) goto abort_return; - } xfs_ichgtime(target_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); /* @@ -464,9 +455,8 @@ xfs_rename( * dir no longer points to it. */ error = xfs_droplink(tp, target_ip); - if (error) { + if (error) goto abort_return; - } target_ip_dropped = 1; if (src_is_directory) { @@ -474,9 +464,8 @@ xfs_rename( * Drop the link from the old "." entry. */ error = xfs_droplink(tp, target_ip); - if (error) { + if (error) goto abort_return; - } } /* Do this test while we still hold the locks */ @@ -488,18 +477,15 @@ xfs_rename( * Remove the source. */ if (new_parent && src_is_directory) { - /* * Rewrite the ".." entry to point to the new * directory. */ - error = XFS_DIR_REPLACE(mp, tp, src_ip, "..", 2, - target_dp->i_ino, &first_block, - &free_list, spaceres); + error = xfs_dir_replace(tp, src_ip, "..", 2, target_dp->i_ino, + &first_block, &free_list, spaceres); ASSERT(error != EEXIST); - if (error) { + if (error) goto abort_return; - } xfs_ichgtime(src_ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); } else { @@ -527,16 +513,14 @@ xfs_rename( * entry that's moved no longer points to it. */ error = xfs_droplink(tp, src_dp); - if (error) { + if (error) goto abort_return; - } } - error = XFS_DIR_REMOVENAME(mp, tp, src_dp, src_name, src_namelen, + error = xfs_dir_removename(tp, src_dp, src_name, src_namelen, src_ip->i_ino, &first_block, &free_list, spaceres); - if (error) { + if (error) goto abort_return; - } xfs_ichgtime(src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); /* @@ -609,7 +593,7 @@ xfs_rename( * Let interposed file systems know about removed links. */ if (target_ip_dropped) { - VOP_LINK_REMOVED(XFS_ITOV(target_ip), target_dir_vp, + bhv_vop_link_removed(XFS_ITOV(target_ip), target_dir_vp, target_link_zero); IRELE(target_ip); } diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 5b41394..5a0b678 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -141,7 +139,7 @@ xfs_growfs_rt_alloc( cancelflags |= XFS_TRANS_ABORT; error = xfs_bmapi(tp, ip, oblocks, nblocks - oblocks, XFS_BMAPI_WRITE | XFS_BMAPI_METADATA, &firstblock, - resblks, &map, &nmap, &flist); + resblks, &map, &nmap, &flist, NULL); if (!error && nmap < 1) error = XFS_ERROR(ENOSPC); if (error) @@ -1931,7 +1929,7 @@ xfs_growfs_rt( /* * Initial error checking. */ - if (mp->m_rtdev_targp || mp->m_rbmip == NULL || + if (mp->m_rtdev_targp == NULL || mp->m_rbmip == NULL || (nrblocks = in->newblocks) <= sbp->sb_rblocks || (sbp->sb_rblocks && (in->extsize != sbp->sb_rextsize))) return XFS_ERROR(EINVAL); @@ -2404,10 +2402,10 @@ xfs_rtprint_range( { xfs_extlen_t i; /* block number in the extent */ - printk("%Ld: ", (long long)start); + cmn_err(CE_DEBUG, "%Ld: ", (long long)start); for (i = 0; i < len; i++) - printk("%d", xfs_rtcheck_bit(mp, tp, start + i, 1)); - printk("\n"); + cmn_err(CE_DEBUG, "%d", xfs_rtcheck_bit(mp, tp, start + i, 1)); + cmn_err(CE_DEBUG, "\n"); } /* @@ -2431,17 +2429,17 @@ xfs_rtprint_summary( (void)xfs_rtget_summary(mp, tp, l, i, &sumbp, &sb, &c); if (c) { if (!p) { - printk("%Ld-%Ld:", 1LL << l, + cmn_err(CE_DEBUG, "%Ld-%Ld:", 1LL << l, XFS_RTMIN((1LL << l) + ((1LL << l) - 1LL), mp->m_sb.sb_rextents)); p = 1; } - printk(" %Ld:%d", (long long)i, c); + cmn_err(CE_DEBUG, " %Ld:%d", (long long)i, c); } } if (p) - printk("\n"); + cmn_err(CE_DEBUG, "\n"); } if (sumbp) xfs_trans_brelse(tp, sumbp); diff --git a/fs/xfs/xfs_rw.c b/fs/xfs/xfs_rw.c index a59c102..defb2fe 100644 --- a/fs/xfs/xfs_rw.c +++ b/fs/xfs/xfs_rw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -92,6 +90,90 @@ xfs_write_clear_setuid( } /* + * Handle logging requirements of various synchronous types of write. + */ +int +xfs_write_sync_logforce( + xfs_mount_t *mp, + xfs_inode_t *ip) +{ + int error = 0; + + /* + * If we're treating this as O_DSYNC and we have not updated the + * size, force the log. + */ + if (!(mp->m_flags & XFS_MOUNT_OSYNCISOSYNC) && + !(ip->i_update_size)) { + xfs_inode_log_item_t *iip = ip->i_itemp; + + /* + * If an allocation transaction occurred + * without extending the size, then we have to force + * the log up the proper point to ensure that the + * allocation is permanent. We can't count on + * the fact that buffered writes lock out direct I/O + * writes - the direct I/O write could have extended + * the size nontransactionally, then finished before + * we started. xfs_write_file will think that the file + * didn't grow but the update isn't safe unless the + * size change is logged. + * + * Force the log if we've committed a transaction + * against the inode or if someone else has and + * the commit record hasn't gone to disk (e.g. + * the inode is pinned). This guarantees that + * all changes affecting the inode are permanent + * when we return. + */ + if (iip && iip->ili_last_lsn) { + xfs_log_force(mp, iip->ili_last_lsn, + XFS_LOG_FORCE | XFS_LOG_SYNC); + } else if (xfs_ipincount(ip) > 0) { + xfs_log_force(mp, (xfs_lsn_t)0, + XFS_LOG_FORCE | XFS_LOG_SYNC); + } + + } else { + xfs_trans_t *tp; + + /* + * O_SYNC or O_DSYNC _with_ a size update are handled + * the same way. + * + * If the write was synchronous then we need to make + * sure that the inode modification time is permanent. + * We'll have updated the timestamp above, so here + * we use a synchronous transaction to log the inode. + * It's not fast, but it's necessary. + * + * If this a dsync write and the size got changed + * non-transactionally, then we need to ensure that + * the size change gets logged in a synchronous + * transaction. + */ + tp = xfs_trans_alloc(mp, XFS_TRANS_WRITE_SYNC); + if ((error = xfs_trans_reserve(tp, 0, + XFS_SWRITE_LOG_RES(mp), + 0, 0, 0))) { + /* Transaction reserve failed */ + xfs_trans_cancel(tp, 0); + } else { + /* Transaction reserve successful */ + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); + xfs_trans_ihold(tp, ip); + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + xfs_trans_set_sync(tp); + error = xfs_trans_commit(tp, 0, NULL); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + } + } + + return error; +} + +/* * Force a shutdown of the filesystem instantly while keeping * the filesystem consistent. We don't do an unmount here; just shutdown * the shop, make sure that absolutely nothing persistent happens to @@ -109,12 +191,12 @@ xfs_do_force_shutdown( xfs_mount_t *mp; mp = XFS_BHVTOM(bdp); - logerror = flags & XFS_LOG_IO_ERROR; + logerror = flags & SHUTDOWN_LOG_IO_ERROR; - if (!(flags & XFS_FORCE_UMOUNT)) { - cmn_err(CE_NOTE, - "xfs_force_shutdown(%s,0x%x) called from line %d of file %s. Return address = 0x%p", - mp->m_fsname,flags,lnnum,fname,__return_address); + if (!(flags & SHUTDOWN_FORCE_UMOUNT)) { + cmn_err(CE_NOTE, "xfs_force_shutdown(%s,0x%x) called from " + "line %d of file %s. Return address = 0x%p", + mp->m_fsname, flags, lnnum, fname, __return_address); } /* * No need to duplicate efforts. @@ -125,33 +207,37 @@ xfs_do_force_shutdown( /* * This flags XFS_MOUNT_FS_SHUTDOWN, makes sure that we don't * queue up anybody new on the log reservations, and wakes up - * everybody who's sleeping on log reservations and tells - * them the bad news. + * everybody who's sleeping on log reservations to tell them + * the bad news. */ if (xfs_log_force_umount(mp, logerror)) return; - if (flags & XFS_CORRUPT_INCORE) { + if (flags & SHUTDOWN_CORRUPT_INCORE) { xfs_cmn_err(XFS_PTAG_SHUTDOWN_CORRUPT, CE_ALERT, mp, "Corruption of in-memory data detected. Shutting down filesystem: %s", mp->m_fsname); if (XFS_ERRLEVEL_HIGH <= xfs_error_level) { xfs_stack_trace(); } - } else if (!(flags & XFS_FORCE_UMOUNT)) { + } else if (!(flags & SHUTDOWN_FORCE_UMOUNT)) { if (logerror) { xfs_cmn_err(XFS_PTAG_SHUTDOWN_LOGERROR, CE_ALERT, mp, - "Log I/O Error Detected. Shutting down filesystem: %s", + "Log I/O Error Detected. Shutting down filesystem: %s", + mp->m_fsname); + } else if (flags & SHUTDOWN_DEVICE_REQ) { + xfs_cmn_err(XFS_PTAG_SHUTDOWN_IOERROR, CE_ALERT, mp, + "All device paths lost. Shutting down filesystem: %s", mp->m_fsname); - } else if (!(flags & XFS_SHUTDOWN_REMOTE_REQ)) { + } else if (!(flags & SHUTDOWN_REMOTE_REQ)) { xfs_cmn_err(XFS_PTAG_SHUTDOWN_IOERROR, CE_ALERT, mp, - "I/O Error Detected. Shutting down filesystem: %s", + "I/O Error Detected. Shutting down filesystem: %s", mp->m_fsname); } } - if (!(flags & XFS_FORCE_UMOUNT)) { - cmn_err(CE_ALERT, - "Please umount the filesystem, and rectify the problem(s)"); + if (!(flags & SHUTDOWN_FORCE_UMOUNT)) { + cmn_err(CE_ALERT, "Please umount the filesystem, " + "and rectify the problem(s)"); } } @@ -335,7 +421,7 @@ xfs_bwrite( * from bwrite and we could be tracing a buffer that has * been reused. */ - xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); } return (error); } diff --git a/fs/xfs/xfs_rw.h b/fs/xfs/xfs_rw.h index e637956..188b296 100644 --- a/fs/xfs/xfs_rw.h +++ b/fs/xfs/xfs_rw.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -75,6 +75,7 @@ xfs_fsb_to_db_io(struct xfs_iocore *io, * Prototypes for functions in xfs_rw.c. */ extern int xfs_write_clear_setuid(struct xfs_inode *ip); +extern int xfs_write_sync_logforce(struct xfs_mount *mp, struct xfs_inode *ip); extern int xfs_bwrite(struct xfs_mount *mp, struct xfs_buf *bp); extern int xfs_bioerror(struct xfs_buf *bp); extern int xfs_bioerror_relse(struct xfs_buf *bp); @@ -87,9 +88,10 @@ extern void xfs_ioerror_alert(char *func /* * Prototypes for functions in xfs_vnodeops.c. */ -extern int xfs_rwlock(bhv_desc_t *bdp, vrwlock_t write_lock); -extern void xfs_rwunlock(bhv_desc_t *bdp, vrwlock_t write_lock); -extern int xfs_setattr(bhv_desc_t *bdp, vattr_t *vap, int flags, cred_t *credp); +extern int xfs_rwlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock); +extern void xfs_rwunlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock); +extern int xfs_setattr(bhv_desc_t *, bhv_vattr_t *vap, int flags, + cred_t *credp); extern int xfs_change_file_space(bhv_desc_t *bdp, int cmd, xfs_flock64_t *bf, xfs_off_t offset, cred_t *credp, int flags); extern int xfs_set_dmattrs(bhv_desc_t *bdp, u_int evmask, u_int16_t state, diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 8d056ce..ee2721e 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -24,7 +24,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -33,7 +32,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -236,11 +234,8 @@ xfs_trans_alloc( xfs_mount_t *mp, uint type) { - fs_check_frozen(XFS_MTOVFS(mp), SB_FREEZE_TRANS); - atomic_inc(&mp->m_active_trans); - - return (_xfs_trans_alloc(mp, type)); - + vfs_wait_for_freeze(XFS_MTOVFS(mp), SB_FREEZE_TRANS); + return _xfs_trans_alloc(mp, type); } xfs_trans_t * @@ -250,12 +245,9 @@ _xfs_trans_alloc( { xfs_trans_t *tp; - ASSERT(xfs_trans_zone != NULL); - tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP); + atomic_inc(&mp->m_active_trans); - /* - * Initialize the transaction structure. - */ + tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP); tp->t_magic = XFS_TRANS_MAGIC; tp->t_type = type; tp->t_mountp = mp; @@ -263,8 +255,7 @@ _xfs_trans_alloc( tp->t_busy_free = XFS_LBC_NUM_SLOTS; XFS_LIC_INIT(&(tp->t_items)); XFS_LBC_INIT(&(tp->t_busy)); - - return (tp); + return tp; } /* @@ -303,7 +294,7 @@ xfs_trans_dup( tp->t_blk_res = tp->t_blk_res_used; ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used; tp->t_rtx_res = tp->t_rtx_res_used; - PFLAGS_DUP(&tp->t_pflags, &ntp->t_pflags); + ntp->t_pflags = tp->t_pflags; XFS_TRANS_DUP_DQINFO(tp->t_mountp, tp, ntp); @@ -335,14 +326,11 @@ xfs_trans_reserve( uint logcount) { int log_flags; - int error; - int rsvd; - - error = 0; - rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0; + int error = 0; + int rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0; /* Mark this thread as being in a transaction */ - PFLAGS_SET_FSTRANS(&tp->t_pflags); + current_set_flags_nested(&tp->t_pflags, PF_FSTRANS); /* * Attempt to reserve the needed disk blocks by decrementing @@ -353,7 +341,7 @@ xfs_trans_reserve( error = xfs_mod_incore_sb(tp->t_mountp, XFS_SBS_FDBLOCKS, -blocks, rsvd); if (error != 0) { - PFLAGS_RESTORE_FSTRANS(&tp->t_pflags); + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); return (XFS_ERROR(ENOSPC)); } tp->t_blk_res += blocks; @@ -426,9 +414,9 @@ undo_blocks: tp->t_blk_res = 0; } - PFLAGS_RESTORE_FSTRANS(&tp->t_pflags); + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); - return (error); + return error; } @@ -819,7 +807,7 @@ shut_us_down: if (commit_lsn == -1 && !shutdown) shutdown = XFS_ERROR(EIO); } - PFLAGS_RESTORE_FSTRANS(&tp->t_pflags); + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); xfs_trans_free_items(tp, shutdown? XFS_TRANS_ABORT : 0); xfs_trans_free_busy(tp); xfs_trans_free(tp); @@ -846,7 +834,7 @@ shut_us_down: */ nvec = xfs_trans_count_vecs(tp); if (nvec == 0) { - xfs_force_shutdown(mp, XFS_LOG_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_LOG_IO_ERROR); goto shut_us_down; } else if (nvec <= XFS_TRANS_LOGVEC_COUNT) { log_vector = log_vector_fast; @@ -884,7 +872,7 @@ shut_us_down: * had pinned, clean up, free trans structure, and return error. */ if (error || commit_lsn == -1) { - PFLAGS_RESTORE_FSTRANS(&tp->t_pflags); + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); xfs_trans_uncommit(tp, flags|XFS_TRANS_ABORT); return XFS_ERROR(EIO); } @@ -926,7 +914,7 @@ shut_us_down: /* * Mark this thread as no longer being in a transaction */ - PFLAGS_RESTORE_FSTRANS(&tp->t_pflags); + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); /* * Once all the items of the transaction have been copied @@ -1148,7 +1136,7 @@ #endif */ if ((tp->t_flags & XFS_TRANS_DIRTY) && !XFS_FORCED_SHUTDOWN(mp)) { XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp); - xfs_force_shutdown(mp, XFS_CORRUPT_INCORE); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); } #ifdef DEBUG if (!(flags & XFS_TRANS_ABORT)) { @@ -1182,7 +1170,7 @@ #endif } /* mark this thread as no longer being in a transaction */ - PFLAGS_RESTORE_FSTRANS(&tp->t_pflags); + current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); xfs_trans_free_items(tp, flags); xfs_trans_free_busy(tp); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 100d9a4..9dc88b3 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -338,8 +338,6 @@ typedef void (*xfs_trans_callback_t)(str typedef struct xfs_trans { unsigned int t_magic; /* magic number */ xfs_log_callback_t t_logcb; /* log callback struct */ - struct xfs_trans *t_forw; /* async list pointers */ - struct xfs_trans *t_back; /* async list pointers */ unsigned int t_type; /* transaction type */ unsigned int t_log_res; /* amt of log space resvd */ unsigned int t_log_count; /* count for perm log res */ @@ -364,9 +362,11 @@ typedef struct xfs_trans { long t_res_fdblocks_delta; /* on-disk only chg */ long t_frextents_delta;/* superblock freextents chg*/ long t_res_frextents_delta; /* on-disk only chg */ +#ifdef DEBUG long t_ag_freeblks_delta; /* debugging counter */ long t_ag_flist_delta; /* debugging counter */ long t_ag_btree_delta; /* debugging counter */ +#endif long t_dblocks_delta;/* superblock dblocks change */ long t_agcount_delta;/* superblock agcount change */ long t_imaxpct_delta;/* superblock imaxpct change */ @@ -805,12 +805,9 @@ #define XFS_CALC_ADDAFORK_LOG_RES(mp) \ ((mp)->m_sb.sb_inodesize + \ (mp)->m_sb.sb_sectsize * 2 + \ (mp)->m_dirblksize + \ - (XFS_DIR_IS_V1(mp) ? 0 : \ - XFS_FSB_TO_B(mp, (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1))) + \ + XFS_FSB_TO_B(mp, (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1)) + \ XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (4 + \ - (XFS_DIR_IS_V1(mp) ? 0 : \ - XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + \ + (128 * (4 + (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + \ XFS_ALLOCFREE_LOG_COUNT(mp, 1)))) #define XFS_ADDAFORK_LOG_RES(mp) ((mp)->m_reservations.tr_addafork) diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index 19ab24a..558c87f 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -22,7 +22,6 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" @@ -363,9 +362,10 @@ xfs_trans_delete_ail( AIL_UNLOCK(mp, s); else { xfs_cmn_err(XFS_PTAG_AILDELETE, CE_ALERT, mp, - "xfs_trans_delete_ail: attempting to delete a log item that is not in the AIL"); + "%s: attempting to delete a log item that is not in the AIL", + __FUNCTION__); AIL_UNLOCK(mp, s); - xfs_force_shutdown(mp, XFS_CORRUPT_INCORE); + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); } } } diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index c74c31e..60b6b89 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -320,7 +318,7 @@ #ifdef DEBUG if (xfs_error_target == target) { if (((xfs_req_num++) % xfs_error_mod) == 0) { xfs_buf_relse(bp); - printk("Returning error!\n"); + cmn_err(CE_DEBUG, "Returning error!\n"); return XFS_ERROR(EIO); } } @@ -369,7 +367,7 @@ #endif */ if (tp->t_flags & XFS_TRANS_DIRTY) xfs_force_shutdown(tp->t_mountp, - XFS_METADATA_IO_ERROR); + SHUTDOWN_META_IO_ERROR); return error; } } @@ -414,7 +412,7 @@ #endif xfs_ioerror_alert("xfs_trans_read_buf", mp, bp, blkno); if (tp->t_flags & XFS_TRANS_DIRTY) - xfs_force_shutdown(tp->t_mountp, XFS_METADATA_IO_ERROR); + xfs_force_shutdown(tp->t_mountp, SHUTDOWN_META_IO_ERROR); xfs_buf_relse(bp); return error; } @@ -423,9 +421,9 @@ #ifdef DEBUG if (xfs_error_target == target) { if (((xfs_req_num++) % xfs_error_mod) == 0) { xfs_force_shutdown(tp->t_mountp, - XFS_METADATA_IO_ERROR); + SHUTDOWN_META_IO_ERROR); xfs_buf_relse(bp); - printk("Returning error in trans!\n"); + cmn_err(CE_DEBUG, "Returning trans error!\n"); return XFS_ERROR(EIO); } } diff --git a/fs/xfs/xfs_trans_extfree.c b/fs/xfs/xfs_trans_extfree.c index 7d7d627..b290270 100644 --- a/fs/xfs/xfs_trans_extfree.c +++ b/fs/xfs/xfs_trans_extfree.c @@ -22,7 +22,6 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_dir.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index 7c5894d..b8db1d5 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -24,14 +24,12 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" diff --git a/fs/xfs/xfs_trans_item.c b/fs/xfs/xfs_trans_item.c index 1117d60..2912aac 100644 --- a/fs/xfs/xfs_trans_item.c +++ b/fs/xfs/xfs_trans_item.c @@ -493,7 +493,7 @@ xfs_trans_add_busy(xfs_trans_t *tp, xfs_ break; } else { /* out-of-order vacancy */ - printk("OOO vacancy lbcp 0x%p\n", lbcp); + cmn_err(CE_DEBUG, "OOO vacancy lbcp 0x%p\n", lbcp); ASSERT(0); } } diff --git a/fs/xfs/xfs_trans_space.h b/fs/xfs/xfs_trans_space.h index 7fe3792..4ea2e50 100644 --- a/fs/xfs/xfs_trans_space.h +++ b/fs/xfs/xfs_trans_space.h @@ -30,8 +30,7 @@ #define XFS_NEXTENTADD_SPACE_RES(mp,b,w) XFS_EXTENTADD_SPACE_RES(mp,w)) #define XFS_DAENTER_1B(mp,w) ((w) == XFS_DATA_FORK ? (mp)->m_dirblkfsbs : 1) #define XFS_DAENTER_DBS(mp,w) \ - (XFS_DA_NODE_MAXDEPTH + \ - ((XFS_DIR_IS_V2(mp) && (w) == XFS_DATA_FORK) ? 2 : 0)) + (XFS_DA_NODE_MAXDEPTH + (((w) == XFS_DATA_FORK) ? 2 : 0)) #define XFS_DAENTER_BLOCKS(mp,w) \ (XFS_DAENTER_1B(mp,w) * XFS_DAENTER_DBS(mp,w)) #define XFS_DAENTER_BMAP1B(mp,w) \ @@ -41,10 +40,7 @@ #define XFS_DAENTER_BMAPS(mp,w) \ #define XFS_DAENTER_SPACE_RES(mp,w) \ (XFS_DAENTER_BLOCKS(mp,w) + XFS_DAENTER_BMAPS(mp,w)) #define XFS_DAREMOVE_SPACE_RES(mp,w) XFS_DAENTER_BMAPS(mp,w) -#define XFS_DIRENTER_MAX_SPLIT(mp,nl) \ - (((mp)->m_sb.sb_blocksize == 512 && \ - XFS_DIR_IS_V1(mp) && \ - (nl) >= XFS_DIR_LEAF_CAN_DOUBLE_SPLIT_LEN) ? 2 : 1) +#define XFS_DIRENTER_MAX_SPLIT(mp,nl) 1 #define XFS_DIRENTER_SPACE_RES(mp,nl) \ (XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK) * \ XFS_DIRENTER_MAX_SPLIT(mp,nl)) @@ -57,8 +53,7 @@ #define XFS_IALLOC_SPACE_RES(mp) \ * Space reservation values for various transactions. */ #define XFS_ADDAFORK_SPACE_RES(mp) \ - ((mp)->m_dirblkfsbs + \ - (XFS_DIR_IS_V1(mp) ? 0 : XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK))) + ((mp)->m_dirblkfsbs + XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK)) #define XFS_ATTRRM_SPACE_RES(mp) \ XFS_DAREMOVE_SPACE_RES(mp, XFS_ATTR_FORK) /* This macro is not used - see inline code in xfs_attr_set */ diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index 34654ec..9014d7e 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -24,12 +24,10 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -51,10 +49,10 @@ #include "xfs_utils.h" */ int xfs_get_dir_entry( - vname_t *dentry, + bhv_vname_t *dentry, xfs_inode_t **ipp) { - vnode_t *vp; + bhv_vnode_t *vp; vp = VNAME_TO_VNODE(dentry); @@ -69,11 +67,11 @@ int xfs_dir_lookup_int( bhv_desc_t *dir_bdp, uint lock_mode, - vname_t *dentry, + bhv_vname_t *dentry, xfs_ino_t *inum, xfs_inode_t **ipp) { - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; xfs_inode_t *dp; int error; @@ -82,8 +80,7 @@ xfs_dir_lookup_int( dp = XFS_BHVTOI(dir_bdp); - error = XFS_DIR_LOOKUP(dp->i_mount, NULL, dp, - VNAME(dentry), VNAMELEN(dentry), inum); + error = xfs_dir_lookup(NULL, dp, VNAME(dentry), VNAMELEN(dentry), inum); if (!error) { /* * Unlock the directory. We do this because we can't diff --git a/fs/xfs/xfs_utils.h b/fs/xfs/xfs_utils.h index 472661a..fe953e9 100644 --- a/fs/xfs/xfs_utils.h +++ b/fs/xfs/xfs_utils.h @@ -23,9 +23,10 @@ #define IHOLD(ip) VN_HOLD(XFS_ITOV(ip)) #define ITRACE(ip) vn_trace_ref(XFS_ITOV(ip), __FILE__, __LINE__, \ (inst_t *)__return_address) -extern int xfs_rename (bhv_desc_t *, vname_t *, vnode_t *, vname_t *, cred_t *); -extern int xfs_get_dir_entry (vname_t *, xfs_inode_t **); -extern int xfs_dir_lookup_int (bhv_desc_t *, uint, vname_t *, xfs_ino_t *, +extern int xfs_rename (bhv_desc_t *, bhv_vname_t *, bhv_vnode_t *, + bhv_vname_t *, cred_t *); +extern int xfs_get_dir_entry (bhv_vname_t *, xfs_inode_t **); +extern int xfs_dir_lookup_int (bhv_desc_t *, uint, bhv_vname_t *, xfs_ino_t *, xfs_inode_t **); extern int xfs_truncate_file (xfs_mount_t *, xfs_inode_t *); extern int xfs_dir_ialloc (xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t, diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c index 36ea1b2..6c96391 100644 --- a/fs/xfs/xfs_vfsops.c +++ b/fs/xfs/xfs_vfsops.c @@ -24,7 +24,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -32,7 +31,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_ialloc_btree.h" #include "xfs_alloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" @@ -131,9 +129,6 @@ #endif #ifdef XFS_BMBT_TRACE xfs_bmbt_trace_buf = ktrace_alloc(XFS_BMBT_TRACE_SIZE, KM_SLEEP); #endif -#ifdef XFS_DIR_TRACE - xfs_dir_trace_buf = ktrace_alloc(XFS_DIR_TRACE_SIZE, KM_SLEEP); -#endif #ifdef XFS_ATTR_TRACE xfs_attr_trace_buf = ktrace_alloc(XFS_ATTR_TRACE_SIZE, KM_SLEEP); #endif @@ -177,9 +172,6 @@ #endif #ifdef XFS_ATTR_TRACE ktrace_free(xfs_attr_trace_buf); #endif -#ifdef XFS_DIR_TRACE - ktrace_free(xfs_dir_trace_buf); -#endif #ifdef XFS_BMBT_TRACE ktrace_free(xfs_bmbt_trace_buf); #endif @@ -212,7 +204,7 @@ #endif */ STATIC int xfs_start_flags( - struct vfs *vfs, + struct bhv_vfs *vfs, struct xfs_mount_args *ap, struct xfs_mount *mp) { @@ -337,7 +329,7 @@ #endif */ STATIC int xfs_finish_flags( - struct vfs *vfs, + struct bhv_vfs *vfs, struct xfs_mount_args *ap, struct xfs_mount *mp) { @@ -423,7 +415,7 @@ xfs_mount( struct xfs_mount_args *args, cred_t *credp) { - struct vfs *vfsp = bhvtovfs(bhvp); + struct bhv_vfs *vfsp = bhvtovfs(bhvp); struct bhv_desc *p; struct xfs_mount *mp = XFS_BHVTOM(bhvp); struct block_device *ddev, *logdev, *rtdev; @@ -552,10 +544,10 @@ xfs_unmount( int flags, cred_t *credp) { - struct vfs *vfsp = bhvtovfs(bdp); + bhv_vfs_t *vfsp = bhvtovfs(bdp); xfs_mount_t *mp = XFS_BHVTOM(bdp); xfs_inode_t *rip; - vnode_t *rvp; + bhv_vnode_t *rvp; int unmount_event_wanted = 0; int unmount_event_flags = 0; int xfs_unmountfs_needed = 0; @@ -665,9 +657,8 @@ xfs_mntupdate( int *flags, struct xfs_mount_args *args) { - struct vfs *vfsp = bhvtovfs(bdp); + bhv_vfs_t *vfsp = bhvtovfs(bdp); xfs_mount_t *mp = XFS_BHVTOM(bdp); - int error; if (!(*flags & MS_RDONLY)) { /* rw/ro -> rw */ if (vfsp->vfs_flag & VFS_RDONLY) @@ -679,7 +670,7 @@ xfs_mntupdate( mp->m_flags &= ~XFS_MOUNT_BARRIER; } } else if (!(vfsp->vfs_flag & VFS_RDONLY)) { /* rw -> ro */ - VFS_SYNC(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL, error); + bhv_vfs_sync(vfsp, SYNC_FSDATA|SYNC_BDFLUSH|SYNC_ATTR, NULL); xfs_quiesce_fs(mp); xfs_log_unmount_write(mp); xfs_unmountfs_writesb(mp); @@ -702,7 +693,7 @@ xfs_unmount_flush( xfs_inode_t *rip = mp->m_rootip; xfs_inode_t *rbmip; xfs_inode_t *rsumip = NULL; - vnode_t *rvp = XFS_ITOV(rip); + bhv_vnode_t *rvp = XFS_ITOV(rip); int error; xfs_ilock(rip, XFS_ILOCK_EXCL); @@ -781,9 +772,9 @@ fscorrupt_out2: STATIC int xfs_root( bhv_desc_t *bdp, - vnode_t **vpp) + bhv_vnode_t **vpp) { - vnode_t *vp; + bhv_vnode_t *vp; vp = XFS_ITOV((XFS_BHVTOM(bdp))->m_rootip); VN_HOLD(vp); @@ -801,8 +792,8 @@ xfs_root( STATIC int xfs_statvfs( bhv_desc_t *bdp, - xfs_statfs_t *statp, - vnode_t *vp) + bhv_statvfs_t *statp, + bhv_vnode_t *vp) { __uint64_t fakeinos; xfs_extlen_t lsize; @@ -900,7 +891,7 @@ xfs_sync( /* * xfs sync routine for internal use * - * This routine supports all of the flags defined for the generic VFS_SYNC + * This routine supports all of the flags defined for the generic vfs_sync * interface as explained above under xfs_sync. In the interests of not * changing interfaces within the 6.5 family, additional internally- * required functions are specified within a separate xflags parameter, @@ -917,7 +908,7 @@ xfs_sync_inodes( xfs_inode_t *ip = NULL; xfs_inode_t *ip_next; xfs_buf_t *bp; - vnode_t *vp = NULL; + bhv_vnode_t *vp = NULL; int error; int last_error; uint64_t fflag; @@ -1156,9 +1147,9 @@ #define XFS_PREEMPT_MASK 0x7f xfs_iunlock(ip, XFS_ILOCK_SHARED); if (XFS_FORCED_SHUTDOWN(mp)) { - VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF); + bhv_vop_toss_pages(vp, 0, -1, FI_REMAPF); } else { - VOP_FLUSHINVAL_PAGES(vp, 0, -1, FI_REMAPF); + bhv_vop_flushinval_pages(vp, 0, -1, FI_REMAPF); } xfs_ilock(ip, XFS_ILOCK_SHARED); @@ -1178,8 +1169,8 @@ #define XFS_PREEMPT_MASK 0x7f * across calls to the buffer cache. */ xfs_iunlock(ip, XFS_ILOCK_SHARED); - VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, - fflag, FI_NONE, error); + error = bhv_vop_flush_pages(vp, (xfs_off_t)0, + -1, fflag, FI_NONE); xfs_ilock(ip, XFS_ILOCK_SHARED); } @@ -1231,9 +1222,7 @@ #define XFS_PREEMPT_MASK 0x7f * marker and free it. */ XFS_MOUNT_ILOCK(mp); - IPOINTER_REMOVE(ip, mp); - XFS_MOUNT_IUNLOCK(mp); ASSERT(!(lock_flags & @@ -1421,7 +1410,7 @@ #define XFS_PREEMPT_MASK 0x7f /* * xfs sync routine for internal use * - * This routine supports all of the flags defined for the generic VFS_SYNC + * This routine supports all of the flags defined for the generic vfs_sync * interface as explained above under xfs_sync. In the interests of not * changing interfaces within the 6.5 family, additional internally- * required functions are specified within a separate xflags parameter, @@ -1574,7 +1563,7 @@ xfs_syncsub( STATIC int xfs_vget( bhv_desc_t *bdp, - vnode_t **vpp, + bhv_vnode_t **vpp, fid_t *fidp) { xfs_mount_t *mp = XFS_BHVTOM(bdp); @@ -1657,10 +1646,10 @@ #define MNTOPT_ATTR2 "attr2" /* do use #define MNTOPT_NOATTR2 "noattr2" /* do not use attr2 attribute format */ STATIC unsigned long -suffix_strtoul(const char *cp, char **endp, unsigned int base) +suffix_strtoul(char *s, char **endp, unsigned int base) { int last, shift_left_factor = 0; - char *value = (char *)cp; + char *value = s; last = strlen(value) - 1; if (value[last] == 'K' || value[last] == 'k') { @@ -1676,7 +1665,7 @@ suffix_strtoul(const char *cp, char **en value[last] = '\0'; } - return simple_strtoul(cp, endp, base) << shift_left_factor; + return simple_strtoul((const char *)s, endp, base) << shift_left_factor; } STATIC int @@ -1686,7 +1675,7 @@ xfs_parseargs( struct xfs_mount_args *args, int update) { - struct vfs *vfsp = bhvtovfs(bhv); + bhv_vfs_t *vfsp = bhvtovfs(bhv); char *this_char, *value, *eov; int dsunit, dswidth, vol_dsunit, vol_dswidth; int iosize; @@ -1708,42 +1697,48 @@ xfs_parseargs( if (!strcmp(this_char, MNTOPT_LOGBUFS)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } args->logbufs = simple_strtoul(value, &eov, 10); } else if (!strcmp(this_char, MNTOPT_LOGBSIZE)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } args->logbufsize = suffix_strtoul(value, &eov, 10); } else if (!strcmp(this_char, MNTOPT_LOGDEV)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } strncpy(args->logname, value, MAXNAMELEN); } else if (!strcmp(this_char, MNTOPT_MTPT)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } strncpy(args->mtpt, value, MAXNAMELEN); } else if (!strcmp(this_char, MNTOPT_RTDEV)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } strncpy(args->rtname, value, MAXNAMELEN); } else if (!strcmp(this_char, MNTOPT_BIOSIZE)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } @@ -1752,7 +1747,8 @@ xfs_parseargs( args->iosizelog = (uint8_t) iosize; } else if (!strcmp(this_char, MNTOPT_ALLOCSIZE)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } @@ -1761,7 +1757,8 @@ xfs_parseargs( args->iosizelog = ffs(iosize) - 1; } else if (!strcmp(this_char, MNTOPT_IHASHSIZE)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } @@ -1782,7 +1779,8 @@ xfs_parseargs( } else if (!strcmp(this_char, MNTOPT_INO64)) { args->flags |= XFSMNT_INO64; #if !XFS_BIG_INUMS - printk("XFS: %s option not allowed on this system\n", + cmn_err(CE_WARN, + "XFS: %s option not allowed on this system", this_char); return EINVAL; #endif @@ -1792,14 +1790,16 @@ #endif args->flags |= XFSMNT_SWALLOC; } else if (!strcmp(this_char, MNTOPT_SUNIT)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } dsunit = simple_strtoul(value, &eov, 10); } else if (!strcmp(this_char, MNTOPT_SWIDTH)) { if (!value || !*value) { - printk("XFS: %s option requires an argument\n", + cmn_err(CE_WARN, + "XFS: %s option requires an argument", this_char); return EINVAL; } @@ -1807,7 +1807,8 @@ #endif } else if (!strcmp(this_char, MNTOPT_64BITINODE)) { args->flags &= ~XFSMNT_32BITINODES; #if !XFS_BIG_INUMS - printk("XFS: %s option not allowed on this system\n", + cmn_err(CE_WARN, + "XFS: %s option not allowed on this system", this_char); return EINVAL; #endif @@ -1831,36 +1832,41 @@ #endif args->flags &= ~XFSMNT_ATTR2; } else if (!strcmp(this_char, "osyncisdsync")) { /* no-op, this is now the default */ -printk("XFS: osyncisdsync is now the default, option is deprecated.\n"); + cmn_err(CE_WARN, + "XFS: osyncisdsync is now the default, option is deprecated."); } else if (!strcmp(this_char, "irixsgid")) { -printk("XFS: irixsgid is now a sysctl(2) variable, option is deprecated.\n"); + cmn_err(CE_WARN, + "XFS: irixsgid is now a sysctl(2) variable, option is deprecated."); } else { - printk("XFS: unknown mount option [%s].\n", this_char); + cmn_err(CE_WARN, + "XFS: unknown mount option [%s].", this_char); return EINVAL; } } if (args->flags & XFSMNT_NORECOVERY) { if ((vfsp->vfs_flag & VFS_RDONLY) == 0) { - printk("XFS: no-recovery mounts must be read-only.\n"); + cmn_err(CE_WARN, + "XFS: no-recovery mounts must be read-only."); return EINVAL; } } if ((args->flags & XFSMNT_NOALIGN) && (dsunit || dswidth)) { - printk( - "XFS: sunit and swidth options incompatible with the noalign option\n"); + cmn_err(CE_WARN, + "XFS: sunit and swidth options incompatible with the noalign option"); return EINVAL; } if ((dsunit && !dswidth) || (!dsunit && dswidth)) { - printk("XFS: sunit and swidth must be specified together\n"); + cmn_err(CE_WARN, + "XFS: sunit and swidth must be specified together"); return EINVAL; } if (dsunit && (dswidth % dsunit != 0)) { - printk( - "XFS: stripe width (%d) must be a multiple of the stripe unit (%d)\n", + cmn_err(CE_WARN, + "XFS: stripe width (%d) must be a multiple of the stripe unit (%d)", dswidth, dsunit); return EINVAL; } @@ -1907,7 +1913,7 @@ xfs_showargs( }; struct proc_xfs_info *xfs_infop; struct xfs_mount *mp = XFS_BHVTOM(bhv); - struct vfs *vfsp = XFS_MTOVFS(mp); + struct bhv_vfs *vfsp = XFS_MTOVFS(mp); for (xfs_infop = xfs_info; xfs_infop->flag; xfs_infop++) { if (mp->m_flags & xfs_infop->flag) @@ -1967,7 +1973,7 @@ xfs_freeze( } -vfsops_t xfs_vfsops = { +bhv_vfsops_t xfs_vfsops = { BHV_IDENTITY_INIT(VFS_BHV_XFS,VFS_POSITION_XFS), .vfs_parseargs = xfs_parseargs, .vfs_showargs = xfs_showargs, diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 7027ae6..23cfa58 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. + * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -16,8 +16,6 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - #include "xfs.h" #include "xfs_fs.h" #include "xfs_types.h" @@ -27,7 +25,6 @@ #include "xfs_inum.h" #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir.h" #include "xfs_dir2.h" #include "xfs_dmapi.h" #include "xfs_mount.h" @@ -35,13 +32,11 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir_sf.h" #include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" -#include "xfs_dir_leaf.h" #include "xfs_itable.h" #include "xfs_btree.h" #include "xfs_ialloc.h" @@ -58,32 +53,14 @@ #include "xfs_trans_space.h" #include "xfs_log_priv.h" #include "xfs_mac.h" - -/* - * The maximum pathlen is 1024 bytes. Since the minimum file system - * blocksize is 512 bytes, we can get a max of 2 extents back from - * bmapi. - */ -#define SYMLINK_MAPS 2 - -/* - * For xfs, we check that the file isn't too big to be opened by this kernel. - * No other open action is required for regular files. Devices are handled - * through the specfs file system, pipes through fifofs. Device and - * fifo vnodes are "wrapped" by specfs and fifofs vnodes, respectively, - * when a new vnode is first looked up or created. - */ STATIC int xfs_open( bhv_desc_t *bdp, cred_t *credp) { int mode; - vnode_t *vp; - xfs_inode_t *ip; - - vp = BHV_TO_VNODE(bdp); - ip = XFS_BHVTOI(bdp); + bhv_vnode_t *vp = BHV_TO_VNODE(bdp); + xfs_inode_t *ip = XFS_BHVTOI(bdp); if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return XFS_ERROR(EIO); @@ -101,6 +78,35 @@ xfs_open( return 0; } +STATIC int +xfs_close( + bhv_desc_t *bdp, + int flags, + lastclose_t lastclose, + cred_t *credp) +{ + bhv_vnode_t *vp = BHV_TO_VNODE(bdp); + xfs_inode_t *ip = XFS_BHVTOI(bdp); + + if (XFS_FORCED_SHUTDOWN(ip->i_mount)) + return XFS_ERROR(EIO); + + if (lastclose != L_TRUE || !VN_ISREG(vp)) + return 0; + + /* + * If we previously truncated this file and removed old data in + * the process, we want to initiate "early" writeout on the last + * close. This is an attempt to combat the notorious NULL files + * problem which is particularly noticable from a truncate down, + * buffered (re-)write (delalloc), followed by a crash. What we + * are effectively doing here is significantly reducing the time + * window where we'd otherwise be exposed to that problem. + */ + if (VUNTRUNCATE(vp) && VN_DIRTY(vp) && ip->i_delayed_blks > 0) + return bhv_vop_flush_pages(vp, 0, -1, XFS_B_ASYNC, FI_NONE); + return 0; +} /* * xfs_getattr @@ -108,13 +114,13 @@ xfs_open( STATIC int xfs_getattr( bhv_desc_t *bdp, - vattr_t *vap, + bhv_vattr_t *vap, int flags, cred_t *credp) { xfs_inode_t *ip; xfs_mount_t *mp; - vnode_t *vp; + bhv_vnode_t *vp; vp = BHV_TO_VNODE(bdp); vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); @@ -241,7 +247,7 @@ #endif int xfs_setattr( bhv_desc_t *bdp, - vattr_t *vap, + bhv_vattr_t *vap, int flags, cred_t *credp) { @@ -255,7 +261,7 @@ xfs_setattr( uid_t uid=0, iuid=0; gid_t gid=0, igid=0; int timeflags = 0; - vnode_t *vp; + bhv_vnode_t *vp; xfs_prid_t projid=0, iprojid=0; int mandlock_before, mandlock_after; struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2; @@ -347,7 +353,6 @@ xfs_setattr( */ tp = NULL; lock_flags = XFS_ILOCK_EXCL; - ASSERT(flags & ATTR_NOLOCK ? flags & ATTR_DMI : 1); if (flags & ATTR_NOLOCK) need_iolock = 0; if (!(mask & XFS_AT_SIZE)) { @@ -666,9 +671,17 @@ #endif ((ip->i_d.di_nlink != 0 || !(mp->m_flags & XFS_MOUNT_WSYNC)) ? 1 : 0)); - if (code) { + if (code) goto abort_return; - } + /* + * Truncated "down", so we're removing references + * to old data here - if we now delay flushing for + * a long time, we expose ourselves unduly to the + * notorious NULL files problem. So, we mark this + * vnode and flush it when the file is closed, and + * do not wait the usual (long) time for writeout. + */ + VTRUNCATE(vp); } /* * Have to do this even if the file's size doesn't change. @@ -800,6 +813,8 @@ #endif di_flags |= XFS_DIFLAG_NODUMP; if (vap->va_xflags & XFS_XFLAG_PROJINHERIT) di_flags |= XFS_DIFLAG_PROJINHERIT; + if (vap->va_xflags & XFS_XFLAG_NODEFRAG) + di_flags |= XFS_DIFLAG_NODEFRAG; if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) { if (vap->va_xflags & XFS_XFLAG_RTINHERIT) di_flags |= XFS_DIFLAG_RTINHERIT; @@ -869,7 +884,7 @@ #endif */ mandlock_after = MANDLOCK(vp, ip->i_d.di_mode); if (mandlock_before != mandlock_after) { - VOP_VNODE_CHANGE(vp, VCHANGE_FLAGS_ENF_LOCKING, + bhv_vop_vnode_change(vp, VCHANGE_FLAGS_ENF_LOCKING, mandlock_after); } @@ -936,6 +951,13 @@ xfs_access( /* + * The maximum pathlen is 1024 bytes. Since the minimum file system + * blocksize is 512 bytes, we can get a max of 2 extents back from + * bmapi. + */ +#define SYMLINK_MAPS 2 + +/* * xfs_readlink * */ @@ -950,7 +972,7 @@ xfs_readlink( int count; xfs_off_t offset; int pathlen; - vnode_t *vp; + bhv_vnode_t *vp; int error = 0; xfs_mount_t *mp; int nmaps; @@ -1000,7 +1022,7 @@ xfs_readlink( nmaps = SYMLINK_MAPS; error = xfs_bmapi(NULL, ip, 0, XFS_B_TO_FSB(mp, pathlen), - 0, NULL, 0, mval, &nmaps, NULL); + 0, NULL, 0, mval, &nmaps, NULL, NULL); if (error) { goto error_return; @@ -1208,8 +1230,8 @@ xfs_inactive_free_eofblocks( nimaps = 1; xfs_ilock(ip, XFS_ILOCK_SHARED); - error = xfs_bmapi(NULL, ip, end_fsb, map_len, 0, - NULL, 0, &imap, &nimaps, NULL); + error = XFS_BMAPI(mp, NULL, &ip->i_iocore, end_fsb, map_len, 0, + NULL, 0, &imap, &nimaps, NULL, NULL); xfs_iunlock(ip, XFS_ILOCK_SHARED); if (!error && (nimaps != 0) && @@ -1338,7 +1360,7 @@ xfs_inactive_symlink_rmt( nmaps = ARRAY_SIZE(mval); if ((error = xfs_bmapi(tp, ip, 0, XFS_B_TO_FSB(mp, size), XFS_BMAPI_METADATA, &first_block, 0, mval, &nmaps, - &free_list))) + &free_list, NULL))) goto error0; /* * Invalidate the block(s). @@ -1353,7 +1375,7 @@ xfs_inactive_symlink_rmt( * Unmap the dead block(s) to the free_list. */ if ((error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps, - &first_block, &free_list, &done))) + &first_block, &free_list, NULL, &done))) goto error1; ASSERT(done); /* @@ -1469,9 +1491,6 @@ xfs_inactive_symlink_local( return 0; } -/* - * - */ STATIC int xfs_inactive_attrs( xfs_inode_t *ip, @@ -1524,16 +1543,16 @@ xfs_release( bhv_desc_t *bdp) { xfs_inode_t *ip; - vnode_t *vp; + bhv_vnode_t *vp; xfs_mount_t *mp; int error; vp = BHV_TO_VNODE(bdp); ip = XFS_BHVTOI(bdp); + mp = ip->i_mount; - if (!VN_ISREG(vp) || (ip->i_d.di_mode == 0)) { + if (!VN_ISREG(vp) || (ip->i_d.di_mode == 0)) return 0; - } /* If this is a read-only mount, don't do this (would generate I/O) */ if (vp->v_vfsp->vfs_flag & VFS_RDONLY) @@ -1545,8 +1564,6 @@ #ifdef HAVE_REFCACHE return 0; #endif - mp = ip->i_mount; - if (ip->i_d.di_nlink != 0) { if ((((ip->i_d.di_mode & S_IFMT) == S_IFREG) && ((ip->i_d.di_size > 0) || (VN_CACHED(vp) > 0 || @@ -1579,8 +1596,8 @@ xfs_inactive( cred_t *credp) { xfs_inode_t *ip; - vnode_t *vp; - xfs_bmap_free_t free_list; + bhv_vnode_t *vp; + xfs_bmap_free_t free_list; xfs_fsblock_t first_block; int committed; xfs_trans_t *tp; @@ -1760,7 +1777,7 @@ xfs_inactive( cmn_err(CE_NOTE, "xfs_inactive: xfs_ifree() returned an error = %d on %s", error, mp->m_fsname); - xfs_force_shutdown(mp, XFS_METADATA_IO_ERROR); + xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); } xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_ABORT); } else { @@ -1795,17 +1812,17 @@ xfs_inactive( STATIC int xfs_lookup( bhv_desc_t *dir_bdp, - vname_t *dentry, - vnode_t **vpp, + bhv_vname_t *dentry, + bhv_vnode_t **vpp, int flags, - vnode_t *rdir, + bhv_vnode_t *rdir, cred_t *credp) { xfs_inode_t *dp, *ip; xfs_ino_t e_inum; int error; uint lock_mode; - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; dir_vp = BHV_TO_VNODE(dir_bdp); vn_trace_entry(dir_vp, __FUNCTION__, (inst_t *)__return_address); @@ -1832,15 +1849,15 @@ xfs_lookup( STATIC int xfs_create( bhv_desc_t *dir_bdp, - vname_t *dentry, - vattr_t *vap, - vnode_t **vpp, + bhv_vname_t *dentry, + bhv_vattr_t *vap, + bhv_vnode_t **vpp, cred_t *credp) { char *name = VNAME(dentry); - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; xfs_inode_t *dp, *ip; - vnode_t *vp=NULL; + bhv_vnode_t *vp = NULL; xfs_trans_t *tp; xfs_mount_t *mp; xfs_dev_t rdev; @@ -1938,8 +1955,7 @@ xfs_create( if (error) goto error_return; - if (resblks == 0 && - (error = XFS_DIR_CANENTER(mp, tp, dp, name, namelen))) + if (resblks == 0 && (error = xfs_dir_canenter(tp, dp, name, namelen))) goto error_return; rdev = (vap->va_mask & XFS_AT_RDEV) ? vap->va_rdev : 0; error = xfs_dir_ialloc(&tp, dp, vap->va_mode, 1, @@ -1970,9 +1986,9 @@ xfs_create( xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); dp_joined_to_trans = B_TRUE; - error = XFS_DIR_CREATENAME(mp, tp, dp, name, namelen, ip->i_ino, - &first_block, &free_list, - resblks ? resblks - XFS_IALLOC_SPACE_RES(mp) : 0); + error = xfs_dir_createname(tp, dp, name, namelen, ip->i_ino, + &first_block, &free_list, resblks ? + resblks - XFS_IALLOC_SPACE_RES(mp) : 0); if (error) { ASSERT(error != ENOSPC); goto abort_return; @@ -2026,7 +2042,7 @@ xfs_create( * Propagate the fact that the vnode changed after the * xfs_inode locks have been released. */ - VOP_VNODE_CHANGE(vp, VCHANGE_FLAGS_TRUNCATED, 3); + bhv_vop_vnode_change(vp, VCHANGE_FLAGS_TRUNCATED, 3); *vpp = vp; @@ -2107,7 +2123,7 @@ #endif STATIC int xfs_lock_dir_and_entry( xfs_inode_t *dp, - vname_t *dentry, + bhv_vname_t *dentry, xfs_inode_t *ip) /* inode of entry 'name' */ { int attempts; @@ -2321,10 +2337,10 @@ #endif /* ! DEBUG */ STATIC int xfs_remove( bhv_desc_t *dir_bdp, - vname_t *dentry, + bhv_vname_t *dentry, cred_t *credp) { - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; char *name = VNAME(dentry); xfs_inode_t *dp, *ip; xfs_trans_t *tp = NULL; @@ -2448,8 +2464,8 @@ xfs_remove( * Entry must exist since we did a lookup in xfs_lock_dir_and_entry. */ XFS_BMAP_INIT(&free_list, &first_block); - error = XFS_DIR_REMOVENAME(mp, tp, dp, name, namelen, ip->i_ino, - &first_block, &free_list, 0); + error = xfs_dir_removename(tp, dp, name, namelen, ip->i_ino, + &first_block, &free_list, 0); if (error) { ASSERT(error != ENOENT); REMOVE_DEBUG_TRACE(__LINE__); @@ -2511,7 +2527,7 @@ xfs_remove( /* * Let interposed file systems know about removed links. */ - VOP_LINK_REMOVED(XFS_ITOV(ip), dir_vp, link_zero); + bhv_vop_link_removed(XFS_ITOV(ip), dir_vp, link_zero); IRELE(ip); @@ -2564,8 +2580,8 @@ xfs_remove( STATIC int xfs_link( bhv_desc_t *target_dir_bdp, - vnode_t *src_vp, - vname_t *dentry, + bhv_vnode_t *src_vp, + bhv_vname_t *dentry, cred_t *credp) { xfs_inode_t *tdp, *sip; @@ -2577,7 +2593,7 @@ xfs_link( xfs_fsblock_t first_block; int cancel_flags; int committed; - vnode_t *target_dir_vp; + bhv_vnode_t *target_dir_vp; int resblks; char *target_name = VNAME(dentry); int target_namelen; @@ -2587,8 +2603,7 @@ xfs_link( vn_trace_entry(src_vp, __FUNCTION__, (inst_t *)__return_address); target_namelen = VNAMELEN(dentry); - if (VN_ISDIR(src_vp)) - return XFS_ERROR(EPERM); + ASSERT(!VN_ISDIR(src_vp)); sip = xfs_vtoi(src_vp); tdp = XFS_BHVTOI(target_dir_bdp); @@ -2668,13 +2683,12 @@ xfs_link( } if (resblks == 0 && - (error = XFS_DIR_CANENTER(mp, tp, tdp, target_name, - target_namelen))) + (error = xfs_dir_canenter(tp, tdp, target_name, target_namelen))) goto error_return; XFS_BMAP_INIT(&free_list, &first_block); - error = XFS_DIR_CREATENAME(mp, tp, tdp, target_name, target_namelen, + error = xfs_dir_createname(tp, tdp, target_name, target_namelen, sip->i_ino, &first_block, &free_list, resblks); if (error) @@ -2684,9 +2698,8 @@ xfs_link( xfs_trans_log_inode(tp, tdp, XFS_ILOG_CORE); error = xfs_bumplink(tp, sip); - if (error) { + if (error) goto abort_return; - } /* * If this is a synchronous mount, make sure that the @@ -2704,9 +2717,8 @@ xfs_link( } error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, NULL); - if (error) { + if (error) goto std_return; - } /* Fall through to std_return with error = 0. */ std_return: @@ -2727,6 +2739,8 @@ std_return: xfs_trans_cancel(tp, cancel_flags); goto std_return; } + + /* * xfs_mkdir * @@ -2734,15 +2748,15 @@ std_return: STATIC int xfs_mkdir( bhv_desc_t *dir_bdp, - vname_t *dentry, - vattr_t *vap, - vnode_t **vpp, + bhv_vname_t *dentry, + bhv_vattr_t *vap, + bhv_vnode_t **vpp, cred_t *credp) { char *dir_name = VNAME(dentry); xfs_inode_t *dp; xfs_inode_t *cdp; /* inode of created dir */ - vnode_t *cvp; /* vnode of created dir */ + bhv_vnode_t *cvp; /* vnode of created dir */ xfs_trans_t *tp; xfs_mount_t *mp; int cancel_flags; @@ -2750,7 +2764,7 @@ xfs_mkdir( int committed; xfs_bmap_free_t free_list; xfs_fsblock_t first_block; - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; boolean_t dp_joined_to_trans; boolean_t created = B_FALSE; int dm_event_sent = 0; @@ -2840,7 +2854,7 @@ xfs_mkdir( goto error_return; if (resblks == 0 && - (error = XFS_DIR_CANENTER(mp, tp, dp, dir_name, dir_namelen))) + (error = xfs_dir_canenter(tp, dp, dir_name, dir_namelen))) goto error_return; /* * create the directory inode. @@ -2867,9 +2881,9 @@ xfs_mkdir( XFS_BMAP_INIT(&free_list, &first_block); - error = XFS_DIR_CREATENAME(mp, tp, dp, dir_name, dir_namelen, - cdp->i_ino, &first_block, &free_list, - resblks ? resblks - XFS_IALLOC_SPACE_RES(mp) : 0); + error = xfs_dir_createname(tp, dp, dir_name, dir_namelen, cdp->i_ino, + &first_block, &free_list, resblks ? + resblks - XFS_IALLOC_SPACE_RES(mp) : 0); if (error) { ASSERT(error != ENOSPC); goto error1; @@ -2883,16 +2897,14 @@ xfs_mkdir( */ dp->i_gen++; - error = XFS_DIR_INIT(mp, tp, cdp, dp); - if (error) { + error = xfs_dir_init(tp, cdp, dp); + if (error) goto error2; - } cdp->i_gen = 1; error = xfs_bumplink(tp, dp); - if (error) { + if (error) goto error2; - } cvp = XFS_ITOV(cdp); @@ -2969,7 +2981,7 @@ std_return: STATIC int xfs_rmdir( bhv_desc_t *dir_bdp, - vname_t *dentry, + bhv_vname_t *dentry, cred_t *credp) { char *name = VNAME(dentry); @@ -2982,7 +2994,7 @@ xfs_rmdir( xfs_fsblock_t first_block; int cancel_flags; int committed; - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; int dm_di_mode = 0; int last_cdp_link; int namelen; @@ -3101,16 +3113,15 @@ xfs_rmdir( error = XFS_ERROR(ENOTEMPTY); goto error_return; } - if (!XFS_DIR_ISEMPTY(mp, cdp)) { + if (!xfs_dir_isempty(cdp)) { error = XFS_ERROR(ENOTEMPTY); goto error_return; } - error = XFS_DIR_REMOVENAME(mp, tp, dp, name, namelen, cdp->i_ino, - &first_block, &free_list, resblks); - if (error) { + error = xfs_dir_removename(tp, dp, name, namelen, cdp->i_ino, + &first_block, &free_list, resblks); + if (error) goto error1; - } xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); @@ -3181,7 +3192,7 @@ xfs_rmdir( /* * Let interposed file systems know about removed links. */ - VOP_LINK_REMOVED(XFS_ITOV(cdp), dir_vp, last_cdp_link); + bhv_vop_link_removed(XFS_ITOV(cdp), dir_vp, last_cdp_link); IRELE(cdp); @@ -3209,8 +3220,6 @@ xfs_rmdir( /* - * xfs_readdir - * * Read dp's entries starting at uiop->uio_offset and translate them into * bufsize bytes worth of struct dirents starting at bufbase. */ @@ -3230,28 +3239,23 @@ xfs_readdir( (inst_t *)__return_address); dp = XFS_BHVTOI(dir_bdp); - if (XFS_FORCED_SHUTDOWN(dp->i_mount)) { + if (XFS_FORCED_SHUTDOWN(dp->i_mount)) return XFS_ERROR(EIO); - } lock_mode = xfs_ilock_map_shared(dp); - error = XFS_DIR_GETDENTS(dp->i_mount, tp, dp, uiop, eofp); + error = xfs_dir_getdents(tp, dp, uiop, eofp); xfs_iunlock_map_shared(dp, lock_mode); return error; } -/* - * xfs_symlink - * - */ STATIC int xfs_symlink( bhv_desc_t *dir_bdp, - vname_t *dentry, - vattr_t *vap, + bhv_vname_t *dentry, + bhv_vattr_t *vap, char *target_path, - vnode_t **vpp, + bhv_vnode_t **vpp, cred_t *credp) { xfs_trans_t *tp; @@ -3263,7 +3267,7 @@ xfs_symlink( xfs_bmap_free_t free_list; xfs_fsblock_t first_block; boolean_t dp_joined_to_trans; - vnode_t *dir_vp; + bhv_vnode_t *dir_vp; uint cancel_flags; int committed; xfs_fileoff_t first_fsb; @@ -3308,7 +3312,7 @@ xfs_symlink( int len, total; char *path; - for(total = 0, path = target_path; total < pathlen;) { + for (total = 0, path = target_path; total < pathlen;) { /* * Skip any slashes. */ @@ -3402,7 +3406,7 @@ xfs_symlink( * Check for ability to enter directory entry, if no space reserved. */ if (resblks == 0 && - (error = XFS_DIR_CANENTER(mp, tp, dp, link_name, link_namelen))) + (error = xfs_dir_canenter(tp, dp, link_name, link_namelen))) goto error_return; /* * Initialize the bmap freelist prior to calling either @@ -3457,7 +3461,7 @@ xfs_symlink( error = xfs_bmapi(tp, ip, first_fsb, fs_blocks, XFS_BMAPI_WRITE | XFS_BMAPI_METADATA, &first_block, resblks, mval, &nmaps, - &free_list); + &free_list, NULL); if (error) { goto error1; } @@ -3489,11 +3493,10 @@ xfs_symlink( /* * Create the directory entry for the symlink. */ - error = XFS_DIR_CREATENAME(mp, tp, dp, link_name, link_namelen, - ip->i_ino, &first_block, &free_list, resblks); - if (error) { + error = xfs_dir_createname(tp, dp, link_name, link_namelen, ip->i_ino, + &first_block, &free_list, resblks); + if (error) goto error1; - } xfs_ichgtime(dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); @@ -3541,7 +3544,7 @@ std_return: } if (!error) { - vnode_t *vp; + bhv_vnode_t *vp; ASSERT(ip); vp = XFS_ITOV(ip); @@ -3606,10 +3609,10 @@ xfs_fid2( int xfs_rwlock( bhv_desc_t *bdp, - vrwlock_t locktype) + bhv_vrwlock_t locktype) { xfs_inode_t *ip; - vnode_t *vp; + bhv_vnode_t *vp; vp = BHV_TO_VNODE(bdp); if (VN_ISDIR(vp)) @@ -3637,10 +3640,10 @@ xfs_rwlock( void xfs_rwunlock( bhv_desc_t *bdp, - vrwlock_t locktype) + bhv_vrwlock_t locktype) { xfs_inode_t *ip; - vnode_t *vp; + bhv_vnode_t *vp; vp = BHV_TO_VNODE(bdp); if (VN_ISDIR(vp)) @@ -3744,7 +3747,6 @@ xfs_inode_flush( return error; } - int xfs_set_dmattrs ( bhv_desc_t *bdp, @@ -3785,16 +3787,12 @@ xfs_set_dmattrs ( return error; } - -/* - * xfs_reclaim - */ STATIC int xfs_reclaim( bhv_desc_t *bdp) { xfs_inode_t *ip; - vnode_t *vp; + bhv_vnode_t *vp; vp = BHV_TO_VNODE(bdp); ip = XFS_BHVTOI(bdp); @@ -3849,7 +3847,7 @@ xfs_finish_reclaim( int sync_mode) { xfs_ihash_t *ih = ip->i_hash; - vnode_t *vp = XFS_ITOV_NULL(ip); + bhv_vnode_t *vp = XFS_ITOV_NULL(ip); int error; if (vp && VN_BAD(vp)) @@ -4116,10 +4114,10 @@ retry: * Issue the xfs_bmapi() call to allocate the blocks */ XFS_BMAP_INIT(&free_list, &firstfsb); - error = xfs_bmapi(tp, ip, startoffset_fsb, + error = XFS_BMAPI(mp, tp, &ip->i_iocore, startoffset_fsb, allocatesize_fsb, bmapi_flag, &firstfsb, 0, imapp, &nimaps, - &free_list); + &free_list, NULL); if (error) { goto error0; } @@ -4199,8 +4197,8 @@ xfs_zero_remaining_bytes( for (offset = startoff; offset <= endoff; offset = lastoffset + 1) { offset_fsb = XFS_B_TO_FSBT(mp, offset); nimap = 1; - error = xfs_bmapi(NULL, ip, offset_fsb, 1, 0, NULL, 0, &imap, - &nimap, NULL); + error = XFS_BMAPI(mp, NULL, &ip->i_iocore, offset_fsb, 1, 0, + NULL, 0, &imap, &nimap, NULL, NULL); if (error || nimap < 1) break; ASSERT(imap.br_blockcount >= 1); @@ -4259,7 +4257,7 @@ xfs_free_file_space( xfs_off_t len, int attr_flags) { - vnode_t *vp; + bhv_vnode_t *vp; int committed; int done; xfs_off_t end_dmi_offset; @@ -4308,7 +4306,6 @@ xfs_free_file_space( return error; } - ASSERT(attr_flags & ATTR_NOLOCK ? attr_flags & ATTR_DMI : 1); if (attr_flags & ATTR_NOLOCK) need_iolock = 0; if (need_iolock) { @@ -4326,7 +4323,7 @@ xfs_free_file_space( if (VN_CACHED(vp) != 0) { xfs_inval_cached_trace(&ip->i_iocore, ioffset, -1, ctooff(offtoct(ioffset)), -1); - VOP_FLUSHINVAL_PAGES(vp, ctooff(offtoct(ioffset)), + bhv_vop_flushinval_pages(vp, ctooff(offtoct(ioffset)), -1, FI_REMAPF_LOCKED); } @@ -4338,8 +4335,8 @@ xfs_free_file_space( */ if (rt && !XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb)) { nimap = 1; - error = xfs_bmapi(NULL, ip, startoffset_fsb, 1, 0, NULL, 0, - &imap, &nimap, NULL); + error = XFS_BMAPI(mp, NULL, &ip->i_iocore, startoffset_fsb, + 1, 0, NULL, 0, &imap, &nimap, NULL, NULL); if (error) goto out_unlock_iolock; ASSERT(nimap == 0 || nimap == 1); @@ -4353,8 +4350,8 @@ xfs_free_file_space( startoffset_fsb += mp->m_sb.sb_rextsize - mod; } nimap = 1; - error = xfs_bmapi(NULL, ip, endoffset_fsb - 1, 1, 0, NULL, 0, - &imap, &nimap, NULL); + error = XFS_BMAPI(mp, NULL, &ip->i_iocore, endoffset_fsb - 1, + 1, 0, NULL, 0, &imap, &nimap, NULL, NULL); if (error) goto out_unlock_iolock; ASSERT(nimap == 0 || nimap == 1); @@ -4426,9 +4423,9 @@ xfs_free_file_space( * issue the bunmapi() call to free the blocks */ XFS_BMAP_INIT(&free_list, &firstfsb); - error = xfs_bunmapi(tp, ip, startoffset_fsb, + error = XFS_BUNMAPI(mp, tp, &ip->i_iocore, startoffset_fsb, endoffset_fsb - startoffset_fsb, - 0, 2, &firstfsb, &free_list, &done); + 0, 2, &firstfsb, &free_list, NULL, &done); if (error) { goto error0; } @@ -4488,8 +4485,8 @@ xfs_change_file_space( xfs_off_t startoffset; xfs_off_t llen; xfs_trans_t *tp; - vattr_t va; - vnode_t *vp; + bhv_vattr_t va; + bhv_vnode_t *vp; vp = BHV_TO_VNODE(bdp); vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address); @@ -4642,9 +4639,10 @@ xfs_change_file_space( return error; } -vnodeops_t xfs_vnodeops = { +bhv_vnodeops_t xfs_vnodeops = { BHV_IDENTITY_INIT(VN_BHV_XFS,VNODE_POSITION_XFS), .vop_open = xfs_open, + .vop_close = xfs_close, .vop_read = xfs_read, #ifdef HAVE_SENDFILE .vop_sendfile = xfs_sendfile, diff --git a/include/Kbuild b/include/Kbuild new file mode 100644 index 0000000..cb25348 --- /dev/null +++ b/include/Kbuild @@ -0,0 +1,2 @@ +header-y += asm-generic/ linux/ scsi/ sound/ mtd/ rdma/ video/ +header-y += asm-$(ARCH)/ diff --git a/include/acpi/acconfig.h b/include/acpi/acconfig.h index e27dc8f..b492857 100644 --- a/include/acpi/acconfig.h +++ b/include/acpi/acconfig.h @@ -63,7 +63,7 @@ #define _ACCONFIG_H /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20060127 +#define ACPI_CA_VERSION 0x20060623 /* * OS name, used for the _OS object. The _OS object is essentially obsolete, @@ -81,6 +81,7 @@ #define ACPI_MAX_STATE_CACHE_DEPTH #define ACPI_MAX_PARSE_CACHE_DEPTH 96 /* Parse tree objects */ #define ACPI_MAX_EXTPARSE_CACHE_DEPTH 96 /* Parse tree objects */ #define ACPI_MAX_OBJECT_CACHE_DEPTH 96 /* Interpreter operand objects */ +#define ACPI_MAX_NAMESPACE_CACHE_DEPTH 96 /* Namespace objects */ /* * Should the subsystem abort the loading of an ACPI table if the @@ -102,9 +103,9 @@ #define ACPI_CA_SUPPORT_LEVEL #define ACPI_MAX_SEMAPHORE_COUNT 256 -/* Max reference count (for debug only) */ +/* Maximum object reference count (detects object deletion issues) */ -#define ACPI_MAX_REFERENCE_COUNT 0x400 +#define ACPI_MAX_REFERENCE_COUNT 0x800 /* Size of cached memory mapping for system memory operation region */ @@ -171,12 +172,7 @@ #define ACPI_MAX_ADDRESS_SPACE /* Array sizes. Used for range checking also */ -#define ACPI_NUM_ACCESS_TYPES 6 -#define ACPI_NUM_UPDATE_RULES 3 -#define ACPI_NUM_LOCK_RULES 2 -#define ACPI_NUM_MATCH_OPS 6 -#define ACPI_NUM_OPCODES 256 -#define ACPI_NUM_FIELD_NAMES 2 +#define ACPI_MAX_MATCH_OPCODE 5 /* RSDP checksums */ @@ -187,10 +183,6 @@ #define ACPI_RSDP_XCHECKSUM_LENGTH #define ACPI_SMBUS_BUFFER_SIZE 34 -/* Number of strings associated with the _OSI reserved method */ - -#define ACPI_NUM_OSI_STRINGS 10 - /****************************************************************************** * * ACPI AML Debugger diff --git a/include/acpi/acdisasm.h b/include/acpi/acdisasm.h index 11a8fe3..9a7d692 100644 --- a/include/acpi/acdisasm.h +++ b/include/acpi/acdisasm.h @@ -50,26 +50,72 @@ #define BLOCK_NONE 0 #define BLOCK_PAREN 1 #define BLOCK_BRACE 2 #define BLOCK_COMMA_LIST 4 +#define ACPI_DEFAULT_RESNAME *(u32 *) "__RD" struct acpi_external_list { char *path; + char *internal_path; struct acpi_external_list *next; + u32 value; + u16 length; + u8 type; }; extern struct acpi_external_list *acpi_gbl_external_list; -/* Strings used for decoding flags to ASL keywords */ +typedef const struct acpi_dmtable_info { + u8 opcode; + u8 offset; + char *name; + +} acpi_dmtable_info; + +/* + * Values for Opcode above. + * Note: 0-7 must not change, used as a flag shift value + */ +#define ACPI_DMT_FLAG0 0 +#define ACPI_DMT_FLAG1 1 +#define ACPI_DMT_FLAG2 2 +#define ACPI_DMT_FLAG3 3 +#define ACPI_DMT_FLAG4 4 +#define ACPI_DMT_FLAG5 5 +#define ACPI_DMT_FLAG6 6 +#define ACPI_DMT_FLAG7 7 +#define ACPI_DMT_FLAGS0 8 +#define ACPI_DMT_FLAGS2 9 +#define ACPI_DMT_UINT8 10 +#define ACPI_DMT_UINT16 11 +#define ACPI_DMT_UINT24 12 +#define ACPI_DMT_UINT32 13 +#define ACPI_DMT_UINT56 14 +#define ACPI_DMT_UINT64 15 +#define ACPI_DMT_STRING 16 +#define ACPI_DMT_NAME4 17 +#define ACPI_DMT_NAME6 18 +#define ACPI_DMT_NAME8 19 +#define ACPI_DMT_CHKSUM 20 +#define ACPI_DMT_SPACEID 21 +#define ACPI_DMT_GAS 22 +#define ACPI_DMT_MADT 23 +#define ACPI_DMT_SRAT 24 +#define ACPI_DMT_EXIT 25 -extern const char *acpi_gbl_word_decode[4]; -extern const char *acpi_gbl_irq_decode[2]; -extern const char *acpi_gbl_lock_rule[ACPI_NUM_LOCK_RULES]; -extern const char *acpi_gbl_access_types[ACPI_NUM_ACCESS_TYPES]; -extern const char *acpi_gbl_update_rules[ACPI_NUM_UPDATE_RULES]; -extern const char *acpi_gbl_match_ops[ACPI_NUM_MATCH_OPS]; +typedef +void (*ACPI_TABLE_HANDLER) (struct acpi_table_header * table); + +struct acpi_dmtable_data { + char *signature; + struct acpi_dmtable_info *table_info; + ACPI_TABLE_HANDLER table_handler; +}; struct acpi_op_walk_info { u32 level; + u32 last_level; + u32 count; u32 bit_offset; + u32 flags; struct acpi_walk_state *walk_state; }; @@ -77,6 +123,100 @@ typedef acpi_status(*asl_walk_callback) (union acpi_parse_object * op, u32 level, void *context); +struct acpi_resource_tag { + u32 bit_index; + char *tag; +}; + +/* Strings used for decoding flags to ASL keywords */ + +extern const char *acpi_gbl_word_decode[]; +extern const char *acpi_gbl_irq_decode[]; +extern const char *acpi_gbl_lock_rule[]; +extern const char *acpi_gbl_access_types[]; +extern const char *acpi_gbl_update_rules[]; +extern const char *acpi_gbl_match_ops[]; + +extern struct acpi_dmtable_info acpi_dm_table_info_asf0[]; +extern struct acpi_dmtable_info acpi_dm_table_info_asf1[]; +extern struct acpi_dmtable_info acpi_dm_table_info_asf2[]; +extern struct acpi_dmtable_info acpi_dm_table_info_asf3[]; +extern struct acpi_dmtable_info acpi_dm_table_info_asf4[]; +extern struct acpi_dmtable_info acpi_dm_table_info_asf_hdr[]; +extern struct acpi_dmtable_info acpi_dm_table_info_boot[]; +extern struct acpi_dmtable_info acpi_dm_table_info_cpep[]; +extern struct acpi_dmtable_info acpi_dm_table_info_cpep0[]; +extern struct acpi_dmtable_info acpi_dm_table_info_dbgp[]; +extern struct acpi_dmtable_info acpi_dm_table_info_ecdt[]; +extern struct acpi_dmtable_info acpi_dm_table_info_facs[]; +extern struct acpi_dmtable_info acpi_dm_table_info_fadt1[]; +extern struct acpi_dmtable_info acpi_dm_table_info_fadt2[]; +extern struct acpi_dmtable_info acpi_dm_table_info_gas[]; +extern struct acpi_dmtable_info acpi_dm_table_info_header[]; +extern struct acpi_dmtable_info acpi_dm_table_info_hpet[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt0[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt1[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt2[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt3[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt4[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt5[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt6[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt7[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt8[]; +extern struct acpi_dmtable_info acpi_dm_table_info_madt_hdr[]; +extern struct acpi_dmtable_info acpi_dm_table_info_mcfg[]; +extern struct acpi_dmtable_info acpi_dm_table_info_mcfg0[]; +extern struct acpi_dmtable_info acpi_dm_table_info_rsdp1[]; +extern struct acpi_dmtable_info acpi_dm_table_info_rsdp2[]; +extern struct acpi_dmtable_info acpi_dm_table_info_sbst[]; +extern struct acpi_dmtable_info acpi_dm_table_info_slit[]; +extern struct acpi_dmtable_info acpi_dm_table_info_spcr[]; +extern struct acpi_dmtable_info acpi_dm_table_info_spmi[]; +extern struct acpi_dmtable_info acpi_dm_table_info_srat[]; +extern struct acpi_dmtable_info acpi_dm_table_info_srat0[]; +extern struct acpi_dmtable_info acpi_dm_table_info_srat1[]; +extern struct acpi_dmtable_info acpi_dm_table_info_tcpa[]; +extern struct acpi_dmtable_info acpi_dm_table_info_wdrt[]; + +/* + * dmtable + */ +void acpi_dm_dump_data_table(struct acpi_table_header *table); + +void +acpi_dm_dump_table(u32 table_length, + u32 table_offset, + void *table, + u32 sub_table_length, struct acpi_dmtable_info *info); + +void acpi_dm_line_header(u32 offset, u32 byte_length, char *name); + +void acpi_dm_line_header2(u32 offset, u32 byte_length, char *name, u32 value); + +/* + * dmtbdump + */ +void acpi_dm_dump_asf(struct acpi_table_header *table); + +void acpi_dm_dump_cpep(struct acpi_table_header *table); + +void acpi_dm_dump_fadt(struct acpi_table_header *table); + +void acpi_dm_dump_srat(struct acpi_table_header *table); + +void acpi_dm_dump_mcfg(struct acpi_table_header *table); + +void acpi_dm_dump_madt(struct acpi_table_header *table); + +u32 acpi_dm_dump_rsdp(struct acpi_table_header *table); + +void acpi_dm_dump_rsdt(struct acpi_table_header *table); + +void acpi_dm_dump_slit(struct acpi_table_header *table); + +void acpi_dm_dump_xsdt(struct acpi_table_header *table); + /* * dmwalk */ @@ -84,6 +224,11 @@ void acpi_dm_disassemble(struct acpi_walk_state *walk_state, union acpi_parse_object *origin, u32 num_opcodes); +void +acpi_dm_walk_parse_tree(union acpi_parse_object *op, + asl_walk_callback descending_callback, + asl_walk_callback ascending_callback, void *context); + /* * dmopcode */ @@ -166,6 +311,7 @@ void acpi_dm_dump_integer64(u64 value, c void acpi_dm_resource_template(struct acpi_op_walk_info *info, + union acpi_parse_object *op, u8 * byte_data, u32 byte_count); u8 acpi_dm_is_resource_template(union acpi_parse_object *op); @@ -176,6 +322,8 @@ void acpi_dm_bit_list(u16 mask); void acpi_dm_decode_attribute(u8 attribute); +void acpi_dm_descriptor_name(void); + /* * dmresrcl */ @@ -248,6 +396,15 @@ acpi_dm_vendor_small_descriptor(union am /* * dmutils */ -void acpi_dm_add_to_external_list(char *path); +void acpi_dm_add_to_external_list(char *path, u8 type, u32 value); + +/* + * dmrestag + */ +void acpi_dm_find_resources(union acpi_parse_object *root); + +void +acpi_dm_check_resource_reference(union acpi_parse_object *op, + struct acpi_walk_state *walk_state); #endif /* __ACDISASM_H__ */ diff --git a/include/acpi/acdispat.h b/include/acpi/acdispat.h index c41a926..a22fe9c 100644 --- a/include/acpi/acdispat.h +++ b/include/acpi/acdispat.h @@ -194,12 +194,14 @@ acpi_status acpi_ds_restart_control_method(struct acpi_walk_state *walk_state, union acpi_operand_object *return_desc); -void acpi_ds_terminate_control_method(struct acpi_walk_state *walk_state); +void +acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, + struct acpi_walk_state *walk_state); acpi_status acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, union acpi_operand_object *obj_desc, - struct acpi_namespace_node *calling_method_node); + struct acpi_walk_state *walk_state); acpi_status acpi_ds_method_error(acpi_status status, struct acpi_walk_state *walk_state); @@ -302,7 +304,7 @@ acpi_ds_init_aml_walk(struct acpi_walk_s struct acpi_namespace_node *method_node, u8 * aml_start, u32 aml_length, - struct acpi_parameter_info *info, u8 pass_number); + struct acpi_evaluate_info *info, u8 pass_number); acpi_status acpi_ds_obj_stack_pop_and_delete(u32 pop_count, diff --git a/include/acpi/acevents.h b/include/acpi/acevents.h index f2717be..2341428 100644 --- a/include/acpi/acevents.h +++ b/include/acpi/acevents.h @@ -93,7 +93,7 @@ struct acpi_gpe_event_info *acpi_ev_get_ */ u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info); -acpi_status acpi_ev_walk_gpe_list(ACPI_GPE_CALLBACK gpe_walk_callback); +acpi_status acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback); acpi_status acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, @@ -138,7 +138,7 @@ acpi_status acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, u32 function, acpi_physical_address address, - u32 bit_width, void *value); + u32 bit_width, acpi_integer * value); acpi_status acpi_ev_attach_region(union acpi_operand_object *handler_obj, diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index dc768aa..797ca1e 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -160,8 +160,9 @@ #define AE_AML_NO_RESOURCE_END_TAG #define AE_AML_BAD_RESOURCE_VALUE (acpi_status) (0x001F | AE_CODE_AML) #define AE_AML_CIRCULAR_REFERENCE (acpi_status) (0x0020 | AE_CODE_AML) #define AE_AML_BAD_RESOURCE_LENGTH (acpi_status) (0x0021 | AE_CODE_AML) +#define AE_AML_ILLEGAL_ADDRESS (acpi_status) (0x0022 | AE_CODE_AML) -#define AE_CODE_AML_MAX 0x0021 +#define AE_CODE_AML_MAX 0x0022 /* * Internal exceptions used for control @@ -275,7 +276,8 @@ char const *acpi_gbl_exception_names_aml "AE_AML_NO_RESOURCE_END_TAG", "AE_AML_BAD_RESOURCE_VALUE", "AE_AML_CIRCULAR_REFERENCE", - "AE_AML_BAD_RESOURCE_LENGTH" + "AE_AML_BAD_RESOURCE_LENGTH", + "AE_AML_ILLEGAL_ADDRESS" }; char const *acpi_gbl_exception_names_ctrl[] = { diff --git a/include/acpi/acglobal.h b/include/acpi/acglobal.h index 734cc77..06972e6 100644 --- a/include/acpi/acglobal.h +++ b/include/acpi/acglobal.h @@ -107,6 +107,7 @@ ACPI_EXTERN u32 acpi_gbl_trace_flags; * 3) Allow access to uninitialized locals/args (auto-init to integer 0) * 4) Allow ANY object type to be a source operand for the Store() operator * 5) Allow unresolved references (invalid target name) in package objects + * 6) Enable warning messages for behavior that is not ACPI spec compliant */ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_enable_interpreter_slack, FALSE); @@ -114,7 +115,7 @@ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl * Automatically serialize ALL control methods? Default is FALSE, meaning * to use the Serialized/not_serialized method flags on a per method basis. * Only change this if the ASL code is poorly written and cannot handle - * reentrancy even though methods are marked "not_serialized". + * reentrancy even though methods are marked "NotSerialized". */ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_all_methods_serialized, FALSE); @@ -149,10 +150,10 @@ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl ACPI_EXTERN u32 acpi_gbl_table_flags; ACPI_EXTERN u32 acpi_gbl_rsdt_table_count; ACPI_EXTERN struct rsdp_descriptor *acpi_gbl_RSDP; -ACPI_EXTERN XSDT_DESCRIPTOR *acpi_gbl_XSDT; -ACPI_EXTERN FADT_DESCRIPTOR *acpi_gbl_FADT; +ACPI_EXTERN struct xsdt_descriptor *acpi_gbl_XSDT; +ACPI_EXTERN struct fadt_descriptor *acpi_gbl_FADT; ACPI_EXTERN struct acpi_table_header *acpi_gbl_DSDT; -ACPI_EXTERN FACS_DESCRIPTOR *acpi_gbl_FACS; +ACPI_EXTERN struct facs_descriptor *acpi_gbl_FACS; ACPI_EXTERN struct acpi_common_facs acpi_gbl_common_fACS; /* * Since there may be multiple SSDTs and PSDTs, a single pointer is not @@ -177,15 +178,35 @@ ACPI_EXTERN u8 acpi_gbl_integer_nybble_w /* * ACPI Table info arrays */ -extern struct acpi_table_list acpi_gbl_table_lists[NUM_ACPI_TABLE_TYPES]; -extern struct acpi_table_support acpi_gbl_table_data[NUM_ACPI_TABLE_TYPES]; +extern struct acpi_table_list acpi_gbl_table_lists[ACPI_TABLE_ID_MAX + 1]; +extern struct acpi_table_support acpi_gbl_table_data[ACPI_TABLE_ID_MAX + 1]; + +/***************************************************************************** + * + * Mutual exlusion within ACPICA subsystem + * + ****************************************************************************/ /* * Predefined mutex objects. This array contains the * actual OS mutex handles, indexed by the local ACPI_MUTEX_HANDLEs. * (The table maps local handles to the real OS handles) */ -ACPI_EXTERN struct acpi_mutex_info acpi_gbl_mutex_info[NUM_MUTEX]; +ACPI_EXTERN struct acpi_mutex_info acpi_gbl_mutex_info[ACPI_NUM_MUTEX]; + +/* + * Global lock semaphore works in conjunction with the actual HW global lock + */ +ACPI_EXTERN acpi_semaphore acpi_gbl_global_lock_semaphore; + +/* + * Spinlocks are used for interfaces that can be possibly called at + * interrupt level + */ +ACPI_EXTERN spinlock_t _acpi_gbl_gpe_lock; /* For GPE data structs and registers */ +ACPI_EXTERN spinlock_t _acpi_gbl_hardware_lock; /* For ACPI H/W except GPE registers */ +#define acpi_gbl_gpe_lock &_acpi_gbl_gpe_lock +#define acpi_gbl_hardware_lock &_acpi_gbl_hardware_lock /***************************************************************************** * @@ -203,6 +224,7 @@ #endif /* Object caches */ +ACPI_EXTERN acpi_cache_t *acpi_gbl_namespace_cache; ACPI_EXTERN acpi_cache_t *acpi_gbl_state_cache; ACPI_EXTERN acpi_cache_t *acpi_gbl_ps_node_cache; ACPI_EXTERN acpi_cache_t *acpi_gbl_ps_node_ext_cache; @@ -215,7 +237,6 @@ ACPI_EXTERN struct acpi_object_notify_ha ACPI_EXTERN acpi_exception_handler acpi_gbl_exception_handler; ACPI_EXTERN acpi_init_handler acpi_gbl_init_handler; ACPI_EXTERN struct acpi_walk_state *acpi_gbl_breakpoint_walk; -ACPI_EXTERN acpi_handle acpi_gbl_global_lock_semaphore; /* Misc */ @@ -244,7 +265,6 @@ extern const char *acpi_gbl_sleep_state_ extern const char *acpi_gbl_highest_dstate_names[4]; extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; -extern const char *acpi_gbl_valid_osi_strings[ACPI_NUM_OSI_STRINGS]; /***************************************************************************** * @@ -291,14 +311,6 @@ ACPI_EXTERN u8 acpi_gbl_cm_single_step; /***************************************************************************** * - * Parser globals - * - ****************************************************************************/ - -ACPI_EXTERN union acpi_parse_object *acpi_gbl_parsed_namespace_root; - -/***************************************************************************** - * * Hardware globals * ****************************************************************************/ @@ -321,7 +333,6 @@ ACPI_EXTERN struct acpi_fixed_event_hand ACPI_EXTERN struct acpi_gpe_xrupt_info *acpi_gbl_gpe_xrupt_list_head; ACPI_EXTERN struct acpi_gpe_block_info *acpi_gbl_gpe_fadt_blocks[ACPI_MAX_GPE_BLOCKS]; -ACPI_EXTERN acpi_handle acpi_gbl_gpe_lock; /***************************************************************************** * diff --git a/include/acpi/acinterp.h b/include/acpi/acinterp.h index 9f22cfc..216339a 100644 --- a/include/acpi/acinterp.h +++ b/include/acpi/acinterp.h @@ -287,7 +287,10 @@ acpi_ex_system_wait_event(union acpi_ope acpi_status acpi_ex_system_reset_event(union acpi_operand_object *obj_desc); -acpi_status acpi_ex_system_wait_semaphore(acpi_handle semaphore, u16 timeout); +acpi_status +acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout); + +acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout); /* * exoparg1 - ACPI AML execution, 1 operand diff --git a/include/acpi/aclocal.h b/include/acpi/aclocal.h index 8361820..56b8024 100644 --- a/include/acpi/aclocal.h +++ b/include/acpi/aclocal.h @@ -44,10 +44,14 @@ #ifndef __ACLOCAL_H__ #define __ACLOCAL_H__ +/* acpisrc:struct_defs -- for acpisrc conversion */ + #define ACPI_WAIT_FOREVER 0xFFFF /* u16, as per ACPI spec */ +#define ACPI_DO_NOT_WAIT 0 +#define ACPI_SERIALIZED 0xFF -typedef void *acpi_mutex; typedef u32 acpi_mutex_handle; +#define ACPI_GLOBAL_LOCK (acpi_semaphore) (-1) /* Total number of aml opcodes defined */ @@ -69,52 +73,53 @@ union acpi_parse_object; * Predefined handles for the mutex objects used within the subsystem * All mutex objects are automatically created by acpi_ut_mutex_initialize. * - * The acquire/release ordering protocol is implied via this list. Mutexes + * The acquire/release ordering protocol is implied via this list. Mutexes * with a lower value must be acquired before mutexes with a higher value. * - * NOTE: any changes here must be reflected in the acpi_gbl_mutex_names table also! + * NOTE: any changes here must be reflected in the acpi_gbl_mutex_names + * table below also! */ -#define ACPI_MTX_EXECUTE 0 -#define ACPI_MTX_INTERPRETER 1 -#define ACPI_MTX_PARSER 2 -#define ACPI_MTX_DISPATCHER 3 -#define ACPI_MTX_TABLES 4 -#define ACPI_MTX_OP_REGIONS 5 -#define ACPI_MTX_NAMESPACE 6 -#define ACPI_MTX_EVENTS 7 -#define ACPI_MTX_HARDWARE 8 -#define ACPI_MTX_CACHES 9 -#define ACPI_MTX_MEMORY 10 -#define ACPI_MTX_DEBUG_CMD_COMPLETE 11 -#define ACPI_MTX_DEBUG_CMD_READY 12 - -#define MAX_MUTEX 12 -#define NUM_MUTEX MAX_MUTEX+1 +#define ACPI_MTX_INTERPRETER 0 /* AML Interpreter, main lock */ +#define ACPI_MTX_TABLES 1 /* Data for ACPI tables */ +#define ACPI_MTX_NAMESPACE 2 /* ACPI Namespace */ +#define ACPI_MTX_EVENTS 3 /* Data for ACPI events */ +#define ACPI_MTX_CACHES 4 /* Internal caches, general purposes */ +#define ACPI_MTX_MEMORY 5 /* Debug memory tracking lists */ +#define ACPI_MTX_DEBUG_CMD_COMPLETE 6 /* AML debugger */ +#define ACPI_MTX_DEBUG_CMD_READY 7 /* AML debugger */ + +#define ACPI_MAX_MUTEX 7 +#define ACPI_NUM_MUTEX ACPI_MAX_MUTEX+1 #if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) #ifdef DEFINE_ACPI_GLOBALS -/* Names for the mutexes used in the subsystem */ +/* Debug names for the mutexes above */ -static char *acpi_gbl_mutex_names[] = { - "ACPI_MTX_Execute", +static char *acpi_gbl_mutex_names[ACPI_NUM_MUTEX] = { "ACPI_MTX_Interpreter", - "ACPI_MTX_Parser", - "ACPI_MTX_Dispatcher", "ACPI_MTX_Tables", - "ACPI_MTX_op_regions", "ACPI_MTX_Namespace", "ACPI_MTX_Events", - "ACPI_MTX_Hardware", "ACPI_MTX_Caches", "ACPI_MTX_Memory", - "ACPI_MTX_debug_cmd_complete", - "ACPI_MTX_debug_cmd_ready", + "ACPI_MTX_CommandComplete", + "ACPI_MTX_CommandReady" }; #endif #endif +/* + * Predefined handles for spinlocks used within the subsystem. + * These spinlocks are created by acpi_ut_mutex_initialize + */ +#define ACPI_LOCK_GPES 0 +#define ACPI_LOCK_HARDWARE 1 + +#define ACPI_MAX_LOCK 1 +#define ACPI_NUM_LOCK ACPI_MAX_LOCK+1 + /* Owner IDs are used to track namespace nodes for selective deletion */ typedef u8 acpi_owner_id; @@ -129,7 +134,7 @@ #define ACPI_MUTEX_NOT_ACQUIRED struct acpi_mutex_info { acpi_mutex mutex; u32 use_count; - u32 thread_id; + acpi_thread_id thread_id; }; /* Lock flag parameter for various interfaces */ @@ -144,6 +149,8 @@ #define ACPI_FIELD_WORD_GRANULARITY #define ACPI_FIELD_DWORD_GRANULARITY 4 #define ACPI_FIELD_QWORD_GRANULARITY 8 +#define ACPI_ENTRY_NOT_FOUND NULL + /***************************************************************************** * * Namespace typedefs and structs @@ -158,49 +165,55 @@ typedef enum { ACPI_IMODE_EXECUTE = 0x0E } acpi_interpreter_mode; -/* - * The Node describes a named object that appears in the AML - * An acpi_node is used to store Nodes. - * - * data_type is used to differentiate between internal descriptors, and MUST - * be the first byte in this structure. - */ union acpi_name_union { u32 integer; char ascii[4]; }; +/* + * The Namespace Node describes a named object that appears in the AML. + * descriptor_type is used to differentiate between internal descriptors. + * + * The node is optimized for both 32-bit and 64-bit platforms: + * 20 bytes for the 32-bit case, 32 bytes for the 64-bit case. + * + * Note: The descriptor_type and Type fields must appear in the identical + * position in both the struct acpi_namespace_node and union acpi_operand_object + * structures. + */ struct acpi_namespace_node { - u8 descriptor; /* Used to differentiate object descriptor types */ - u8 type; /* Type associated with this name */ - u16 reference_count; /* Current count of references and children */ + union acpi_operand_object *object; /* Interpreter object */ + u8 descriptor_type; /* Differentiate object descriptor types */ + u8 type; /* ACPI Type associated with this name */ + u8 flags; /* Miscellaneous flags */ + acpi_owner_id owner_id; /* Node creator */ union acpi_name_union name; /* ACPI Name, always 4 chars per ACPI spec */ - union acpi_operand_object *object; /* Pointer to attached ACPI object (optional) */ struct acpi_namespace_node *child; /* First child */ - struct acpi_namespace_node *peer; /* Next peer */ - u8 owner_id; /* Who created this node */ - u8 flags; - - /* Fields used by the ASL compiler only */ + struct acpi_namespace_node *peer; /* Peer. Parent if ANOBJ_END_OF_PEER_LIST set */ -#ifdef ACPI_ASL_COMPILER - u32 value; + /* + * The following fields are used by the ASL compiler and disassembler only + */ +#ifdef ACPI_LARGE_NAMESPACE_NODE union acpi_parse_object *op; + u32 value; + u32 length; #endif }; -#define ACPI_ENTRY_NOT_FOUND NULL +/* Namespace Node flags */ -/* Node flags */ +#define ANOBJ_END_OF_PEER_LIST 0x01 /* End-of-list, Peer field points to parent */ +#define ANOBJ_DATA_WIDTH_32 0x02 /* Parent table uses 32-bit math */ +#define ANOBJ_METHOD_ARG 0x04 /* Node is a method argument */ +#define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */ +#define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */ -#define ANOBJ_RESERVED 0x01 -#define ANOBJ_END_OF_PEER_LIST 0x02 -#define ANOBJ_DATA_WIDTH_32 0x04 /* Parent table is 64-bits */ -#define ANOBJ_METHOD_ARG 0x08 -#define ANOBJ_METHOD_LOCAL 0x10 -#define ANOBJ_METHOD_NO_RETVAL 0x20 -#define ANOBJ_METHOD_SOME_NO_RETVAL 0x40 -#define ANOBJ_IS_BIT_OFFSET 0x80 +#define ANOBJ_IS_EXTERNAL 0x08 /* i_aSL only: This object created via External() */ +#define ANOBJ_METHOD_NO_RETVAL 0x10 /* i_aSL only: Method has no return value */ +#define ANOBJ_METHOD_SOME_NO_RETVAL 0x20 /* i_aSL only: Method has at least one return value */ +#define ANOBJ_IS_BIT_OFFSET 0x40 /* i_aSL only: Reference is a bit offset */ +#define ANOBJ_IS_REFERENCED 0x80 /* i_aSL only: Object was referenced */ /* * ACPI Table Descriptor. One per ACPI table @@ -212,8 +225,8 @@ struct acpi_table_desc { struct acpi_table_header *pointer; u8 *aml_start; u64 physical_address; - u32 aml_length; acpi_size length; + u32 aml_length; acpi_owner_id owner_id; u8 type; u8 allocation; @@ -276,6 +289,9 @@ struct acpi_create_field_info { u8 field_type; }; +typedef +acpi_status(*ACPI_INTERNAL_METHOD) (struct acpi_walk_state * walk_state); + /* * Bitmapped ACPI types. Used internally only */ @@ -377,7 +393,7 @@ struct acpi_gpe_walk_info { struct acpi_gpe_block_info *gpe_block; }; -typedef acpi_status(*ACPI_GPE_CALLBACK) (struct acpi_gpe_xrupt_info * +typedef acpi_status(*acpi_gpe_callback) (struct acpi_gpe_xrupt_info * gpe_xrupt_info, struct acpi_gpe_block_info * gpe_block); @@ -416,13 +432,14 @@ #define ACPI_CONTROL_PREDICATE_EXECUTING #define ACPI_CONTROL_PREDICATE_FALSE 0xC3 #define ACPI_CONTROL_PREDICATE_TRUE 0xC4 -#define ACPI_STATE_COMMON /* Two 32-bit fields and a pointer */\ - u8 data_type; /* To differentiate various internal objs */\ - u8 flags; \ - u16 value; \ - u16 state; \ - u16 reserved; \ - void *next; +#define ACPI_STATE_COMMON \ + void *next; \ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 flags; \ + u16 value; \ + u16 state; + + /* There are 2 bytes available here until the next natural alignment boundary */ struct acpi_common_state { ACPI_STATE_COMMON}; @@ -438,12 +455,12 @@ struct acpi_update_state { * Pkg state - used to traverse nested package structures */ struct acpi_pkg_state { - ACPI_STATE_COMMON union acpi_operand_object *source_object; + ACPI_STATE_COMMON u16 index; + union acpi_operand_object *source_object; union acpi_operand_object *dest_object; struct acpi_walk_state *walk_state; void *this_target_obj; u32 num_packages; - u16 index; }; /* @@ -451,10 +468,10 @@ struct acpi_pkg_state { * Allows nesting of these constructs */ struct acpi_control_state { - ACPI_STATE_COMMON union acpi_parse_object *predicate_op; + ACPI_STATE_COMMON u16 opcode; + union acpi_parse_object *predicate_op; u8 *aml_predicate_start; /* Start of if/while predicate */ u8 *package_end; /* End of if/while block */ - u16 opcode; }; /* @@ -465,11 +482,11 @@ struct acpi_scope_state { }; struct acpi_pscope_state { - ACPI_STATE_COMMON union acpi_parse_object *op; /* Current op being parsed */ + ACPI_STATE_COMMON u32 arg_count; /* Number of fixed arguments */ + union acpi_parse_object *op; /* Current op being parsed */ u8 *arg_end; /* Current argument end */ u8 *pkg_end; /* Current package end */ u32 arg_list; /* Next argument to parse */ - u32 arg_count; /* Number of fixed arguments */ }; /* @@ -477,10 +494,10 @@ struct acpi_pscope_state { * states are created when there are nested control methods executing. */ struct acpi_thread_state { - ACPI_STATE_COMMON struct acpi_walk_state *walk_state_list; /* Head of list of walk_states for this thread */ + ACPI_STATE_COMMON u8 current_sync_level; /* Mutex Sync (nested acquire) level */ + struct acpi_walk_state *walk_state_list; /* Head of list of walk_states for this thread */ union acpi_operand_object *acquired_mutex_list; /* List of all currently acquired mutexes */ - u32 thread_id; /* Running thread ID */ - u8 current_sync_level; /* Mutex Sync (nested acquire) level */ + acpi_thread_id thread_id; /* Running thread ID */ }; /* @@ -488,10 +505,9 @@ struct acpi_thread_state { * AML arguments */ struct acpi_result_values { - ACPI_STATE_COMMON - union acpi_operand_object *obj_desc[ACPI_OBJ_NUM_OPERANDS]; - u8 num_results; + ACPI_STATE_COMMON u8 num_results; u8 last_insert; + union acpi_operand_object *obj_desc[ACPI_OBJ_NUM_OPERANDS]; }; typedef @@ -546,7 +562,7 @@ #if defined(ACPI_DISASSEMBLER) || define #endif u32 parse_args; /* Grammar/Parse time arguments */ u32 runtime_args; /* Interpret time arguments */ - u32 flags; /* Misc flags */ + u16 flags; /* Misc flags */ u8 object_type; /* Corresponding internal object type */ u8 class; /* Opcode class */ u8 type; /* Opcode type */ @@ -563,29 +579,31 @@ union acpi_parse_value { }; #define ACPI_PARSE_COMMON \ - u8 data_type; /* To differentiate various internal objs */\ - u8 flags; /* Type of Op */\ - u16 aml_opcode; /* AML opcode */\ - u32 aml_offset; /* Offset of declaration in AML */\ - union acpi_parse_object *parent; /* Parent op */\ - union acpi_parse_object *next; /* Next op */\ + union acpi_parse_object *parent; /* Parent op */\ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 flags; /* Type of Op */\ + u16 aml_opcode; /* AML opcode */\ + u32 aml_offset; /* Offset of declaration in AML */\ + union acpi_parse_object *next; /* Next op */\ + struct acpi_namespace_node *node; /* For use by interpreter */\ + union acpi_parse_value value; /* Value or args associated with the opcode */\ ACPI_DISASM_ONLY_MEMBERS (\ - u8 disasm_flags; /* Used during AML disassembly */\ - u8 disasm_opcode; /* Subtype used for disassembly */\ - char aml_op_name[16]) /* Op name (debug only) */\ - /* NON-DEBUG members below: */\ - struct acpi_namespace_node *node; /* For use by interpreter */\ - union acpi_parse_value value; /* Value or args associated with the opcode */ - -#define ACPI_DASM_BUFFER 0x00 -#define ACPI_DASM_RESOURCE 0x01 -#define ACPI_DASM_STRING 0x02 -#define ACPI_DASM_UNICODE 0x03 -#define ACPI_DASM_EISAID 0x04 -#define ACPI_DASM_MATCHOP 0x05 + u8 disasm_flags; /* Used during AML disassembly */\ + u8 disasm_opcode; /* Subtype used for disassembly */\ + char aml_op_name[16]) /* Op name (debug only) */ + +#define ACPI_DASM_BUFFER 0x00 +#define ACPI_DASM_RESOURCE 0x01 +#define ACPI_DASM_STRING 0x02 +#define ACPI_DASM_UNICODE 0x03 +#define ACPI_DASM_EISAID 0x04 +#define ACPI_DASM_MATCHOP 0x05 +#define ACPI_DASM_LNOT_PREFIX 0x06 +#define ACPI_DASM_LNOT_SUFFIX 0x07 +#define ACPI_DASM_IGNORE 0x08 /* - * generic operation (for example: If, While, Store) + * Generic operation (for example: If, While, Store) */ struct acpi_parse_obj_common { ACPI_PARSE_COMMON}; @@ -601,7 +619,7 @@ struct acpi_parse_obj_named { u32 name; /* 4-byte name or zero if no name */ }; -/* The parse node is the fundamental element of the parse tree */ +/* This version is used by the i_aSL compiler only */ #define ACPI_MAX_PARSEOP_NAME 20 @@ -643,7 +661,6 @@ union acpi_parse_object { * method. */ struct acpi_parse_state { - u32 aml_size; u8 *aml_start; /* First AML byte */ u8 *aml; /* Next AML byte */ u8 *aml_end; /* (last + 1) AML byte */ @@ -653,22 +670,23 @@ struct acpi_parse_state { struct acpi_namespace_node *start_node; union acpi_generic_state *scope; /* Current scope */ union acpi_parse_object *start_scope; + u32 aml_size; }; /* Parse object flags */ -#define ACPI_PARSEOP_GENERIC 0x01 -#define ACPI_PARSEOP_NAMED 0x02 -#define ACPI_PARSEOP_DEFERRED 0x04 -#define ACPI_PARSEOP_BYTELIST 0x08 -#define ACPI_PARSEOP_IN_CACHE 0x80 +#define ACPI_PARSEOP_GENERIC 0x01 +#define ACPI_PARSEOP_NAMED 0x02 +#define ACPI_PARSEOP_DEFERRED 0x04 +#define ACPI_PARSEOP_BYTELIST 0x08 +#define ACPI_PARSEOP_IN_CACHE 0x80 /* Parse object disasm_flags */ -#define ACPI_PARSEOP_IGNORE 0x01 -#define ACPI_PARSEOP_PARAMLIST 0x02 -#define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 -#define ACPI_PARSEOP_SPECIAL 0x10 +#define ACPI_PARSEOP_IGNORE 0x01 +#define ACPI_PARSEOP_PARAMLIST 0x02 +#define ACPI_PARSEOP_EMPTY_TERMLIST 0x04 +#define ACPI_PARSEOP_SPECIAL 0x10 /***************************************************************************** * @@ -676,8 +694,8 @@ #define ACPI_PARSEOP_SPECIAL * ****************************************************************************/ -#define PCI_ROOT_HID_STRING "PNP0A03" -#define PCI_EXPRESS_ROOT_HID_STRING "PNP0A08" +#define PCI_ROOT_HID_STRING "PNP0A03" +#define PCI_EXPRESS_ROOT_HID_STRING "PNP0A08" struct acpi_bit_register_info { u8 parent_register; @@ -686,6 +704,13 @@ struct acpi_bit_register_info { }; /* + * Some ACPI registers have bits that must be ignored -- meaning that they + * must be preserved. + */ +#define ACPI_PM1_STATUS_PRESERVED_BITS 0x0800 /* Bit 11 */ +#define ACPI_PM1_CONTROL_PRESERVED_BITS 0x0201 /* Bit 9, Bit 0 (SCI_EN) */ + +/* * Register IDs * These are the full ACPI registers */ @@ -710,13 +735,14 @@ #define ACPI_BITMASK_RT_CLOCK_STATUS #define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ #define ACPI_BITMASK_WAKE_STATUS 0x8000 -#define ACPI_BITMASK_ALL_FIXED_STATUS (ACPI_BITMASK_TIMER_STATUS | \ - ACPI_BITMASK_BUS_MASTER_STATUS | \ - ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ - ACPI_BITMASK_POWER_BUTTON_STATUS | \ - ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ - ACPI_BITMASK_RT_CLOCK_STATUS | \ - ACPI_BITMASK_WAKE_STATUS) +#define ACPI_BITMASK_ALL_FIXED_STATUS (\ + ACPI_BITMASK_TIMER_STATUS | \ + ACPI_BITMASK_BUS_MASTER_STATUS | \ + ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ + ACPI_BITMASK_POWER_BUTTON_STATUS | \ + ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ + ACPI_BITMASK_RT_CLOCK_STATUS | \ + ACPI_BITMASK_WAKE_STATUS) #define ACPI_BITMASK_TIMER_ENABLE 0x0001 #define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020 @@ -820,7 +846,7 @@ #define ACPI_RESOURCE_NAME_LARGE_MAX * ****************************************************************************/ -#define ACPI_ASCII_ZERO 0x30 +#define ACPI_ASCII_ZERO 0x30 /***************************************************************************** * @@ -842,9 +868,9 @@ struct acpi_integrity_info { u32 objects; }; -#define ACPI_DB_REDIRECTABLE_OUTPUT 0x01 -#define ACPI_DB_CONSOLE_OUTPUT 0x02 -#define ACPI_DB_DUPLICATE_OUTPUT 0x03 +#define ACPI_DB_REDIRECTABLE_OUTPUT 0x01 +#define ACPI_DB_CONSOLE_OUTPUT 0x02 +#define ACPI_DB_DUPLICATE_OUTPUT 0x03 /***************************************************************************** * @@ -854,18 +880,18 @@ #define ACPI_DB_DUPLICATE_OUTPUT 0x0 /* Entry for a memory allocation (debug only) */ -#define ACPI_MEM_MALLOC 0 -#define ACPI_MEM_CALLOC 1 -#define ACPI_MAX_MODULE_NAME 16 +#define ACPI_MEM_MALLOC 0 +#define ACPI_MEM_CALLOC 1 +#define ACPI_MAX_MODULE_NAME 16 #define ACPI_COMMON_DEBUG_MEM_HEADER \ - struct acpi_debug_mem_block *previous; \ - struct acpi_debug_mem_block *next; \ - u32 size; \ - u32 component; \ - u32 line; \ - char module[ACPI_MAX_MODULE_NAME]; \ - u8 alloc_type; + struct acpi_debug_mem_block *previous; \ + struct acpi_debug_mem_block *next; \ + u32 size; \ + u32 component; \ + u32 line; \ + char module[ACPI_MAX_MODULE_NAME]; \ + u8 alloc_type; struct acpi_debug_mem_header { ACPI_COMMON_DEBUG_MEM_HEADER}; diff --git a/include/acpi/acmacros.h b/include/acpi/acmacros.h index f2be2a8..f1ac610 100644 --- a/include/acpi/acmacros.h +++ b/include/acpi/acmacros.h @@ -56,6 +56,10 @@ #define ACPI_SET_BIT(target,bit) #define ACPI_CLEAR_BIT(target,bit) ((target) &= ~(bit)) #define ACPI_MIN(a,b) (((a)<(b))?(a):(b)) +/* Size calculation */ + +#define ACPI_ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) + #if ACPI_MACHINE_WIDTH == 16 /* @@ -99,7 +103,7 @@ #endif * printf() format helpers */ -/* Split 64-bit integer into two 32-bit values. Use with %8.8X%8.8X */ +/* Split 64-bit integer into two 32-bit values. Use with %8.8_x%8.8_x */ #define ACPI_FORMAT_UINT64(i) ACPI_HIDWORD(i),ACPI_LODWORD(i) @@ -130,7 +134,6 @@ #define ACPI_PTR_DIFF(a,b) #define ACPI_TO_POINTER(i) ACPI_ADD_PTR (void,(void *) NULL,(acpi_native_uint) i) #define ACPI_TO_INTEGER(p) ACPI_PTR_DIFF (p,(void *) NULL) #define ACPI_OFFSET(d,f) (acpi_size) ACPI_PTR_DIFF (&(((d *)0)->f),(void *) NULL) -#define ACPI_FADT_OFFSET(f) ACPI_OFFSET (FADT_DESCRIPTOR, f) #if ACPI_MACHINE_WIDTH == 16 #define ACPI_STORE_POINTER(d,s) ACPI_MOVE_32_TO_32(d,s) @@ -141,6 +144,12 @@ #define ACPI_PHYSADDR_TO_PTR(i) #define ACPI_PTR_TO_PHYSADDR(i) ACPI_TO_INTEGER(i) #endif +#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED +#define ACPI_COMPARE_NAME(a,b) (*ACPI_CAST_PTR (u32,(a)) == *ACPI_CAST_PTR (u32,(b))) +#else +#define ACPI_COMPARE_NAME(a,b) (!ACPI_STRNCMP (ACPI_CAST_PTR (char,(a)), ACPI_CAST_PTR (char,(b)), ACPI_NAME_SIZE)) +#endif + /* * Macros for moving data around to/from buffers that are possibly unaligned. * If the hardware supports the transfer of unaligned data, just do the store. @@ -341,29 +350,33 @@ #define ACPI_MOD_32(a) /* * Rounding macros (Power of two boundaries only) */ -#define ACPI_ROUND_DOWN(value,boundary) (((acpi_native_uint)(value)) & \ +#define ACPI_ROUND_DOWN(value,boundary) (((acpi_native_uint)(value)) & \ (~(((acpi_native_uint) boundary)-1))) -#define ACPI_ROUND_UP(value,boundary) ((((acpi_native_uint)(value)) + \ +#define ACPI_ROUND_UP(value,boundary) ((((acpi_native_uint)(value)) + \ (((acpi_native_uint) boundary)-1)) & \ (~(((acpi_native_uint) boundary)-1))) -#define ACPI_ROUND_DOWN_TO_32_BITS(a) ACPI_ROUND_DOWN(a,4) -#define ACPI_ROUND_DOWN_TO_64_BITS(a) ACPI_ROUND_DOWN(a,8) -#define ACPI_ROUND_DOWN_TO_NATIVE_WORD(a) ACPI_ROUND_DOWN(a,ALIGNED_ADDRESS_BOUNDARY) +/* Note: sizeof(acpi_native_uint) evaluates to either 2, 4, or 8 */ + +#define ACPI_ROUND_DOWN_TO_32BIT(a) ACPI_ROUND_DOWN(a,4) +#define ACPI_ROUND_DOWN_TO_64BIT(a) ACPI_ROUND_DOWN(a,8) +#define ACPI_ROUND_DOWN_TO_NATIVE_WORD(a) ACPI_ROUND_DOWN(a,sizeof(acpi_native_uint)) -#define ACPI_ROUND_UP_to_32_bITS(a) ACPI_ROUND_UP(a,4) -#define ACPI_ROUND_UP_to_64_bITS(a) ACPI_ROUND_UP(a,8) -#define ACPI_ROUND_UP_TO_NATIVE_WORD(a) ACPI_ROUND_UP(a,ALIGNED_ADDRESS_BOUNDARY) +#define ACPI_ROUND_UP_TO_32BIT(a) ACPI_ROUND_UP(a,4) +#define ACPI_ROUND_UP_TO_64BIT(a) ACPI_ROUND_UP(a,8) +#define ACPI_ROUND_UP_TO_NATIVE_WORD(a) ACPI_ROUND_UP(a,sizeof(acpi_native_uint)) -#define ACPI_ROUND_BITS_UP_TO_BYTES(a) ACPI_DIV_8((a) + 7) -#define ACPI_ROUND_BITS_DOWN_TO_BYTES(a) ACPI_DIV_8((a)) +#define ACPI_ROUND_BITS_UP_TO_BYTES(a) ACPI_DIV_8((a) + 7) +#define ACPI_ROUND_BITS_DOWN_TO_BYTES(a) ACPI_DIV_8((a)) -#define ACPI_ROUND_UP_TO_1K(a) (((a) + 1023) >> 10) +#define ACPI_ROUND_UP_TO_1K(a) (((a) + 1023) >> 10) /* Generic (non-power-of-two) rounding */ -#define ACPI_ROUND_UP_TO(value,boundary) (((value) + ((boundary)-1)) / (boundary)) +#define ACPI_ROUND_UP_TO(value,boundary) (((value) + ((boundary)-1)) / (boundary)) + +#define ACPI_IS_MISALIGNED(value) (((acpi_native_uint)value) & (sizeof(acpi_native_uint)-1)) /* * Bitmask creation @@ -371,16 +384,18 @@ #define ACPI_ROUND_UP_TO(value,boundary) * MASK_BITS_ABOVE creates a mask starting AT the position and above * MASK_BITS_BELOW creates a mask starting one bit BELOW the position */ -#define ACPI_MASK_BITS_ABOVE(position) (~((ACPI_INTEGER_MAX) << ((u32) (position)))) -#define ACPI_MASK_BITS_BELOW(position) ((ACPI_INTEGER_MAX) << ((u32) (position))) +#define ACPI_MASK_BITS_ABOVE(position) (~((ACPI_INTEGER_MAX) << ((u32) (position)))) +#define ACPI_MASK_BITS_BELOW(position) ((ACPI_INTEGER_MAX) << ((u32) (position))) -#define ACPI_IS_OCTAL_DIGIT(d) (((char)(d) >= '0') && ((char)(d) <= '7')) +#define ACPI_IS_OCTAL_DIGIT(d) (((char)(d) >= '0') && ((char)(d) <= '7')) /* Bitfields within ACPI registers */ #define ACPI_REGISTER_PREPARE_BITS(val, pos, mask) ((val << pos) & mask) #define ACPI_REGISTER_INSERT_VALUE(reg, pos, mask, val) reg = (reg & (~(mask))) | ACPI_REGISTER_PREPARE_BITS(val, pos, mask) +#define ACPI_INSERT_BITS(target, mask, source) target = ((target & (~(mask))) | (source & mask)) + /* Generate a UUID */ #define ACPI_INIT_UUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ @@ -396,8 +411,8 @@ #define ACPI_INIT_UUID(a,b,c,d0,d1,d2,d3 * * The "Descriptor" field is the first field in both structures. */ -#define ACPI_GET_DESCRIPTOR_TYPE(d) (((union acpi_descriptor *)(void *)(d))->descriptor_id) -#define ACPI_SET_DESCRIPTOR_TYPE(d,t) (((union acpi_descriptor *)(void *)(d))->descriptor_id = t) +#define ACPI_GET_DESCRIPTOR_TYPE(d) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type) +#define ACPI_SET_DESCRIPTOR_TYPE(d,t) (((union acpi_descriptor *)(void *)(d))->common.descriptor_type = t) /* Macro to test the object type */ @@ -486,7 +501,6 @@ #define ACPI_EXCEPTION(plist) #define ACPI_ERROR(plist) #define ACPI_ERROR_NAMESPACE(s,e) #define ACPI_ERROR_METHOD(s,n,p,e) - #endif /* @@ -514,12 +528,12 @@ #ifndef ACPI_GET_FUNCTION_NAME #define ACPI_GET_FUNCTION_NAME _acpi_function_name /* * The Name parameter should be the procedure name as a quoted string. - * This is declared as a local string ("my_function_name") so that it can + * This is declared as a local string ("MyFunctionName") so that it can * be also used by the function exit macros below. * Note: (const char) is used to be compatible with the debug interfaces * and macros such as __FUNCTION__. */ -#define ACPI_FUNCTION_NAME(name) const char *_acpi_function_name = name; +#define ACPI_FUNCTION_NAME(name) const char *_acpi_function_name = #name; #else /* Compiler supports __FUNCTION__ (or equivalent) -- Ignore this macro */ @@ -528,13 +542,13 @@ #define ACPI_FUNCTION_NAME(name) #endif #define ACPI_FUNCTION_TRACE(a) ACPI_FUNCTION_NAME(a) \ - acpi_ut_trace(ACPI_DEBUG_PARAMETERS) + acpi_ut_trace(ACPI_DEBUG_PARAMETERS) #define ACPI_FUNCTION_TRACE_PTR(a,b) ACPI_FUNCTION_NAME(a) \ - acpi_ut_trace_ptr(ACPI_DEBUG_PARAMETERS,(void *)b) + acpi_ut_trace_ptr(ACPI_DEBUG_PARAMETERS,(void *)b) #define ACPI_FUNCTION_TRACE_U32(a,b) ACPI_FUNCTION_NAME(a) \ - acpi_ut_trace_u32(ACPI_DEBUG_PARAMETERS,(u32)b) + acpi_ut_trace_u32(ACPI_DEBUG_PARAMETERS,(u32)b) #define ACPI_FUNCTION_TRACE_STR(a,b) ACPI_FUNCTION_NAME(a) \ - acpi_ut_trace_str(ACPI_DEBUG_PARAMETERS,(char *)b) + acpi_ut_trace_str(ACPI_DEBUG_PARAMETERS,(char *)b) #define ACPI_FUNCTION_ENTRY() acpi_ut_track_stack_ptr() @@ -543,7 +557,7 @@ #define ACPI_FUNCTION_ENTRY() * WARNING: These macros include a return statement. This is usually considered * bad form, but having a separate exit macro is very ugly and difficult to maintain. * One of the FUNCTION_TRACE macros above must be used in conjunction with these macros - * so that "_acpi_function_name" is defined. + * so that "_AcpiFunctionName" is defined. * * Note: the DO_WHILE0 macro is used to prevent some compilers from complaining * about these constructs. @@ -654,6 +668,7 @@ #define ACPI_FUNCTION_ENTRY() #define ACPI_DUMP_STACK_ENTRY(a) #define ACPI_DUMP_OPERANDS(a,b,c,d,e) #define ACPI_DUMP_ENTRY(a,b) +#define ACPI_DUMP_TABLES(a,b) #define ACPI_DUMP_PATHNAME(a,b,c,d) #define ACPI_DUMP_RESOURCE_LIST(a) #define ACPI_DUMP_BUFFER(a,b) @@ -709,19 +724,19 @@ #ifndef ACPI_DBG_TRACK_ALLOCATIONS /* Memory allocation */ -#define ACPI_MEM_ALLOCATE(a) acpi_ut_allocate((acpi_size)(a),_COMPONENT,_acpi_module_name,__LINE__) -#define ACPI_MEM_CALLOCATE(a) acpi_ut_callocate((acpi_size)(a), _COMPONENT,_acpi_module_name,__LINE__) -#define ACPI_MEM_FREE(a) acpi_os_free(a) +#define ACPI_ALLOCATE(a) acpi_ut_allocate((acpi_size)(a),_COMPONENT,_acpi_module_name,__LINE__) +#define ACPI_ALLOCATE_ZEROED(a) acpi_ut_allocate_zeroed((acpi_size)(a), _COMPONENT,_acpi_module_name,__LINE__) +#define ACPI_FREE(a) kfree(a) #define ACPI_MEM_TRACKING(a) #else /* Memory allocation */ -#define ACPI_MEM_ALLOCATE(a) acpi_ut_allocate_and_track((acpi_size)(a),_COMPONENT,_acpi_module_name,__LINE__) -#define ACPI_MEM_CALLOCATE(a) acpi_ut_callocate_and_track((acpi_size)(a), _COMPONENT,_acpi_module_name,__LINE__) -#define ACPI_MEM_FREE(a) acpi_ut_free_and_track(a,_COMPONENT,_acpi_module_name,__LINE__) -#define ACPI_MEM_TRACKING(a) a +#define ACPI_ALLOCATE(a) acpi_ut_allocate_and_track((acpi_size)(a),_COMPONENT,_acpi_module_name,__LINE__) +#define ACPI_ALLOCATE_ZEROED(a) acpi_ut_allocate_zeroed_and_track((acpi_size)(a), _COMPONENT,_acpi_module_name,__LINE__) +#define ACPI_FREE(a) acpi_ut_free_and_track(a,_COMPONENT,_acpi_module_name,__LINE__) +#define ACPI_MEM_TRACKING(a) a #endif /* ACPI_DBG_TRACK_ALLOCATIONS */ diff --git a/include/acpi/acnamesp.h b/include/acpi/acnamesp.h index b667a80..83b52f9 100644 --- a/include/acpi/acnamesp.h +++ b/include/acpi/acnamesp.h @@ -63,6 +63,8 @@ #define ACPI_NS_SEARCH_PARENT 0x01 #define ACPI_NS_DONT_OPEN_SCOPE 0x02 #define ACPI_NS_NO_PEER_SEARCH 0x04 #define ACPI_NS_ERROR_IF_FOUND 0x08 +#define ACPI_NS_PREFIX_IS_SCOPE 0x10 +#define ACPI_NS_EXTERNAL 0x20 #define ACPI_NS_WALK_UNLOCK TRUE #define ACPI_NS_WALK_NO_UNLOCK FALSE @@ -171,19 +173,17 @@ #endif /* ACPI_FUTURE_USAGE */ /* * nseval - Namespace evaluation functions */ -acpi_status acpi_ns_evaluate_by_handle(struct acpi_parameter_info *info); - -acpi_status -acpi_ns_evaluate_by_name(char *pathname, struct acpi_parameter_info *info); - -acpi_status -acpi_ns_evaluate_relative(char *pathname, struct acpi_parameter_info *info); +acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info); /* * nsnames - Name and Scope manipulation */ u32 acpi_ns_opens_scope(acpi_object_type type); +void +acpi_ns_build_external_path(struct acpi_namespace_node *node, + acpi_size size, char *name_buffer); + char *acpi_ns_get_external_pathname(struct acpi_namespace_node *node); char *acpi_ns_name_of_current_scope(struct acpi_walk_state *walk_state); @@ -196,9 +196,9 @@ u8 acpi_ns_pattern_match(struct acpi_namespace_node *obj_node, char *search_for); acpi_status -acpi_ns_get_node_by_path(char *external_pathname, - struct acpi_namespace_node *in_prefix_node, - u32 flags, struct acpi_namespace_node **out_node); +acpi_ns_get_node(struct acpi_namespace_node *prefix_node, + char *external_pathname, + u32 flags, struct acpi_namespace_node **out_node); acpi_size acpi_ns_get_pathname_length(struct acpi_namespace_node *node); @@ -241,10 +241,10 @@ acpi_ns_search_and_enter(u32 entry_name, u32 flags, struct acpi_namespace_node **ret_node); acpi_status -acpi_ns_search_node(u32 entry_name, - struct acpi_namespace_node *node, - acpi_object_type type, - struct acpi_namespace_node **ret_node); +acpi_ns_search_one_scope(u32 entry_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **ret_node); void acpi_ns_install_node(struct acpi_walk_state *walk_state, diff --git a/include/acpi/acobject.h b/include/acpi/acobject.h index d130cfe..8fdee31 100644 --- a/include/acpi/acobject.h +++ b/include/acpi/acobject.h @@ -1,7 +1,7 @@ /****************************************************************************** * - * Name: acobject.h - Definition of union acpi_operand_object (Internal object only) + * Name: acobject.h - Definition of union acpi_operand_object (Internal object only) * *****************************************************************************/ @@ -45,10 +45,12 @@ #ifndef _ACOBJECT_H #define _ACOBJECT_H +/* acpisrc:struct_defs -- for acpisrc conversion */ + /* - * The union acpi_operand_object is used to pass AML operands from the dispatcher + * The union acpi_operand_object is used to pass AML operands from the dispatcher * to the interpreter, and to keep track of the various handlers such as - * address space handlers and notify handlers. The object is a constant + * address space handlers and notify handlers. The object is a constant * size in order to allow it to be cached and reused. */ @@ -61,17 +63,25 @@ #define _ACOBJECT_H /* * Common area for all objects. * - * data_type is used to differentiate between internal descriptors, and MUST - * be the first byte in this structure. + * descriptor_type is used to differentiate between internal descriptors, and + * must be in the same place across all descriptors + * + * Note: The descriptor_type and Type fields must appear in the identical + * position in both the struct acpi_namespace_node and union acpi_operand_object + * structures. */ -#define ACPI_OBJECT_COMMON_HEADER /* SIZE/ALIGNMENT: 32 bits, one ptr plus trailing 8-bit flag */\ - u8 descriptor; /* To differentiate various internal objs */\ - u8 type; /* acpi_object_type */\ - u16 reference_count; /* For object deletion management */\ - union acpi_operand_object *next_object; /* Objects linked to parent NS node */\ - u8 flags; - -/* Values for flag byte above */ +#define ACPI_OBJECT_COMMON_HEADER \ + union acpi_operand_object *next_object; /* Objects linked to parent NS node */\ + u8 descriptor_type; /* To differentiate various internal objs */\ + u8 type; /* acpi_object_type */\ + u16 reference_count; /* For object deletion management */\ + u8 flags; + /* + * Note: There are 3 bytes available here before the + * next natural alignment boundary (for both 32/64 cases) + */ + +/* Values for Flag byte above */ #define AOPOBJ_AML_CONSTANT 0x01 #define AOPOBJ_STATIC_POINTER 0x02 @@ -79,36 +89,7 @@ #define AOPOBJ_DATA_VALID 0x04 #define AOPOBJ_OBJECT_INITIALIZED 0x08 #define AOPOBJ_SETUP_COMPLETE 0x10 #define AOPOBJ_SINGLE_DATUM 0x20 - -/* - * Common bitfield for the field objects - * "Field Datum" -- a datum from the actual field object - * "Buffer Datum" -- a datum from a user buffer, read from or to be written to the field - */ -#define ACPI_COMMON_FIELD_INFO /* SIZE/ALIGNMENT: 24 bits + three 32-bit values */\ - u8 field_flags; /* Access, update, and lock bits */\ - u8 attribute; /* From access_as keyword */\ - u8 access_byte_width; /* Read/Write size in bytes */\ - u32 bit_length; /* Length of field in bits */\ - u32 base_byte_offset; /* Byte offset within containing object */\ - u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ - u8 access_bit_width; /* Read/Write size in bits (8-64) */\ - u32 value; /* Value to store into the Bank or Index register */\ - struct acpi_namespace_node *node; /* Link back to parent node */ - -/* - * Fields common to both Strings and Buffers - */ -#define ACPI_COMMON_BUFFER_INFO \ - u32 length; - -/* - * Common fields for objects that support ASL notifications - */ -#define ACPI_COMMON_NOTIFY_INFO \ - union acpi_operand_object *system_notify; /* Handler for system notifies */\ - union acpi_operand_object *device_notify; /* Handler for driver notifies */\ - union acpi_operand_object *handler; /* Handler for Address space */ +#define AOPOBJ_INVALID 0x40 /* Used if host OS won't allow an op_region address */ /****************************************************************************** * @@ -125,25 +106,31 @@ struct acpi_object_integer { /* * Note: The String and Buffer object must be identical through the Pointer - * element. There is code that depends on this. + * and length elements. There is code that depends on this. + * + * Fields common to both Strings and Buffers */ +#define ACPI_COMMON_BUFFER_INFO(_type) \ + _type *pointer; \ + u32 length; + struct acpi_object_string { /* Null terminated, ASCII characters only */ - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO char *pointer; /* String in AML stream or allocated string */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(char) /* String in AML stream or allocated string */ }; struct acpi_object_buffer { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO u8 * pointer; /* Buffer in AML stream or allocated buffer */ - struct acpi_namespace_node *node; /* Link back to parent node */ - u8 *aml_start; + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_BUFFER_INFO(u8) /* Buffer in AML stream or allocated buffer */ u32 aml_length; + u8 *aml_start; + struct acpi_namespace_node *node; /* Link back to parent node */ }; struct acpi_object_package { - ACPI_OBJECT_COMMON_HEADER u32 count; /* # of elements in package */ - u32 aml_length; - u8 *aml_start; - struct acpi_namespace_node *node; /* Link back to parent node */ + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *node; /* Link back to parent node */ union acpi_operand_object **elements; /* Array of pointers to acpi_objects */ + u8 *aml_start; + u32 aml_length; + u32 count; /* # of elements in package */ }; /****************************************************************************** @@ -153,31 +140,14 @@ struct acpi_object_package { *****************************************************************************/ struct acpi_object_event { - ACPI_OBJECT_COMMON_HEADER void *semaphore; -}; - -#define ACPI_INFINITE_CONCURRENCY 0xFF - -typedef -acpi_status(*ACPI_INTERNAL_METHOD) (struct acpi_walk_state * walk_state); - -struct acpi_object_method { - ACPI_OBJECT_COMMON_HEADER u8 method_flags; - u8 param_count; - u32 aml_length; - void *semaphore; - u8 *aml_start; - ACPI_INTERNAL_METHOD implementation; - u8 concurrency; - u8 thread_count; - acpi_owner_id owner_id; + ACPI_OBJECT_COMMON_HEADER acpi_semaphore os_semaphore; /* Actual OS synchronization object */ }; struct acpi_object_mutex { ACPI_OBJECT_COMMON_HEADER u8 sync_level; /* 0-15, specified in Mutex() call */ u16 acquisition_depth; /* Allow multiple Acquires, same thread */ struct acpi_thread_state *owner_thread; /* Current owner of the mutex */ - void *semaphore; /* Actual OS synchronization object */ + acpi_mutex os_mutex; /* Actual OS synchronization object */ union acpi_operand_object *prev; /* Link for list of acquired mutexes */ union acpi_operand_object *next; /* Link for list of acquired mutexes */ struct acpi_namespace_node *node; /* Containing namespace node */ @@ -186,11 +156,23 @@ struct acpi_object_mutex { struct acpi_object_region { ACPI_OBJECT_COMMON_HEADER u8 space_id; - union acpi_operand_object *handler; /* Handler for region access */ struct acpi_namespace_node *node; /* Containing namespace node */ + union acpi_operand_object *handler; /* Handler for region access */ union acpi_operand_object *next; - u32 length; acpi_physical_address address; + u32 length; +}; + +struct acpi_object_method { + ACPI_OBJECT_COMMON_HEADER u8 method_flags; + u8 param_count; + u8 sync_level; + union acpi_operand_object *mutex; + u8 *aml_start; + ACPI_INTERNAL_METHOD implementation; + u32 aml_length; + u8 thread_count; + acpi_owner_id owner_id; }; /****************************************************************************** @@ -199,6 +181,14 @@ struct acpi_object_region { * *****************************************************************************/ +/* + * Common fields for objects that support ASL notifications + */ +#define ACPI_COMMON_NOTIFY_INFO \ + union acpi_operand_object *system_notify; /* Handler for system notifies */\ + union acpi_operand_object *device_notify; /* Handler for driver notifies */\ + union acpi_operand_object *handler; /* Handler for Address space */ + struct acpi_object_notify_common { /* COMMON NOTIFY for POWER, PROCESSOR, DEVICE, and THERMAL */ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO}; @@ -213,9 +203,9 @@ struct acpi_object_power_resource { }; struct acpi_object_processor { - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NOTIFY_INFO u32 proc_id; - u32 length; - acpi_io_address address; + ACPI_OBJECT_COMMON_HEADER u8 proc_id; + u8 length; + ACPI_COMMON_NOTIFY_INFO acpi_io_address address; }; struct acpi_object_thermal_zone { @@ -227,9 +217,24 @@ ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_NO * *****************************************************************************/ +/* + * Common bitfield for the field objects + * "Field Datum" -- a datum from the actual field object + * "Buffer Datum" -- a datum from a user buffer, read from or to be written to the field + */ +#define ACPI_COMMON_FIELD_INFO \ + u8 field_flags; /* Access, update, and lock bits */\ + u8 attribute; /* From access_as keyword */\ + u8 access_byte_width; /* Read/Write size in bytes */\ + struct acpi_namespace_node *node; /* Link back to parent node */\ + u32 bit_length; /* Length of field in bits */\ + u32 base_byte_offset; /* Byte offset within containing object */\ + u32 value; /* Value to store into the Bank or Index register */\ + u8 start_field_bit_offset;/* Bit offset within first field datum (0-63) */\ + u8 access_bit_width; /* Read/Write size in bits (8-64) */ + struct acpi_object_field_common { /* COMMON FIELD (for BUFFER, REGION, BANK, and INDEX fields) */ - ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Containing Operation Region object */ - /* (REGION/BANK fields only) */ + ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *region_obj; /* Parent Operation Region object (REGION/BANK fields only) */ }; struct acpi_object_region_field { @@ -244,7 +249,7 @@ struct acpi_object_bank_field { struct acpi_object_index_field { ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO /* - * No "region_obj" pointer needed since the Index and Data registers + * No "RegionObj" pointer needed since the Index and Data registers * are each field definitions unto themselves. */ union acpi_operand_object *index_obj; /* Index register */ @@ -269,13 +274,9 @@ struct acpi_object_notify_handler { void *context; }; -/* Flags for address handler */ - -#define ACPI_ADDR_HANDLER_DEFAULT_INSTALLED 0x1 - struct acpi_object_addr_handler { ACPI_OBJECT_COMMON_HEADER u8 space_id; - u16 hflags; + u8 handler_flags; acpi_adr_space_handler handler; struct acpi_namespace_node *node; /* Parent device */ void *context; @@ -284,6 +285,10 @@ struct acpi_object_addr_handler { union acpi_operand_object *next; }; +/* Flags for address handler (handler_flags) */ + +#define ACPI_ADDR_HANDLER_DEFAULT_INSTALLED 0x01 + /****************************************************************************** * * Special internal objects @@ -297,10 +302,10 @@ struct acpi_object_addr_handler { struct acpi_object_reference { ACPI_OBJECT_COMMON_HEADER u8 target_type; /* Used for index_op */ u16 opcode; - u32 offset; /* Used for arg_op, local_op, and index_op */ - void *object; /* name_op=>HANDLE to obj, index_op=>union acpi_operand_object */ + void *object; /* name_op=>HANDLE to obj, index_op=>union acpi_operand_object */ struct acpi_namespace_node *node; union acpi_operand_object **where; + u32 offset; /* Used for arg_op, local_op, and index_op */ }; /* @@ -311,12 +316,10 @@ struct acpi_object_reference { * Currently: Region and field_unit types */ struct acpi_object_extra { - ACPI_OBJECT_COMMON_HEADER u8 byte_fill1; - u16 word_fill1; - u32 aml_length; - u8 *aml_start; - struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ + ACPI_OBJECT_COMMON_HEADER struct acpi_namespace_node *method_REG; /* _REG method for this region (if any) */ void *region_context; /* Region-specific data */ + u8 *aml_start; + u32 aml_length; }; /* Additional data that can be attached to namespace nodes */ @@ -391,8 +394,13 @@ #define ACPI_DESC_TYPE_OPERAND #define ACPI_DESC_TYPE_NAMED 0x0F #define ACPI_DESC_TYPE_MAX 0x0F +struct acpi_common_descriptor { + void *common_pointer; + u8 descriptor_type; /* To differentiate various internal objs */ +}; + union acpi_descriptor { - u8 descriptor_id; /* To differentiate various internal objs */ + struct acpi_common_descriptor common; union acpi_operand_object object; struct acpi_namespace_node node; union acpi_parse_object op; diff --git a/include/acpi/acopcode.h b/include/acpi/acopcode.h index e6d78bd..7659a46 100644 --- a/include/acpi/acopcode.h +++ b/include/acpi/acopcode.h @@ -94,7 +94,7 @@ #define ARGP_CONCAT_OP #define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) #define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME) #define ARGP_CONTINUE_OP ARG_NONE -#define ARGP_COPY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SIMPLENAME) +#define ARGP_COPY_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SIMPLENAME) #define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) #define ARGP_CREATE_BYTE_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) #define ARGP_CREATE_DWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index 7785d48..8d5039d 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -50,7 +50,7 @@ #define __ACOUTPUT_H__ * component basis and a per-exception-type basis. */ -/* Component IDs are used in the global "debug_layer" */ +/* Component IDs are used in the global "DebugLayer" */ #define ACPI_UTILITIES 0x00000001 #define ACPI_HARDWARE 0x00000002 @@ -121,7 +121,7 @@ #define ACPI_LV_IO 0x04 #define ACPI_LV_INTERRUPTS 0x08000000 #define ACPI_LV_VERBOSITY3 0x0F000000 | ACPI_LV_VERBOSITY2 -/* Exceptionally verbose output -- also used in the global "debug_level" */ +/* Exceptionally verbose output -- also used in the global "DebugLevel" */ #define ACPI_LV_AML_DISASSEMBLE 0x10000000 #define ACPI_LV_VERBOSE_INFO 0x20000000 @@ -135,7 +135,7 @@ #define ACPI_LV_VERBOSE 0xF0 */ #define ACPI_DEBUG_LEVEL(dl) (u32) dl,ACPI_DEBUG_PARAMETERS -/* Exception level -- used in the global "debug_level" */ +/* Exception level -- used in the global "DebugLevel" */ #define ACPI_DB_INIT ACPI_DEBUG_LEVEL (ACPI_LV_INIT) #define ACPI_DB_DEBUG_OBJECT ACPI_DEBUG_LEVEL (ACPI_LV_DEBUG_OBJECT) @@ -144,13 +144,13 @@ #define ACPI_DB_ALL_EXCEPTIONS ACPI /* * These two levels are essentially obsolete, all instances in the - * ACPICA core code have been replaced by REPORT_ERROR and REPORT_WARNING + * ACPICA core code have been replaced by ACPI_ERROR and ACPI_WARNING * (Kept here because some drivers may still use them) */ #define ACPI_DB_ERROR ACPI_DEBUG_LEVEL (ACPI_LV_ERROR) #define ACPI_DB_WARN ACPI_DEBUG_LEVEL (ACPI_LV_WARN) -/* Trace level -- also used in the global "debug_level" */ +/* Trace level -- also used in the global "DebugLevel" */ #define ACPI_DB_INIT_NAMES ACPI_DEBUG_LEVEL (ACPI_LV_INIT_NAMES) #define ACPI_DB_THREADS ACPI_DEBUG_LEVEL (ACPI_LV_THREADS) diff --git a/include/acpi/acparser.h b/include/acpi/acparser.h index 5a1ff48..9d49d3c 100644 --- a/include/acpi/acparser.h +++ b/include/acpi/acparser.h @@ -46,7 +46,7 @@ #define __ACPARSER_H__ #define OP_HAS_RETURN_VALUE 1 -/* variable # arguments */ +/* Variable number of arguments. This field must be 32 bits */ #define ACPI_VAR_ARGS ACPI_UINT32_MAX @@ -71,7 +71,7 @@ #define ACPI_PARSE_DISASSEMBLE /* * psxface - Parser external interfaces */ -acpi_status acpi_ps_execute_method(struct acpi_parameter_info *info); +acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info); /* * psargs - Parse AML opcode arguments diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 6dca3d5..f338e40 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -26,7 +26,7 @@ #ifndef __ACPI_BUS_H__ #define __ACPI_BUS_H__ -#include +#include #include @@ -59,7 +59,7 @@ #include #define ACPI_BUS_FILE_ROOT "acpi" extern struct proc_dir_entry *acpi_root_dir; -extern FADT_DESCRIPTOR acpi_fadt; +extern struct fadt_descriptor acpi_fadt; enum acpi_bus_removal_type { ACPI_BUS_REMOVAL_NORMAL = 0, @@ -169,7 +169,8 @@ struct acpi_device_flags { u32 power_manageable:1; u32 performance_manageable:1; u32 wake_capable:1; /* Wakeup(_PRW) supported? */ - u32 reserved:20; + u32 force_power_state:1; + u32 reserved:19; }; /* File System */ @@ -296,6 +297,7 @@ struct acpi_device { struct acpi_driver *driver; void *driver_data; struct kobject kobj; + struct device dev; }; #define acpi_driver_data(d) ((d)->driver_data) @@ -327,12 +329,12 @@ int acpi_bus_set_power(acpi_handle handl int acpi_bus_generate_event(struct acpi_device *device, u8 type, int data); int acpi_bus_receive_event(struct acpi_bus_event *event); int acpi_bus_register_driver(struct acpi_driver *driver); -int acpi_bus_unregister_driver(struct acpi_driver *driver); +void acpi_bus_unregister_driver(struct acpi_driver *driver); int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type); int acpi_bus_trim(struct acpi_device *start, int rmdevice); int acpi_bus_start(struct acpi_device *device); - +acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd); int acpi_match_ids(struct acpi_device *device, char *ids); int acpi_create_dir(struct acpi_device *); void acpi_remove_dir(struct acpi_device *); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index b425f9b..6a5bdce 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -110,4 +110,21 @@ int acpi_processor_set_thermal_limit(acp extern int acpi_specific_hotkey_enabled; +/*-------------------------------------------------------------------------- + Dock Station + -------------------------------------------------------------------------- */ +#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE) +extern int is_dock_device(acpi_handle handle); +extern int register_dock_notifier(struct notifier_block *nb); +extern void unregister_dock_notifier(struct notifier_block *nb); +extern int register_hotplug_dock_device(acpi_handle handle, + acpi_notify_handler handler, void *context); +extern void unregister_hotplug_dock_device(acpi_handle handle); +#else +#define is_dock_device(h) (0) +#define register_dock_notifier(nb) (-ENODEV) +#define unregister_dock_notifier(nb) do { } while(0) +#define register_hotplug_dock_device(h1, h2, c) (-ENODEV) +#define unregister_hotplug_dock_device(h) do { } while(0) +#endif #endif /*__ACPI_DRIVERS_H__*/ diff --git a/include/acpi/acpi_numa.h b/include/acpi/acpi_numa.h new file mode 100644 index 0000000..1049f2a --- /dev/null +++ b/include/acpi/acpi_numa.h @@ -0,0 +1,23 @@ +#ifndef __ACPI_NUMA_H +#define __ACPI_NUMA_H + +#ifdef CONFIG_ACPI_NUMA +#include + +/* Proximity bitmap length */ +#if MAX_NUMNODES > 256 +#define MAX_PXM_DOMAINS MAX_NUMNODES +#else +#define MAX_PXM_DOMAINS (256) /* Old pxm spec is defined 8 bit */ +#endif + +extern int __cpuinitdata pxm_to_node_map[MAX_PXM_DOMAINS]; +extern int __cpuinitdata node_to_pxm_map[MAX_NUMNODES]; + +extern int __cpuinit pxm_to_node(int); +extern int __cpuinit node_to_pxm(int); +extern int __cpuinit acpi_map_pxm_to_node(int); +extern void __cpuinit acpi_unmap_pxm_to_node(int); + +#endif /* CONFIG_ACPI_NUMA */ +#endif /* __ACP_NUMA_H */ diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index 970e9a6..0cd63bc 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -50,12 +50,16 @@ #define __ACPIOSXF_H__ #include "platform/acenv.h" #include "actypes.h" -/* Priorities for acpi_os_queue_for_execution */ +/* Types for acpi_os_execute */ -#define OSD_PRIORITY_GPE 1 -#define OSD_PRIORITY_HIGH 2 -#define OSD_PRIORITY_MED 3 -#define OSD_PRIORITY_LO 4 +typedef enum { + OSL_GLOBAL_LOCK_HANDLER, + OSL_NOTIFY_HANDLER, + OSL_GPE_HANDLER, + OSL_DEBUGGER_THREAD, + OSL_EC_POLL_HANDLER, + OSL_EC_BURST_HANDLER +} acpi_execute_type; #define ACPI_NO_UNIT_LIMIT ((u32) -1) #define ACPI_MUTEX_SEM 1 @@ -92,33 +96,53 @@ acpi_os_table_override(struct acpi_table struct acpi_table_header **new_table); /* - * Synchronization primitives + * Spinlock primitives + */ +acpi_status acpi_os_create_lock(acpi_spinlock * out_handle); + +void acpi_os_delete_lock(acpi_spinlock handle); + +acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock handle); + +void acpi_os_release_lock(acpi_spinlock handle, acpi_cpu_flags flags); + +/* + * Semaphore primitives */ acpi_status acpi_os_create_semaphore(u32 max_units, - u32 initial_units, acpi_handle * out_handle); + u32 initial_units, acpi_semaphore * out_handle); -acpi_status acpi_os_delete_semaphore(acpi_handle handle); +acpi_status acpi_os_delete_semaphore(acpi_semaphore handle); -acpi_status acpi_os_wait_semaphore(acpi_handle handle, u32 units, u16 timeout); +acpi_status +acpi_os_wait_semaphore(acpi_semaphore handle, u32 units, u16 timeout); + +acpi_status acpi_os_signal_semaphore(acpi_semaphore handle, u32 units); + +/* + * Mutex primitives + */ +acpi_status acpi_os_create_mutex(acpi_mutex * out_handle); -acpi_status acpi_os_signal_semaphore(acpi_handle handle, u32 units); +void acpi_os_delete_mutex(acpi_mutex handle); -acpi_status acpi_os_create_lock(acpi_handle * out_handle); +acpi_status acpi_os_acquire_mutex(acpi_mutex handle, u16 timeout); -void acpi_os_delete_lock(acpi_handle handle); +void acpi_os_release_mutex(acpi_mutex handle); -acpi_cpu_flags acpi_os_acquire_lock(acpi_handle handle); +/* Temporary macros for Mutex* interfaces, map to existing semaphore xfaces */ -void acpi_os_release_lock(acpi_handle handle, acpi_cpu_flags flags); +#define acpi_os_create_mutex(out_handle) acpi_os_create_semaphore (1, 1, out_handle) +#define acpi_os_delete_mutex(handle) (void) acpi_os_delete_semaphore (handle) +#define acpi_os_acquire_mutex(handle,time) acpi_os_wait_semaphore (handle, 1, time) +#define acpi_os_release_mutex(handle) (void) acpi_os_signal_semaphore (handle, 1) /* * Memory allocation and mapping */ void *acpi_os_allocate(acpi_size size); -void acpi_os_free(void *memory); - acpi_status acpi_os_map_memory(acpi_physical_address physical_address, acpi_size size, void __iomem ** logical_address); @@ -161,13 +185,11 @@ acpi_os_remove_interrupt_handler(u32 gsi /* * Threads and Scheduling */ -u32 acpi_os_get_thread_id(void); +acpi_thread_id acpi_os_get_thread_id(void); acpi_status -acpi_os_queue_for_execution(u32 priority, - acpi_osd_exec_callback function, void *context); - -void acpi_os_wait_events_complete(void *context); +acpi_os_execute(acpi_execute_type type, + acpi_osd_exec_callback function, void *context); void acpi_os_wait_events_complete(void *context); @@ -214,6 +236,12 @@ acpi_os_derive_pci_id(acpi_handle rhandl /* * Miscellaneous */ +acpi_status acpi_os_validate_interface(char *interface); + +acpi_status +acpi_os_validate_address(u8 space_id, + acpi_physical_address address, acpi_size length); + u8 acpi_os_readable(void *pointer, acpi_size length); #ifdef ACPI_FUTURE_USAGE @@ -255,11 +283,4 @@ char *acpi_os_get_next_filename(void *di void acpi_os_close_directory(void *dir_handle); -/* - * Debug - */ -void -acpi_os_dbg_assert(void *failed_assertion, - void *file_name, u32 line_number, char *message); - #endif /* __ACPIOSXF_H__ */ diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 66cf2ec..049e9aa 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -268,7 +268,7 @@ acpi_status acpi_remove_gpe_block(acpi_h * Resource interfaces */ typedef -acpi_status(*ACPI_WALK_RESOURCE_CALLBACK) (struct acpi_resource * resource, +acpi_status(*acpi_walk_resource_callback) (struct acpi_resource * resource, void *context); acpi_status @@ -290,7 +290,7 @@ #endif acpi_status acpi_walk_resources(acpi_handle device_handle, char *name, - ACPI_WALK_RESOURCE_CALLBACK user_function, void *context); + acpi_walk_resource_callback user_function, void *context); acpi_status acpi_set_current_resources(acpi_handle device_handle, diff --git a/include/acpi/acresrc.h b/include/acpi/acresrc.h index fa02e80..ad11fc1 100644 --- a/include/acpi/acresrc.h +++ b/include/acpi/acresrc.h @@ -164,23 +164,26 @@ acpi_rs_create_pci_routing_table(union a /* * rsutils */ + acpi_status -acpi_rs_get_prt_method_data(acpi_handle handle, struct acpi_buffer *ret_buffer); +acpi_rs_get_prt_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); acpi_status -acpi_rs_get_crs_method_data(acpi_handle handle, struct acpi_buffer *ret_buffer); +acpi_rs_get_crs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); -#ifdef ACPI_FUTURE_USAGE acpi_status -acpi_rs_get_prs_method_data(acpi_handle handle, struct acpi_buffer *ret_buffer); -#endif /* ACPI_FUTURE_USAGE */ +acpi_rs_get_prs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); acpi_status acpi_rs_get_method_data(acpi_handle handle, char *path, struct acpi_buffer *ret_buffer); acpi_status -acpi_rs_set_srs_method_data(acpi_handle handle, struct acpi_buffer *ret_buffer); +acpi_rs_set_srs_method_data(struct acpi_namespace_node *node, + struct acpi_buffer *ret_buffer); /* * rscalc @@ -198,8 +201,9 @@ acpi_rs_get_pci_routing_table_length(uni acpi_size * buffer_size_needed); acpi_status -acpi_rs_convert_aml_to_resources(u8 * aml_buffer, - u32 aml_buffer_length, u8 * output_buffer); +acpi_rs_convert_aml_to_resources(u8 * aml, + u32 length, + u32 offset, u8 resource_index, void **context); acpi_status acpi_rs_convert_resources_to_aml(struct acpi_resource *resource, diff --git a/include/acpi/acstruct.h b/include/acpi/acstruct.h index d8c1c2c..5e8095f 100644 --- a/include/acpi/acstruct.h +++ b/include/acpi/acstruct.h @@ -44,6 +44,8 @@ #ifndef __ACSTRUCT_H__ #define __ACSTRUCT_H__ +/* acpisrc:struct_defs -- for acpisrc conversion */ + /***************************************************************************** * * Tree walking typedefs and structs @@ -51,67 +53,76 @@ #define __ACSTRUCT_H__ ****************************************************************************/ /* - * Walk state - current state of a parse tree walk. Used for both a leisurely stroll through - * the tree (for whatever reason), and for control method execution. + * Walk state - current state of a parse tree walk. Used for both a leisurely + * stroll through the tree (for whatever reason), and for control method + * execution. */ #define ACPI_NEXT_OP_DOWNWARD 1 #define ACPI_NEXT_OP_UPWARD 2 +/* + * Groups of definitions for walk_type used for different implementations of + * walkers (never simultaneously) - flags for interpreter: + */ #define ACPI_WALK_NON_METHOD 0 -#define ACPI_WALK_METHOD 1 -#define ACPI_WALK_METHOD_RESTART 2 -#define ACPI_WALK_CONST_REQUIRED 3 -#define ACPI_WALK_CONST_OPTIONAL 4 +#define ACPI_WALK_METHOD 0x01 +#define ACPI_WALK_METHOD_RESTART 0x02 + +/* Flags for i_aSL compiler only */ + +#define ACPI_WALK_CONST_REQUIRED 0x10 +#define ACPI_WALK_CONST_OPTIONAL 0x20 struct acpi_walk_state { - u8 data_type; /* To differentiate various internal objs MUST BE FIRST! */ + struct acpi_walk_state *next; /* Next walk_state in list */ + u8 descriptor_type; /* To differentiate various internal objs */ u8 walk_type; - acpi_owner_id owner_id; /* Owner of objects created during the walk */ - u8 last_predicate; /* Result of last predicate */ - u8 current_result; /* */ + u16 opcode; /* Current AML opcode */ u8 next_op_info; /* Info about next_op */ u8 num_operands; /* Stack pointer for Operands[] array */ + acpi_owner_id owner_id; /* Owner of objects created during the walk */ + u8 last_predicate; /* Result of last predicate */ + u8 current_result; u8 return_used; - u16 opcode; /* Current AML opcode */ u8 scope_depth; u8 pass_number; /* Parse pass during table load */ - u32 arg_count; /* push for fixed or var args */ u32 aml_offset; u32 arg_types; u32 method_breakpoint; /* For single stepping */ u32 user_breakpoint; /* User AML breakpoint */ u32 parse_flags; + + struct acpi_parse_state parser_state; /* Current state of parser */ u32 prev_arg_types; + u32 arg_count; /* push for fixed or var args */ - u8 *aml_last_while; struct acpi_namespace_node arguments[ACPI_METHOD_NUM_ARGS]; /* Control method arguments */ + struct acpi_namespace_node local_variables[ACPI_METHOD_NUM_LOCALS]; /* Control method locals */ + union acpi_operand_object *operands[ACPI_OBJ_NUM_OPERANDS + 1]; /* Operands passed to the interpreter (+1 for NULL terminator) */ + union acpi_operand_object **params; + + u8 *aml_last_while; union acpi_operand_object **caller_return_desc; union acpi_generic_state *control_state; /* List of control states (nested IFs) */ struct acpi_namespace_node *deferred_node; /* Used when executing deferred opcodes */ struct acpi_gpe_event_info *gpe_event_info; /* Info for GPE (_Lxx/_Exx methods only */ union acpi_operand_object *implicit_return_obj; - struct acpi_namespace_node local_variables[ACPI_METHOD_NUM_LOCALS]; /* Control method locals */ struct acpi_namespace_node *method_call_node; /* Called method Node */ union acpi_parse_object *method_call_op; /* method_call Op if running a method */ union acpi_operand_object *method_desc; /* Method descriptor if running a method */ struct acpi_namespace_node *method_node; /* Method node if running a method. */ union acpi_parse_object *op; /* Current parser op */ - union acpi_operand_object *operands[ACPI_OBJ_NUM_OPERANDS + 1]; /* Operands passed to the interpreter (+1 for NULL terminator) */ const struct acpi_opcode_info *op_info; /* Info on current opcode */ union acpi_parse_object *origin; /* Start of walk [Obsolete] */ - union acpi_operand_object **params; - struct acpi_parse_state parser_state; /* Current state of parser */ union acpi_operand_object *result_obj; union acpi_generic_state *results; /* Stack of accumulated results */ union acpi_operand_object *return_desc; /* Return object, if any */ union acpi_generic_state *scope_info; /* Stack of nested scopes */ - union acpi_parse_object *prev_op; /* Last op that was processed */ union acpi_parse_object *next_op; /* next op to be processed */ + struct acpi_thread_state *thread; acpi_parse_downwards descending_callback; acpi_parse_upwards ascending_callback; - struct acpi_thread_state *thread; - struct acpi_walk_state *next; /* Next walk_state in list */ }; /* Info used by acpi_ps_init_objects */ @@ -131,32 +142,6 @@ struct acpi_init_walk_info { struct acpi_table_desc *table_desc; }; -/* Info used by acpi_ns_initialize_devices */ - -struct acpi_device_walk_info { - u16 device_count; - u16 num_STA; - u16 num_INI; - struct acpi_table_desc *table_desc; -}; - -/* TBD: [Restructure] Merge with struct above */ - -struct acpi_walk_info { - u32 debug_level; - u32 count; - acpi_owner_id owner_id; - u8 display_type; -}; - -/* Display Types */ - -#define ACPI_DISPLAY_SUMMARY (u8) 0 -#define ACPI_DISPLAY_OBJECTS (u8) 1 -#define ACPI_DISPLAY_MASK (u8) 1 - -#define ACPI_DISPLAY_SHORT (u8) 2 - struct acpi_get_devices_info { acpi_walk_callback user_function; void *context; @@ -189,16 +174,21 @@ union acpi_aml_operands { } mid; }; -/* Internal method parameter list */ - -struct acpi_parameter_info { - struct acpi_namespace_node *node; +/* + * Structure used to pass object evaluation parameters. + * Purpose is to reduce CPU stack use. + */ +struct acpi_evaluate_info { + struct acpi_namespace_node *prefix_node; + char *pathname; union acpi_operand_object *obj_desc; union acpi_operand_object **parameters; + struct acpi_namespace_node *resolved_node; union acpi_operand_object *return_object; u8 pass_number; u8 parameter_type; u8 return_object_type; + u8 flags; }; /* Types for parameter_type above */ @@ -206,4 +196,35 @@ struct acpi_parameter_info { #define ACPI_PARAM_ARGS 0 #define ACPI_PARAM_GPE 1 +/* Values for Flags above */ + +#define ACPI_IGNORE_RETURN_VALUE 1 + +/* Info used by acpi_ns_initialize_devices */ + +struct acpi_device_walk_info { + u16 device_count; + u16 num_STA; + u16 num_INI; + struct acpi_table_desc *table_desc; + struct acpi_evaluate_info *evaluate_info; +}; + +/* TBD: [Restructure] Merge with struct above */ + +struct acpi_walk_info { + u32 debug_level; + u32 count; + acpi_owner_id owner_id; + u8 display_type; +}; + +/* Display Types */ + +#define ACPI_DISPLAY_SUMMARY (u8) 0 +#define ACPI_DISPLAY_OBJECTS (u8) 1 +#define ACPI_DISPLAY_MASK (u8) 1 + +#define ACPI_DISPLAY_SHORT (u8) 2 + #endif diff --git a/include/acpi/actables.h b/include/acpi/actables.h index 30a4754..4dbaf02 100644 --- a/include/acpi/actables.h +++ b/include/acpi/actables.h @@ -136,7 +136,11 @@ acpi_status acpi_tb_is_table_installed(s acpi_status acpi_tb_verify_table_checksum(struct acpi_table_header *table_header); -u8 acpi_tb_generate_checksum(void *buffer, u32 length); +u8 acpi_tb_sum_table(void *buffer, u32 length); + +u8 acpi_tb_generate_checksum(struct acpi_table_header *table); + +void acpi_tb_set_checksum(struct acpi_table_header *table); acpi_status acpi_tb_validate_table_header(struct acpi_table_header *table_header); diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index ed53f84..b125cee 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Name: actbl.h - Table data structures defined in ACPI specification + * Name: actbl.h - Basic ACPI Table Definitions * *****************************************************************************/ @@ -45,66 +45,45 @@ #ifndef __ACTBL_H__ #define __ACTBL_H__ /* - * Note about bitfields: The u8 type is used for bitfields in ACPI tables. - * This is the only type that is even remotely portable. Anything else is not - * portable, so do not use any other bitfield types. - */ - -/* - * Values for description table header signatures + * Values for description table header signatures. Useful because they make + * it more difficult to inadvertently type in the wrong signature. */ -#define RSDP_NAME "RSDP" -#define RSDP_SIG "RSD PTR " /* RSDT Pointer signature */ -#define APIC_SIG "APIC" /* Multiple APIC Description Table */ #define DSDT_SIG "DSDT" /* Differentiated System Description Table */ #define FADT_SIG "FACP" /* Fixed ACPI Description Table */ #define FACS_SIG "FACS" /* Firmware ACPI Control Structure */ #define PSDT_SIG "PSDT" /* Persistent System Description Table */ +#define RSDP_SIG "RSD PTR " /* Root System Description Pointer */ #define RSDT_SIG "RSDT" /* Root System Description Table */ #define XSDT_SIG "XSDT" /* Extended System Description Table */ #define SSDT_SIG "SSDT" /* Secondary System Description Table */ -#define SBST_SIG "SBST" /* Smart Battery Specification Table */ -#define SPIC_SIG "SPIC" /* IOSAPIC table */ -#define BOOT_SIG "BOOT" /* Boot table */ - -#define GL_OWNED 0x02 /* Ownership of global lock is bit 1 */ +#define RSDP_NAME "RSDP" /* - * Common table types. The base code can remain - * constant if the underlying tables are changed + * All tables and structures must be byte-packed to match the ACPI + * specification, since the tables are provided by the system BIOS */ -#define RSDT_DESCRIPTOR struct rsdt_descriptor_rev2 -#define XSDT_DESCRIPTOR struct xsdt_descriptor_rev2 -#define FACS_DESCRIPTOR struct facs_descriptor_rev2 -#define FADT_DESCRIPTOR struct fadt_descriptor_rev2 - #pragma pack(1) /* - * ACPI Version-independent tables + * These are the ACPI tables that are directly consumed by the subsystem. + * + * The RSDP and FACS do not use the common ACPI table header. All other ACPI + * tables use the header. * - * NOTE: The tables that are specific to ACPI versions (1.0, 2.0, etc.) - * are in separate files. + * Note about bitfields: The u8 type is used for bitfields in ACPI tables. + * This is the only type that is even remotely portable. Anything else is not + * portable, so do not use any other bitfield types. */ -struct rsdp_descriptor { /* Root System Descriptor Pointer */ - char signature[8]; /* ACPI signature, contains "RSD PTR " */ - u8 checksum; /* ACPI 1.0 checksum */ - char oem_id[6]; /* OEM identification */ - u8 revision; /* Must be (0) for ACPI 1.0 or (2) for ACPI 2.0+ */ - u32 rsdt_physical_address; /* 32-bit physical address of the RSDT */ - u32 length; /* XSDT Length in bytes, including header */ - u64 xsdt_physical_address; /* 64-bit physical address of the XSDT */ - u8 extended_checksum; /* Checksum of entire table (ACPI 2.0) */ - char reserved[3]; /* Reserved, must be zero */ -}; -struct acpi_common_facs { /* Common FACS for internal use */ - u32 *global_lock; - u64 *firmware_waking_vector; - u8 vector_width; -}; +/******************************************************************************* + * + * ACPI Table Header. This common header is used by all tables except the + * RSDP and FACS. The define is used for direct inclusion of header into + * other ACPI tables + * + ******************************************************************************/ -#define ACPI_TABLE_HEADER_DEF /* ACPI common table header */ \ +#define ACPI_TABLE_HEADER_DEF \ char signature[4]; /* ASCII table signature */\ u32 length; /* Length of table in bytes, including this header */\ u8 revision; /* ACPI Specification minor version # */\ @@ -112,154 +91,239 @@ #define ACPI_TABLE_HEADER_DEF /* ACPI char oem_id[6]; /* ASCII OEM identification */\ char oem_table_id[8]; /* ASCII OEM table identification */\ u32 oem_revision; /* OEM revision number */\ - char asl_compiler_id [4]; /* ASCII ASL compiler vendor ID */\ + char asl_compiler_id[4]; /* ASCII ASL compiler vendor ID */\ u32 asl_compiler_revision; /* ASL compiler version */ -struct acpi_table_header { /* ACPI common table header */ +struct acpi_table_header { ACPI_TABLE_HEADER_DEF}; /* - * MADT values and structures + * GAS - Generic Address Structure (ACPI 2.0+) */ +struct acpi_generic_address { + u8 address_space_id; /* Address space where struct or register exists */ + u8 register_bit_width; /* Size in bits of given register */ + u8 register_bit_offset; /* Bit offset within the register */ + u8 access_width; /* Minimum Access size (ACPI 3.0) */ + u64 address; /* 64-bit address of struct or register */ +}; -/* Values for MADT PCATCompat */ +/******************************************************************************* + * + * RSDP - Root System Description Pointer (Signature is "RSD PTR ") + * + ******************************************************************************/ + +struct rsdp_descriptor { + char signature[8]; /* ACPI signature, contains "RSD PTR " */ + u8 checksum; /* ACPI 1.0 checksum */ + char oem_id[6]; /* OEM identification */ + u8 revision; /* Must be (0) for ACPI 1.0 or (2) for ACPI 2.0+ */ + u32 rsdt_physical_address; /* 32-bit physical address of the RSDT */ + u32 length; /* Table length in bytes, including header (ACPI 2.0+) */ + u64 xsdt_physical_address; /* 64-bit physical address of the XSDT (ACPI 2.0+) */ + u8 extended_checksum; /* Checksum of entire table (ACPI 2.0+) */ + u8 reserved[3]; /* Reserved, must be zero */ +}; -#define DUAL_PIC 0 -#define MULTIPLE_APIC 1 +#define ACPI_RSDP_REV0_SIZE 20 /* Size of original ACPI 1.0 RSDP */ -/* Master MADT */ +/******************************************************************************* + * + * RSDT/XSDT - Root System Description Tables + * + ******************************************************************************/ -struct multiple_apic_table { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - u32 local_apic_address; /* Physical address of local APIC */ +struct rsdt_descriptor { + ACPI_TABLE_HEADER_DEF u32 table_offset_entry[1]; /* Array of pointers to ACPI tables */ +}; + +struct xsdt_descriptor { + ACPI_TABLE_HEADER_DEF u64 table_offset_entry[1]; /* Array of pointers to ACPI tables */ +}; + +/******************************************************************************* + * + * FACS - Firmware ACPI Control Structure (FACS) + * + ******************************************************************************/ + +struct facs_descriptor { + char signature[4]; /* ASCII table signature */ + u32 length; /* Length of structure, in bytes */ + u32 hardware_signature; /* Hardware configuration signature */ + u32 firmware_waking_vector; /* 32-bit physical address of the Firmware Waking Vector */ + u32 global_lock; /* Global Lock for shared hardware resources */ /* Flags (32 bits) */ - u8 PCATcompat:1; /* 00: System also has dual 8259s */ + u8 S4bios_f:1; /* 00: S4BIOS support is present */ u8:7; /* 01-07: Reserved, must be zero */ u8 reserved1[3]; /* 08-31: Reserved, must be zero */ -}; -/* Values for Type in APIC_HEADER_DEF */ + u64 xfirmware_waking_vector; /* 64-bit version of the Firmware Waking Vector (ACPI 2.0+) */ + u8 version; /* Version of this table (ACPI 2.0+) */ + u8 reserved[31]; /* Reserved, must be zero */ +}; -#define APIC_PROCESSOR 0 -#define APIC_IO 1 -#define APIC_XRUPT_OVERRIDE 2 -#define APIC_NMI 3 -#define APIC_LOCAL_NMI 4 -#define APIC_ADDRESS_OVERRIDE 5 -#define APIC_IO_SAPIC 6 -#define APIC_LOCAL_SAPIC 7 -#define APIC_XRUPT_SOURCE 8 -#define APIC_RESERVED 9 /* 9 and greater are reserved */ +#define ACPI_GLOCK_PENDING 0x01 /* 00: Pending global lock ownership */ +#define ACPI_GLOCK_OWNED 0x02 /* 01: Global lock is owned */ /* - * MADT sub-structures (Follow MULTIPLE_APIC_DESCRIPTION_TABLE) + * Common FACS - This is a version-independent FACS structure used for internal use only */ -#define APIC_HEADER_DEF /* Common APIC sub-structure header */\ - u8 type; \ - u8 length; - -struct apic_header { -APIC_HEADER_DEF}; - -/* Values for MPS INTI flags */ - -#define POLARITY_CONFORMS 0 -#define POLARITY_ACTIVE_HIGH 1 -#define POLARITY_RESERVED 2 -#define POLARITY_ACTIVE_LOW 3 - -#define TRIGGER_CONFORMS 0 -#define TRIGGER_EDGE 1 -#define TRIGGER_RESERVED 2 -#define TRIGGER_LEVEL 3 - -/* Common flag definitions (16 bits each) */ - -#define MPS_INTI_FLAGS \ - u8 polarity : 2; /* 00-01: Polarity of APIC I/O input signals */\ - u8 trigger_mode : 2; /* 02-03: Trigger mode of APIC input signals */\ - u8 : 4; /* 04-07: Reserved, must be zero */\ - u8 reserved1; /* 08-15: Reserved, must be zero */ - -#define LOCAL_APIC_FLAGS \ - u8 processor_enabled: 1; /* 00: Processor is usable if set */\ - u8 : 7; /* 01-07: Reserved, must be zero */\ - u8 reserved2; /* 08-15: Reserved, must be zero */ - -/* Sub-structures for MADT */ - -struct madt_processor_apic { - APIC_HEADER_DEF u8 processor_id; /* ACPI processor id */ - u8 local_apic_id; /* Processor's local APIC id */ - LOCAL_APIC_FLAGS}; - -struct madt_io_apic { - APIC_HEADER_DEF u8 io_apic_id; /* I/O APIC ID */ - u8 reserved; /* Reserved - must be zero */ - u32 address; /* APIC physical address */ - u32 interrupt; /* Global system interrupt where INTI - * lines start */ +struct acpi_common_facs { + u32 *global_lock; + u64 *firmware_waking_vector; + u8 vector_width; }; -struct madt_interrupt_override { - APIC_HEADER_DEF u8 bus; /* 0 - ISA */ - u8 source; /* Interrupt source (IRQ) */ - u32 interrupt; /* Global system interrupt */ - MPS_INTI_FLAGS}; +/******************************************************************************* + * + * FADT - Fixed ACPI Description Table (Signature "FACP") + * + ******************************************************************************/ + +/* Fields common to all versions of the FADT */ + +#define ACPI_FADT_COMMON \ + ACPI_TABLE_HEADER_DEF \ + u32 V1_firmware_ctrl; /* 32-bit physical address of FACS */ \ + u32 V1_dsdt; /* 32-bit physical address of DSDT */ \ + u8 reserved1; /* System Interrupt Model isn't used in ACPI 2.0*/ \ + u8 prefer_PM_profile; /* Conveys preferred power management profile to OSPM. */ \ + u16 sci_int; /* System vector of SCI interrupt */ \ + u32 smi_cmd; /* Port address of SMI command port */ \ + u8 acpi_enable; /* Value to write to smi_cmd to enable ACPI */ \ + u8 acpi_disable; /* Value to write to smi_cmd to disable ACPI */ \ + u8 S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ \ + u8 pstate_cnt; /* Processor performance state control*/ \ + u32 V1_pm1a_evt_blk; /* Port address of Power Mgt 1a Event Reg Blk */ \ + u32 V1_pm1b_evt_blk; /* Port address of Power Mgt 1b Event Reg Blk */ \ + u32 V1_pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ \ + u32 V1_pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ \ + u32 V1_pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ \ + u32 V1_pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ \ + u32 V1_gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ \ + u32 V1_gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ \ + u8 pm1_evt_len; /* Byte Length of ports at pm1_x_evt_blk */ \ + u8 pm1_cnt_len; /* Byte Length of ports at pm1_x_cnt_blk */ \ + u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ \ + u8 pm_tm_len; /* Byte Length of ports at pm_tm_blk */ \ + u8 gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ \ + u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ \ + u8 gpe1_base; /* Offset in gpe model where gpe1 events start */ \ + u8 cst_cnt; /* Support for the _CST object and C States change notification.*/ \ + u16 plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ \ + u16 plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ \ + u16 flush_size; /* Processor's memory cache line width, in bytes */ \ + u16 flush_stride; /* Number of flush strides that need to be read */ \ + u8 duty_offset; /* Processor's duty cycle index in processor's P_CNT reg*/ \ + u8 duty_width; /* Processor's duty cycle value bit width in P_CNT register.*/ \ + u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ \ + u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ \ + u8 century; /* Index to century in RTC CMOS RAM */ \ + u16 iapc_boot_arch; /* IA-PC Boot Architecture Flags. See Table 5-10 for description*/ \ + u8 reserved2; /* Reserved, must be zero */ -struct madt_nmi_source { - APIC_HEADER_DEF MPS_INTI_FLAGS u32 interrupt; /* Global system interrupt */ +/* + * ACPI 2.0+ FADT + */ +struct fadt_descriptor { + ACPI_FADT_COMMON + /* Flags (32 bits) */ + u8 wb_invd:1; /* 00: The wbinvd instruction works properly */ + u8 wb_invd_flush:1; /* 01: The wbinvd flushes but does not invalidate */ + u8 proc_c1:1; /* 02: All processors support C1 state */ + u8 plvl2_up:1; /* 03: C2 state works on MP system */ + u8 pwr_button:1; /* 04: Power button is handled as a generic feature */ + u8 sleep_button:1; /* 05: Sleep button is handled as a generic feature, or not present */ + u8 fixed_rTC:1; /* 06: RTC wakeup stat not in fixed register space */ + u8 rtcs4:1; /* 07: RTC wakeup stat not possible from S4 */ + u8 tmr_val_ext:1; /* 08: tmr_val is 32 bits 0=24-bits */ + u8 dock_cap:1; /* 09: Docking supported */ + u8 reset_reg_sup:1; /* 10: System reset via the FADT RESET_REG supported */ + u8 sealed_case:1; /* 11: No internal expansion capabilities and case is sealed */ + u8 headless:1; /* 12: No local video capabilities or local input devices */ + u8 cpu_sw_sleep:1; /* 13: Must execute native instruction after writing SLP_TYPx register */ + + u8 pci_exp_wak:1; /* 14: System supports PCIEXP_WAKE (STS/EN) bits (ACPI 3.0) */ + u8 use_platform_clock:1; /* 15: OSPM should use platform-provided timer (ACPI 3.0) */ + u8 S4rtc_sts_valid:1; /* 16: Contents of RTC_STS valid after S4 wake (ACPI 3.0) */ + u8 remote_power_on_capable:1; /* 17: System is compatible with remote power on (ACPI 3.0) */ + u8 force_apic_cluster_model:1; /* 18: All local APICs must use cluster model (ACPI 3.0) */ + u8 force_apic_physical_destination_mode:1; /* 19: All local x_aPICs must use physical dest mode (ACPI 3.0) */ + u8:4; /* 20-23: Reserved, must be zero */ + u8 reserved3; /* 24-31: Reserved, must be zero */ + + struct acpi_generic_address reset_register; /* Reset register address in GAS format */ + u8 reset_value; /* Value to write to the reset_register port to reset the system */ + u8 reserved4[3]; /* These three bytes must be zero */ + u64 xfirmware_ctrl; /* 64-bit physical address of FACS */ + u64 Xdsdt; /* 64-bit physical address of DSDT */ + struct acpi_generic_address xpm1a_evt_blk; /* Extended Power Mgt 1a acpi_event Reg Blk address */ + struct acpi_generic_address xpm1b_evt_blk; /* Extended Power Mgt 1b acpi_event Reg Blk address */ + struct acpi_generic_address xpm1a_cnt_blk; /* Extended Power Mgt 1a Control Reg Blk address */ + struct acpi_generic_address xpm1b_cnt_blk; /* Extended Power Mgt 1b Control Reg Blk address */ + struct acpi_generic_address xpm2_cnt_blk; /* Extended Power Mgt 2 Control Reg Blk address */ + struct acpi_generic_address xpm_tmr_blk; /* Extended Power Mgt Timer Ctrl Reg Blk address */ + struct acpi_generic_address xgpe0_blk; /* Extended General Purpose acpi_event 0 Reg Blk address */ + struct acpi_generic_address xgpe1_blk; /* Extended General Purpose acpi_event 1 Reg Blk address */ }; -struct madt_local_apic_nmi { - APIC_HEADER_DEF u8 processor_id; /* ACPI processor id */ - MPS_INTI_FLAGS u8 lint; /* LINTn to which NMI is connected */ +/* + * "Down-revved" ACPI 2.0 FADT descriptor + * Defined here to allow compiler to generate the length of the struct + */ +struct fadt_descriptor_rev2_minus { + ACPI_FADT_COMMON u32 flags; + struct acpi_generic_address reset_register; /* Reset register address in GAS format */ + u8 reset_value; /* Value to write to the reset_register port to reset the system. */ + u8 reserved7[3]; /* Reserved, must be zero */ }; -struct madt_address_override { - APIC_HEADER_DEF u16 reserved; /* Reserved, must be zero */ - u64 address; /* APIC physical address */ +/* + * ACPI 1.0 FADT + * Defined here to allow compiler to generate the length of the struct + */ +struct fadt_descriptor_rev1 { + ACPI_FADT_COMMON u32 flags; }; -struct madt_io_sapic { - APIC_HEADER_DEF u8 io_sapic_id; /* I/O SAPIC ID */ - u8 reserved; /* Reserved, must be zero */ - u32 interrupt_base; /* Glocal interrupt for SAPIC start */ - u64 address; /* SAPIC physical address */ -}; +/* FADT: Prefered Power Management Profiles */ -struct madt_local_sapic { - APIC_HEADER_DEF u8 processor_id; /* ACPI processor id */ - u8 local_sapic_id; /* SAPIC ID */ - u8 local_sapic_eid; /* SAPIC EID */ - u8 reserved[3]; /* Reserved, must be zero */ - LOCAL_APIC_FLAGS u32 processor_uID; /* Numeric UID - ACPI 3.0 */ - char processor_uIDstring[1]; /* String UID - ACPI 3.0 */ -}; +#define PM_UNSPECIFIED 0 +#define PM_DESKTOP 1 +#define PM_MOBILE 2 +#define PM_WORKSTATION 3 +#define PM_ENTERPRISE_SERVER 4 +#define PM_SOHO_SERVER 5 +#define PM_APPLIANCE_PC 6 -struct madt_interrupt_source { - APIC_HEADER_DEF MPS_INTI_FLAGS u8 interrupt_type; /* 1=PMI, 2=INIT, 3=corrected */ - u8 processor_id; /* Processor ID */ - u8 processor_eid; /* Processor EID */ - u8 io_sapic_vector; /* Vector value for PMI interrupts */ - u32 interrupt; /* Global system interrupt */ - u32 flags; /* Interrupt Source Flags */ -}; +/* FADT: Boot Arch Flags */ -/* - * Smart Battery - */ -struct smart_battery_table { - ACPI_TABLE_HEADER_DEF u32 warning_level; - u32 low_level; - u32 critical_level; -}; +#define BAF_LEGACY_DEVICES 0x0001 +#define BAF_8042_KEYBOARD_CONTROLLER 0x0002 + +#define FADT2_REVISION_ID 3 +#define FADT2_MINUS_REVISION_ID 2 + +/* Reset to default packing */ #pragma pack() /* + * This macro is temporary until the table bitfield flag definitions + * are removed and replaced by a Flags field. + */ +#define ACPI_FLAG_OFFSET(d,f,o) (u8) (ACPI_OFFSET (d,f) + \ + sizeof(((d *)0)->f) + o) +/* + * Get the remaining ACPI tables + */ +#include "actbl1.h" + +/* * ACPI Table information. We save the table address, length, * and type of memory allocation (mapped or allocated) for each * table for 1) when we exit, and 2) if a new table is installed @@ -290,27 +354,17 @@ struct acpi_table_support { u8 flags; }; -/* - * Get the ACPI version-specific tables - */ -#include "actbl1.h" /* Acpi 1.0 table definitions */ -#include "actbl2.h" /* Acpi 2.0 table definitions */ - extern u8 acpi_fadt_is_v1; /* is set to 1 if FADT is revision 1, * needed for certain workarounds */ +/* Macros used to generate offsets to specific table fields */ -#pragma pack(1) -/* - * High performance timer - */ -struct hpet_table { - ACPI_TABLE_HEADER_DEF u32 hardware_id; - struct acpi_generic_address base_address; - u8 hpet_number; - u16 clock_tick; - u8 attributes; -}; +#define ACPI_FACS_OFFSET(f) (u8) ACPI_OFFSET (struct facs_descriptor,f) +#define ACPI_FADT_OFFSET(f) (u8) ACPI_OFFSET (struct fadt_descriptor, f) +#define ACPI_GAS_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_generic_address,f) +#define ACPI_HDR_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_header,f) +#define ACPI_RSDP_OFFSET(f) (u8) ACPI_OFFSET (struct rsdp_descriptor,f) -#pragma pack() +#define ACPI_FADT_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct fadt_descriptor,f,o) +#define ACPI_FACS_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct facs_descriptor,f,o) #endif /* __ACTBL_H__ */ diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index cd428d5..745a644 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Name: actbl1.h - ACPI 1.0 tables + * Name: actbl1.h - Additional ACPI table definitions * *****************************************************************************/ @@ -44,92 +44,599 @@ #ifndef __ACTBL1_H__ #define __ACTBL1_H__ +/******************************************************************************* + * + * Additional ACPI Tables + * + * These tables are not consumed directly by the ACPICA subsystem, but are + * included here to support device drivers and the AML disassembler. + * + ******************************************************************************/ + +/* + * Values for description table header signatures. Useful because they make + * it more difficult to inadvertently type in the wrong signature. + */ +#define ACPI_SIG_ASF "ASF!" /* Alert Standard Format table */ +#define ACPI_SIG_BOOT "BOOT" /* Simple Boot Flag Table */ +#define ACPI_SIG_CPEP "CPEP" /* Corrected Platform Error Polling table */ +#define ACPI_SIG_DBGP "DBGP" /* Debug Port table */ +#define ACPI_SIG_ECDT "ECDT" /* Embedded Controller Boot Resources Table */ +#define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ +#define ACPI_SIG_MADT "APIC" /* Multiple APIC Description Table */ +#define ACPI_SIG_MCFG "MCFG" /* PCI Memory Mapped Configuration table */ +#define ACPI_SIG_SBST "SBST" /* Smart Battery Specification Table */ +#define ACPI_SIG_SLIT "SLIT" /* System Locality Distance Information Table */ +#define ACPI_SIG_SPCR "SPCR" /* Serial Port Console Redirection table */ +#define ACPI_SIG_SPMI "SPMI" /* Server Platform Management Interface table */ +#define ACPI_SIG_SRAT "SRAT" /* System Resource Affinity Table */ +#define ACPI_SIG_TCPA "TCPA" /* Trusted Computing Platform Alliance table */ +#define ACPI_SIG_WDRT "WDRT" /* Watchdog Resource Table */ + +/* Legacy names */ + +#define APIC_SIG "APIC" /* Multiple APIC Description Table */ +#define BOOT_SIG "BOOT" /* Simple Boot Flag Table */ +#define SBST_SIG "SBST" /* Smart Battery Specification Table */ + +/* + * All tables must be byte-packed to match the ACPI specification, since + * the tables are provided by the system BIOS. + */ #pragma pack(1) /* - * ACPI 1.0 Root System Description Table (RSDT) + * Note about bitfields: The u8 type is used for bitfields in ACPI tables. + * This is the only type that is even remotely portable. Anything else is not + * portable, so do not use any other bitfield types. */ -struct rsdt_descriptor_rev1 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - u32 table_offset_entry[1]; /* Array of pointers to ACPI tables */ -}; + +/******************************************************************************* + * + * ASF - Alert Standard Format table (Signature "ASF!") + * + ******************************************************************************/ + +struct acpi_table_asf { +ACPI_TABLE_HEADER_DEF}; + +#define ACPI_ASF_HEADER_DEF \ + u8 type; \ + u8 reserved; \ + u16 length; + +struct acpi_asf_header { +ACPI_ASF_HEADER_DEF}; + +/* Values for Type field */ + +#define ASF_INFO 0 +#define ASF_ALERT 1 +#define ASF_CONTROL 2 +#define ASF_BOOT 3 +#define ASF_ADDRESS 4 +#define ASF_RESERVED 5 /* - * ACPI 1.0 Firmware ACPI Control Structure (FACS) + * ASF subtables */ -struct facs_descriptor_rev1 { - char signature[4]; /* ASCII table signature */ - u32 length; /* Length of structure in bytes */ - u32 hardware_signature; /* Hardware configuration signature */ - u32 firmware_waking_vector; /* ACPI OS waking vector */ - u32 global_lock; /* Global Lock */ + +/* 0: ASF Information */ + +struct acpi_asf_info { + ACPI_ASF_HEADER_DEF u8 min_reset_value; + u8 min_poll_interval; + u16 system_id; + u32 mfg_id; + u8 flags; + u8 reserved2[3]; +}; + +/* 1: ASF Alerts */ + +struct acpi_asf_alert { + ACPI_ASF_HEADER_DEF u8 assert_mask; + u8 deassert_mask; + u8 alerts; + u8 data_length; + u8 array[1]; +}; + +/* 2: ASF Remote Control */ + +struct acpi_asf_remote { + ACPI_ASF_HEADER_DEF u8 controls; + u8 data_length; + u16 reserved2; + u8 array[1]; +}; + +/* 3: ASF RMCP Boot Options */ + +struct acpi_asf_rmcp { + ACPI_ASF_HEADER_DEF u8 capabilities[7]; + u8 completion_code; + u32 enterprise_id; + u8 command; + u16 parameter; + u16 boot_options; + u16 oem_parameters; +}; + +/* 4: ASF Address */ + +struct acpi_asf_address { + ACPI_ASF_HEADER_DEF u8 eprom_address; + u8 devices; + u8 smbus_addresses[1]; +}; + +/******************************************************************************* + * + * BOOT - Simple Boot Flag Table + * + ******************************************************************************/ + +struct acpi_table_boot { + ACPI_TABLE_HEADER_DEF u8 cmos_index; /* Index in CMOS RAM for the boot register */ + u8 reserved[3]; +}; + +/******************************************************************************* + * + * CPEP - Corrected Platform Error Polling table + * + ******************************************************************************/ + +struct acpi_table_cpep { + ACPI_TABLE_HEADER_DEF u64 reserved; +}; + +/* Subtable */ + +struct acpi_cpep_polling { + u8 type; + u8 length; + u8 processor_id; /* Processor ID */ + u8 processor_eid; /* Processor EID */ + u32 polling_interval; /* Polling interval (msec) */ +}; + +/******************************************************************************* + * + * DBGP - Debug Port table + * + ******************************************************************************/ + +struct acpi_table_dbgp { + ACPI_TABLE_HEADER_DEF u8 interface_type; /* 0=full 16550, 1=subset of 16550 */ + u8 reserved[3]; + struct acpi_generic_address debug_port; +}; + +/******************************************************************************* + * + * ECDT - Embedded Controller Boot Resources Table + * + ******************************************************************************/ + +struct ec_boot_resources { + ACPI_TABLE_HEADER_DEF struct acpi_generic_address ec_control; /* Address of EC command/status register */ + struct acpi_generic_address ec_data; /* Address of EC data register */ + u32 uid; /* Unique ID - must be same as the EC _UID method */ + u8 gpe_bit; /* The GPE for the EC */ + u8 ec_id[1]; /* Full namepath of the EC in the ACPI namespace */ +}; + +/******************************************************************************* + * + * HPET - High Precision Event Timer table + * + ******************************************************************************/ + +struct acpi_hpet_table { + ACPI_TABLE_HEADER_DEF u32 hardware_id; /* Hardware ID of event timer block */ + struct acpi_generic_address base_address; /* Address of event timer block */ + u8 hpet_number; /* HPET sequence number */ + u16 clock_tick; /* Main counter min tick, periodic mode */ + u8 attributes; +}; + +#if 0 /* HPET flags to be converted to macros */ +struct { /* Flags (8 bits) */ + u8 page_protect:1; /* 00: No page protection */ + u8 page_protect4:1; /* 01: 4_kB page protected */ + u8 page_protect64:1; /* 02: 64_kB page protected */ + u8:5; /* 03-07: Reserved, must be zero */ +} flags; +#endif + +/******************************************************************************* + * + * MADT - Multiple APIC Description Table + * + ******************************************************************************/ + +struct multiple_apic_table { + ACPI_TABLE_HEADER_DEF u32 local_apic_address; /* Physical address of local APIC */ /* Flags (32 bits) */ - u8 S4bios_f:1; /* 00: S4BIOS support is present */ + u8 PCATcompat:1; /* 00: System also has dual 8259s */ u8:7; /* 01-07: Reserved, must be zero */ u8 reserved1[3]; /* 08-31: Reserved, must be zero */ - - u8 reserved2[40]; /* Reserved, must be zero */ }; +/* Values for MADT PCATCompat */ + +#define DUAL_PIC 0 +#define MULTIPLE_APIC 1 + +/* Common MADT Sub-table header */ + +#define APIC_HEADER_DEF \ + u8 type; \ + u8 length; + +struct apic_header { +APIC_HEADER_DEF}; + +/* Values for Type in struct apic_header */ + +#define APIC_PROCESSOR 0 +#define APIC_IO 1 +#define APIC_XRUPT_OVERRIDE 2 +#define APIC_NMI 3 +#define APIC_LOCAL_NMI 4 +#define APIC_ADDRESS_OVERRIDE 5 +#define APIC_IO_SAPIC 6 +#define APIC_LOCAL_SAPIC 7 +#define APIC_XRUPT_SOURCE 8 +#define APIC_RESERVED 9 /* 9 and greater are reserved */ + +/* Flag definitions for MADT sub-tables */ + +#define ACPI_MADT_IFLAGS /* INTI flags (16 bits) */ \ + u8 polarity : 2; /* 00-01: Polarity of APIC I/O input signals */\ + u8 trigger_mode : 2; /* 02-03: Trigger mode of APIC input signals */\ + u8 : 4; /* 04-07: Reserved, must be zero */\ + u8 reserved1; /* 08-15: Reserved, must be zero */ + +#define ACPI_MADT_LFLAGS /* Local Sapic flags (32 bits) */ \ + u8 processor_enabled: 1; /* 00: Processor is usable if set */\ + u8 : 7; /* 01-07: Reserved, must be zero */\ + u8 reserved2[3]; /* 08-31: Reserved, must be zero */ + +/* Values for MPS INTI flags */ + +#define POLARITY_CONFORMS 0 +#define POLARITY_ACTIVE_HIGH 1 +#define POLARITY_RESERVED 2 +#define POLARITY_ACTIVE_LOW 3 + +#define TRIGGER_CONFORMS 0 +#define TRIGGER_EDGE 1 +#define TRIGGER_RESERVED 2 +#define TRIGGER_LEVEL 3 + /* - * ACPI 1.0 Fixed ACPI Description Table (FADT) + * MADT Sub-tables, correspond to Type in struct apic_header */ -struct fadt_descriptor_rev1 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - u32 firmware_ctrl; /* Physical address of FACS */ - u32 dsdt; /* Physical address of DSDT */ - u8 model; /* System Interrupt Model */ - u8 reserved1; /* Reserved, must be zero */ - u16 sci_int; /* System vector of SCI interrupt */ - u32 smi_cmd; /* Port address of SMI command port */ - u8 acpi_enable; /* Value to write to smi_cmd to enable ACPI */ - u8 acpi_disable; /* Value to write to smi_cmd to disable ACPI */ - u8 S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ - u8 reserved2; /* Reserved, must be zero */ - u32 pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */ - u32 pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */ - u32 pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ - u32 pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ - u32 pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ - u32 pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ - u32 gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ - u32 gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ - u8 pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ - u8 pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ - u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ - u8 pm_tm_len; /* Byte Length of ports at pm_tm_blk */ - u8 gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ - u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ - u8 gpe1_base; /* Offset in gpe model where gpe1 events start */ - u8 reserved3; /* Reserved, must be zero */ - u16 plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ - u16 plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ - u16 flush_size; /* Size of area read to flush caches */ - u16 flush_stride; /* Stride used in flushing caches */ - u8 duty_offset; /* Bit location of duty cycle field in p_cnt reg */ - u8 duty_width; /* Bit width of duty cycle field in p_cnt reg */ - u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ - u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ - u8 century; /* Index to century in RTC CMOS RAM */ - u8 reserved4[3]; /* Reserved, must be zero */ + +/* 0: processor APIC */ + +struct madt_processor_apic { + APIC_HEADER_DEF u8 processor_id; /* ACPI processor id */ + u8 local_apic_id; /* Processor's local APIC id */ + ACPI_MADT_LFLAGS}; + +/* 1: IO APIC */ + +struct madt_io_apic { + APIC_HEADER_DEF u8 io_apic_id; /* I/O APIC ID */ + u8 reserved; /* Reserved - must be zero */ + u32 address; /* APIC physical address */ + u32 interrupt; /* Global system interrupt where INTI lines start */ +}; + +/* 2: Interrupt Override */ + +struct madt_interrupt_override { + APIC_HEADER_DEF u8 bus; /* 0 - ISA */ + u8 source; /* Interrupt source (IRQ) */ + u32 interrupt; /* Global system interrupt */ + ACPI_MADT_IFLAGS}; + +/* 3: NMI Sources */ + +struct madt_nmi_source { + APIC_HEADER_DEF ACPI_MADT_IFLAGS u32 interrupt; /* Global system interrupt */ +}; + +/* 4: Local APIC NMI */ + +struct madt_local_apic_nmi { + APIC_HEADER_DEF u8 processor_id; /* ACPI processor id */ + ACPI_MADT_IFLAGS u8 lint; /* LINTn to which NMI is connected */ +}; + +/* 5: Address Override */ + +struct madt_address_override { + APIC_HEADER_DEF u16 reserved; /* Reserved, must be zero */ + u64 address; /* APIC physical address */ +}; + +/* 6: I/O Sapic */ + +struct madt_io_sapic { + APIC_HEADER_DEF u8 io_sapic_id; /* I/O SAPIC ID */ + u8 reserved; /* Reserved, must be zero */ + u32 interrupt_base; /* Glocal interrupt for SAPIC start */ + u64 address; /* SAPIC physical address */ +}; + +/* 7: Local Sapic */ + +struct madt_local_sapic { + APIC_HEADER_DEF u8 processor_id; /* ACPI processor id */ + u8 local_sapic_id; /* SAPIC ID */ + u8 local_sapic_eid; /* SAPIC EID */ + u8 reserved[3]; /* Reserved, must be zero */ + ACPI_MADT_LFLAGS u32 processor_uID; /* Numeric UID - ACPI 3.0 */ + char processor_uIDstring[1]; /* String UID - ACPI 3.0 */ +}; + +/* 8: Platform Interrupt Source */ + +struct madt_interrupt_source { + APIC_HEADER_DEF ACPI_MADT_IFLAGS u8 interrupt_type; /* 1=PMI, 2=INIT, 3=corrected */ + u8 processor_id; /* Processor ID */ + u8 processor_eid; /* Processor EID */ + u8 io_sapic_vector; /* Vector value for PMI interrupts */ + u32 interrupt; /* Global system interrupt */ + u32 flags; /* Interrupt Source Flags */ +}; + +#ifdef DUPLICATE_DEFINITION_WITH_LINUX_ACPI_H +/******************************************************************************* + * + * MCFG - PCI Memory Mapped Configuration table and sub-table + * + ******************************************************************************/ + +struct acpi_table_mcfg { + ACPI_TABLE_HEADER_DEF u8 reserved[8]; +}; + +struct acpi_mcfg_allocation { + u64 base_address; /* Base address, processor-relative */ + u16 pci_segment; /* PCI segment group number */ + u8 start_bus_number; /* Starting PCI Bus number */ + u8 end_bus_number; /* Final PCI Bus number */ + u32 reserved; +}; +#endif + +/******************************************************************************* + * + * SBST - Smart Battery Specification Table + * + ******************************************************************************/ + +struct smart_battery_table { + ACPI_TABLE_HEADER_DEF u32 warning_level; + u32 low_level; + u32 critical_level; +}; + +/******************************************************************************* + * + * SLIT - System Locality Distance Information Table + * + ******************************************************************************/ + +struct system_locality_info { + ACPI_TABLE_HEADER_DEF u64 locality_count; + u8 entry[1][1]; +}; + +/******************************************************************************* + * + * SPCR - Serial Port Console Redirection table + * + ******************************************************************************/ + +struct acpi_table_spcr { + ACPI_TABLE_HEADER_DEF u8 interface_type; /* 0=full 16550, 1=subset of 16550 */ + u8 reserved[3]; + struct acpi_generic_address serial_port; + u8 interrupt_type; + u8 pc_interrupt; + u32 interrupt; + u8 baud_rate; + u8 parity; + u8 stop_bits; + u8 flow_control; + u8 terminal_type; + u8 reserved2; + u16 pci_device_id; + u16 pci_vendor_id; + u8 pci_bus; + u8 pci_device; + u8 pci_function; + u32 pci_flags; + u8 pci_segment; + u32 reserved3; +}; + +/******************************************************************************* + * + * SPMI - Server Platform Management Interface table + * + ******************************************************************************/ + +struct acpi_table_spmi { + ACPI_TABLE_HEADER_DEF u8 reserved; + u8 interface_type; + u16 spec_revision; /* Version of IPMI */ + u8 interrupt_type; + u8 gpe_number; /* GPE assigned */ + u8 reserved2; + u8 pci_device_flag; + u32 interrupt; + struct acpi_generic_address ipmi_register; + u8 pci_segment; + u8 pci_bus; + u8 pci_device; + u8 pci_function; +}; + +/******************************************************************************* + * + * SRAT - System Resource Affinity Table + * + ******************************************************************************/ + +struct system_resource_affinity { + ACPI_TABLE_HEADER_DEF u32 reserved1; /* Must be value '1' */ + u64 reserved2; /* Reserved, must be zero */ +}; + +/* SRAT common sub-table header */ + +#define SRAT_SUBTABLE_HEADER \ + u8 type; \ + u8 length; + +/* Values for Type above */ + +#define SRAT_CPU_AFFINITY 0 +#define SRAT_MEMORY_AFFINITY 1 +#define SRAT_RESERVED 2 + +/* SRAT sub-tables */ + +struct static_resource_alloc { + SRAT_SUBTABLE_HEADER u8 proximity_domain_lo; + u8 apic_id; + + /* Flags (32 bits) */ + + u8 enabled:1; /* 00: Use affinity structure */ + u8:7; /* 01-07: Reserved, must be zero */ + u8 reserved3[3]; /* 08-31: Reserved, must be zero */ + + u8 local_sapic_eid; + u8 proximity_domain_hi[3]; + u32 reserved4; /* Reserved, must be zero */ +}; + +struct memory_affinity { + SRAT_SUBTABLE_HEADER u32 proximity_domain; + u16 reserved3; + u64 base_address; + u64 address_length; + u32 reserved4; /* Flags (32 bits) */ - u8 wb_invd:1; /* 00: The wbinvd instruction works properly */ - u8 wb_invd_flush:1; /* 01: The wbinvd flushes but does not invalidate */ - u8 proc_c1:1; /* 02: All processors support C1 state */ - u8 plvl2_up:1; /* 03: C2 state works on MP system */ - u8 pwr_button:1; /* 04: Power button is handled as a generic feature */ - u8 sleep_button:1; /* 05: Sleep button is handled as a generic feature, or not present */ - u8 fixed_rTC:1; /* 06: RTC wakeup stat not in fixed register space */ - u8 rtcs4:1; /* 07: RTC wakeup stat not possible from S4 */ - u8 tmr_val_ext:1; /* 08: tmr_val width is 32 bits (0 = 24 bits) */ - u8:7; /* 09-15: Reserved, must be zero */ - u8 reserved5[2]; /* 16-31: Reserved, must be zero */ + u8 enabled:1; /* 00: Use affinity structure */ + u8 hot_pluggable:1; /* 01: Memory region is hot pluggable */ + u8 non_volatile:1; /* 02: Memory is non-volatile */ + u8:5; /* 03-07: Reserved, must be zero */ + u8 reserved5[3]; /* 08-31: Reserved, must be zero */ + + u64 reserved6; /* Reserved, must be zero */ +}; + +/******************************************************************************* + * + * TCPA - Trusted Computing Platform Alliance table + * + ******************************************************************************/ + +struct acpi_table_tcpa { + ACPI_TABLE_HEADER_DEF u16 reserved; + u32 max_log_length; /* Maximum length for the event log area */ + u64 log_address; /* Address of the event log area */ }; +/******************************************************************************* + * + * WDRT - Watchdog Resource Table + * + ******************************************************************************/ + +struct acpi_table_wdrt { + ACPI_TABLE_HEADER_DEF u32 header_length; /* Watchdog Header Length */ + u8 pci_segment; /* PCI Segment number */ + u8 pci_bus; /* PCI Bus number */ + u8 pci_device; /* PCI Device number */ + u8 pci_function; /* PCI Function number */ + u32 timer_period; /* Period of one timer count (msec) */ + u32 max_count; /* Maximum counter value supported */ + u32 min_count; /* Minimum counter value */ + u8 flags; + u8 reserved[3]; + u32 entries; /* Number of watchdog entries that follow */ +}; + +#if 0 /* Flags, will be converted to macros */ +u8 enabled:1; /* 00: Timer enabled */ +u8:6; /* 01-06: Reserved */ +u8 sleep_stop:1; /* 07: Timer stopped in sleep state */ +#endif + +/* Macros used to generate offsets to specific table fields */ + +#define ACPI_ASF0_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_asf_info,f) +#define ACPI_ASF1_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_asf_alert,f) +#define ACPI_ASF2_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_asf_remote,f) +#define ACPI_ASF3_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_asf_rmcp,f) +#define ACPI_ASF4_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_asf_address,f) +#define ACPI_BOOT_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_boot,f) +#define ACPI_CPEP_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_cpep,f) +#define ACPI_CPEP0_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_cpep_polling,f) +#define ACPI_DBGP_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_dbgp,f) +#define ACPI_ECDT_OFFSET(f) (u8) ACPI_OFFSET (struct ec_boot_resources,f) +#define ACPI_HPET_OFFSET(f) (u8) ACPI_OFFSET (struct hpet_table,f) +#define ACPI_MADT_OFFSET(f) (u8) ACPI_OFFSET (struct multiple_apic_table,f) +#define ACPI_MADT0_OFFSET(f) (u8) ACPI_OFFSET (struct madt_processor_apic,f) +#define ACPI_MADT1_OFFSET(f) (u8) ACPI_OFFSET (struct madt_io_apic,f) +#define ACPI_MADT2_OFFSET(f) (u8) ACPI_OFFSET (struct madt_interrupt_override,f) +#define ACPI_MADT3_OFFSET(f) (u8) ACPI_OFFSET (struct madt_nmi_source,f) +#define ACPI_MADT4_OFFSET(f) (u8) ACPI_OFFSET (struct madt_local_apic_nmi,f) +#define ACPI_MADT5_OFFSET(f) (u8) ACPI_OFFSET (struct madt_address_override,f) +#define ACPI_MADT6_OFFSET(f) (u8) ACPI_OFFSET (struct madt_io_sapic,f) +#define ACPI_MADT7_OFFSET(f) (u8) ACPI_OFFSET (struct madt_local_sapic,f) +#define ACPI_MADT8_OFFSET(f) (u8) ACPI_OFFSET (struct madt_interrupt_source,f) +#define ACPI_MADTH_OFFSET(f) (u8) ACPI_OFFSET (struct apic_header,f) +#define ACPI_MCFG_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_mcfg,f) +#define ACPI_MCFG0_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_mcfg_allocation,f) +#define ACPI_SBST_OFFSET(f) (u8) ACPI_OFFSET (struct smart_battery_table,f) +#define ACPI_SLIT_OFFSET(f) (u8) ACPI_OFFSET (struct system_locality_info,f) +#define ACPI_SPCR_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_spcr,f) +#define ACPI_SPMI_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_spmi,f) +#define ACPI_SRAT_OFFSET(f) (u8) ACPI_OFFSET (struct system_resource_affinity,f) +#define ACPI_SRAT0_OFFSET(f) (u8) ACPI_OFFSET (struct static_resource_alloc,f) +#define ACPI_SRAT1_OFFSET(f) (u8) ACPI_OFFSET (struct memory_affinity,f) +#define ACPI_TCPA_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_tcpa,f) +#define ACPI_WDRT_OFFSET(f) (u8) ACPI_OFFSET (struct acpi_table_wdrt,f) + +#define ACPI_HPET_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct hpet_table,f,o) +#define ACPI_SRAT0_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct static_resource_alloc,f,o) +#define ACPI_SRAT1_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct memory_affinity,f,o) +#define ACPI_MADT_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct multiple_apic_table,f,o) +#define ACPI_MADT0_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct madt_processor_apic,f,o) +#define ACPI_MADT2_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct madt_interrupt_override,f,o) +#define ACPI_MADT3_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct madt_nmi_source,f,o) +#define ACPI_MADT4_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct madt_local_apic_nmi,f,o) +#define ACPI_MADT7_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct madt_local_sapic,f,o) +#define ACPI_MADT8_FLAG_OFFSET(f,o) ACPI_FLAG_OFFSET (struct madt_interrupt_source,f,o) + +/* Reset to default packing */ + #pragma pack() #endif /* __ACTBL1_H__ */ diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index dfc7ac1..67efe6c 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -44,234 +44,6 @@ #ifndef __ACTBL2_H__ #define __ACTBL2_H__ -/* - * Prefered Power Management Profiles - */ -#define PM_UNSPECIFIED 0 -#define PM_DESKTOP 1 -#define PM_MOBILE 2 -#define PM_WORKSTATION 3 -#define PM_ENTERPRISE_SERVER 4 -#define PM_SOHO_SERVER 5 -#define PM_APPLIANCE_PC 6 - -/* - * ACPI Boot Arch Flags - */ -#define BAF_LEGACY_DEVICES 0x0001 -#define BAF_8042_KEYBOARD_CONTROLLER 0x0002 - -#define FADT2_REVISION_ID 3 -#define FADT2_MINUS_REVISION_ID 2 - -#pragma pack(1) - -/* - * ACPI 2.0 Root System Description Table (RSDT) - */ -struct rsdt_descriptor_rev2 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - u32 table_offset_entry[1]; /* Array of pointers to ACPI tables */ -}; - -/* - * ACPI 2.0 Extended System Description Table (XSDT) - */ -struct xsdt_descriptor_rev2 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - u64 table_offset_entry[1]; /* Array of pointers to ACPI tables */ -}; - -/* - * ACPI 2.0 Firmware ACPI Control Structure (FACS) - */ -struct facs_descriptor_rev2 { - char signature[4]; /* ASCII table signature */ - u32 length; /* Length of structure, in bytes */ - u32 hardware_signature; /* Hardware configuration signature */ - u32 firmware_waking_vector; /* 32-bit physical address of the Firmware Waking Vector. */ - u32 global_lock; /* Global Lock used to synchronize access to shared hardware resources */ - - /* Flags (32 bits) */ - - u8 S4bios_f:1; /* 00: S4BIOS support is present */ - u8:7; /* 01-07: Reserved, must be zero */ - u8 reserved1[3]; /* 08-31: Reserved, must be zero */ - - u64 xfirmware_waking_vector; /* 64-bit physical address of the Firmware Waking Vector. */ - u8 version; /* Version of this table */ - u8 reserved3[31]; /* Reserved, must be zero */ -}; - -/* - * ACPI 2.0+ Generic Address Structure (GAS) - */ -struct acpi_generic_address { - u8 address_space_id; /* Address space where struct or register exists. */ - u8 register_bit_width; /* Size in bits of given register */ - u8 register_bit_offset; /* Bit offset within the register */ - u8 access_width; /* Minimum Access size (ACPI 3.0) */ - u64 address; /* 64-bit address of struct or register */ -}; - -#define FADT_REV2_COMMON \ - u32 V1_firmware_ctrl; /* 32-bit physical address of FACS */ \ - u32 V1_dsdt; /* 32-bit physical address of DSDT */ \ - u8 reserved1; /* System Interrupt Model isn't used in ACPI 2.0*/ \ - u8 prefer_PM_profile; /* Conveys preferred power management profile to OSPM. */ \ - u16 sci_int; /* System vector of SCI interrupt */ \ - u32 smi_cmd; /* Port address of SMI command port */ \ - u8 acpi_enable; /* Value to write to smi_cmd to enable ACPI */ \ - u8 acpi_disable; /* Value to write to smi_cmd to disable ACPI */ \ - u8 S4bios_req; /* Value to write to SMI CMD to enter S4BIOS state */ \ - u8 pstate_cnt; /* Processor performance state control*/ \ - u32 V1_pm1a_evt_blk; /* Port address of Power Mgt 1a acpi_event Reg Blk */ \ - u32 V1_pm1b_evt_blk; /* Port address of Power Mgt 1b acpi_event Reg Blk */ \ - u32 V1_pm1a_cnt_blk; /* Port address of Power Mgt 1a Control Reg Blk */ \ - u32 V1_pm1b_cnt_blk; /* Port address of Power Mgt 1b Control Reg Blk */ \ - u32 V1_pm2_cnt_blk; /* Port address of Power Mgt 2 Control Reg Blk */ \ - u32 V1_pm_tmr_blk; /* Port address of Power Mgt Timer Ctrl Reg Blk */ \ - u32 V1_gpe0_blk; /* Port addr of General Purpose acpi_event 0 Reg Blk */ \ - u32 V1_gpe1_blk; /* Port addr of General Purpose acpi_event 1 Reg Blk */ \ - u8 pm1_evt_len; /* Byte length of ports at pm1_x_evt_blk */ \ - u8 pm1_cnt_len; /* Byte length of ports at pm1_x_cnt_blk */ \ - u8 pm2_cnt_len; /* Byte Length of ports at pm2_cnt_blk */ \ - u8 pm_tm_len; /* Byte Length of ports at pm_tm_blk */ \ - u8 gpe0_blk_len; /* Byte Length of ports at gpe0_blk */ \ - u8 gpe1_blk_len; /* Byte Length of ports at gpe1_blk */ \ - u8 gpe1_base; /* Offset in gpe model where gpe1 events start */ \ - u8 cst_cnt; /* Support for the _CST object and C States change notification.*/ \ - u16 plvl2_lat; /* Worst case HW latency to enter/exit C2 state */ \ - u16 plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ \ - u16 flush_size; /* Number of flush strides that need to be read */ \ - u16 flush_stride; /* Processor's memory cache line width, in bytes */ \ - u8 duty_offset; /* Processor's duty cycle index in processor's P_CNT reg*/ \ - u8 duty_width; /* Processor's duty cycle value bit width in P_CNT register.*/ \ - u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ \ - u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ \ - u8 century; /* Index to century in RTC CMOS RAM */ \ - u16 iapc_boot_arch; /* IA-PC Boot Architecture Flags. See Table 5-10 for description*/ - -/* - * ACPI 2.0+ Fixed ACPI Description Table (FADT) - */ -struct fadt_descriptor_rev2 { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - FADT_REV2_COMMON u8 reserved2; /* Reserved, must be zero */ - - /* Flags (32 bits) */ - - u8 wb_invd:1; /* 00: The wbinvd instruction works properly */ - u8 wb_invd_flush:1; /* 01: The wbinvd flushes but does not invalidate */ - u8 proc_c1:1; /* 02: All processors support C1 state */ - u8 plvl2_up:1; /* 03: C2 state works on MP system */ - u8 pwr_button:1; /* 04: Power button is handled as a generic feature */ - u8 sleep_button:1; /* 05: Sleep button is handled as a generic feature, or not present */ - u8 fixed_rTC:1; /* 06: RTC wakeup stat not in fixed register space */ - u8 rtcs4:1; /* 07: RTC wakeup stat not possible from S4 */ - u8 tmr_val_ext:1; /* 08: tmr_val is 32 bits 0=24-bits */ - u8 dock_cap:1; /* 09: Docking supported */ - u8 reset_reg_sup:1; /* 10: System reset via the FADT RESET_REG supported */ - u8 sealed_case:1; /* 11: No internal expansion capabilities and case is sealed */ - u8 headless:1; /* 12: No local video capabilities or local input devices */ - u8 cpu_sw_sleep:1; /* 13: Must execute native instruction after writing SLP_TYPx register */ - - u8 pci_exp_wak:1; /* 14: System supports PCIEXP_WAKE (STS/EN) bits (ACPI 3.0) */ - u8 use_platform_clock:1; /* 15: OSPM should use platform-provided timer (ACPI 3.0) */ - u8 S4rtc_sts_valid:1; /* 16: Contents of RTC_STS valid after S4 wake (ACPI 3.0) */ - u8 remote_power_on_capable:1; /* 17: System is compatible with remote power on (ACPI 3.0) */ - u8 force_apic_cluster_model:1; /* 18: All local APICs must use cluster model (ACPI 3.0) */ - u8 force_apic_physical_destination_mode:1; /* 19: all local x_aPICs must use physical dest mode (ACPI 3.0) */ - u8:4; /* 20-23: Reserved, must be zero */ - u8 reserved3; /* 24-31: Reserved, must be zero */ - - struct acpi_generic_address reset_register; /* Reset register address in GAS format */ - u8 reset_value; /* Value to write to the reset_register port to reset the system */ - u8 reserved4[3]; /* These three bytes must be zero */ - u64 xfirmware_ctrl; /* 64-bit physical address of FACS */ - u64 Xdsdt; /* 64-bit physical address of DSDT */ - struct acpi_generic_address xpm1a_evt_blk; /* Extended Power Mgt 1a acpi_event Reg Blk address */ - struct acpi_generic_address xpm1b_evt_blk; /* Extended Power Mgt 1b acpi_event Reg Blk address */ - struct acpi_generic_address xpm1a_cnt_blk; /* Extended Power Mgt 1a Control Reg Blk address */ - struct acpi_generic_address xpm1b_cnt_blk; /* Extended Power Mgt 1b Control Reg Blk address */ - struct acpi_generic_address xpm2_cnt_blk; /* Extended Power Mgt 2 Control Reg Blk address */ - struct acpi_generic_address xpm_tmr_blk; /* Extended Power Mgt Timer Ctrl Reg Blk address */ - struct acpi_generic_address xgpe0_blk; /* Extended General Purpose acpi_event 0 Reg Blk address */ - struct acpi_generic_address xgpe1_blk; /* Extended General Purpose acpi_event 1 Reg Blk address */ -}; - -/* "Down-revved" ACPI 2.0 FADT descriptor */ - -struct fadt_descriptor_rev2_minus { - ACPI_TABLE_HEADER_DEF /* ACPI common table header */ - FADT_REV2_COMMON u8 reserved2; /* Reserved, must be zero */ - u32 flags; - struct acpi_generic_address reset_register; /* Reset register address in GAS format */ - u8 reset_value; /* Value to write to the reset_register port to reset the system. */ - u8 reserved7[3]; /* Reserved, must be zero */ -}; - -/* ECDT - Embedded Controller Boot Resources Table */ - -struct ec_boot_resources { - ACPI_TABLE_HEADER_DEF struct acpi_generic_address ec_control; /* Address of EC command/status register */ - struct acpi_generic_address ec_data; /* Address of EC data register */ - u32 uid; /* Unique ID - must be same as the EC _UID method */ - u8 gpe_bit; /* The GPE for the EC */ - u8 ec_id[1]; /* Full namepath of the EC in the ACPI namespace */ -}; - -/* SRAT - System Resource Affinity Table */ - -struct static_resource_alloc { - u8 type; - u8 length; - u8 proximity_domain_lo; - u8 apic_id; - - /* Flags (32 bits) */ - - u8 enabled:1; /* 00: Use affinity structure */ - u8:7; /* 01-07: Reserved, must be zero */ - u8 reserved3[3]; /* 08-31: Reserved, must be zero */ - - u8 local_sapic_eid; - u8 proximity_domain_hi[3]; - u32 reserved4; /* Reserved, must be zero */ -}; - -struct memory_affinity { - u8 type; - u8 length; - u32 proximity_domain; - u16 reserved3; - u64 base_address; - u64 address_length; - u32 reserved4; - - /* Flags (32 bits) */ - - u8 enabled:1; /* 00: Use affinity structure */ - u8 hot_pluggable:1; /* 01: Memory region is hot pluggable */ - u8 non_volatile:1; /* 02: Memory is non-volatile */ - u8:5; /* 03-07: Reserved, must be zero */ - u8 reserved5[3]; /* 08-31: Reserved, must be zero */ - - u64 reserved6; /* Reserved, must be zero */ -}; - -struct system_resource_affinity { - ACPI_TABLE_HEADER_DEF u32 reserved1; /* Must be value '1' */ - u64 reserved2; /* Reserved, must be zero */ -}; - -/* SLIT - System Locality Distance Information Table */ - -struct system_locality_info { - ACPI_TABLE_HEADER_DEF u64 locality_count; - u8 entry[1][1]; -}; - -#pragma pack() +/* Code moved to both actbl.h and actbl1.h */ #endif /* __ACTBL2_H__ */ diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 7ca89cd..64b603c 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -44,6 +44,8 @@ #ifndef __ACTYPES_H__ #define __ACTYPES_H__ +/* acpisrc:struct_defs -- for acpisrc conversion */ + /* * ACPI_MACHINE_WIDTH must be specified in an OS- or compiler-dependent header * and must be either 16, 32, or 64 @@ -154,7 +156,6 @@ typedef u64 acpi_physical_address; #define ACPI_MAX_PTR ACPI_UINT64_MAX #define ACPI_SIZE_MAX ACPI_UINT64_MAX -#define ALIGNED_ADDRESS_BOUNDARY 0x00000008 #define ACPI_USE_NATIVE_DIVIDE /* Has native 64-bit integer support */ /* @@ -195,8 +196,6 @@ typedef u64 acpi_physical_address; #define ACPI_MAX_PTR ACPI_UINT32_MAX #define ACPI_SIZE_MAX ACPI_UINT32_MAX -#define ALIGNED_ADDRESS_BOUNDARY 0x00000004 - /******************************************************************************* * * Types specific to 16-bit targets @@ -223,7 +222,6 @@ typedef char *acpi_physical_address; #define ACPI_MAX_PTR ACPI_UINT16_MAX #define ACPI_SIZE_MAX ACPI_UINT16_MAX -#define ALIGNED_ADDRESS_BOUNDARY 0x00000002 #define ACPI_USE_NATIVE_DIVIDE /* No 64-bit integers, ok to use native divide */ /* 64-bit integers cannot be supported */ @@ -243,7 +241,7 @@ typedef acpi_native_uint acpi_size; /******************************************************************************* * - * OS- or compiler-dependent types + * OS-dependent and compiler-dependent types * * If the defaults below are not appropriate for the host system, they can * be defined in the compiler-specific or OS-specific header, and this will @@ -251,27 +249,34 @@ typedef acpi_native_uint acpi_size; * ******************************************************************************/ -/* Use C99 uintptr_t for pointer casting if available, "void *" otherwise */ +/* Value returned by acpi_os_get_thread_id */ -#ifndef acpi_uintptr_t -#define acpi_uintptr_t void * +#ifndef acpi_thread_id +#define acpi_thread_id acpi_native_uint #endif -/* - * If acpi_cache_t was not defined in the OS-dependent header, - * define it now. This is typically the case where the local cache - * manager implementation is to be used (ACPI_USE_LOCAL_CACHE) - */ -#ifndef acpi_cache_t -#define acpi_cache_t struct acpi_memory_list +/* Object returned from acpi_os_create_lock */ + +#ifndef acpi_spinlock +#define acpi_spinlock void * #endif -/* - * Allow the CPU flags word to be defined per-OS to simplify the use of the - * lock and unlock OSL interfaces. - */ +/* Flags for acpi_os_acquire_lock/acpi_os_release_lock */ + #ifndef acpi_cpu_flags -#define acpi_cpu_flags acpi_native_uint +#define acpi_cpu_flags acpi_native_uint +#endif + +/* Object returned from acpi_os_create_cache */ + +#ifndef acpi_cache_t +#define acpi_cache_t struct acpi_memory_list +#endif + +/* Use C99 uintptr_t for pointer casting if available, "void *" otherwise */ + +#ifndef acpi_uintptr_t +#define acpi_uintptr_t void * #endif /* @@ -292,6 +297,14 @@ #ifndef ACPI_UNUSED_VAR #define ACPI_UNUSED_VAR #endif +/* + * All ACPICA functions that are available to the rest of the kernel are + * tagged with this macro which can be defined as appropriate for the host. + */ +#ifndef ACPI_EXPORT_SYMBOL +#define ACPI_EXPORT_SYMBOL(symbol) +#endif + /******************************************************************************* * * Independent types @@ -367,6 +380,11 @@ struct uint32_struct { u32 hi; }; +/* Synchronization objects */ + +#define acpi_mutex void * +#define acpi_semaphore void * + /* * Acpi integer width. In ACPI version 1, integers are * 32 bits. In ACPI version 2, integers are 64 bits. @@ -477,15 +495,15 @@ #define ACPI_NOTIFY_POWER_FAULT */ typedef u32 acpi_table_type; -#define ACPI_TABLE_RSDP (acpi_table_type) 0 -#define ACPI_TABLE_DSDT (acpi_table_type) 1 -#define ACPI_TABLE_FADT (acpi_table_type) 2 -#define ACPI_TABLE_FACS (acpi_table_type) 3 -#define ACPI_TABLE_PSDT (acpi_table_type) 4 -#define ACPI_TABLE_SSDT (acpi_table_type) 5 -#define ACPI_TABLE_XSDT (acpi_table_type) 6 -#define ACPI_TABLE_MAX 6 -#define NUM_ACPI_TABLE_TYPES (ACPI_TABLE_MAX+1) +#define ACPI_TABLE_ID_RSDP (acpi_table_type) 0 +#define ACPI_TABLE_ID_DSDT (acpi_table_type) 1 +#define ACPI_TABLE_ID_FADT (acpi_table_type) 2 +#define ACPI_TABLE_ID_FACS (acpi_table_type) 3 +#define ACPI_TABLE_ID_PSDT (acpi_table_type) 4 +#define ACPI_TABLE_ID_SSDT (acpi_table_type) 5 +#define ACPI_TABLE_ID_XSDT (acpi_table_type) 6 +#define ACPI_TABLE_ID_MAX 6 +#define ACPI_NUM_TABLE_TYPES (ACPI_TABLE_ID_MAX+1) /* * Types associated with ACPI names and objects. The first group of @@ -816,7 +834,7 @@ struct acpi_system_info { u32 debug_level; u32 debug_layer; u32 num_table_types; - struct acpi_table_info table_info[NUM_ACPI_TABLE_TYPES]; + struct acpi_table_info table_info[ACPI_TABLE_ID_MAX + 1]; }; /* @@ -858,7 +876,7 @@ acpi_status(*acpi_adr_space_handler) (u3 void *handler_context, void *region_context); -#define ACPI_DEFAULT_HANDLER NULL +#define ACPI_DEFAULT_HANDLER NULL typedef acpi_status(*acpi_adr_space_setup) (acpi_handle region_handle, @@ -911,12 +929,13 @@ #define ACPI_VALID_SXDS #define ACPI_STA_DEVICE_PRESENT 0x01 #define ACPI_STA_DEVICE_ENABLED 0x02 #define ACPI_STA_DEVICE_UI 0x04 -#define ACPI_STA_DEVICE_OK 0x08 +#define ACPI_STA_DEVICE_FUNCTIONING 0x08 +#define ACPI_STA_DEVICE_OK 0x08 /* Synonym */ #define ACPI_STA_BATTERY_PRESENT 0x10 #define ACPI_COMMON_OBJ_INFO \ - acpi_object_type type; /* ACPI object type */ \ - acpi_name name /* ACPI object Name */ + acpi_object_type type; /* ACPI object type */ \ + acpi_name name /* ACPI object Name */ struct acpi_obj_info_header { ACPI_COMMON_OBJ_INFO; @@ -957,7 +976,7 @@ struct acpi_mem_space_context { * Definitions for Resource Attributes */ typedef u16 acpi_rs_length; /* Resource Length field is fixed at 16 bits */ -typedef u32 acpi_rsdesc_size; /* Max Resource Descriptor size is (length+3) = (64_k-1)+3 */ +typedef u32 acpi_rsdesc_size; /* Max Resource Descriptor size is (Length+3) = (64_k-1)+3 */ /* * Memory Attributes @@ -972,8 +991,8 @@ #define ACPI_PREFETCHABLE_MEMORY /* * IO Attributes - * The ISA Io ranges are: n000-n0_ffh, n400-n4_ffh, n800-n8_ffh, n_c00-n_cFFh. - * The non-ISA Io ranges are: n100-n3_ffh, n500-n7_ffh, n900-n_bFfh, n_cd0-n_fFFh. + * The ISA IO ranges are: n000-n0_fFh, n400-n4_fFh, n800-n8_fFh, n_c00-n_cFFh. + * The non-ISA IO ranges are: n100-n3_fFh, n500-n7_fFh, n900-n_bFFh, n_cd0-n_fFFh. */ #define ACPI_NON_ISA_ONLY_RANGES (u8) 0x01 #define ACPI_ISA_ONLY_RANGES (u8) 0x02 @@ -1171,12 +1190,12 @@ struct acpi_resource_source { /* Fields common to all address descriptors, 16/32/64 bit */ #define ACPI_RESOURCE_ADDRESS_COMMON \ - u8 resource_type; \ - u8 producer_consumer; \ - u8 decode; \ - u8 min_address_fixed; \ - u8 max_address_fixed; \ - union acpi_resource_attribute info; + u8 resource_type; \ + u8 producer_consumer; \ + u8 decode; \ + u8 min_address_fixed; \ + u8 max_address_fixed; \ + union acpi_resource_attribute info; struct acpi_resource_address { ACPI_RESOURCE_ADDRESS_COMMON}; @@ -1297,16 +1316,6 @@ #define ACPI_RS_SIZE(type) #define ACPI_NEXT_RESOURCE(res) (struct acpi_resource *)((u8 *) res + res->length) -#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED -#define ACPI_ALIGN_RESOURCE_SIZE(length) (length) -#else -#define ACPI_ALIGN_RESOURCE_SIZE(length) ACPI_ROUND_UP_TO_NATIVE_WORD(length) -#endif - -/* - * END: of definitions for Resource Attributes - */ - struct acpi_pci_routing_table { u32 length; u32 pin; @@ -1315,8 +1324,4 @@ struct acpi_pci_routing_table { char source[4]; /* pad to 64 bits so sizeof() works in all cases */ }; -/* - * END: of definitions for PCI Routing tables - */ - #endif /* __ACTYPES_H__ */ diff --git a/include/acpi/acutils.h b/include/acpi/acutils.h index 0927765..ba039ea 100644 --- a/include/acpi/acutils.h +++ b/include/acpi/acutils.h @@ -50,24 +50,24 @@ extern const u8 acpi_gbl_resource_aml_si #if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUGGER) -extern const char *acpi_gbl_BMdecode[2]; -extern const char *acpi_gbl_config_decode[4]; -extern const char *acpi_gbl_consume_decode[2]; -extern const char *acpi_gbl_DECdecode[2]; -extern const char *acpi_gbl_HEdecode[2]; -extern const char *acpi_gbl_io_decode[2]; -extern const char *acpi_gbl_LLdecode[2]; -extern const char *acpi_gbl_max_decode[2]; -extern const char *acpi_gbl_MEMdecode[4]; -extern const char *acpi_gbl_min_decode[2]; -extern const char *acpi_gbl_MTPdecode[4]; -extern const char *acpi_gbl_RNGdecode[4]; -extern const char *acpi_gbl_RWdecode[2]; -extern const char *acpi_gbl_SHRdecode[2]; -extern const char *acpi_gbl_SIZdecode[4]; -extern const char *acpi_gbl_TRSdecode[2]; -extern const char *acpi_gbl_TTPdecode[2]; -extern const char *acpi_gbl_TYPdecode[4]; +extern const char *acpi_gbl_bm_decode[]; +extern const char *acpi_gbl_config_decode[]; +extern const char *acpi_gbl_consume_decode[]; +extern const char *acpi_gbl_dec_decode[]; +extern const char *acpi_gbl_he_decode[]; +extern const char *acpi_gbl_io_decode[]; +extern const char *acpi_gbl_ll_decode[]; +extern const char *acpi_gbl_max_decode[]; +extern const char *acpi_gbl_mem_decode[]; +extern const char *acpi_gbl_min_decode[]; +extern const char *acpi_gbl_mtp_decode[]; +extern const char *acpi_gbl_rng_decode[]; +extern const char *acpi_gbl_rw_decode[]; +extern const char *acpi_gbl_shr_decode[]; +extern const char *acpi_gbl_siz_decode[]; +extern const char *acpi_gbl_trs_decode[]; +extern const char *acpi_gbl_ttp_decode[]; +extern const char *acpi_gbl_typ_decode[]; #endif /* Types for Resource descriptor entries */ @@ -78,6 +78,12 @@ #define ACPI_VARIABLE_LENGTH #define ACPI_SMALL_VARIABLE_LENGTH 3 typedef +acpi_status(*acpi_walk_aml_callback) (u8 * aml, + u32 length, + u32 offset, + u8 resource_index, void **context); + +typedef acpi_status(*acpi_pkg_callback) (u8 object_type, union acpi_operand_object * source_object, union acpi_generic_state * state, @@ -277,6 +283,8 @@ acpi_ut_ptr_exit(u32 line_number, void acpi_ut_dump_buffer(u8 * buffer, u32 count, u32 display, u32 component_id); +void acpi_ut_dump_buffer2(u8 * buffer, u32 count, u32 display); + void acpi_ut_report_error(char *module_name, u32 line_number); void acpi_ut_report_info(char *module_name, u32 line_number); @@ -445,6 +453,8 @@ acpi_ut_short_divide(acpi_integer in_div /* * utmisc */ +u8 acpi_ut_is_aml_table(struct acpi_table_header *table); + acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id); void acpi_ut_release_owner_id(acpi_owner_id * owner_id); @@ -460,7 +470,9 @@ void acpi_ut_print_string(char *string, u8 acpi_ut_valid_acpi_name(u32 name); -u8 acpi_ut_valid_acpi_character(char character); +acpi_name acpi_ut_repair_name(acpi_name name); + +u8 acpi_ut_valid_acpi_char(char character, acpi_native_uint position); acpi_status acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer); @@ -469,6 +481,25 @@ acpi_ut_strtoul64(char *string, u32 base #define ACPI_ANY_BASE 0 +u32 acpi_ut_dword_byte_swap(u32 value); + +void acpi_ut_set_integer_width(u8 revision); + +#ifdef ACPI_DEBUG_OUTPUT +void +acpi_ut_display_init_pathname(u8 type, + struct acpi_namespace_node *obj_handle, + char *path); +#endif + +/* + * utresrc + */ +acpi_status +acpi_ut_walk_aml_resources(u8 * aml, + acpi_size aml_length, + acpi_walk_aml_callback user_function, void **context); + acpi_status acpi_ut_validate_resource(void *aml, u8 * return_index); u32 acpi_ut_get_descriptor_length(void *aml); @@ -483,20 +514,6 @@ acpi_status acpi_ut_get_resource_end_tag(union acpi_operand_object *obj_desc, u8 ** end_tag); -u8 acpi_ut_generate_checksum(u8 * buffer, u32 length); - -u32 acpi_ut_dword_byte_swap(u32 value); - -void acpi_ut_set_integer_width(u8 revision); - -#ifdef ACPI_DEBUG_OUTPUT -void -acpi_ut_display_init_pathname(u8 type, - struct acpi_namespace_node *obj_handle, - char *path); - -#endif - /* * utmutex - mutex support */ @@ -523,14 +540,15 @@ acpi_ut_initialize_buffer(struct acpi_bu void *acpi_ut_allocate(acpi_size size, u32 component, char *module, u32 line); -void *acpi_ut_callocate(acpi_size size, u32 component, char *module, u32 line); +void *acpi_ut_allocate_zeroed(acpi_size size, + u32 component, char *module, u32 line); #ifdef ACPI_DBG_TRACK_ALLOCATIONS void *acpi_ut_allocate_and_track(acpi_size size, u32 component, char *module, u32 line); -void *acpi_ut_callocate_and_track(acpi_size size, - u32 component, char *module, u32 line); +void *acpi_ut_allocate_zeroed_and_track(acpi_size size, + u32 component, char *module, u32 line); void acpi_ut_free_and_track(void *address, u32 component, char *module, u32 line); @@ -540,6 +558,11 @@ void acpi_ut_dump_allocation_info(void); #endif /* ACPI_FUTURE_USAGE */ void acpi_ut_dump_allocations(u32 component, char *module); + +acpi_status +acpi_ut_create_list(char *list_name, + u16 object_size, struct acpi_memory_list **return_cache); + #endif #endif /* _ACUTILS_H */ diff --git a/include/acpi/amlcode.h b/include/acpi/amlcode.h index 37964a5..cf18426 100644 --- a/include/acpi/amlcode.h +++ b/include/acpi/amlcode.h @@ -180,8 +180,10 @@ #define AML_INDEX_FIELD_OP (u16 #define AML_BANK_FIELD_OP (u16) 0x5b87 #define AML_DATA_REGION_OP (u16) 0x5b88 /* ACPI 2.0 */ -/* Bogus opcodes (they are actually two separate opcodes) */ - +/* + * Combination opcodes (actually two one-byte opcodes) + * Used by the disassembler and i_aSL compiler + */ #define AML_LGREATEREQUAL_OP (u16) 0x9295 #define AML_LLESSEQUAL_OP (u16) 0x9294 #define AML_LNOTEQUAL_OP (u16) 0x9293 diff --git a/include/acpi/amlresrc.h b/include/acpi/amlresrc.h index fb47353..be03818 100644 --- a/include/acpi/amlresrc.h +++ b/include/acpi/amlresrc.h @@ -42,39 +42,45 @@ * POSSIBILITY OF SUCH DAMAGES. */ +/* acpisrc:struct_defs -- for acpisrc conversion */ + #ifndef __AMLRESRC_H #define __AMLRESRC_H -#define ASL_RESNAME_ADDRESS "_ADR" -#define ASL_RESNAME_ALIGNMENT "_ALN" -#define ASL_RESNAME_ADDRESSSPACE "_ASI" -#define ASL_RESNAME_ACCESSSIZE "_ASZ" -#define ASL_RESNAME_TYPESPECIFICATTRIBUTES "_ATT" -#define ASL_RESNAME_BASEADDRESS "_BAS" -#define ASL_RESNAME_BUSMASTER "_BM_" /* Master(1), Slave(0) */ -#define ASL_RESNAME_DECODE "_DEC" -#define ASL_RESNAME_DMA "_DMA" -#define ASL_RESNAME_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */ -#define ASL_RESNAME_GRANULARITY "_GRA" -#define ASL_RESNAME_INTERRUPT "_INT" -#define ASL_RESNAME_INTERRUPTLEVEL "_LL_" /* active_lo(1), active_hi(0) */ -#define ASL_RESNAME_INTERRUPTSHARE "_SHR" /* Shareable(1), no_share(0) */ -#define ASL_RESNAME_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */ -#define ASL_RESNAME_LENGTH "_LEN" -#define ASL_RESNAME_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */ -#define ASL_RESNAME_MEMTYPE "_MEM" /* non_cache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */ -#define ASL_RESNAME_MAXADDR "_MAX" -#define ASL_RESNAME_MINADDR "_MIN" -#define ASL_RESNAME_MAXTYPE "_MAF" -#define ASL_RESNAME_MINTYPE "_MIF" -#define ASL_RESNAME_REGISTERBITOFFSET "_RBO" -#define ASL_RESNAME_REGISTERBITWIDTH "_RBW" -#define ASL_RESNAME_RANGETYPE "_RNG" -#define ASL_RESNAME_READWRITETYPE "_RW_" /* read_only(0), Writeable (1) */ -#define ASL_RESNAME_TRANSLATION "_TRA" -#define ASL_RESNAME_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */ -#define ASL_RESNAME_TYPE "_TTP" /* Translation(1), Static (0) */ -#define ASL_RESNAME_XFERTYPE "_SIz" /* 8(0), 8_and16(1), 16(2) */ +/* + * Resource descriptor tags, as defined in the ACPI specification. + * Used to symbolically reference fields within a descriptor. + */ +#define ACPI_RESTAG_ADDRESS "_ADR" +#define ACPI_RESTAG_ALIGNMENT "_ALN" +#define ACPI_RESTAG_ADDRESSSPACE "_ASI" +#define ACPI_RESTAG_ACCESSSIZE "_ASZ" +#define ACPI_RESTAG_TYPESPECIFICATTRIBUTES "_ATT" +#define ACPI_RESTAG_BASEADDRESS "_BAS" +#define ACPI_RESTAG_BUSMASTER "_BM_" /* Master(1), Slave(0) */ +#define ACPI_RESTAG_DECODE "_DEC" +#define ACPI_RESTAG_DMA "_DMA" +#define ACPI_RESTAG_DMATYPE "_TYP" /* Compatible(0), A(1), B(2), F(3) */ +#define ACPI_RESTAG_GRANULARITY "_GRA" +#define ACPI_RESTAG_INTERRUPT "_INT" +#define ACPI_RESTAG_INTERRUPTLEVEL "_LL_" /* active_lo(1), active_hi(0) */ +#define ACPI_RESTAG_INTERRUPTSHARE "_SHR" /* Shareable(1), no_share(0) */ +#define ACPI_RESTAG_INTERRUPTTYPE "_HE_" /* Edge(1), Level(0) */ +#define ACPI_RESTAG_LENGTH "_LEN" +#define ACPI_RESTAG_MEMATTRIBUTES "_MTP" /* Memory(0), Reserved(1), ACPI(2), NVS(3) */ +#define ACPI_RESTAG_MEMTYPE "_MEM" /* non_cache(0), Cacheable(1) Cache+combine(2), Cache+prefetch(3) */ +#define ACPI_RESTAG_MAXADDR "_MAX" +#define ACPI_RESTAG_MINADDR "_MIN" +#define ACPI_RESTAG_MAXTYPE "_MAF" +#define ACPI_RESTAG_MINTYPE "_MIF" +#define ACPI_RESTAG_REGISTERBITOFFSET "_RBO" +#define ACPI_RESTAG_REGISTERBITWIDTH "_RBW" +#define ACPI_RESTAG_RANGETYPE "_RNG" +#define ACPI_RESTAG_READWRITETYPE "_RW_" /* read_only(0), Writeable (1) */ +#define ACPI_RESTAG_TRANSLATION "_TRA" +#define ACPI_RESTAG_TRANSTYPE "_TRS" /* Sparse(1), Dense(0) */ +#define ACPI_RESTAG_TYPE "_TTP" /* Translation(1), Static (0) */ +#define ACPI_RESTAG_XFERTYPE "_SIZ" /* 8(0), 8_and16(1), 16(2) */ /* Default sizes for "small" resource descriptors */ @@ -109,7 +115,7 @@ #pragma pack(1) * SMALL descriptors */ #define AML_RESOURCE_SMALL_HEADER_COMMON \ - u8 descriptor_type; + u8 descriptor_type; struct aml_resource_small_header { AML_RESOURCE_SMALL_HEADER_COMMON}; @@ -162,8 +168,8 @@ struct aml_resource_end_tag { * LARGE descriptors */ #define AML_RESOURCE_LARGE_HEADER_COMMON \ - u8 descriptor_type;\ - u16 resource_length; + u8 descriptor_type;\ + u16 resource_length; struct aml_resource_large_header { AML_RESOURCE_LARGE_HEADER_COMMON}; @@ -194,9 +200,9 @@ struct aml_resource_fixed_memory32 { }; #define AML_RESOURCE_ADDRESS_COMMON \ - u8 resource_type; \ - u8 flags; \ - u8 specific_flags; + u8 resource_type; \ + u8 flags; \ + u8 specific_flags; struct aml_resource_address { AML_RESOURCE_LARGE_HEADER_COMMON AML_RESOURCE_ADDRESS_COMMON}; @@ -266,6 +272,7 @@ #pragma pack() union aml_resource { /* Descriptor headers */ + u8 descriptor_type; struct aml_resource_small_header small_header; struct aml_resource_large_header large_header; @@ -296,9 +303,9 @@ union aml_resource { /* Utility overlays */ struct aml_resource_address address; - u32 u32_item; - u16 u16_item; - u8 U8item; + u32 dword_item; + u16 word_item; + u8 byte_item; }; #endif diff --git a/include/acpi/pdc_intel.h b/include/acpi/pdc_intel.h index 3fa81d5..c5472be 100644 --- a/include/acpi/pdc_intel.h +++ b/include/acpi/pdc_intel.h @@ -18,6 +18,11 @@ #define ACPI_PDC_EST_CAPABILITY_SMP (ACP ACPI_PDC_C_C1_HALT | \ ACPI_PDC_P_FFH) +#define ACPI_PDC_EST_CAPABILITY_SWSMP (ACPI_PDC_SMP_C1PT | \ + ACPI_PDC_C_C1_HALT | \ + ACPI_PDC_SMP_P_SWCOORD | \ + ACPI_PDC_P_FFH) + #define ACPI_PDC_C_CAPABILITY_SMP (ACPI_PDC_SMP_C2C3 | \ ACPI_PDC_SMP_C1PT | \ ACPI_PDC_C_C1_HALT) diff --git a/include/acpi/platform/acenv.h b/include/acpi/platform/acenv.h index 223ec64..453a469 100644 --- a/include/acpi/platform/acenv.h +++ b/include/acpi/platform/acenv.h @@ -49,33 +49,41 @@ #define __ACENV_H__ */ #ifdef ACPI_LIBRARY +/* + * Note: The non-debug version of the acpi_library does not contain any + * debug support, for minimimal size. The debug version uses ACPI_FULL_DEBUG + */ #define ACPI_USE_LOCAL_CACHE #endif -#ifdef ACPI_DUMP_APP -#ifndef MSDOS +#ifdef ACPI_ASL_COMPILER #define ACPI_DEBUG_OUTPUT -#endif #define ACPI_APPLICATION #define ACPI_DISASSEMBLER -#define ACPI_NO_METHOD_EXECUTION +#define ACPI_CONSTANT_EVAL_ONLY +#define ACPI_LARGE_NAMESPACE_NODE +#define ACPI_DATA_TABLE_DISASSEMBLY #endif #ifdef ACPI_EXEC_APP #undef DEBUGGER_THREADING #define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED -#define ACPI_DEBUG_OUTPUT +#define ACPI_FULL_DEBUG #define ACPI_APPLICATION #define ACPI_DEBUGGER -#define ACPI_DISASSEMBLER #define ACPI_MUTEX_DEBUG +#define ACPI_DBG_TRACK_ALLOCATIONS #endif -#ifdef ACPI_ASL_COMPILER +#ifdef ACPI_DASM_APP +#ifndef MSDOS #define ACPI_DEBUG_OUTPUT +#endif #define ACPI_APPLICATION #define ACPI_DISASSEMBLER -#define ACPI_CONSTANT_EVAL_ONLY +#define ACPI_NO_METHOD_EXECUTION +#define ACPI_LARGE_NAMESPACE_NODE +#define ACPI_DATA_TABLE_DISASSEMBLY #endif #ifdef ACPI_APPLICATION @@ -83,6 +91,12 @@ #define ACPI_USE_SYSTEM_CLIBRARY #define ACPI_USE_LOCAL_CACHE #endif +#ifdef ACPI_FULL_DEBUG +#define ACPI_DEBUGGER +#define ACPI_DEBUG_OUTPUT +#define ACPI_DISASSEMBLER +#endif + /* * Environment configuration. The purpose of this file is to interface to the * local generation environment. @@ -137,7 +151,7 @@ #include "acwin64.h" #elif defined(MSDOS) /* Must appear after WIN32 and WIN64 check */ #include "acdos16.h" -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include "acfreebsd.h" #elif defined(__NetBSD__) @@ -163,17 +177,6 @@ #define COMPILER_DEPENDENT_UINT64 unsig #endif -/* - * Memory allocation tracking. Used only if - * 1) This is the debug version - * 2) This is NOT a 16-bit version of the code (not enough real-mode memory) - */ -#ifdef ACPI_DEBUG_OUTPUT -#if ACPI_MACHINE_WIDTH != 16 -#define ACPI_DBG_TRACK_ALLOCATIONS -#endif -#endif - /*! [End] no source code translation !*/ /* @@ -271,8 +274,8 @@ #endif /* _VALIST */ /* * Storage alignment properties */ -#define _AUPBND (sizeof (acpi_native_uint) - 1) -#define _ADNBND (sizeof (acpi_native_uint) - 1) +#define _AUPBND (sizeof (acpi_native_int) - 1) +#define _ADNBND (sizeof (acpi_native_int) - 1) /* * Variable argument list macro definitions diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 2e6d545..3f853ca 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -49,30 +49,26 @@ #define ACPI_USE_DO_WHILE_0 #ifdef __KERNEL__ -#include #include #include +#include #include #include #include #include #include +#include +#include -#define strtoul simple_strtoul - -#define ACPI_MACHINE_WIDTH BITS_PER_LONG +/* Host-dependent types and defines */ -/* Type(s) for the OSL */ - -#ifdef ACPI_USE_LOCAL_CACHE -#define acpi_cache_t struct acpi_memory_list -#else -#include -#define acpi_cache_t kmem_cache_t -#endif +#define ACPI_MACHINE_WIDTH BITS_PER_LONG +#define acpi_cache_t kmem_cache_t +#define acpi_spinlock spinlock_t * +#define ACPI_EXPORT_SYMBOL(symbol) EXPORT_SYMBOL(symbol); +#define strtoul simple_strtoul /* Full namespace pathname length limit - arbitrary */ - #define ACPI_PATHNAME_MAX 256 #else /* !__KERNEL__ */ @@ -104,4 +100,8 @@ #include "acgcc.h" #define acpi_cpu_flags unsigned long +#define acpi_thread_id u32 + +static inline acpi_thread_id acpi_os_get_thread_id(void) { return 0; } + #endif /* __ACLINUX_H__ */ diff --git a/include/acpi/processor.h b/include/acpi/processor.h index badf027..9dd5b75 100644 --- a/include/acpi/processor.h +++ b/include/acpi/processor.h @@ -2,7 +2,7 @@ #ifndef __ACPI_PROCESSOR_H #define __ACPI_PROCESSOR_H #include -#include +#include #include @@ -18,6 +18,17 @@ #define ACPI_PROCESSOR_MAX_DUTY_WIDTH 4 #define ACPI_PDC_REVISION_ID 0x1 +#define ACPI_PSD_REV0_REVISION 0 /* Support for _PSD as in ACPI 3.0 */ +#define ACPI_PSD_REV0_ENTRIES 5 + +/* + * Types of coordination defined in ACPI 3.0. Same macros can be used across + * P, C and T states + */ +#define DOMAIN_COORD_TYPE_SW_ALL 0xfc +#define DOMAIN_COORD_TYPE_SW_ANY 0xfd +#define DOMAIN_COORD_TYPE_HW_ALL 0xfe + /* Power Management */ struct acpi_processor_cx; @@ -51,6 +62,7 @@ struct acpi_processor_cx { u32 latency_ticks; u32 power; u32 usage; + u64 time; struct acpi_processor_cx_policy promotion; struct acpi_processor_cx_policy demotion; }; @@ -66,6 +78,14 @@ struct acpi_processor_power { /* Performance Management */ +struct acpi_psd_package { + acpi_integer num_entries; + acpi_integer revision; + acpi_integer domain; + acpi_integer coord_type; + acpi_integer num_processors; +} __attribute__ ((packed)); + struct acpi_pct_register { u8 descriptor; u16 length; @@ -92,7 +112,9 @@ struct acpi_processor_performance { struct acpi_pct_register status_register; unsigned int state_count; struct acpi_processor_px *states; - + struct acpi_psd_package domain_info; + cpumask_t shared_cpu_map; + unsigned int shared_type; }; /* Throttling Control */ @@ -161,6 +183,9 @@ struct acpi_processor_errata { } piix4; }; +extern int acpi_processor_preregister_performance( + struct acpi_processor_performance **performance); + extern int acpi_processor_register_performance(struct acpi_processor_performance *performance, unsigned int cpu); extern void acpi_processor_unregister_performance(struct diff --git a/include/asm-alpha/Kbuild b/include/asm-alpha/Kbuild new file mode 100644 index 0000000..e57fd57 --- /dev/null +++ b/include/asm-alpha/Kbuild @@ -0,0 +1,5 @@ +include include/asm-generic/Kbuild.asm + +unifdef-y += console.h fpu.h sysinfo.h + +header-y += gentrap.h regdef.h pal.h reg.h diff --git a/include/asm-alpha/bitops.h b/include/asm-alpha/bitops.h index 3f88715..4b6ef7f 100644 --- a/include/asm-alpha/bitops.h +++ b/include/asm-alpha/bitops.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_BITOPS_H #define _ALPHA_BITOPS_H -#include #include /* diff --git a/include/asm-alpha/cache.h b/include/asm-alpha/cache.h index e6d4d16..f199e69 100644 --- a/include/asm-alpha/cache.h +++ b/include/asm-alpha/cache.h @@ -4,7 +4,6 @@ #ifndef __ARCH_ALPHA_CACHE_H #define __ARCH_ALPHA_CACHE_H -#include /* Bytes per L1 (data) cache line. */ #if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EV6) diff --git a/include/asm-alpha/cacheflush.h b/include/asm-alpha/cacheflush.h index 3fc6ef7..805640b 100644 --- a/include/asm-alpha/cacheflush.h +++ b/include/asm-alpha/cacheflush.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_CACHEFLUSH_H #define _ALPHA_CACHEFLUSH_H -#include #include /* Caches aren't brain-dead on the Alpha. */ diff --git a/include/asm-alpha/core_cia.h b/include/asm-alpha/core_cia.h index 3a70d68..9e0516c 100644 --- a/include/asm-alpha/core_cia.h +++ b/include/asm-alpha/core_cia.h @@ -4,7 +4,6 @@ #define __ALPHA_CIA__H__ /* Define to experiment with fitting everything into one 512MB HAE window. */ #define CIA_ONE_HAE_WINDOW 1 -#include #include #include diff --git a/include/asm-alpha/core_t2.h b/include/asm-alpha/core_t2.h index 5c1c403..457c34b 100644 --- a/include/asm-alpha/core_t2.h +++ b/include/asm-alpha/core_t2.h @@ -1,7 +1,6 @@ #ifndef __ALPHA_T2__H__ #define __ALPHA_T2__H__ -#include #include #include #include @@ -436,7 +435,7 @@ #define t2_set_hae { \ set_hae(msb); \ } -static spinlock_t t2_hae_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(t2_hae_lock); __EXTERN_INLINE u8 t2_readb(const volatile void __iomem *xaddr) { diff --git a/include/asm-alpha/dma-mapping.h b/include/asm-alpha/dma-mapping.h index 62d0d66..b9ff4d8 100644 --- a/include/asm-alpha/dma-mapping.h +++ b/include/asm-alpha/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_DMA_MAPPING_H #define _ALPHA_DMA_MAPPING_H -#include #ifdef CONFIG_PCI diff --git a/include/asm-alpha/dma.h b/include/asm-alpha/dma.h index 683afaa..87cfdbd 100644 --- a/include/asm-alpha/dma.h +++ b/include/asm-alpha/dma.h @@ -18,7 +18,6 @@ #ifndef _ASM_DMA_H #define _ASM_DMA_H -#include #include #include diff --git a/include/asm-alpha/floppy.h b/include/asm-alpha/floppy.h index 289a00d..6a9f02a 100644 --- a/include/asm-alpha/floppy.h +++ b/include/asm-alpha/floppy.h @@ -10,7 +10,6 @@ #ifndef __ASM_ALPHA_FLOPPY_H #define __ASM_ALPHA_FLOPPY_H -#include #define fd_inb(port) inb_p(port) #define fd_outb(value,port) outb_p(value,port) @@ -26,9 +25,8 @@ #define fd_set_dma_count(count) set_dma_ #define fd_enable_irq() enable_irq(FLOPPY_IRQ) #define fd_disable_irq() disable_irq(FLOPPY_IRQ) #define fd_cacheflush(addr,size) /* nothing */ -#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt, \ - SA_INTERRUPT|SA_SAMPLE_RANDOM, \ - "floppy", NULL) +#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt,\ + IRQF_DISABLED, "floppy", NULL) #define fd_free_irq() free_irq(FLOPPY_IRQ, NULL); #ifdef CONFIG_PCI diff --git a/include/asm-alpha/hardirq.h b/include/asm-alpha/hardirq.h index 7bb6a36..d953e23 100644 --- a/include/asm-alpha/hardirq.h +++ b/include/asm-alpha/hardirq.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_HARDIRQ_H #define _ALPHA_HARDIRQ_H -#include #include #include diff --git a/include/asm-alpha/hw_irq.h b/include/asm-alpha/hw_irq.h index a310b9e..a37db0f 100644 --- a/include/asm-alpha/hw_irq.h +++ b/include/asm-alpha/hw_irq.h @@ -1,9 +1,6 @@ #ifndef _ALPHA_HW_IRQ_H #define _ALPHA_HW_IRQ_H -#include - -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} extern volatile unsigned long irq_err_count; diff --git a/include/asm-alpha/ide.h b/include/asm-alpha/ide.h index 6126afe..2a5cc0b 100644 --- a/include/asm-alpha/ide.h +++ b/include/asm-alpha/ide.h @@ -13,7 +13,6 @@ #define __ASMalpha_IDE_H #ifdef __KERNEL__ -#include #define IDE_ARCH_OBSOLETE_DEFAULTS diff --git a/include/asm-alpha/io.h b/include/asm-alpha/io.h index 3ebbeee..f5ae98c 100644 --- a/include/asm-alpha/io.h +++ b/include/asm-alpha/io.h @@ -3,7 +3,6 @@ #define __ALPHA_IO_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-alpha/irq.h b/include/asm-alpha/irq.h index 566db72..917b9fe 100644 --- a/include/asm-alpha/irq.h +++ b/include/asm-alpha/irq.h @@ -8,7 +8,6 @@ #define _ALPHA_IRQ_H */ #include -#include #if defined(CONFIG_ALPHA_GENERIC) @@ -93,8 +92,4 @@ extern void enable_irq(unsigned int); struct pt_regs; extern void (*perf_irq)(unsigned long, struct pt_regs *); -struct irqaction; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - - #endif /* _ALPHA_IRQ_H */ diff --git a/include/asm-alpha/kmap_types.h b/include/asm-alpha/kmap_types.h index 3d10cd3..3e6735a 100644 --- a/include/asm-alpha/kmap_types.h +++ b/include/asm-alpha/kmap_types.h @@ -3,7 +3,6 @@ #define _ASM_KMAP_TYPES_H /* Dummy header just to define km_type. */ -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-alpha/machvec.h b/include/asm-alpha/machvec.h index ece166a..aced22f 100644 --- a/include/asm-alpha/machvec.h +++ b/include/asm-alpha/machvec.h @@ -1,7 +1,6 @@ #ifndef __ALPHA_MACHVEC_H #define __ALPHA_MACHVEC_H 1 -#include #include /* diff --git a/include/asm-alpha/mmu_context.h b/include/asm-alpha/mmu_context.h index 0c017fc..fe249e9 100644 --- a/include/asm-alpha/mmu_context.h +++ b/include/asm-alpha/mmu_context.h @@ -7,7 +7,6 @@ #define __ALPHA_MMU_CONTEXT_H * Copyright (C) 1996, Linus Torvalds */ -#include #include #include #include diff --git a/include/asm-alpha/mmzone.h b/include/asm-alpha/mmzone.h index 192d80c..64d0ab9 100644 --- a/include/asm-alpha/mmzone.h +++ b/include/asm-alpha/mmzone.h @@ -5,7 +5,6 @@ #ifndef _ASM_MMZONE_H_ #define _ASM_MMZONE_H_ -#include #include struct bootmem_data_t; /* stupid forward decl. */ diff --git a/include/asm-alpha/page.h b/include/asm-alpha/page.h index 61bcf70..8c7cd50 100644 --- a/include/asm-alpha/page.h +++ b/include/asm-alpha/page.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_PAGE_H #define _ALPHA_PAGE_H -#include #include /* PAGE_SHIFT determines the page size */ diff --git a/include/asm-alpha/param.h b/include/asm-alpha/param.h index 3ed0b3b..214e799 100644 --- a/include/asm-alpha/param.h +++ b/include/asm-alpha/param.h @@ -5,7 +5,6 @@ #define _ASM_ALPHA_PARAM_H hardware ignores reprogramming. We also need userland buy-in to the change in HZ, since this is visible in the wait4 resources etc. */ -#include #ifndef HZ # ifndef CONFIG_ALPHA_RAWHIDE diff --git a/include/asm-alpha/pgalloc.h b/include/asm-alpha/pgalloc.h index 3084756..471864e 100644 --- a/include/asm-alpha/pgalloc.h +++ b/include/asm-alpha/pgalloc.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_PGALLOC_H #define _ALPHA_PGALLOC_H -#include #include #include diff --git a/include/asm-alpha/pgtable.h b/include/asm-alpha/pgtable.h index a985cd2..93eaa58 100644 --- a/include/asm-alpha/pgtable.h +++ b/include/asm-alpha/pgtable.h @@ -10,7 +10,6 @@ #include * This hopefully works with any standard Alpha page-size, as defined * in (currently 8192). */ -#include #include #include diff --git a/include/asm-alpha/rwsem.h b/include/asm-alpha/rwsem.h index fafdd4f..1570c0b 100644 --- a/include/asm-alpha/rwsem.h +++ b/include/asm-alpha/rwsem.h @@ -36,20 +36,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_AC #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; -#endif }; -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 -#else -#define __RWSEM_DEBUG_INIT /* */ -#endif - #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ - LIST_HEAD_INIT((name).wait_list) __RWSEM_DEBUG_INIT } + LIST_HEAD_INIT((name).wait_list) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) @@ -59,9 +50,6 @@ static inline void init_rwsem(struct rw_ sem->count = RWSEM_UNLOCKED_VALUE; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif } static inline void __down_read(struct rw_semaphore *sem) diff --git a/include/asm-alpha/serial.h b/include/asm-alpha/serial.h index 7e4b298..9d263e8 100644 --- a/include/asm-alpha/serial.h +++ b/include/asm-alpha/serial.h @@ -2,7 +2,6 @@ * include/asm-alpha/serial.h */ -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-alpha/signal.h b/include/asm-alpha/signal.h index 1a2c52a..13c2305 100644 --- a/include/asm-alpha/signal.h +++ b/include/asm-alpha/signal.h @@ -77,7 +77,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -98,7 +97,6 @@ #define SA_SIGINFO 0x00000040 #define SA_ONESHOT SA_RESETHAND #define SA_NOMASK SA_NODEFER -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ /* * sigaltstack controls diff --git a/include/asm-alpha/smp.h b/include/asm-alpha/smp.h index e143210..a1a1eca 100644 --- a/include/asm-alpha/smp.h +++ b/include/asm-alpha/smp.h @@ -1,7 +1,6 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H -#include #include #include #include diff --git a/include/asm-alpha/socket.h b/include/asm-alpha/socket.h index b519322..d22ab97 100644 --- a/include/asm-alpha/socket.h +++ b/include/asm-alpha/socket.h @@ -51,6 +51,7 @@ #define SO_TIMESTAMP 29 #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 30 +#define SO_PASSSEC 34 /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 19 diff --git a/include/asm-alpha/spinlock.h b/include/asm-alpha/spinlock.h index 8197c69..0c294c9 100644 --- a/include/asm-alpha/spinlock.h +++ b/include/asm-alpha/spinlock.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_SPINLOCK_H #define _ALPHA_SPINLOCK_H -#include #include #include #include diff --git a/include/asm-alpha/system.h b/include/asm-alpha/system.h index f3b7b1a..03e9c0e 100644 --- a/include/asm-alpha/system.h +++ b/include/asm-alpha/system.h @@ -1,7 +1,6 @@ #ifndef __ALPHA_SYSTEM_H #define __ALPHA_SYSTEM_H -#include #include #include #include diff --git a/include/asm-alpha/tlbflush.h b/include/asm-alpha/tlbflush.h index 9d484c1..1ca3ed3 100644 --- a/include/asm-alpha/tlbflush.h +++ b/include/asm-alpha/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _ALPHA_TLBFLUSH_H #define _ALPHA_TLBFLUSH_H -#include #include #include diff --git a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h index ef25b65..bc6e6a9 100644 --- a/include/asm-alpha/unistd.h +++ b/include/asm-alpha/unistd.h @@ -383,6 +383,8 @@ #define __NR_inotify_init 444 #define __NR_inotify_add_watch 445 #define __NR_inotify_rm_watch 446 +#ifdef __KERNEL__ + #define NR_SYSCALLS 447 #if defined(__GNUC__) @@ -565,9 +567,8 @@ type name (type1 arg1,type2 arg2,type3 a _syscall_return(type); \ } -#endif /* __LIBRARY__ && __GNUC__ */ +#endif /* __GNUC__ */ -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 @@ -578,7 +579,6 @@ #define __ARCH_WANT_SYS_GETPGRP #define __ARCH_WANT_SYS_OLD_GETRLIMIT #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING -#endif #ifdef __KERNEL_SYSCALLS__ @@ -661,4 +661,5 @@ #endif /* __KERNEL_SYSCALLS__ */ #define cond_syscall(x) asm(".weak\t" #x "\n" #x " = sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ALPHA_UNISTD_H */ diff --git a/include/asm-alpha/vga.h b/include/asm-alpha/vga.h index 8ca4f6b..ed06f59 100644 --- a/include/asm-alpha/vga.h +++ b/include/asm-alpha/vga.h @@ -46,6 +46,6 @@ extern void scr_memcpyw(u16 *d, const u1 #define vga_readb(a) readb((u8 __iomem *)(a)) #define vga_writeb(v,a) writeb(v, (u8 __iomem *)(a)) -#define VGA_MAP_MEM(x) ((unsigned long) ioremap(x, 0)) +#define VGA_MAP_MEM(x,s) ((unsigned long) ioremap(x, s)) #endif diff --git a/include/asm-arm/Kbuild b/include/asm-arm/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-arm/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-arm/apm.h b/include/asm-arm/apm.h index 3a50eb7..d09113b 100644 --- a/include/asm-arm/apm.h +++ b/include/asm-arm/apm.h @@ -13,7 +13,6 @@ #ifndef ARM_ASM_SA1100_APM_H #define ARM_ASM_SA1100_APM_H -#include #include /* diff --git a/include/asm-arm/arch-aaec2000/io.h b/include/asm-arm/arch-aaec2000/io.h index 8d67907..d710204 100644 --- a/include/asm-arm/arch-aaec2000/io.h +++ b/include/asm-arm/arch-aaec2000/io.h @@ -16,6 +16,5 @@ #define IO_SPACE_LIMIT 0xffffffff */ #define __io(a) ((void __iomem *)(a)) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif diff --git a/include/asm-arm/arch-aaec2000/memory.h b/include/asm-arm/arch-aaec2000/memory.h index d8209f8..24b51cc 100644 --- a/include/asm-arm/arch-aaec2000/memory.h +++ b/include/asm-arm/arch-aaec2000/memory.h @@ -11,7 +11,6 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#include #define PHYS_OFFSET UL(0xf0000000) diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_spi.h b/include/asm-arm/arch-at91rm9200/at91rm9200_spi.h new file mode 100644 index 0000000..bff5ea4 --- /dev/null +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_spi.h @@ -0,0 +1,81 @@ +/* + * include/asm-arm/arch-at91rm9200/at91rm9200_spi.h + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * Serial Peripheral Interface (SPI) registers. + * Based on AT91RM9200 datasheet revision E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef AT91RM9200_SPI_H +#define AT91RM9200_SPI_H + +#define AT91_SPI_CR 0x00 /* Control Register */ +#define AT91_SPI_SPIEN (1 << 0) /* SPI Enable */ +#define AT91_SPI_SPIDIS (1 << 1) /* SPI Disable */ +#define AT91_SPI_SWRST (1 << 7) /* SPI Software Reset */ +#define AT91_SPI_LASTXFER (1 << 24) /* Last Transfer [SAM9261 only] */ + +#define AT91_SPI_MR 0x04 /* Mode Register */ +#define AT91_SPI_MSTR (1 << 0) /* Master/Slave Mode */ +#define AT91_SPI_PS (1 << 1) /* Peripheral Select */ +#define AT91_SPI_PS_FIXED (0 << 1) +#define AT91_SPI_PS_VARIABLE (1 << 1) +#define AT91_SPI_PCSDEC (1 << 2) /* Chip Select Decode */ +#define AT91_SPI_DIV32 (1 << 3) /* Clock Selection */ +#define AT91_SPI_MODFDIS (1 << 4) /* Mode Fault Detection */ +#define AT91_SPI_LLB (1 << 7) /* Local Loopback Enable */ +#define AT91_SPI_PCS (0xf << 16) /* Peripheral Chip Select */ +#define AT91_SPI_DLYBCS (0xff << 24) /* Delay Between Chip Selects */ + +#define AT91_SPI_RDR 0x08 /* Receive Data Register */ +#define AT91_SPI_RD (0xffff << 0) /* Receive Data */ +#define AT91_SPI_PCS (0xf << 16) /* Peripheral Chip Select */ + +#define AT91_SPI_TDR 0x0c /* Transmit Data Register */ +#define AT91_SPI_TD (0xffff << 0) /* Transmit Data */ +#define AT91_SPI_PCS (0xf << 16) /* Peripheral Chip Select */ +#define AT91_SPI_LASTXFER (1 << 24) /* Last Transfer [SAM9261 only] */ + +#define AT91_SPI_SR 0x10 /* Status Register */ +#define AT91_SPI_RDRF (1 << 0) /* Receive Data Register Full */ +#define AT91_SPI_TDRE (1 << 1) /* Transmit Data Register Full */ +#define AT91_SPI_MODF (1 << 2) /* Mode Fault Error */ +#define AT91_SPI_OVRES (1 << 3) /* Overrun Error Status */ +#define AT91_SPI_ENDRX (1 << 4) /* End of RX buffer */ +#define AT91_SPI_ENDTX (1 << 5) /* End of TX buffer */ +#define AT91_SPI_RXBUFF (1 << 6) /* RX Buffer Full */ +#define AT91_SPI_TXBUFE (1 << 7) /* TX Buffer Empty */ +#define AT91_SPI_NSSR (1 << 8) /* NSS Rising [SAM9261 only] */ +#define AT91_SPI_TXEMPTY (1 << 9) /* Transmission Register Empty [SAM9261 only] */ +#define AT91_SPI_SPIENS (1 << 16) /* SPI Enable Status */ + +#define AT91_SPI_IER 0x14 /* Interrupt Enable Register */ +#define AT91_SPI_IDR 0x18 /* Interrupt Disable Register */ +#define AT91_SPI_IMR 0x1c /* Interrupt Mask Register */ + +#define AT91_SPI_CSR(n) (0x30 + ((n) * 4)) /* Chip Select Registers 0-3 */ +#define AT91_SPI_CPOL (1 << 0) /* Clock Polarity */ +#define AT91_SPI_NCPHA (1 << 1) /* Clock Phase */ +#define AT91_SPI_CSAAT (1 << 3) /* Chip Select Active After Transfer [SAM9261 only] */ +#define AT91_SPI_BITS (0xf << 4) /* Bits Per Transfer */ +#define AT91_SPI_BITS_8 (0 << 4) +#define AT91_SPI_BITS_9 (1 << 4) +#define AT91_SPI_BITS_10 (2 << 4) +#define AT91_SPI_BITS_11 (3 << 4) +#define AT91_SPI_BITS_12 (4 << 4) +#define AT91_SPI_BITS_13 (5 << 4) +#define AT91_SPI_BITS_14 (6 << 4) +#define AT91_SPI_BITS_15 (7 << 4) +#define AT91_SPI_BITS_16 (8 << 4) +#define AT91_SPI_SCBR (0xff << 8) /* Serial Clock Baud Rate */ +#define AT91_SPI_DLYBS (0xff << 16) /* Delay before SPCK */ +#define AT91_SPI_DLYBCT (0xff << 24) /* Delay between Consecutive Transfers */ + +#endif diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_ssc.h b/include/asm-arm/arch-at91rm9200/at91rm9200_ssc.h new file mode 100644 index 0000000..ac88022 --- /dev/null +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_ssc.h @@ -0,0 +1,96 @@ +/* + * include/asm-arm/arch-at91rm9200/at91rm9200_ssc.h + * + * Copyright (C) SAN People + * + * Serial Synchronous Controller (SSC) registers. + * Based on AT91RM9200 datasheet revision E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef AT91RM9200_SSC_H +#define AT91RM9200_SSC_H + +#define AT91_SSC_CR 0x00 /* Control Register */ +#define AT91_SSC_RXEN (1 << 0) /* Receive Enable */ +#define AT91_SSC_RXDIS (1 << 1) /* Receive Disable */ +#define AT91_SSC_TXEN (1 << 8) /* Transmit Enable */ +#define AT91_SSC_TXDIS (1 << 9) /* Transmit Disable */ +#define AT91_SSC_SWRST (1 << 15) /* Software Reset */ + +#define AT91_SSC_CMR 0x04 /* Clock Mode Register */ +#define AT91_SSC_CMR_DIV (0xfff << 0) /* Clock Divider */ + +#define AT91_SSC_RCMR 0x10 /* Receive Clock Mode Register */ +#define AT91_SSC_CKS (3 << 0) /* Clock Selection */ +#define AT91_SSC_CKS_DIV (0 << 0) +#define AT91_SSC_CKS_CLOCK (1 << 0) +#define AT91_SSC_CKS_PIN (2 << 0) +#define AT91_SSC_CKO (7 << 2) /* Clock Output Mode Selection */ +#define AT91_SSC_CKO_NONE (0 << 2) +#define AT91_SSC_CKO_CONTINUOUS (1 << 2) +#define AT91_SSC_CKI (1 << 5) /* Clock Inversion */ +#define AT91_SSC_CKI_FALLING (0 << 5) +#define AT91_SSC_CK_RISING (1 << 5) +#define AT91_SSC_START (0xf << 8) /* Start Selection */ +#define AT91_SSC_START_CONTINUOUS (0 << 8) +#define AT91_SSC_START_TX_RX (1 << 8) +#define AT91_SSC_START_LOW_RF (2 << 8) +#define AT91_SSC_START_HIGH_RF (3 << 8) +#define AT91_SSC_START_FALLING_RF (4 << 8) +#define AT91_SSC_START_RISING_RF (5 << 8) +#define AT91_SSC_START_LEVEL_RF (6 << 8) +#define AT91_SSC_START_EDGE_RF (7 << 8) +#define AT91_SSC_STTDLY (0xff << 16) /* Start Delay */ +#define AT91_SSC_PERIOD (0xff << 24) /* Period Divider Selection */ + +#define AT91_SSC_RFMR 0x14 /* Receive Frame Mode Register */ +#define AT91_SSC_DATALEN (0x1f << 0) /* Data Length */ +#define AT91_SSC_LOOP (1 << 5) /* Loop Mode */ +#define AT91_SSC_MSBF (1 << 7) /* Most Significant Bit First */ +#define AT91_SSC_DATNB (0xf << 8) /* Data Number per Frame */ +#define AT91_SSC_FSLEN (0xf << 16) /* Frame Sync Length */ +#define AT91_SSC_FSOS (7 << 20) /* Frame Sync Output Selection */ +#define AT91_SSC_FSOS_NONE (0 << 20) +#define AT91_SSC_FSOS_NEGATIVE (1 << 20) +#define AT91_SSC_FSOS_POSITIVE (2 << 20) +#define AT91_SSC_FSOS_LOW (3 << 20) +#define AT91_SSC_FSOS_HIGH (4 << 20) +#define AT91_SSC_FSOS_TOGGLE (5 << 20) +#define AT91_SSC_FSEDGE (1 << 24) /* Frame Sync Edge Detection */ +#define AT91_SSC_FSEDGE_POSITIVE (0 << 24) +#define AT91_SSC_FSEDGE_NEGATIVE (1 << 24) + +#define AT91_SSC_TCMR 0x18 /* Transmit Clock Mode Register */ +#define AT91_SSC_TFMR 0x1c /* Transmit Fram Mode Register */ +#define AT91_SSC_DATDEF (1 << 5) /* Data Default Value */ +#define AT91_SSC_FSDEN (1 << 23) /* Frame Sync Data Enable */ + +#define AT91_SSC_RHR 0x20 /* Receive Holding Register */ +#define AT91_SSC_THR 0x24 /* Transmit Holding Register */ +#define AT91_SSC_RSHR 0x30 /* Receive Sync Holding Register */ +#define AT91_SSC_TSHR 0x34 /* Transmit Sync Holding Register */ + +#define AT91_SSC_SR 0x40 /* Status Register */ +#define AT91_SSC_TXRDY (1 << 0) /* Transmit Ready */ +#define AT91_SSC_TXEMPTY (1 << 1) /* Transmit Empty */ +#define AT91_SSC_ENDTX (1 << 2) /* End of Transmission */ +#define AT91_SSC_TXBUFE (1 << 3) /* Transmit Buffer Empty */ +#define AT91_SSC_RXRDY (1 << 4) /* Receive Ready */ +#define AT91_SSC_OVRUN (1 << 5) /* Receive Overrun */ +#define AT91_SSC_ENDRX (1 << 6) /* End of Reception */ +#define AT91_SSC_RXBUFF (1 << 7) /* Receive Buffer Full */ +#define AT91_SSC_TXSYN (1 << 10) /* Transmit Sync */ +#define AT91_SSC_RXSYN (1 << 11) /* Receive Sync */ +#define AT91_SSC_TXENA (1 << 16) /* Transmit Enable */ +#define AT91_SSC_RXENA (1 << 17) /* Receive Enable */ + +#define AT91_SSC_IER 0x44 /* Interrupt Enable Register */ +#define AT91_SSC_IDR 0x48 /* Interrupt Disable Register */ +#define AT91_SSC_IMR 0x4c /* Interrupt Mask Register */ + +#endif diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h b/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h index 2910d35..0f4c12d 100644 --- a/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_sys.h @@ -68,8 +68,17 @@ #define AT91_DBGU_SR (AT91_DBGU + 0x14) #define AT91_DBGU_RHR (AT91_DBGU + 0x18) /* Receiver Holding Register */ #define AT91_DBGU_THR (AT91_DBGU + 0x1c) /* Transmitter Holding Register */ #define AT91_DBGU_BRGR (AT91_DBGU + 0x20) /* Baud Rate Generator Register */ + #define AT91_DBGU_CIDR (AT91_DBGU + 0x40) /* Chip ID Register */ #define AT91_DBGU_EXID (AT91_DBGU + 0x44) /* Chip ID Extension Register */ +#define AT91_CIDR_VERSION (0x1f << 0) /* Version of the Device */ +#define AT91_CIDR_EPROC (7 << 5) /* Embedded Processor */ +#define AT91_CIDR_NVPSIZ (0xf << 8) /* Nonvolatile Program Memory Size */ +#define AT91_CIDR_NVPSIZ2 (0xf << 12) /* Second Nonvolatile Program Memory Size */ +#define AT91_CIDR_SRAMSIZ (0xf << 16) /* Internal SRAM Size */ +#define AT91_CIDR_ARCH (0xff << 20) /* Architecture Identifier */ +#define AT91_CIDR_NVPTYP (7 << 28) /* Nonvolatile Program Memory Type */ +#define AT91_CIDR_EXT (1 << 31) /* Extension Flag */ /* @@ -241,7 +250,7 @@ #define AT91_RTC_TIMR (AT91_RTC + 0x08) #define AT91_RTC_SEC (0x7f << 0) /* Current Second */ #define AT91_RTC_MIN (0x7f << 8) /* Current Minute */ #define AT91_RTC_HOUR (0x3f << 16) /* Current Hour */ -#define At91_RTC_AMPM (1 << 22) /* Ante Meridiem Post Meridiem Indicator */ +#define AT91_RTC_AMPM (1 << 22) /* Ante Meridiem Post Meridiem Indicator */ #define AT91_RTC_CALR (AT91_RTC + 0x0c) /* Calendar Register */ #define AT91_RTC_CENT (0x7f << 0) /* Current Century */ diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_tc.h b/include/asm-arm/arch-at91rm9200/at91rm9200_tc.h new file mode 100644 index 0000000..f4da752 --- /dev/null +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_tc.h @@ -0,0 +1,146 @@ +/* + * include/asm-arm/arch-at91rm9200/at91rm9200_tc.h + * + * Copyright (C) SAN People + * + * Timer/Counter Unit (TC) registers. + * Based on AT91RM9200 datasheet revision E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef AT91RM9200_TC_H +#define AT91RM9200_TC_H + +#define AT91_TC_BCR 0xc0 /* TC Block Control Register */ +#define AT91_TC_SYNC (1 << 0) /* Synchro Command */ + +#define AT91_TC_BMR 0xc4 /* TC Block Mode Register */ +#define AT91_TC_TC0XC0S (3 << 0) /* External Clock Signal 0 Selection */ +#define AT91_TC_TC0XC0S_TCLK0 (0 << 0) +#define AT91_TC_TC0XC0S_NONE (1 << 0) +#define AT91_TC_TC0XC0S_TIOA1 (2 << 0) +#define AT91_TC_TC0XC0S_TIOA2 (3 << 0) +#define AT91_TC_TC1XC1S (3 << 2) /* External Clock Signal 1 Selection */ +#define AT91_TC_TC1XC1S_TCLK1 (0 << 2) +#define AT91_TC_TC1XC1S_NONE (1 << 2) +#define AT91_TC_TC1XC1S_TIOA0 (2 << 2) +#define AT91_TC_TC1XC1S_TIOA2 (3 << 2) +#define AT91_TC_TC2XC2S (3 << 4) /* External Clock Signal 2 Selection */ +#define AT91_TC_TC2XC2S_TCLK2 (0 << 4) +#define AT91_TC_TC2XC2S_NONE (1 << 4) +#define AT91_TC_TC2XC2S_TIOA0 (2 << 4) +#define AT91_TC_TC2XC2S_TIOA1 (3 << 4) + + +#define AT91_TC_CCR 0x00 /* Channel Control Register */ +#define AT91_TC_CLKEN (1 << 0) /* Counter Clock Enable Command */ +#define AT91_TC_CLKDIS (1 << 1) /* Counter CLock Disable Command */ +#define AT91_TC_SWTRG (1 << 2) /* Software Trigger Command */ + +#define AT91_TC_CMR 0x04 /* Channel Mode Register */ +#define AT91_TC_TCCLKS (7 << 0) /* Capture/Waveform Mode: Clock Selection */ +#define AT91_TC_TIMER_CLOCK1 (0 << 0) +#define AT91_TC_TIMER_CLOCK2 (1 << 0) +#define AT91_TC_TIMER_CLOCK3 (2 << 0) +#define AT91_TC_TIMER_CLOCK4 (3 << 0) +#define AT91_TC_TIMER_CLOCK5 (4 << 0) +#define AT91_TC_XC0 (5 << 0) +#define AT91_TC_XC1 (6 << 0) +#define AT91_TC_XC2 (7 << 0) +#define AT91_TC_CLKI (1 << 3) /* Capture/Waveform Mode: Clock Invert */ +#define AT91_TC_BURST (3 << 4) /* Capture/Waveform Mode: Burst Signal Selection */ +#define AT91_TC_LDBSTOP (1 << 6) /* Capture Mode: Counter Clock Stopped with TB Loading */ +#define AT91_TC_LDBDIS (1 << 7) /* Capture Mode: Counter Clock Disable with RB Loading */ +#define AT91_TC_ETRGEDG (3 << 8) /* Capture Mode: External Trigger Edge Selection */ +#define AT91_TC_ABETRG (1 << 10) /* Capture Mode: TIOA or TIOB External Trigger Selection */ +#define AT91_TC_CPCTRG (1 << 14) /* Capture Mode: RC Compare Trigger Enable */ +#define AT91_TC_WAVE (1 << 15) /* Capture/Waveform mode */ +#define AT91_TC_LDRA (3 << 16) /* Capture Mode: RA Loading Selection */ +#define AT91_TC_LDRB (3 << 18) /* Capture Mode: RB Loading Selection */ + +#define AT91_TC_CPCSTOP (1 << 6) /* Waveform Mode: Counter Clock Stopped with RC Compare */ +#define AT91_TC_CPCDIS (1 << 7) /* Waveform Mode: Counter Clock Disable with RC Compare */ +#define AT91_TC_EEVTEDG (3 << 8) /* Waveform Mode: External Event Edge Selection */ +#define AT91_TC_EEVTEDG_NONE (0 << 8) +#define AT91_TC_EEVTEDG_RISING (1 << 8) +#define AT91_TC_EEVTEDG_FALLING (2 << 8) +#define AT91_TC_EEVTEDG_BOTH (3 << 8) +#define AT91_TC_EEVT (3 << 10) /* Waveform Mode: External Event Selection */ +#define AT91_TC_EEVT_TIOB (0 << 10) +#define AT91_TC_EEVT_XC0 (1 << 10) +#define AT91_TC_EEVT_XC1 (2 << 10) +#define AT91_TC_EEVT_XC2 (3 << 10) +#define AT91_TC_ENETRG (1 << 12) /* Waveform Mode: External Event Trigger Enable */ +#define AT91_TC_WAVESEL (3 << 13) /* Waveform Mode: Waveform Selection */ +#define AT91_TC_WAVESEL_UP (0 << 13) +#define AT91_TC_WAVESEL_UP_AUTO (2 << 13) +#define AT91_TC_WAVESEL_UPDOWN (1 << 13) +#define AT91_TC_WAVESEL_UPDOWN_AUTO (3 << 13) +#define AT91_TC_ACPA (3 << 16) /* Waveform Mode: RA Compare Effect on TIOA */ +#define AT91_TC_ACPA_NONE (0 << 16) +#define AT91_TC_ACPA_SET (1 << 16) +#define AT91_TC_ACPA_CLEAR (2 << 16) +#define AT91_TC_ACPA_TOGGLE (3 << 16) +#define AT91_TC_ACPC (3 << 18) /* Waveform Mode: RC Compre Effect on TIOA */ +#define AT91_TC_ACPC_NONE (0 << 18) +#define AT91_TC_ACPC_SET (1 << 18) +#define AT91_TC_ACPC_CLEAR (2 << 18) +#define AT91_TC_ACPC_TOGGLE (3 << 18) +#define AT91_TC_AEEVT (3 << 20) /* Waveform Mode: External Event Effect on TIOA */ +#define AT91_TC_AEEVT_NONE (0 << 20) +#define AT91_TC_AEEVT_SET (1 << 20) +#define AT91_TC_AEEVT_CLEAR (2 << 20) +#define AT91_TC_AEEVT_TOGGLE (3 << 20) +#define AT91_TC_ASWTRG (3 << 22) /* Waveform Mode: Software Trigger Effect on TIOA */ +#define AT91_TC_ASWTRG_NONE (0 << 22) +#define AT91_TC_ASWTRG_SET (1 << 22) +#define AT91_TC_ASWTRG_CLEAR (2 << 22) +#define AT91_TC_ASWTRG_TOGGLE (3 << 22) +#define AT91_TC_BCPB (3 << 24) /* Waveform Mode: RB Compare Effect on TIOB */ +#define AT91_TC_BCPB_NONE (0 << 24) +#define AT91_TC_BCPB_SET (1 << 24) +#define AT91_TC_BCPB_CLEAR (2 << 24) +#define AT91_TC_BCPB_TOGGLE (3 << 24) +#define AT91_TC_BCPC (3 << 26) /* Waveform Mode: RC Compare Effect on TIOB */ +#define AT91_TC_BCPC_NONE (0 << 26) +#define AT91_TC_BCPC_SET (1 << 26) +#define AT91_TC_BCPC_CLEAR (2 << 26) +#define AT91_TC_BCPC_TOGGLE (3 << 26) +#define AT91_TC_BEEVT (3 << 28) /* Waveform Mode: External Event Effect on TIOB */ +#define AT91_TC_BEEVT_NONE (0 << 28) +#define AT91_TC_BEEVT_SET (1 << 28) +#define AT91_TC_BEEVT_CLEAR (2 << 28) +#define AT91_TC_BEEVT_TOGGLE (3 << 28) +#define AT91_TC_BSWTRG (3 << 30) /* Waveform Mode: Software Trigger Effect on TIOB */ +#define AT91_TC_BSWTRG_NONE (0 << 30) +#define AT91_TC_BSWTRG_SET (1 << 30) +#define AT91_TC_BSWTRG_CLEAR (2 << 30) +#define AT91_TC_BSWTRG_TOGGLE (3 << 30) + +#define AT91_TC_CV 0x10 /* Counter Value */ +#define AT91_TC_RA 0x14 /* Register A */ +#define AT91_TC_RB 0x18 /* Register B */ +#define AT91_TC_RC 0x1c /* Register C */ + +#define AT91_TC_SR 0x20 /* Status Register */ +#define AT91_TC_COVFS (1 << 0) /* Counter Overflow Status */ +#define AT91_TC_LOVRS (1 << 1) /* Load Overrun Status */ +#define AT91_TC_CPAS (1 << 2) /* RA Compare Status */ +#define AT91_TC_CPBS (1 << 3) /* RB Compare Status */ +#define AT91_TC_CPCS (1 << 4) /* RC Compare Status */ +#define AT91_TC_LDRAS (1 << 5) /* RA Loading Status */ +#define AT91_TC_LDRBS (1 << 6) /* RB Loading Status */ +#define AT91_TC_ETRGS (1 << 7) /* External Trigger Status */ +#define AT91_TC_CLKSTA (1 << 16) /* Clock Enabling Status */ +#define AT91_TC_MTIOA (1 << 17) /* TIOA Mirror */ +#define AT91_TC_MTIOB (1 << 18) /* TIOB Mirror */ + +#define AT91_TC_IER 0x24 /* Interrupt Enable Register */ +#define AT91_TC_IDR 0x28 /* Interrupt Disable Register */ +#define AT91_TC_IMR 0x2c /* Interrupt Mask Register */ + +#endif diff --git a/include/asm-arm/arch-at91rm9200/at91rm9200_udp.h b/include/asm-arm/arch-at91rm9200/at91rm9200_udp.h new file mode 100644 index 0000000..951e3f6 --- /dev/null +++ b/include/asm-arm/arch-at91rm9200/at91rm9200_udp.h @@ -0,0 +1,77 @@ +/* + * include/asm-arm/arch-at91rm9200/at91rm9200_udp.h + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * USB Device Port (UDP) registers. + * Based on AT91RM9200 datasheet revision E. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef AT91RM9200_UDP_H +#define AT91RM9200_UDP_H + +#define AT91_UDP_FRM_NUM 0x00 /* Frame Number Register */ +#define AT91_UDP_NUM (0x7ff << 0) /* Frame Number */ +#define AT91_UDP_FRM_ERR (1 << 16) /* Frame Error */ +#define AT91_UDP_FRM_OK (1 << 17) /* Frame OK */ + +#define AT91_UDP_GLB_STAT 0x04 /* Global State Register */ +#define AT91_UDP_FADDEN (1 << 0) /* Function Address Enable */ +#define AT91_UDP_CONFG (1 << 1) /* Configured */ +#define AT91_UDP_ESR (1 << 2) /* Enable Send Resume */ +#define AT91_UDP_RSMINPR (1 << 3) /* Resume has been sent */ +#define AT91_UDP_RMWUPE (1 << 4) /* Remote Wake Up Enable */ + +#define AT91_UDP_FADDR 0x08 /* Function Address Register */ +#define AT91_UDP_FADD (0x7f << 0) /* Function Address Value */ +#define AT91_UDP_FEN (1 << 8) /* Function Enable */ + +#define AT91_UDP_IER 0x10 /* Interrupt Enable Register */ +#define AT91_UDP_IDR 0x14 /* Interrupt Disable Register */ +#define AT91_UDP_IMR 0x18 /* Interrupt Mask Register */ + +#define AT91_UDP_ISR 0x1c /* Interrupt Status Register */ +#define AT91_UDP_EP(n) (1 << (n)) /* Endpoint Interrupt Status */ +#define AT91_UDP_RXSUSP (1 << 8) /* USB Suspend Interrupt Status */ +#define AT91_UDP_RXRSM (1 << 9) /* USB Resume Interrupt Status */ +#define AT91_UDP_EXTRSM (1 << 10) /* External Resume Interrupt Status */ +#define AT91_UDP_SOFINT (1 << 11) /* Start of Frame Interrupt Status */ +#define AT91_UDP_ENDBUSRES (1 << 12) /* End of Bus Reset Interrpt Status */ +#define AT91_UDP_WAKEUP (1 << 13) /* USB Wakeup Interrupt Status */ + +#define AT91_UDP_ICR 0x20 /* Interrupt Clear Register */ +#define AT91_UDP_RST_EP 0x28 /* Reset Endpoint Register */ + +#define AT91_UDP_CSR(n) (0x30 + ((n) * 4)) /* Endpoint Control/Status Registers 0-7 */ +#define AT91_UDP_TXCOMP (1 << 0) /* Generates IN packet with data previously written in DPR */ +#define AT91_UDP_RX_DATA_BK0 (1 << 1) /* Receive Data Bank 0 */ +#define AT91_UDP_RXSETUP (1 << 2) /* Send STALL to the host */ +#define AT91_UDP_STALLSENT (1 << 3) /* Stall Sent / Isochronous error (Isochronous endpoints) */ +#define AT91_UDP_TXPKTRDY (1 << 4) /* Transmit Packet Ready */ +#define AT91_UDP_FORCESTALL (1 << 5) /* Force Stall */ +#define AT91_UDP_RX_DATA_BK1 (1 << 6) /* Receive Data Bank 1 */ +#define AT91_UDP_DIR (1 << 7) /* Transfer Direction */ +#define AT91_UDP_EPTYPE (7 << 8) /* Endpoint Type */ +#define AT91_UDP_EPTYPE_CTRL (0 << 8) +#define AT91_UDP_EPTYPE_ISO_OUT (1 << 8) +#define AT91_UDP_EPTYPE_BULK_OUT (2 << 8) +#define AT91_UDP_EPTYPE_INT_OUT (3 << 8) +#define AT91_UDP_EPTYPE_ISO_IN (5 << 8) +#define AT91_UDP_EPTYPE_BULK_IN (6 << 8) +#define AT91_UDP_EPTYPE_INT_IN (7 << 8) +#define AT91_UDP_DTGLE (1 << 11) /* Data Toggle */ +#define AT91_UDP_EPEDS (1 << 15) /* Endpoint Enable/Disable */ +#define AT91_UDP_RXBYTECNT (0x7ff << 16) /* Number of bytes in FIFO */ + +#define AT91_UDP_FDR(n) (0x50 + ((n) * 4)) /* Endpoint FIFO Data Registers 0-7 */ + +#define AT91_UDP_TXVC 0x74 /* Transceiver Control Register */ +#define AT91_UDP_TXVC_TXVDIS (1 << 8) /* Transceiver Disable */ + +#endif diff --git a/include/asm-arm/arch-at91rm9200/board.h b/include/asm-arm/arch-at91rm9200/board.h index 4fdef13..c1ca9a4 100644 --- a/include/asm-arm/arch-at91rm9200/board.h +++ b/include/asm-arm/arch-at91rm9200/board.h @@ -20,7 +20,7 @@ /* * These are data structures found in platform_device.dev.platform_data, - * and describing board-specfic data needed by drivers. For example, + * and describing board-specific data needed by drivers. For example, * which pin is used for a given GPIO role. * * In 2.6, drivers should strongly avoid board-specific knowledge so @@ -31,14 +31,9 @@ #ifndef __ASM_ARCH_BOARD_H #define __ASM_ARCH_BOARD_H - /* Clocks */ -extern unsigned long at91_master_clock; - - /* Serial Port */ -extern int at91_serial_map[AT91_NR_UART]; -extern int at91_console_port; - #include +#include +#include /* USB Device */ struct at91_udc_data { @@ -91,10 +86,25 @@ struct at91_nand_data { extern void __init at91_add_device_nand(struct at91_nand_data *data); /* I2C*/ -void __init at91_add_device_i2c(void); +extern void __init at91_add_device_i2c(void); + + /* SPI */ +extern void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices); - /* RTC */ -void __init at91_add_device_rtc(void); + /* Serial */ +struct at91_uart_config { + unsigned short console_tty; /* tty number of serial console */ + unsigned short nr_tty; /* number of serial tty's */ + short tty_map[]; /* map UART to tty number */ +}; +extern struct platform_device *at91_default_console_device; +extern void __init at91_init_serial(struct at91_uart_config *config); + +struct at91_uart_data { + short use_dma_tx; /* use transmit DMA? */ + short use_dma_rx; /* use receive DMA? */ +}; +extern void __init at91_add_device_serial(void); /* LEDs */ extern u8 at91_leds_cpu; diff --git a/include/asm-arm/arch-at91rm9200/gpio.h b/include/asm-arm/arch-at91rm9200/gpio.h index 6176ab2..dbde1ba 100644 --- a/include/asm-arm/arch-at91rm9200/gpio.h +++ b/include/asm-arm/arch-at91rm9200/gpio.h @@ -13,6 +13,8 @@ #ifndef __ASM_ARCH_AT91RM9200_GPIO_H #define __ASM_ARCH_AT91RM9200_GPIO_H +#include + #define PIN_BASE NR_AIC_IRQS #define PQFP_GPIO_BANKS 3 /* PQFP package has 3 banks */ @@ -188,6 +190,9 @@ extern int at91_set_multi_drive(unsigned /* callable at any time */ extern int at91_set_gpio_value(unsigned pin, int value); extern int at91_get_gpio_value(unsigned pin); + +extern void at91_gpio_suspend(void); +extern void at91_gpio_resume(void); #endif #endif diff --git a/include/asm-arm/arch-at91rm9200/hardware.h b/include/asm-arm/arch-at91rm9200/hardware.h index 59e6f44..235d39d 100644 --- a/include/asm-arm/arch-at91rm9200/hardware.h +++ b/include/asm-arm/arch-at91rm9200/hardware.h @@ -50,9 +50,12 @@ #define AT91_VA_BASE_TCB1 AT91_IO_P2V(AT #define AT91_VA_BASE_TCB0 AT91_IO_P2V(AT91_BASE_TCB0) /* Internal SRAM */ -#define AT91_BASE_SRAM 0x00200000 /* Internal SRAM base address */ +#define AT91_SRAM_BASE 0x00200000 /* Internal SRAM base address */ #define AT91_SRAM_SIZE 0x00004000 /* Internal SRAM SIZE (16Kb) */ + /* Internal SRAM is mapped below the IO devices */ +#define AT91_SRAM_VIRT_BASE (AT91_IO_VIRT_BASE - AT91_SRAM_SIZE) + /* Serial ports */ #define AT91_NR_UART 5 /* 4 USART3's and one DBGU port */ @@ -81,14 +84,14 @@ static inline unsigned int at91_sys_read { void __iomem *addr = (void __iomem *)AT91_VA_BASE_SYS; - return readl(addr + reg_offset); + return __raw_readl(addr + reg_offset); } static inline void at91_sys_write(unsigned int reg_offset, unsigned long value) { void __iomem *addr = (void __iomem *)AT91_VA_BASE_SYS; - writel(value, addr + reg_offset); + __raw_writel(value, addr + reg_offset); } #endif diff --git a/include/asm-arm/arch-at91rm9200/io.h b/include/asm-arm/arch-at91rm9200/io.h index 23e670d..88fd1be 100644 --- a/include/asm-arm/arch-at91rm9200/io.h +++ b/include/asm-arm/arch-at91rm9200/io.h @@ -21,7 +21,6 @@ #ifndef __ASM_ARCH_IO_H #define __ASM_ARCH_IO_H -#include #include #define IO_SPACE_LIMIT 0xFFFFFFFF diff --git a/include/asm-arm/arch-at91rm9200/irqs.h b/include/asm-arm/arch-at91rm9200/irqs.h index 27b0497..f63842c 100644 --- a/include/asm-arm/arch-at91rm9200/irqs.h +++ b/include/asm-arm/arch-at91rm9200/irqs.h @@ -37,16 +37,6 @@ #define irq_finish(irq) do { at91_sys_wr * symbols in gpio.h for ones handled indirectly as GPIOs. * We make provision for 4 banks of GPIO. */ -#include - #define NR_IRQS (NR_AIC_IRQS + (4 * 32)) - -#ifndef __ASSEMBLY__ -/* - * Initialize the IRQ controller. - */ -extern void at91rm9200_init_irq(unsigned int priority[]); -#endif - #endif diff --git a/include/asm-arm/arch-at91rm9200/memory.h b/include/asm-arm/arch-at91rm9200/memory.h index 462f1f0..f985069 100644 --- a/include/asm-arm/arch-at91rm9200/memory.h +++ b/include/asm-arm/arch-at91rm9200/memory.h @@ -21,7 +21,7 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#include +#include #define PHYS_OFFSET (AT91_SDRAM_BASE) @@ -33,9 +33,7 @@ #define PHYS_OFFSET (AT91_SDRAM_BASE) * bus_to_virt: Used to convert an address for DMA operations * to an address that the kernel can use. */ -#define __virt_to_bus__is_a_macro #define __virt_to_bus(x) __virt_to_phys(x) -#define __bus_to_virt__is_a_macro #define __bus_to_virt(x) __phys_to_virt(x) #endif diff --git a/include/asm-arm/arch-at91rm9200/pio.h b/include/asm-arm/arch-at91rm9200/pio.h deleted file mode 100644 index a89501b..0000000 --- a/include/asm-arm/arch-at91rm9200/pio.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * include/asm-arm/arch-at91rm9200/pio.h - * - * Copyright (C) 2003 SAN People - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - */ - -#ifndef __ASM_ARCH_PIO_H -#define __ASM_ARCH_PIO_H - -#include - -static inline void AT91_CfgPIO_USART0(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA17_TXD0 | AT91_PA18_RXD0 | AT91_PA20_CTS0); - - /* - * Errata #39 - RTS0 is not internally connected to PA21. We need to drive - * the pin manually. Default is off (RTS is active low). - */ - at91_sys_write(AT91_PIOA + PIO_PER, AT91_PA21_RTS0); - at91_sys_write(AT91_PIOA + PIO_OER, AT91_PA21_RTS0); - at91_sys_write(AT91_PIOA + PIO_SODR, AT91_PA21_RTS0); -} - -static inline void AT91_CfgPIO_USART1(void) { - at91_sys_write(AT91_PIOB + PIO_PDR, AT91_PB18_RI1 | AT91_PB19_DTR1 - | AT91_PB20_TXD1 | AT91_PB21_RXD1 | AT91_PB23_DCD1 - | AT91_PB24_CTS1 | AT91_PB25_DSR1 | AT91_PB26_RTS1); -} - -static inline void AT91_CfgPIO_USART2(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA22_RXD2 | AT91_PA23_TXD2); -} - -static inline void AT91_CfgPIO_USART3(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA5_TXD3 | AT91_PA6_RXD3); - at91_sys_write(AT91_PIOA + PIO_BSR, AT91_PA5_TXD3 | AT91_PA6_RXD3); -} - -static inline void AT91_CfgPIO_DBGU(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA31_DTXD | AT91_PA30_DRXD); -} - -/* - * Enable the Two-Wire interface. - */ -static inline void AT91_CfgPIO_TWI(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA25_TWD | AT91_PA26_TWCK); - at91_sys_write(AT91_PIOA + PIO_ASR, AT91_PA25_TWD | AT91_PA26_TWCK); - at91_sys_write(AT91_PIOA + PIO_MDER, AT91_PA25_TWD | AT91_PA26_TWCK); /* open drain */ -} - -/* - * Enable the Serial Peripheral Interface. - */ -static inline void AT91_CfgPIO_SPI(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA0_MISO | AT91_PA1_MOSI | AT91_PA2_SPCK); -} - -static inline void AT91_CfgPIO_SPI_CS0(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA3_NPCS0); -} - -static inline void AT91_CfgPIO_SPI_CS1(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA4_NPCS1); -} - -static inline void AT91_CfgPIO_SPI_CS2(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA5_NPCS2); -} - -static inline void AT91_CfgPIO_SPI_CS3(void) { - at91_sys_write(AT91_PIOA + PIO_PDR, AT91_PA6_NPCS3); -} - -/* - * Select the DataFlash card. - */ -static inline void AT91_CfgPIO_DataFlashCard(void) { - at91_sys_write(AT91_PIOB + PIO_PER, AT91_PIO_P(7)); - at91_sys_write(AT91_PIOB + PIO_OER, AT91_PIO_P(7)); - at91_sys_write(AT91_PIOB + PIO_CODR, AT91_PIO_P(7)); -} - -/* - * Enable NAND Flash (SmartMedia) interface. - */ -static inline void AT91_CfgPIO_SmartMedia(void) { - /* enable PC0=SMCE, PC1=SMOE, PC3=SMWE, A21=CLE, A22=ALE */ - at91_sys_write(AT91_PIOC + PIO_ASR, AT91_PC0_BFCK | AT91_PC1_BFRDY_SMOE | AT91_PC3_BFBAA_SMWE); - at91_sys_write(AT91_PIOC + PIO_PDR, AT91_PC0_BFCK | AT91_PC1_BFRDY_SMOE | AT91_PC3_BFBAA_SMWE); - - /* Configure PC2 as input (signal READY of the SmartMedia) */ - at91_sys_write(AT91_PIOC + PIO_PER, AT91_PC2_BFAVD); /* enable direct output enable */ - at91_sys_write(AT91_PIOC + PIO_ODR, AT91_PC2_BFAVD); /* disable output */ - - /* Configure PB1 as input (signal Card Detect of the SmartMedia) */ - at91_sys_write(AT91_PIOB + PIO_PER, AT91_PIO_P(1)); /* enable direct output enable */ - at91_sys_write(AT91_PIOB + PIO_ODR, AT91_PIO_P(1)); /* disable output */ -} - -static inline int AT91_PIO_SmartMedia_RDY(void) { - return (at91_sys_read(AT91_PIOC + PIO_PDSR) & AT91_PIO_P(2)) ? 1 : 0; -} - -static inline int AT91_PIO_SmartMedia_CardDetect(void) { - return (at91_sys_read(AT91_PIOB + PIO_PDSR) & AT91_PIO_P(1)) ? 1 : 0; -} - -#endif diff --git a/include/asm-arm/arch-at91rm9200/system.h b/include/asm-arm/arch-at91rm9200/system.h index 29c4265..8a2ff47 100644 --- a/include/asm-arm/arch-at91rm9200/system.h +++ b/include/asm-arm/arch-at91rm9200/system.h @@ -21,7 +21,7 @@ #ifndef __ASM_ARCH_SYSTEM_H #define __ASM_ARCH_SYSTEM_H -#include +#include static inline void arch_idle(void) { @@ -48,4 +48,12 @@ static inline void arch_reset(char mode) at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); } +#define ARCH_ID_AT91RM9200 0x09200080 +#define ARCH_ID_AT91SAM9261 0x019000a0 + +static inline unsigned long arch_identify(void) +{ + return at91_sys_read(AT91_DBGU_CIDR) & (AT91_CIDR_EPROC | AT91_CIDR_ARCH); +} + #endif diff --git a/include/asm-arm/arch-at91rm9200/timex.h b/include/asm-arm/arch-at91rm9200/timex.h index 3f112dd..88687ce 100644 --- a/include/asm-arm/arch-at91rm9200/timex.h +++ b/include/asm-arm/arch-at91rm9200/timex.h @@ -21,7 +21,7 @@ #ifndef __ASM_ARCH_TIMEX_H #define __ASM_ARCH_TIMEX_H -#include +#include #define CLOCK_TICK_RATE (AT91_SLOW_CLOCK) diff --git a/include/asm-arm/arch-at91rm9200/uncompress.h b/include/asm-arm/arch-at91rm9200/uncompress.h index 7b38497..ec7811a 100644 --- a/include/asm-arm/arch-at91rm9200/uncompress.h +++ b/include/asm-arm/arch-at91rm9200/uncompress.h @@ -21,7 +21,7 @@ #ifndef __ASM_ARCH_UNCOMPRESS_H #define __ASM_ARCH_UNCOMPRESS_H -#include +#include /* * The following code assumes the serial port has already been diff --git a/include/asm-arm/arch-at91rm9200/vmalloc.h b/include/asm-arm/arch-at91rm9200/vmalloc.h index 34d9718..4c367eb 100644 --- a/include/asm-arm/arch-at91rm9200/vmalloc.h +++ b/include/asm-arm/arch-at91rm9200/vmalloc.h @@ -21,6 +21,6 @@ #ifndef __ASM_ARCH_VMALLOC_H #define __ASM_ARCH_VMALLOC_H -#define VMALLOC_END (AT91_IO_VIRT_BASE & PGDIR_MASK) +#define VMALLOC_END (AT91_SRAM_VIRT_BASE & PGDIR_MASK) #endif diff --git a/include/asm-arm/arch-cl7500/acornfb.h b/include/asm-arm/arch-cl7500/acornfb.h index 3867231..aea6330 100644 --- a/include/asm-arm/arch-cl7500/acornfb.h +++ b/include/asm-arm/arch-cl7500/acornfb.h @@ -1,4 +1,3 @@ -#include #define acornfb_valid_pixrate(var) (var->pixclock >= 39325 && var->pixclock <= 40119) static inline void diff --git a/include/asm-arm/arch-clps711x/hardware.h b/include/asm-arm/arch-clps711x/hardware.h index 1386871..0fdbe72 100644 --- a/include/asm-arm/arch-clps711x/hardware.h +++ b/include/asm-arm/arch-clps711x/hardware.h @@ -22,7 +22,6 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#include #define CLPS7111_VIRT_BASE 0xff000000 #define CLPS7111_BASE CLPS7111_VIRT_BASE diff --git a/include/asm-arm/arch-clps711x/io.h b/include/asm-arm/arch-clps711x/io.h index 62613b0..53d7902 100644 --- a/include/asm-arm/arch-clps711x/io.h +++ b/include/asm-arm/arch-clps711x/io.h @@ -26,7 +26,6 @@ #define IO_SPACE_LIMIT 0xffffffff #define __io(a) ((void __iomem *)(a)) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) /* * We don't support ins[lb]/outs[lb]. Make them fault. diff --git a/include/asm-arm/arch-clps711x/memory.h b/include/asm-arm/arch-clps711x/memory.h index 61d8717..c6e8dcf 100644 --- a/include/asm-arm/arch-clps711x/memory.h +++ b/include/asm-arm/arch-clps711x/memory.h @@ -20,7 +20,6 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#include /* * Physical DRAM offset. diff --git a/include/asm-arm/arch-clps711x/uncompress.h b/include/asm-arm/arch-clps711x/uncompress.h index 07157b7..03d233a 100644 --- a/include/asm-arm/arch-clps711x/uncompress.h +++ b/include/asm-arm/arch-clps711x/uncompress.h @@ -17,7 +17,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/include/asm-arm/arch-ebsa285/hardware.h b/include/asm-arm/arch-ebsa285/hardware.h index ec51fe9..daad8ee 100644 --- a/include/asm-arm/arch-ebsa285/hardware.h +++ b/include/asm-arm/arch-ebsa285/hardware.h @@ -12,7 +12,6 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#include #include #ifdef CONFIG_ARCH_FOOTBRIDGE diff --git a/include/asm-arm/arch-ebsa285/io.h b/include/asm-arm/arch-ebsa285/io.h index 776f9d3..f9c7291 100644 --- a/include/asm-arm/arch-ebsa285/io.h +++ b/include/asm-arm/arch-ebsa285/io.h @@ -24,7 +24,6 @@ #define IO_SPACE_LIMIT 0xffff #define __io(a) ((void __iomem *)(PCIO_BASE + (a))) #if 1 #define __mem_pci(a) (a) -#define __mem_isa(a) ((a) + PCIMEM_BASE) #else static inline void __iomem *___mem_pci(void __iomem *p) @@ -34,14 +33,7 @@ static inline void __iomem *___mem_pci(v return p; } -static inline void __iomem *___mem_isa(void __iomem *p) -{ - unsigned long a = (unsigned long)p; - BUG_ON(a >= 16*1048576); - return p + PCIMEM_BASE; -} #define __mem_pci(a) ___mem_pci(a) -#define __mem_isa(a) ___mem_isa(a) #endif #endif diff --git a/include/asm-arm/arch-ebsa285/memory.h b/include/asm-arm/arch-ebsa285/memory.h index 99181ff..cbd7ae6 100644 --- a/include/asm-arm/arch-ebsa285/memory.h +++ b/include/asm-arm/arch-ebsa285/memory.h @@ -19,7 +19,6 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#include #if defined(CONFIG_FOOTBRIDGE_ADDIN) /* diff --git a/include/asm-arm/arch-ebsa285/vmalloc.h b/include/asm-arm/arch-ebsa285/vmalloc.h index d1ca955..0259820 100644 --- a/include/asm-arm/arch-ebsa285/vmalloc.h +++ b/include/asm-arm/arch-ebsa285/vmalloc.h @@ -6,7 +6,6 @@ * published by the Free Software Foundation. */ -#include #ifdef CONFIG_ARCH_FOOTBRIDGE #define VMALLOC_END (PAGE_OFFSET + 0x30000000) diff --git a/include/asm-arm/arch-ep93xx/ep93xx-regs.h b/include/asm-arm/arch-ep93xx/ep93xx-regs.h index 71cea0b..8c32297 100644 --- a/include/asm-arm/arch-ep93xx/ep93xx-regs.h +++ b/include/asm-arm/arch-ep93xx/ep93xx-regs.h @@ -115,6 +115,8 @@ #define EP93XX_SYSCON_CLOCK_UARTBAUD 0x2 #define EP93XX_SYSCON_CLOCK_USH_EN 0x10000000 #define EP93XX_SYSCON_HALT EP93XX_SYSCON_REG(0x08) #define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c) +#define EP93XX_SYSCON_CLOCK_SET1 EP93XX_SYSCON_REG(0x20) +#define EP93XX_SYSCON_CLOCK_SET2 EP93XX_SYSCON_REG(0x24) #define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80) #define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000 #define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0) diff --git a/include/asm-arm/arch-ep93xx/platform.h b/include/asm-arm/arch-ep93xx/platform.h index df9cbb6..d7a34ce 100644 --- a/include/asm-arm/arch-ep93xx/platform.h +++ b/include/asm-arm/arch-ep93xx/platform.h @@ -8,6 +8,7 @@ void ep93xx_map_io(void); void ep93xx_init_irq(void); void ep93xx_init_time(unsigned long); void ep93xx_init_devices(void); +void ep93xx_clock_init(void); extern struct sys_timer ep93xx_timer; diff --git a/include/asm-arm/arch-h720x/memory.h b/include/asm-arm/arch-h720x/memory.h index 4a1bfd7..53e923d 100644 --- a/include/asm-arm/arch-h720x/memory.h +++ b/include/asm-arm/arch-h720x/memory.h @@ -23,9 +23,7 @@ #define PHYS_OFFSET UL(0x40000000) * There is something to do here later !, Mar 2000, Jungjun Kim */ -#define __virt_to_bus__is_a_macro #define __virt_to_bus(x) __virt_to_phys(x) -#define __bus_to_virt__is_a_macro #define __bus_to_virt(x) __phys_to_virt(x) #endif diff --git a/include/asm-arm/arch-imx/imx-dma.h b/include/asm-arm/arch-imx/imx-dma.h index f2063c1..599f03e 100644 --- a/include/asm-arm/arch-imx/imx-dma.h +++ b/include/asm-arm/arch-imx/imx-dma.h @@ -46,7 +46,7 @@ #define IMX_DMA_CHANNELS 11 struct imx_dma_channel { const char *name; void (*irq_handler) (int, void *, struct pt_regs *); - void (*err_handler) (int, void *, struct pt_regs *); + void (*err_handler) (int, void *, struct pt_regs *, int errcode); void *data; dmamode_t dma_mode; struct scatterlist *sg; @@ -58,6 +58,10 @@ struct imx_dma_channel { extern struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS]; +#define IMX_DMA_ERR_BURST 1 +#define IMX_DMA_ERR_REQUEST 2 +#define IMX_DMA_ERR_TRANSFER 4 +#define IMX_DMA_ERR_BUFFER 8 /* The type to distinguish channel numbers parameter from ordinal int type */ typedef int imx_dmach_t; @@ -74,7 +78,7 @@ imx_dma_setup_sg(imx_dmach_t dma_ch, int imx_dma_setup_handlers(imx_dmach_t dma_ch, void (*irq_handler) (int, void *, struct pt_regs *), - void (*err_handler) (int, void *, struct pt_regs *), void *data); + void (*err_handler) (int, void *, struct pt_regs *, int), void *data); void imx_dma_enable(imx_dmach_t dma_ch); diff --git a/include/asm-arm/arch-imx/memory.h b/include/asm-arm/arch-imx/memory.h index d09ae32..5ad9012 100644 --- a/include/asm-arm/arch-imx/memory.h +++ b/include/asm-arm/arch-imx/memory.h @@ -30,9 +30,7 @@ #define PHYS_OFFSET UL(0x08000000) * bus_to_virt: Used to convert an address for DMA operations * to an address that the kernel can use. */ -#define __virt_to_bus__is_a_macro -#define __virt_to_bus(x) (x - PAGE_OFFSET + PHYS_OFFSET) -#define __bus_to_virt__is_a_macro -#define __bus_to_virt(x) (x - PHYS_OFFSET + PAGE_OFFSET) +#define __virt_to_bus(x) (x - PAGE_OFFSET + PHYS_OFFSET) +#define __bus_to_virt(x) (x - PHYS_OFFSET + PAGE_OFFSET) #endif diff --git a/include/asm-arm/arch-integrator/io.h b/include/asm-arm/arch-integrator/io.h index 31f2dea..c8f2175 100644 --- a/include/asm-arm/arch-integrator/io.h +++ b/include/asm-arm/arch-integrator/io.h @@ -32,6 +32,5 @@ #define PCI_IO_VADDR 0xee0000 #define __io(a) ((void __iomem *)(PCI_IO_VADDR + (a))) #define __mem_pci(a) (a) -#define __mem_isa(a) ((a) + PCI_MEMORY_VADDR) #endif diff --git a/include/asm-arm/arch-integrator/smp.h b/include/asm-arm/arch-integrator/smp.h index da6981e..ab2c79b 100644 --- a/include/asm-arm/arch-integrator/smp.h +++ b/include/asm-arm/arch-integrator/smp.h @@ -1,7 +1,6 @@ #ifndef ASMARM_ARCH_SMP_H #define ASMARM_ARCH_SMP_H -#include #include #include diff --git a/include/asm-arm/arch-iop3xx/io.h b/include/asm-arm/arch-iop3xx/io.h index f39046a..36adbdf 100644 --- a/include/asm-arm/arch-iop3xx/io.h +++ b/include/asm-arm/arch-iop3xx/io.h @@ -17,6 +17,5 @@ #define IO_SPACE_LIMIT 0xffffffff #define __io(p) ((void __iomem *)(p)) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif diff --git a/include/asm-arm/arch-iop3xx/memory.h b/include/asm-arm/arch-iop3xx/memory.h index bc62f4b..e43ebd9 100644 --- a/include/asm-arm/arch-iop3xx/memory.h +++ b/include/asm-arm/arch-iop3xx/memory.h @@ -5,7 +5,6 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#include #include /* diff --git a/include/asm-arm/arch-iop3xx/timex.h b/include/asm-arm/arch-iop3xx/timex.h index 472badb..14ca8d0 100644 --- a/include/asm-arm/arch-iop3xx/timex.h +++ b/include/asm-arm/arch-iop3xx/timex.h @@ -3,7 +3,6 @@ * * IOP3xx architecture timex specifications */ -#include #include #if defined(CONFIG_ARCH_IQ80321) || defined(CONFIG_ARCH_IQ31244) diff --git a/include/asm-arm/arch-iop3xx/uncompress.h b/include/asm-arm/arch-iop3xx/uncompress.h index c98eb62..fbdd5af 100644 --- a/include/asm-arm/arch-iop3xx/uncompress.h +++ b/include/asm-arm/arch-iop3xx/uncompress.h @@ -1,7 +1,6 @@ /* * linux/include/asm-arm/arch-iop3xx/uncompress.h */ -#include #include #include #include diff --git a/include/asm-arm/arch-ixp23xx/entry-macro.S b/include/asm-arm/arch-ixp23xx/entry-macro.S index 0ef4e60..8677616 100644 --- a/include/asm-arm/arch-ixp23xx/entry-macro.S +++ b/include/asm-arm/arch-ixp23xx/entry-macro.S @@ -8,7 +8,7 @@ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp ldr \irqnr, =(IXP23XX_INTC_VIRT + IXP23XX_INTR_IRQ_ENC_ST_OFFSET) ldr \irqnr, [\irqnr] @ get interrupt number - cmp \irqnr, #0x0 @ suprious interrupt ? + cmp \irqnr, #0x0 @ spurious interrupt ? movne \irqnr, \irqnr, lsr #2 @ skip unwanted low order bits subne \irqnr, \irqnr, #1 @ convert to 0 based diff --git a/include/asm-arm/arch-ixp23xx/ixp23xx.h b/include/asm-arm/arch-ixp23xx/ixp23xx.h index e49e1ca..3927b1d 100644 --- a/include/asm-arm/arch-ixp23xx/ixp23xx.h +++ b/include/asm-arm/arch-ixp23xx/ixp23xx.h @@ -124,6 +124,7 @@ #define IXP23XX_EXP_CFG0_FLASH_WIDTH (1 #define IXP23XX_EXP_UNIT_FUSE IXP23XX_EXP_CFG_REG(0x28) #define IXP23XX_EXP_MSF_MUX IXP23XX_EXP_CFG_REG(0x30) +#define IXP23XX_EXP_CFG_FUSE IXP23XX_EXP_CFG_REG(0x34) #define IXP23XX_EXP_BUS_PHYS 0x90000000 #define IXP23XX_EXP_BUS_WINDOW_SIZE 0x01000000 @@ -251,7 +252,7 @@ #define IXP23XX_TIMER_STATUS_WARM_RESET * CAP CSRs. ****************************************************************************/ #define IXP23XX_GLOBAL_REG(x) ((volatile unsigned long *)(IXP23XX_CAP_CSR_VIRT + 0x4a00 + (x))) -#define IXP23XX_PROD_IDG IXP23XX_GLOBAL_REG(0x00) +#define IXP23XX_PRODUCT_ID IXP23XX_GLOBAL_REG(0x00) #define IXP23XX_MISC_CONTROL IXP23XX_GLOBAL_REG(0x04) #define IXP23XX_MSF_CLK_CNTRL IXP23XX_GLOBAL_REG(0x08) #define IXP23XX_RESET0 IXP23XX_GLOBAL_REG(0x0c) @@ -265,6 +266,8 @@ #define IXP23XX_RESET_PCI (1 << 2) #define IXP23XX_PCI_UNIT_RESET (1 << 1) #define IXP23XX_XSCALE_RESET (1 << 0) +#define IXP23XX_UENGINE_CSR_VIRT_BASE (IXP23XX_CAP_CSR_VIRT + 0x18000) + /**************************************************************************** * PCI CSRs. @@ -292,15 +295,4 @@ #define IXP23XX_PCI_XSCALE_INT_ENABLE IX #define IXP23XX_PCI_CPP_ADDR_BITS IXP23XX_PCI_CSR(0x0160) -#ifndef __ASSEMBLY__ -/* - * Is system memory on the XSI or CPP bus? - */ -static inline unsigned ixp23xx_cpp_boot(void) -{ - return (*IXP23XX_EXP_CFG0 & IXP23XX_EXP_CFG0_XSI_NOT_PRES); -} -#endif - - #endif diff --git a/include/asm-arm/arch-ixp23xx/platform.h b/include/asm-arm/arch-ixp23xx/platform.h index e4d9906..56e16d6 100644 --- a/include/asm-arm/arch-ixp23xx/platform.h +++ b/include/asm-arm/arch-ixp23xx/platform.h @@ -14,6 +14,21 @@ #ifndef __ASSEMBLY__ +extern inline unsigned long ixp2000_reg_read(volatile void *reg) +{ + return *((volatile unsigned long *)reg); +} + +extern inline void ixp2000_reg_write(volatile void *reg, unsigned long val) +{ + *((volatile unsigned long *)reg) = val; +} + +extern inline void ixp2000_reg_wrb(volatile void *reg, unsigned long val) +{ + *((volatile unsigned long *)reg) = val; +} + struct pci_sys_data; void ixp23xx_map_io(void); @@ -28,5 +43,15 @@ extern struct sys_timer ixp23xx_timer; #define IXP23XX_UART_XTAL 14745600 +#ifndef __ASSEMBLY__ +/* + * Is system memory on the XSI or CPP bus? + */ +static inline unsigned ixp23xx_cpp_boot(void) +{ + return (*IXP23XX_EXP_CFG0 & IXP23XX_EXP_CFG0_XSI_NOT_PRES); +} +#endif + #endif diff --git a/include/asm-arm/arch-ixp23xx/uncompress.h b/include/asm-arm/arch-ixp23xx/uncompress.h index 013575e..16c1110 100644 --- a/include/asm-arm/arch-ixp23xx/uncompress.h +++ b/include/asm-arm/arch-ixp23xx/uncompress.h @@ -11,7 +11,7 @@ #ifndef __ASM_ARCH_UNCOMPRESS_H #define __ASM_ARCH_UNCOMPRESS_H -#include +#include #include #define UART_BASE ((volatile u32 *)IXP23XX_UART1_PHYS) diff --git a/include/asm-arm/arch-ixp4xx/dma.h b/include/asm-arm/arch-ixp4xx/dma.h index b1a071e..789f7f5 100644 --- a/include/asm-arm/arch-ixp4xx/dma.h +++ b/include/asm-arm/arch-ixp4xx/dma.h @@ -11,7 +11,6 @@ #ifndef __ASM_ARCH_DMA_H #define __ASM_ARCH_DMA_H -#include #include #include #include diff --git a/include/asm-arm/arch-ixp4xx/io.h b/include/asm-arm/arch-ixp4xx/io.h index b59520e..0d51726 100644 --- a/include/asm-arm/arch-ixp4xx/io.h +++ b/include/asm-arm/arch-ixp4xx/io.h @@ -38,7 +38,7 @@ extern int ixp4xx_pci_write(u32 addr, u3 * 2) If > 64MB of memory space is required, the IXP4xx can be configured * to use indirect registers to access PCI (as we do below for I/O * transactions). This allows for up to 128MB (0x48000000 to 0x4fffffff) - * of memory on the bus. The disadvantadge of this is that every + * of memory on the bus. The disadvantage of this is that every * PCI access requires three local register accesses plus a spinlock, * but in some cases the performance hit is acceptable. In addition, * you cannot mmap() PCI devices in this case. diff --git a/include/asm-arm/arch-l7200/io.h b/include/asm-arm/arch-l7200/io.h index cab8ad0..cd080d8 100644 --- a/include/asm-arm/arch-l7200/io.h +++ b/include/asm-arm/arch-l7200/io.h @@ -19,7 +19,6 @@ #define IO_SPACE_LIMIT 0xffffffff */ #define __io_pci(a) ((void __iomem *)(PCIO_BASE + (a))) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #define __ioaddr(p) __io_pci(p) diff --git a/include/asm-arm/arch-lh7a40x/clocks.h b/include/asm-arm/arch-lh7a40x/clocks.h new file mode 100644 index 0000000..bee02fd --- /dev/null +++ b/include/asm-arm/arch-lh7a40x/clocks.h @@ -0,0 +1,20 @@ +/* include/asm-arm/arch-lh7a40x/clocks.h + * + * Copyright (C) 2004 Marc Singer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + */ + +#include + +#ifndef __ASM_ARCH_CLOCKS_H +#define __ASM_ARCH_CLOCKS_H + +unsigned int fclkfreq_get (void); +unsigned int hclkfreq_get (void); +unsigned int pclkfreq_get (void); + +#endif /* _ASM_ARCH_CLOCKS_H */ diff --git a/include/asm-arm/arch-lh7a40x/constants.h b/include/asm-arm/arch-lh7a40x/constants.h index 52c1cb9..51de96e 100644 --- a/include/asm-arm/arch-lh7a40x/constants.h +++ b/include/asm-arm/arch-lh7a40x/constants.h @@ -12,7 +12,6 @@ #ifndef __ASM_ARCH_CONSTANTS_H #define __ASM_ARCH_CONSTANTS_H -#include /* Addressing constants */ @@ -29,8 +28,7 @@ #endif #if defined (CONFIG_MACH_LPD7A400) || defined (CONFIG_MACH_LPD7A404) -# define IOBARRIER_PHYS 0xc0000000 /* Start of SDRAM */ -/*# define IOBARRIER_PHYS 0x00000000 */ /* Start of flash */ +# define IOBARRIER_PHYS 0x10000000 /* Second bank, fastest timing */ # define IOBARRIER_VIRT 0xf0000000 # define IOBARRIER_SIZE PAGE_SIZE @@ -53,6 +51,9 @@ # define CPLD06_SIZE PAGE_SIZE # define CPLD08_PHYS CPLDX_PHYS (0x08) # define CPLD08_VIRT CPLDX_VIRT (0x08) # define CPLD08_SIZE PAGE_SIZE +# define CPLD0A_PHYS CPLDX_PHYS (0x0a) +# define CPLD0A_VIRT CPLDX_VIRT (0x0a) +# define CPLD0A_SIZE PAGE_SIZE # define CPLD0C_PHYS CPLDX_PHYS (0x0c) # define CPLD0C_VIRT CPLDX_VIRT (0x0c) # define CPLD0C_SIZE PAGE_SIZE @@ -84,5 +85,7 @@ #endif #define XTAL_IN 14745600 /* 14.7456 MHz crystal */ #define PLL_CLOCK (XTAL_IN * 21) /* 309 MHz PLL clock */ #define MAX_HCLK_KHZ 100000 /* HCLK max limit ~100MHz */ +#define HCLK (99993600) +//#define HCLK (119808000) #endif /* __ASM_ARCH_CONSTANTS_H */ diff --git a/include/asm-arm/arch-lh7a40x/dma.h b/include/asm-arm/arch-lh7a40x/dma.h index 15492e3..a8cbd14 100644 --- a/include/asm-arm/arch-lh7a40x/dma.h +++ b/include/asm-arm/arch-lh7a40x/dma.h @@ -1,9 +1,86 @@ /* include/asm-arm/arch-lh7a40x/dma.h * - * Copyright (C) 2003 Coastal Environmental Systems + * Copyright (C) 2005 Marc Singer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * */ + +typedef enum { + DMA_M2M0 = 0, + DMA_M2M1 = 1, + DMA_M2P0 = 2, /* Tx */ + DMA_M2P1 = 3, /* Rx */ + DMA_M2P2 = 4, /* Tx */ + DMA_M2P3 = 5, /* Rx */ + DMA_M2P4 = 6, /* Tx - AC97 */ + DMA_M2P5 = 7, /* Rx - AC97 */ + DMA_M2P6 = 8, /* Tx */ + DMA_M2P7 = 9, /* Rx */ +} dma_device_t; + +#define DMA_LENGTH_MAX ((64*1024) - 4) /* bytes */ + +#define DMAC_GCA __REG(DMAC_PHYS + 0x2b80) +#define DMAC_GIR __REG(DMAC_PHYS + 0x2bc0) + +#define DMAC_GIR_MMI1 (1<<11) +#define DMAC_GIR_MMI0 (1<<10) +#define DMAC_GIR_MPI8 (1<<9) +#define DMAC_GIR_MPI9 (1<<8) +#define DMAC_GIR_MPI6 (1<<7) +#define DMAC_GIR_MPI7 (1<<6) +#define DMAC_GIR_MPI4 (1<<5) +#define DMAC_GIR_MPI5 (1<<4) +#define DMAC_GIR_MPI2 (1<<3) +#define DMAC_GIR_MPI3 (1<<2) +#define DMAC_GIR_MPI0 (1<<1) +#define DMAC_GIR_MPI1 (1<<0) + +#define DMAC_M2P0 0x0000 +#define DMAC_M2P1 0x0040 +#define DMAC_M2P2 0x0080 +#define DMAC_M2P3 0x00c0 +#define DMAC_M2P4 0x0240 +#define DMAC_M2P5 0x0200 +#define DMAC_M2P6 0x02c0 +#define DMAC_M2P7 0x0280 +#define DMAC_M2P8 0x0340 +#define DMAC_M2P9 0x0300 +#define DMAC_M2M0 0x0100 +#define DMAC_M2M1 0x0140 + +#define DMAC_P_PCONTROL(c) __REG(DMAC_PHYS + (c) + 0x00) +#define DMAC_P_PINTERRUPT(c) __REG(DMAC_PHYS + (c) + 0x04) +#define DMAC_P_PPALLOC(c) __REG(DMAC_PHYS + (c) + 0x08) +#define DMAC_P_PSTATUS(c) __REG(DMAC_PHYS + (c) + 0x0c) +#define DMAC_P_REMAIN(c) __REG(DMAC_PHYS + (c) + 0x14) +#define DMAC_P_MAXCNT0(c) __REG(DMAC_PHYS + (c) + 0x20) +#define DMAC_P_BASE0(c) __REG(DMAC_PHYS + (c) + 0x24) +#define DMAC_P_CURRENT0(c) __REG(DMAC_PHYS + (c) + 0x28) +#define DMAC_P_MAXCNT1(c) __REG(DMAC_PHYS + (c) + 0x30) +#define DMAC_P_BASE1(c) __REG(DMAC_PHYS + (c) + 0x34) +#define DMAC_P_CURRENT1(c) __REG(DMAC_PHYS + (c) + 0x38) + +#define DMAC_PCONTROL_ENABLE (1<<4) + +#define DMAC_PORT_USB 0 +#define DMAC_PORT_SDMMC 1 +#define DMAC_PORT_AC97_1 2 +#define DMAC_PORT_AC97_2 3 +#define DMAC_PORT_AC97_3 4 +#define DMAC_PORT_UART1 6 +#define DMAC_PORT_UART2 7 +#define DMAC_PORT_UART3 8 + +#define DMAC_PSTATUS_CURRSTATE_SHIFT 4 +#define DMAC_PSTATUS_CURRSTATE_MASK 0x3 + +#define DMAC_PSTATUS_NEXTBUF (1<<6) +#define DMAC_PSTATUS_STALLRINT (1<<0) + +#define DMAC_INT_CHE (1<<3) +#define DMAC_INT_NFB (1<<1) +#define DMAC_INT_STALL (1<<0) diff --git a/include/asm-arm/arch-lh7a40x/entry-macro.S b/include/asm-arm/arch-lh7a40x/entry-macro.S index a2f67c0..9fc7f49 100644 --- a/include/asm-arm/arch-lh7a40x/entry-macro.S +++ b/include/asm-arm/arch-lh7a40x/entry-macro.S @@ -10,11 +10,73 @@ #include #include -# if defined (CONFIG_ARCH_LH7A400) && defined (CONFIG_ARCH_LH7A404) -# error "LH7A400 and LH7A404 are mutually exclusive" -# endif +/* In order to allow there to be support for both of the processor + classes at the same time, we make a hack here that isn't very + pretty. At startup, the link pointed to with the + branch_irq_lh7a400 symbol is replaced with a NOP when the CPU is + detected as a lh7a404. -# if defined (CONFIG_ARCH_LH7A400) + *** FIXME: we should clean this up so that there is only one + implementation for each CPU's design. + +*/ + +#if defined (CONFIG_ARCH_LH7A400) && defined (CONFIG_ARCH_LH7A404) + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + +branch_irq_lh7a400: b 1000f + +@ Implementation of the LH7A404 get_irqnr_and_base. + + mov \irqnr, #0 @ VIC1 irq base + mov \base, #io_p2v(0x80000000) @ APB registers + add \base, \base, #0x8000 + ldr \tmp, [\base, #0x0030] @ VIC1_VECTADDR + tst \tmp, #VA_VECTORED @ Direct vectored + bne 1002f + tst \tmp, #VA_VIC1DEFAULT @ Default vectored VIC1 + ldrne \irqstat, [\base, #0] @ VIC1_IRQSTATUS + bne 1001f + add \base, \base, #(0xa000 - 0x8000) + ldr \tmp, [\base, #0x0030] @ VIC2_VECTADDR + tst \tmp, #VA_VECTORED @ Direct vectored + bne 1002f + ldr \irqstat, [\base, #0] @ VIC2_IRQSTATUS + mov \irqnr, #32 @ VIC2 irq base + +1001: movs \irqstat, \irqstat, lsr #1 @ Shift into carry + bcs 1008f @ Bit set; irq found + add \irqnr, \irqnr, #1 + bne 1001b @ Until no bits + b 1009f @ Nothing? Hmm. +1002: and \irqnr, \tmp, #0x3f @ Mask for valid bits +1008: movs \irqstat, #1 @ Force !Z + str \tmp, [\base, #0x0030] @ Clear vector + b 1009f + +@ Implementation of the LH7A400 get_irqnr_and_base. + +1000: mov \irqnr, #0 + mov \base, #io_p2v(0x80000000) @ APB registers + ldr \irqstat, [\base, #0x500] @ PIC INTSR + +1001: movs \irqstat, \irqstat, lsr #1 @ Shift into carry + bcs 1008f @ Bit set; irq found + add \irqnr, \irqnr, #1 + bne 1001b @ Until no bits + b 1009f @ Nothing? Hmm. +1008: movs \irqstat, #1 @ Force !Z + +1009: + .endm + + + +#elif defined (CONFIG_ARCH_LH7A400) .macro disable_fiq .endm diff --git a/include/asm-arm/arch-lh7a40x/hardware.h b/include/asm-arm/arch-lh7a40x/hardware.h index aeb07c1..e9ff74f 100644 --- a/include/asm-arm/arch-lh7a40x/hardware.h +++ b/include/asm-arm/arch-lh7a40x/hardware.h @@ -13,6 +13,8 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H +#include /* Added for the sake of amba-clcd driver */ + #define io_p2v(x) (0xf0000000 | (((x) & 0xfff00000) >> 4) | ((x) & 0x0000ffff)) #define io_v2p(x) ( (((x) & 0x0fff0000) << 4) | ((x) & 0x0000ffff)) @@ -53,6 +55,8 @@ # define __PREG(x) (io_v2p((u32)&(x))) #endif +#define MASK_AND_SET(v,m,s) (v) = ((v)&~(m))|(s) + #include "registers.h" #endif /* _ASM_ARCH_HARDWARE_H */ diff --git a/include/asm-arm/arch-lh7a40x/io.h b/include/asm-arm/arch-lh7a40x/io.h index bbcd433..17bc940 100644 --- a/include/asm-arm/arch-lh7a40x/io.h +++ b/include/asm-arm/arch-lh7a40x/io.h @@ -18,6 +18,5 @@ #define IO_SPACE_LIMIT 0xffffffff /* No ISA or PCI bus on this machine. */ #define __io(a) ((void __iomem *)(a)) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif /* __ASM_ARCH_IO_H */ diff --git a/include/asm-arm/arch-lh7a40x/irqs.h b/include/asm-arm/arch-lh7a40x/irqs.h index f91f3e5..afe8c7c 100644 --- a/include/asm-arm/arch-lh7a40x/irqs.h +++ b/include/asm-arm/arch-lh7a40x/irqs.h @@ -18,7 +18,6 @@ #ifndef __ASM_ARCH_IRQS_H #define __ASM_ARCH_IRQS_H -#include #define FIQ_START 80 @@ -154,9 +153,10 @@ #endif #if !defined (IRQ_GPIO0INTR) # define IRQ_GPIO0INTR IRQ_GPIO0FIQ #endif -#define IRQ_TICK IRQ_TINTR +#define IRQ_TICK IRQ_TINTR #define IRQ_PCC1_RDY IRQ_GPIO6INTR /* PCCard 1 ready */ #define IRQ_PCC2_RDY IRQ_GPIO7INTR /* PCCard 2 ready */ +#define IRQ_USB IRQ_USBINTR /* USB device */ #ifdef CONFIG_MACH_KEV7A400 # define IRQ_TS IRQ_GPIOFIQ /* Touchscreen */ @@ -191,6 +191,10 @@ # define IRQ_LPD7A40X_ETH_INT IRQ_LPD7A4 # define IRQ_LPD7A400_TS IRQ_LPD7A40X_CPLD + 1 /* Touch screen */ #endif +#if defined (CONFIG_MACH_LPD7A400) +# define IRQ_TOUCH IRQ_LPD7A400_TS +#endif + #define NR_IRQS (NR_IRQ_CPU + NR_IRQ_BOARD) #endif diff --git a/include/asm-arm/arch-lh7a40x/registers.h b/include/asm-arm/arch-lh7a40x/registers.h index 2edb22e..b4f09b3 100644 --- a/include/asm-arm/arch-lh7a40x/registers.h +++ b/include/asm-arm/arch-lh7a40x/registers.h @@ -9,7 +9,6 @@ * */ -#include #include #ifndef __ASM_ARCH_REGISTERS_H @@ -18,7 +17,7 @@ #define __ASM_ARCH_REGISTERS_H /* Physical register base addresses */ -#define AC97_PHYS (0x80000000) /* AC97 Controller */ +#define AC97C_PHYS (0x80000000) /* AC97 Controller */ #define MMC_PHYS (0x80000100) /* Multimedia Card Controller */ #define USB_PHYS (0x80000200) /* USB Client */ #define SCI_PHYS (0x80000300) /* Secure Card Interface */ @@ -35,6 +34,8 @@ #define TIMER_PHYS (0x80000c00) /* Timer #define RTC_PHYS (0x80000d00) /* Real-time Clock */ #define GPIO_PHYS (0x80000e00) /* General Purpose IO */ #define BMI_PHYS (0x80000f00) /* Battery Monitor Interface */ +#define HRTFTC_PHYS (0x80001000) /* High-res TFT Controller (LH7A400) */ +#define ALI_PHYS (0x80001000) /* Advanced LCD Interface (LH7A404) */ #define WDT_PHYS (0x80001400) /* Watchdog Timer */ #define SMC_PHYS (0x80002000) /* Static Memory Controller */ #define SDRC_PHYS (0x80002400) /* SDRAM Controller */ @@ -43,6 +44,7 @@ #define CLCDC_PHYS (0x80003000) /* Color /* Physical registers of the LH7A404 */ +#define ADC_PHYS (0x80001300) /* A/D & Touchscreen Controller */ #define VIC1_PHYS (0x80008000) /* Vectored Interrupt Controller 1 */ #define USBH_PHYS (0x80009000) /* USB OHCI host controller */ #define VIC2_PHYS (0x8000a000) /* Vectored Interrupt Controller 2 */ @@ -53,10 +55,32 @@ #define VIC2_PHYS (0x8000a000) /* Vector /* Clock/State Controller register */ +#define CSC_PWRSR __REG(CSC_PHYS + 0x00) /* Reset register & ID */ #define CSC_PWRCNT __REG(CSC_PHYS + 0x04) /* Power control */ +#define CSC_CLKSET __REG(CSC_PHYS + 0x20) /* Clock speed control */ +#define CSC_USBDRESET __REG(CSC_PHYS + 0x4c) /* USB Device resets */ #define CSC_PWRCNT_USBH_EN (1<<28) /* USB Host power enable */ - +#define CSC_PWRCNT_DMAC_M2M1_EN (1<<27) +#define CSC_PWRCNT_DMAC_M2M0_EN (1<<26) +#define CSC_PWRCNT_DMAC_M2P8_EN (1<<25) +#define CSC_PWRCNT_DMAC_M2P9_EN (1<<24) +#define CSC_PWRCNT_DMAC_M2P6_EN (1<<23) +#define CSC_PWRCNT_DMAC_M2P7_EN (1<<22) +#define CSC_PWRCNT_DMAC_M2P4_EN (1<<21) +#define CSC_PWRCNT_DMAC_M2P5_EN (1<<20) +#define CSC_PWRCNT_DMAC_M2P2_EN (1<<19) +#define CSC_PWRCNT_DMAC_M2P3_EN (1<<18) +#define CSC_PWRCNT_DMAC_M2P0_EN (1<<17) +#define CSC_PWRCNT_DMAC_M2P1_EN (1<<16) + +#define CSC_PWRSR_CHIPMAN_SHIFT (24) +#define CSC_PWRSR_CHIPMAN_MASK (0xff) +#define CSC_PWRSR_CHIPID_SHIFT (16) +#define CSC_PWRSR_CHIPID_MASK (0xff) + +#define CSC_USBDRESET_APBRESETREG (1<<1) +#define CSC_USBDRESET_IORESETREG (1<<0) /* Interrupt Controller registers */ @@ -109,6 +133,13 @@ #define GPIO_INTTYPE2 __REG(GPIO_PHYS + #define GPIO_GPIOFEOI __REG(GPIO_PHYS + 0x54) /* GPIO End-of-Interrupt */ #define GPIO_GPIOINTEN __REG(GPIO_PHYS + 0x58) /* GPIO Interrupt Enable */ #define GPIO_INTSTATUS __REG(GPIO_PHYS + 0x5c) /* GPIO Interrupt Status */ +#define GPIO_PINMUX __REG(GPIO_PHYS + 0x2c) +#define GPIO_PADD __REG(GPIO_PHYS + 0x10) +#define GPIO_PAD __REG(GPIO_PHYS + 0x00) +#define GPIO_PCD __REG(GPIO_PHYS + 0x08) +#define GPIO_PCDD __REG(GPIO_PHYS + 0x18) +#define GPIO_PEDD __REG(GPIO_PHYS + 0x24) +#define GPIO_PED __REG(GPIO_PHYS + 0x20) /* Static Memory Controller registers */ @@ -138,20 +169,21 @@ # define CPLD_SEVEN_SEG __REG16(CPLD_PH #endif #if defined (CONFIG_MACH_LPD7A400) || defined (CONFIG_MACH_LPD7A404) -# define CPLD_CONTROL __REG8(CPLD02_PHYS) -# define CPLD_SPI_DATA __REG8(CPLD06_PHYS) -# define CPLD_SPI_CONTROL __REG8(CPLD08_PHYS) -# define CPLD_SPI_EEPROM __REG8(CPLD0A_PHYS) -# define CPLD_INTERRUPTS __REG8(CPLD0C_PHYS) /* IRQ mask/status */ -# define CPLD_BOOT_MODE __REG8(CPLD0E_PHYS) -# define CPLD_FLASH __REG8(CPLD10_PHYS) -# define CPLD_POWER_MGMT __REG8(CPLD12_PHYS) -# define CPLD_REVISION __REG8(CPLD14_PHYS) -# define CPLD_GPIO_EXT __REG8(CPLD16_PHYS) -# define CPLD_GPIO_DATA __REG8(CPLD18_PHYS) -# define CPLD_GPIO_DIR __REG8(CPLD1A_PHYS) -#endif +# define CPLD_CONTROL __REG16(CPLD02_PHYS) +# define CPLD_SPI_DATA __REG16(CPLD06_PHYS) +# define CPLD_SPI_CONTROL __REG16(CPLD08_PHYS) +# define CPLD_SPI_EEPROM __REG16(CPLD0A_PHYS) +# define CPLD_INTERRUPTS __REG16(CPLD0C_PHYS) /* IRQ mask/status */ +# define CPLD_BOOT_MODE __REG16(CPLD0E_PHYS) +# define CPLD_FLASH __REG16(CPLD10_PHYS) +# define CPLD_POWER_MGMT __REG16(CPLD12_PHYS) +# define CPLD_REVISION __REG16(CPLD14_PHYS) +# define CPLD_GPIO_EXT __REG16(CPLD16_PHYS) +# define CPLD_GPIO_DATA __REG16(CPLD18_PHYS) +# define CPLD_GPIO_DIR __REG16(CPLD1A_PHYS) + +#endif /* Timer registers */ @@ -190,4 +222,3 @@ #define GPIO_RAWINTSTATUS __REG(GPIO_PHY #endif /* _ASM_ARCH_REGISTERS_H */ - diff --git a/include/asm-arm/arch-lh7a40x/ssp.h b/include/asm-arm/arch-lh7a40x/ssp.h new file mode 100644 index 0000000..132b1c4 --- /dev/null +++ b/include/asm-arm/arch-lh7a40x/ssp.h @@ -0,0 +1,71 @@ +/* ssp.h + $Id$ + + written by Marc Singer + 6 Dec 2004 + + Copyright (C) 2004 Marc Singer + + ----------- + DESCRIPTION + ----------- + + This SSP header is available throughout the kernel, for this + machine/architecture, because drivers that use it may be dispersed. + + This file was cloned from the 7952x implementation. It would be + better to share them, but we're taking an easier approach for the + time being. + +*/ + +#if !defined (__SSP_H__) +# define __SSP_H__ + +/* ----- Includes */ + +/* ----- Types */ + +struct ssp_driver { + int (*init) (void); + void (*exit) (void); + void (*acquire) (void); + void (*release) (void); + int (*configure) (int device, int mode, int speed, + int frame_size_write, int frame_size_read); + void (*chip_select) (int enable); + void (*set_callbacks) (void* handle, + irqreturn_t (*callback_tx)(void*), + irqreturn_t (*callback_rx)(void*)); + void (*enable) (void); + void (*disable) (void); +// int (*save_state) (void*); +// void (*restore_state) (void*); + int (*read) (void); + int (*write) (u16 data); + int (*write_read) (u16 data); + void (*flush) (void); + void (*write_async) (void* pv, size_t cb); + size_t (*write_pos) (void); +}; + + /* These modes are only available on the LH79524 */ +#define SSP_MODE_SPI (1) +#define SSP_MODE_SSI (2) +#define SSP_MODE_MICROWIRE (3) +#define SSP_MODE_I2S (4) + + /* CPLD SPI devices */ +#define DEVICE_EEPROM 0 /* Configuration eeprom */ +#define DEVICE_MAC 1 /* MAC eeprom (LPD79524) */ +#define DEVICE_CODEC 2 /* Audio codec */ +#define DEVICE_TOUCH 3 /* Touch screen (LPD79520) */ + +/* ----- Globals */ + +/* ----- Prototypes */ + +//extern struct ssp_driver lh79520_i2s_driver; +extern struct ssp_driver lh7a400_cpld_ssp_driver; + +#endif /* __SSP_H__ */ diff --git a/include/asm-arm/arch-lh7a40x/uncompress.h b/include/asm-arm/arch-lh7a40x/uncompress.h index f805334..3d1ce04 100644 --- a/include/asm-arm/arch-lh7a40x/uncompress.h +++ b/include/asm-arm/arch-lh7a40x/uncompress.h @@ -16,7 +16,7 @@ #endif #ifndef UART_R_STATUS # define UART_R_STATUS (0x10) #endif -#define nTxRdy (0x20) /* Not TxReady (literally Tx FIFO full) */ +#define nTxRdy (0x20) /* Not TxReady (literally Tx FIFO full) */ /* Access UART with physical addresses before MMU is setup */ #define UART_STATUS (*(volatile unsigned long*) (UART2_PHYS + UART_R_STATUS)) diff --git a/include/asm-arm/arch-netx/debug-macro.S b/include/asm-arm/arch-netx/debug-macro.S new file mode 100644 index 0000000..a940d0e --- /dev/null +++ b/include/asm-arm/arch-netx/debug-macro.S @@ -0,0 +1,38 @@ +/* linux/include/asm-arm/arch-netx/debug-macro.S + * + * Debugging macro include header + * + * Copyright (C) 1994-1999 Russell King + * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +*/ + +#include "hardware.h" + + .macro addruart,rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + moveq \rx, #0x00100000 @ physical + movne \rx, #io_p2v(0x00100000) @ virtual + orr \rx, \rx, #0x00000a00 + .endm + + .macro senduart,rd,rx + str \rd, [\rx, #0] + .endm + + .macro busyuart,rd,rx +1002: ldr \rd, [\rx, #0x18] + tst \rd, #(1 << 3) + bne 1002b + .endm + + .macro waituart,rd,rx +1001: ldr \rd, [\rx, #0x18] + tst \rd, #(1 << 3) + bne 1001b + .endm diff --git a/include/asm-arm/arch-netx/dma.h b/include/asm-arm/arch-netx/dma.h new file mode 100644 index 0000000..4eda5fe --- /dev/null +++ b/include/asm-arm/arch-netx/dma.h @@ -0,0 +1,21 @@ +/* + * linux/include/asm-arm/arch-netx/dma.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MAX_DMA_CHANNELS 0 +#define MAX_DMA_ADDRESS ~0 diff --git a/include/asm-arm/arch-netx/entry-macro.S b/include/asm-arm/arch-netx/entry-macro.S new file mode 100644 index 0000000..658df4d --- /dev/null +++ b/include/asm-arm/arch-netx/entry-macro.S @@ -0,0 +1,35 @@ +/* + * include/asm-arm/arch-netx/entry-macro.S + * + * Low-level IRQ helper macros for Hilscher netX based platforms + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp + mov \base, #io_p2v(0x00100000) + add \base, \base, #0x000ff000 + + ldr \irqstat, [\base, #0] + clz \irqnr, \irqstat + rsb \irqnr, \irqnr, #31 + cmp \irqstat, #0 + .endm + diff --git a/include/asm-arm/arch-netx/eth.h b/include/asm-arm/arch-netx/eth.h new file mode 100644 index 0000000..643c90e --- /dev/null +++ b/include/asm-arm/arch-netx/eth.h @@ -0,0 +1,27 @@ +/* + * include/asm-arm/arch-netx/eth.h + * + * Copyright (c) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ASMARM_ARCH_ETH_H +#define ASMARM_ARCH_ETH_H + +struct netxeth_platform_data { + unsigned int xcno; /* number of xmac/xpec engine this eth uses */ +}; + +#endif diff --git a/include/asm-arm/arch-netx/hardware.h b/include/asm-arm/arch-netx/hardware.h new file mode 100644 index 0000000..7786c45 --- /dev/null +++ b/include/asm-arm/arch-netx/hardware.h @@ -0,0 +1,39 @@ +/* + * include/asm-arm/arch-netx/hardware.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#define NETX_IO_PHYS 0x00100000 +#define NETX_IO_VIRT 0xe0000000 +#define NETX_IO_SIZE 0x00100000 + +#define SRAM_INTERNAL_PHYS_0 0x00000 +#define SRAM_INTERNAL_PHYS_1 0x08000 +#define SRAM_INTERNAL_PHYS_2 0x10000 +#define SRAM_INTERNAL_PHYS_3 0x18000 +#define SRAM_INTERNAL_PHYS(no) ((no) * 0x8000) + +#define XPEC_MEM_SIZE 0x4000 +#define XMAC_MEM_SIZE 0x1000 +#define SRAM_MEM_SIZE 0x8000 + +#define io_p2v(x) ((x) - NETX_IO_PHYS + NETX_IO_VIRT) +#define io_v2p(x) ((x) - NETX_IO_VIRT + NETX_IO_PHYS) + +#endif diff --git a/include/asm-arm/arch-netx/io.h b/include/asm-arm/arch-netx/io.h new file mode 100644 index 0000000..a7a53f8 --- /dev/null +++ b/include/asm-arm/arch-netx/io.h @@ -0,0 +1,28 @@ +/* + * linux/include/asm-arm/arch-netx/io.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +#define __io(a) ((void __iomem *)(a)) +#define __mem_pci(a) (a) + +#endif diff --git a/include/asm-arm/arch-netx/irqs.h b/include/asm-arm/arch-netx/irqs.h new file mode 100644 index 0000000..a487dc6 --- /dev/null +++ b/include/asm-arm/arch-netx/irqs.h @@ -0,0 +1,70 @@ +/* + * include/asm-arm/arch-netx/irqs.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define NETX_IRQ_VIC_START 0 +#define NETX_IRQ_SOFTINT 0 +#define NETX_IRQ_TIMER0 1 +#define NETX_IRQ_TIMER1 2 +#define NETX_IRQ_TIMER2 3 +#define NETX_IRQ_SYSTIME_NS 4 +#define NETX_IRQ_SYSTIME_S 5 +#define NETX_IRQ_GPIO_15 6 +#define NETX_IRQ_WATCHDOG 7 +#define NETX_IRQ_UART0 8 +#define NETX_IRQ_UART1 9 +#define NETX_IRQ_UART2 10 +#define NETX_IRQ_USB 11 +#define NETX_IRQ_SPI 12 +#define NETX_IRQ_I2C 13 +#define NETX_IRQ_LCD 14 +#define NETX_IRQ_HIF 15 +#define NETX_IRQ_GPIO_0_14 16 +#define NETX_IRQ_XPEC0 17 +#define NETX_IRQ_XPEC1 18 +#define NETX_IRQ_XPEC2 19 +#define NETX_IRQ_XPEC3 20 +#define NETX_IRQ_XPEC(no) (17 + (no)) +#define NETX_IRQ_MSYNC0 21 +#define NETX_IRQ_MSYNC1 22 +#define NETX_IRQ_MSYNC2 23 +#define NETX_IRQ_MSYNC3 24 +#define NETX_IRQ_IRQ_PHY 25 +#define NETX_IRQ_ISO_AREA 26 +/* int 27 is reserved */ +/* int 28 is reserved */ +#define NETX_IRQ_TIMER3 29 +#define NETX_IRQ_TIMER4 30 +/* int 31 is reserved */ + +#define NETX_IRQS 32 + +/* for multiplexed irqs on gpio 0..14 */ +#define NETX_IRQ_GPIO(x) (NETX_IRQS + (x)) +#define NETX_IRQ_GPIO_LAST NETX_IRQ_GPIO(14) + +/* Host interface interrupts */ +#define NETX_IRQ_HIF_CHAINED(x) (NETX_IRQ_GPIO_LAST + 1 + (x)) +#define NETX_IRQ_HIF_PIO35 NETX_IRQ_HIF_CHAINED(0) +#define NETX_IRQ_HIF_PIO36 NETX_IRQ_HIF_CHAINED(1) +#define NETX_IRQ_HIF_PIO40 NETX_IRQ_HIF_CHAINED(2) +#define NETX_IRQ_HIF_PIO47 NETX_IRQ_HIF_CHAINED(3) +#define NETX_IRQ_HIF_PIO72 NETX_IRQ_HIF_CHAINED(4) +#define NETX_IRQ_HIF_LAST NETX_IRQ_HIF_CHAINED(4) + +#define NR_IRQS (NETX_IRQ_HIF_LAST + 1) diff --git a/include/asm-arm/arch-netx/memory.h b/include/asm-arm/arch-netx/memory.h new file mode 100644 index 0000000..6d8d2df --- /dev/null +++ b/include/asm-arm/arch-netx/memory.h @@ -0,0 +1,36 @@ +/* + * linux/include/asm-arm/arch-netx/memory.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +#define PHYS_OFFSET UL(0x80000000) + +/* + * Virtual view <-> DMA view memory address translations + * virt_to_bus: Used to translate the virtual address to an + * address suitable to be passed to set_dma_addr + * bus_to_virt: Used to convert an address for DMA operations + * to an address that the kernel can use. + */ +#define __virt_to_bus(x) __virt_to_phys(x) +#define __bus_to_virt(x) __phys_to_virt(x) + +#endif + diff --git a/include/asm-arm/arch-netx/netx-regs.h b/include/asm-arm/arch-netx/netx-regs.h new file mode 100644 index 0000000..8ab45be --- /dev/null +++ b/include/asm-arm/arch-netx/netx-regs.h @@ -0,0 +1,410 @@ +/* + * include/asm-arm/arch-netx/netx-regs.h + * + * Copyright (c) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARCH_NETX_REGS_H +#define __ASM_ARCH_NETX_REGS_H + +/* offsets relative to the beginning of the io space */ +#define NETX_OFS_SYSTEM 0x00000 +#define NETX_OFS_MEMCR 0x00100 +#define NETX_OFS_DPMAS 0x03000 +#define NETX_OFS_GPIO 0x00800 +#define NETX_OFS_PIO 0x00900 +#define NETX_OFS_UART0 0x00a00 +#define NETX_OFS_UART1 0x00a40 +#define NETX_OFS_UART2 0x00a80 +#define NETX_OF_MIIMU 0x00b00 +#define NETX_OFS_SPI 0x00c00 +#define NETX_OFS_I2C 0x00d00 +#define NETX_OFS_SYSTIME 0x01100 +#define NETX_OFS_RTC 0x01200 +#define NETX_OFS_EXTBUS 0x03600 +#define NETX_OFS_LCD 0x04000 +#define NETX_OFS_USB 0x20000 +#define NETX_OFS_XMAC0 0x60000 +#define NETX_OFS_XMAC1 0x61000 +#define NETX_OFS_XMAC2 0x62000 +#define NETX_OFS_XMAC3 0x63000 +#define NETX_OFS_XMAC(no) (0x60000 + (no) * 0x1000) +#define NETX_OFS_PFIFO 0x64000 +#define NETX_OFS_XPEC0 0x70000 +#define NETX_OFS_XPEC1 0x74000 +#define NETX_OFS_XPEC2 0x78000 +#define NETX_OFS_XPEC3 0x7c000 +#define NETX_OFS_XPEC(no) (0x70000 + (no) * 0x4000) +#define NETX_OFS_VIC 0xff000 + +/* physical addresses */ +#define NETX_PA_SYSTEM (NETX_IO_PHYS + NETX_OFS_SYSTEM) +#define NETX_PA_MEMCR (NETX_IO_PHYS + NETX_OFS_MEMCR) +#define NETX_PA_DPMAS (NETX_IO_PHYS + NETX_OFS_DPMAS) +#define NETX_PA_GPIO (NETX_IO_PHYS + NETX_OFS_GPIO) +#define NETX_PA_PIO (NETX_IO_PHYS + NETX_OFS_PIO) +#define NETX_PA_UART0 (NETX_IO_PHYS + NETX_OFS_UART0) +#define NETX_PA_UART1 (NETX_IO_PHYS + NETX_OFS_UART1) +#define NETX_PA_UART2 (NETX_IO_PHYS + NETX_OFS_UART2) +#define NETX_PA_MIIMU (NETX_IO_PHYS + NETX_OF_MIIMU) +#define NETX_PA_SPI (NETX_IO_PHYS + NETX_OFS_SPI) +#define NETX_PA_I2C (NETX_IO_PHYS + NETX_OFS_I2C) +#define NETX_PA_SYSTIME (NETX_IO_PHYS + NETX_OFS_SYSTIME) +#define NETX_PA_RTC (NETX_IO_PHYS + NETX_OFS_RTC) +#define NETX_PA_EXTBUS (NETX_IO_PHYS + NETX_OFS_EXTBUS) +#define NETX_PA_LCD (NETX_IO_PHYS + NETX_OFS_LCD) +#define NETX_PA_USB (NETX_IO_PHYS + NETX_OFS_USB) +#define NETX_PA_XMAC0 (NETX_IO_PHYS + NETX_OFS_XMAC0) +#define NETX_PA_XMAC1 (NETX_IO_PHYS + NETX_OFS_XMAC1) +#define NETX_PA_XMAC2 (NETX_IO_PHYS + NETX_OFS_XMAC2) +#define NETX_PA_XMAC3 (NETX_IO_PHYS + NETX_OFS_XMAC3) +#define NETX_PA_XMAC(no) (NETX_IO_PHYS + NETX_OFS_XMAC(no)) +#define NETX_PA_PFIFO (NETX_IO_PHYS + NETX_OFS_PFIFO) +#define NETX_PA_XPEC0 (NETX_IO_PHYS + NETX_OFS_XPEC0) +#define NETX_PA_XPEC1 (NETX_IO_PHYS + NETX_OFS_XPEC1) +#define NETX_PA_XPEC2 (NETX_IO_PHYS + NETX_OFS_XPEC2) +#define NETX_PA_XPEC3 (NETX_IO_PHYS + NETX_OFS_XPEC3) +#define NETX_PA_XPEC(no) (NETX_IO_PHYS + NETX_OFS_XPEC(no)) +#define NETX_PA_VIC (NETX_IO_PHYS + NETX_OFS_VIC) + +/* virual addresses */ +#define NETX_VA_SYSTEM (NETX_IO_VIRT + NETX_OFS_SYSTEM) +#define NETX_VA_MEMCR (NETX_IO_VIRT + NETX_OFS_MEMCR) +#define NETX_VA_DPMAS (NETX_IO_VIRT + NETX_OFS_DPMAS) +#define NETX_VA_GPIO (NETX_IO_VIRT + NETX_OFS_GPIO) +#define NETX_VA_PIO (NETX_IO_VIRT + NETX_OFS_PIO) +#define NETX_VA_UART0 (NETX_IO_VIRT + NETX_OFS_UART0) +#define NETX_VA_UART1 (NETX_IO_VIRT + NETX_OFS_UART1) +#define NETX_VA_UART2 (NETX_IO_VIRT + NETX_OFS_UART2) +#define NETX_VA_MIIMU (NETX_IO_VIRT + NETX_OF_MIIMU) +#define NETX_VA_SPI (NETX_IO_VIRT + NETX_OFS_SPI) +#define NETX_VA_I2C (NETX_IO_VIRT + NETX_OFS_I2C) +#define NETX_VA_SYSTIME (NETX_IO_VIRT + NETX_OFS_SYSTIME) +#define NETX_VA_RTC (NETX_IO_VIRT + NETX_OFS_RTC) +#define NETX_VA_EXTBUS (NETX_IO_VIRT + NETX_OFS_EXTBUS) +#define NETX_VA_LCD (NETX_IO_VIRT + NETX_OFS_LCD) +#define NETX_VA_USB (NETX_IO_VIRT + NETX_OFS_USB) +#define NETX_VA_XMAC0 (NETX_IO_VIRT + NETX_OFS_XMAC0) +#define NETX_VA_XMAC1 (NETX_IO_VIRT + NETX_OFS_XMAC1) +#define NETX_VA_XMAC2 (NETX_IO_VIRT + NETX_OFS_XMAC2) +#define NETX_VA_XMAC3 (NETX_IO_VIRT + NETX_OFS_XMAC3) +#define NETX_VA_XMAC(no) (NETX_IO_VIRT + NETX_OFS_XMAC(no)) +#define NETX_VA_PFIFO (NETX_IO_VIRT + NETX_OFS_PFIFO) +#define NETX_VA_XPEC0 (NETX_IO_VIRT + NETX_OFS_XPEC0) +#define NETX_VA_XPEC1 (NETX_IO_VIRT + NETX_OFS_XPEC1) +#define NETX_VA_XPEC2 (NETX_IO_VIRT + NETX_OFS_XPEC2) +#define NETX_VA_XPEC3 (NETX_IO_VIRT + NETX_OFS_XPEC3) +#define NETX_VA_XPEC(no) (NETX_IO_VIRT + NETX_OFS_XPEC(no)) +#define NETX_VA_VIC (NETX_IO_VIRT + NETX_OFS_VIC) + +/********************************* + * System functions * + *********************************/ + +/* Registers */ +#define NETX_SYSTEM_REG(ofs) __io(NETX_VA_SYSTEM + (ofs)) +#define NETX_SYSTEM_BOO_SR NETX_SYSTEM_REG(0x00) +#define NETX_SYSTEM_IOC_CR NETX_SYSTEM_REG(0x04) +#define NETX_SYSTEM_IOC_MR NETX_SYSTEM_REG(0x08) + +/* FIXME: Docs are not consistent */ +#define NETX_SYSTEM_RES_CR NETX_SYSTEM_REG(0x08) +/* #define NETX_SYSTEM_RES_CR NETX_SYSTEM_REG(0x0c) */ + +#define NETX_SYSTEM_PHY_CONTROL NETX_SYSTEM_REG(0x10) +#define NETX_SYSTEM_REV NETX_SYSTEM_REG(0x34) +#define NETX_SYSTEM_IOC_ACCESS_KEY NETX_SYSTEM_REG(0x70) +#define NETX_SYSTEM_WDG_TR NETX_SYSTEM_REG(0x200) +#define NETX_SYSTEM_WDG_CTR NETX_SYSTEM_REG(0x204) +#define NETX_SYSTEM_WDG_IRQ_TIMEOUT NETX_SYSTEM_REG(0x208) +#define NETX_SYSTEM_WDG_RES_TIMEOUT NETX_SYSTEM_REG(0x20c) + +/* Bits */ +#define NETX_SYSTEM_RES_CR_RSTIN (1<<0) +#define NETX_SYSTEM_RES_CR_WDG_RES (1<<1) +#define NETX_SYSTEM_RES_CR_HOST_RES (1<<2) +#define NETX_SYSTEM_RES_CR_FIRMW_RES (1<<3) +#define NETX_SYSTEM_RES_CR_XPEC0_RES (1<<4) +#define NETX_SYSTEM_RES_CR_XPEC1_RES (1<<5) +#define NETX_SYSTEM_RES_CR_XPEC2_RES (1<<6) +#define NETX_SYSTEM_RES_CR_XPEC3_RES (1<<7) +#define NETX_SYSTEM_RES_CR_DIS_XPEC0_RES (1<<16) +#define NETX_SYSTEM_RES_CR_DIS_XPEC1_RES (1<<17) +#define NETX_SYSTEM_RES_CR_DIS_XPEC2_RES (1<<18) +#define NETX_SYSTEM_RES_CR_DIS_XPEC3_RES (1<<19) +#define NETX_SYSTEM_RES_CR_FIRMW_FLG0 (1<<20) +#define NETX_SYSTEM_RES_CR_FIRMW_FLG1 (1<<21) +#define NETX_SYSTEM_RES_CR_FIRMW_FLG2 (1<<22) +#define NETX_SYSTEM_RES_CR_FIRMW_FLG3 (1<<23) +#define NETX_SYSTEM_RES_CR_FIRMW_RES_EN (1<<24) +#define NETX_SYSTEM_RES_CR_RSTOUT (1<<25) +#define NETX_SYSTEM_RES_CR_EN_RSTOUT (1<<26) + +#define PHY_CONTROL_RESET (1<<31) +#define PHY_CONTROL_SIM_BYP (1<<30) +#define PHY_CONTROL_CLK_XLATIN (1<<29) +#define PHY_CONTROL_PHY1_EN (1<<21) +#define PHY_CONTROL_PHY1_NP_MSG_CODE +#define PHY_CONTROL_PHY1_AUTOMDIX (1<<17) +#define PHY_CONTROL_PHY1_FIXMODE (1<<16) +#define PHY_CONTROL_PHY1_MODE(mode) (((mode) & 0x7) << 13) +#define PHY_CONTROL_PHY0_EN (1<<12) +#define PHY_CONTROL_PHY0_NP_MSG_CODE +#define PHY_CONTROL_PHY0_AUTOMDIX (1<<8) +#define PHY_CONTROL_PHY0_FIXMODE (1<<7) +#define PHY_CONTROL_PHY0_MODE(mode) (((mode) & 0x7) << 4) +#define PHY_CONTROL_PHY_ADDRESS(adr) ((adr) & 0xf) + +#define PHY_MODE_10BASE_T_HALF 0 +#define PHY_MODE_10BASE_T_FULL 1 +#define PHY_MODE_100BASE_TX_FX_FULL 2 +#define PHY_MODE_100BASE_TX_FX_HALF 3 +#define PHY_MODE_100BASE_TX_HALF 4 +#define PHY_MODE_REPEATER 5 +#define PHY_MODE_POWER_DOWN 6 +#define PHY_MODE_ALL 7 + +/* Bits */ +#define VECT_CNTL_ENABLE (1 << 5) + +/******************************* + * GPIO and timer module * + *******************************/ + +/* Registers */ +#define NETX_GPIO_REG(ofs) __io(NETX_VA_GPIO + (ofs)) +#define NETX_GPIO_CFG(gpio) NETX_GPIO_REG(0x0 + ((gpio)<<2)) +#define NETX_GPIO_THRESHOLD_CAPTURE(gpio) NETX_GPIO_REG(0x40 + ((gpio)<<2)) +#define NETX_GPIO_COUNTER_CTRL(counter) NETX_GPIO_REG(0x80 + ((counter)<<2)) +#define NETX_GPIO_COUNTER_MAX(counter) NETX_GPIO_REG(0x94 + ((counter)<<2)) +#define NETX_GPIO_COUNTER_CURRENT(counter) NETX_GPIO_REG(0xa8 + ((counter)<<2)) +#define NETX_GPIO_IRQ_ENABLE NETX_GPIO_REG(0xbc) +#define NETX_GPIO_IRQ_DISABLE NETX_GPIO_REG(0xc0) +#define NETX_GPIO_SYSTIME_NS_CMP NETX_GPIO_REG(0xc4) +#define NETX_GPIO_LINE NETX_GPIO_REG(0xc8) +#define NETX_GPIO_IRQ NETX_GPIO_REG(0xd0) + +/* Bits */ +#define NETX_GPIO_CFG_IOCFG_GP_INPUT (0x0) +#define NETX_GPIO_CFG_IOCFG_GP_OUTPUT (0x1) +#define NETX_GPIO_CFG_IOCFG_GP_UART (0x2) +#define NETX_GPIO_CFG_INV (1<<2) +#define NETX_GPIO_CFG_MODE_INPUT_READ (0<<3) +#define NETX_GPIO_CFG_MODE_INPUT_CAPTURE_CONT_RISING (1<<3) +#define NETX_GPIO_CFG_MODE_INPUT_CAPTURE_ONCE_RISING (2<<3) +#define NETX_GPIO_CFG_MODE_INPUT_CAPTURE_HIGH_LEVEL (3<<3) +#define NETX_GPIO_CFG_COUNT_REF_COUNTER0 (0<<5) +#define NETX_GPIO_CFG_COUNT_REF_COUNTER1 (1<<5) +#define NETX_GPIO_CFG_COUNT_REF_COUNTER2 (2<<5) +#define NETX_GPIO_CFG_COUNT_REF_COUNTER3 (3<<5) +#define NETX_GPIO_CFG_COUNT_REF_COUNTER4 (4<<5) +#define NETX_GPIO_CFG_COUNT_REF_SYSTIME (7<<5) + +#define NETX_GPIO_COUNTER_CTRL_RUN (1<<0) +#define NETX_GPIO_COUNTER_CTRL_SYM (1<<1) +#define NETX_GPIO_COUNTER_CTRL_ONCE (1<<2) +#define NETX_GPIO_COUNTER_CTRL_IRQ_EN (1<<3) +#define NETX_GPIO_COUNTER_CTRL_CNT_EVENT (1<<4) +#define NETX_GPIO_COUNTER_CTRL_RST_EN (1<<5) +#define NETX_GPIO_COUNTER_CTRL_SEL_EVENT (1<<6) +#define NETX_GPIO_COUNTER_CTRL_GPIO_REF /* FIXME */ + +#define GPIO_BIT(gpio) (1<<(gpio)) +#define COUNTER_BIT(counter) ((1<<16)<<(counter)) + +/******************************* + * PIO * + *******************************/ + +/* Registers */ +#define NETX_PIO_REG(ofs) __io(NETX_VA_PIO + (ofs)) +#define NETX_PIO_INPIO NETX_PIO_REG(0x0) +#define NETX_PIO_OUTPIO NETX_PIO_REG(0x4) +#define NETX_PIO_OEPIO NETX_PIO_REG(0x8) + +/******************************* + * MII Unit * + *******************************/ + +/* Registers */ +#define NETX_MIIMU __io(NETX_VA_MIIMU) + +/* Bits */ +#define MIIMU_SNRDY (1<<0) +#define MIIMU_PREAMBLE (1<<1) +#define MIIMU_OPMODE_WRITE (1<<2) +#define MIIMU_MDC_PERIOD (1<<3) +#define MIIMU_PHY_NRES (1<<4) +#define MIIMU_RTA (1<<5) +#define MIIMU_REGADDR(adr) (((adr) & 0x1f) << 6) +#define MIIMU_PHYADDR(adr) (((adr) & 0x1f) << 11) +#define MIIMU_DATA(data) (((data) & 0xffff) << 16) + +/******************************* + * xmac / xpec * + *******************************/ + +/* XPEC register offsets relative to NETX_VA_XPEC(no) */ +#define NETX_XPEC_R0_OFS 0x00 +#define NETX_XPEC_R1_OFS 0x04 +#define NETX_XPEC_R2_OFS 0x08 +#define NETX_XPEC_R3_OFS 0x0c +#define NETX_XPEC_R4_OFS 0x10 +#define NETX_XPEC_R5_OFS 0x14 +#define NETX_XPEC_R6_OFS 0x18 +#define NETX_XPEC_R7_OFS 0x1c +#define NETX_XPEC_RANGE01_OFS 0x20 +#define NETX_XPEC_RANGE23_OFS 0x24 +#define NETX_XPEC_RANGE45_OFS 0x28 +#define NETX_XPEC_RANGE67_OFS 0x2c +#define NETX_XPEC_PC_OFS 0x48 +#define NETX_XPEC_TIMER_OFS(timer) (0x30 + ((timer)<<2)) +#define NETX_XPEC_IRQ_OFS 0x8c +#define NETX_XPEC_SYSTIME_NS_OFS 0x90 +#define NETX_XPEC_FIFO_DATA_OFS 0x94 +#define NETX_XPEC_SYSTIME_S_OFS 0x98 +#define NETX_XPEC_ADC_OFS 0x9c +#define NETX_XPEC_URX_COUNT_OFS 0x40 +#define NETX_XPEC_UTX_COUNT_OFS 0x44 +#define NETX_XPEC_PC_OFS 0x48 +#define NETX_XPEC_ZERO_OFS 0x4c +#define NETX_XPEC_STATCFG_OFS 0x50 +#define NETX_XPEC_EC_MASKA_OFS 0x54 +#define NETX_XPEC_EC_MASKB_OFS 0x58 +#define NETX_XPEC_EC_MASK0_OFS 0x5c +#define NETX_XPEC_EC_MASK8_OFS 0x7c +#define NETX_XPEC_EC_MASK9_OFS 0x80 +#define NETX_XPEC_XPU_HOLD_PC_OFS 0x100 +#define NETX_XPEC_RAM_START_OFS 0x2000 + +/* Bits */ +#define XPU_HOLD_PC (1<<0) + +/* XMAC register offsets relative to NETX_VA_XMAC(no) */ +#define NETX_XMAC_RPU_PROGRAM_START_OFS 0x000 +#define NETX_XMAC_RPU_PROGRAM_END_OFS 0x3ff +#define NETX_XMAC_TPU_PROGRAM_START_OFS 0x400 +#define NETX_XMAC_TPU_PROGRAM_END_OFS 0x7ff +#define NETX_XMAC_RPU_HOLD_PC_OFS 0xa00 +#define NETX_XMAC_TPU_HOLD_PC_OFS 0xa04 +#define NETX_XMAC_STATUS_SHARED0_OFS 0x840 +#define NETX_XMAC_CONFIG_SHARED0_OFS 0x844 +#define NETX_XMAC_STATUS_SHARED1_OFS 0x848 +#define NETX_XMAC_CONFIG_SHARED1_OFS 0x84c +#define NETX_XMAC_STATUS_SHARED2_OFS 0x850 +#define NETX_XMAC_CONFIG_SHARED2_OFS 0x854 +#define NETX_XMAC_STATUS_SHARED3_OFS 0x858 +#define NETX_XMAC_CONFIG_SHARED3_OFS 0x85c + +#define RPU_HOLD_PC (1<<15) +#define TPU_HOLD_PC (1<<15) + +/******************************* + * Pointer FIFO * + *******************************/ + +/* Registers */ +#define NETX_PFIFO_REG(ofs) __io(NETX_VA_PFIFO + (ofs)) +#define NETX_PFIFO_BASE(pfifo) NETX_PFIFO_REG(0x00 + ((pfifo)<<2)) +#define NETX_PFIFO_BORDER_BASE(pfifo) NETX_PFIFO_REG(0x80 + ((pfifo)<<2)) +#define NETX_PFIFO_RESET NETX_PFIFO_REG(0x100) +#define NETX_PFIFO_FULL NETX_PFIFO_REG(0x104) +#define NETX_PFIFO_EMPTY NETX_PFIFO_REG(0x108) +#define NETX_PFIFO_OVEFLOW NETX_PFIFO_REG(0x10c) +#define NETX_PFIFO_UNDERRUN NETX_PFIFO_REG(0x110) +#define NETX_PFIFO_FILL_LEVEL(pfifo) NETX_PFIFO_REG(0x180 + ((pfifo)<<2)) +#define NETX_PFIFO_XPEC_ISR(xpec) NETX_PFIFO_REG(0x400 + ((xpec) << 2)) + +/******************************* + * Dual Port Memory * + *******************************/ + +/* Registers */ +#define NETX_DPMAS_REG(ofs) __io(NETX_VA_DPMAS + (ofs)) +#define NETX_DPMAS_SYS_STAT NETX_DPMAS_REG(0x4d8) +#define NETX_DPMAS_INT_STAT NETX_DPMAS_REG(0x4e0) +#define NETX_DPMAS_INT_EN NETX_DPMAS_REG(0x4f0) +#define NETX_DPMAS_IF_CONF0 NETX_DPMAS_REG(0x608) +#define NETX_DPMAS_IF_CONF1 NETX_DPMAS_REG(0x60c) +#define NETX_DPMAS_EXT_CONFIG(cs) NETX_DPMAS_REG(0x610 + 4 * (cs)) +#define NETX_DPMAS_IO_MODE0 NETX_DPMAS_REG(0x620) /* I/O 32..63 */ +#define NETX_DPMAS_DRV_EN0 NETX_DPMAS_REG(0x624) +#define NETX_DPMAS_DATA0 NETX_DPMAS_REG(0x628) +#define NETX_DPMAS_IO_MODE1 NETX_DPMAS_REG(0x630) /* I/O 64..84 */ +#define NETX_DPMAS_DRV_EN1 NETX_DPMAS_REG(0x634) +#define NETX_DPMAS_DATA1 NETX_DPMAS_REG(0x638) + +/* Bits */ +#define NETX_DPMAS_INT_EN_GLB_EN (1<<31) +#define NETX_DPMAS_INT_EN_MEM_LCK (1<<30) +#define NETX_DPMAS_INT_EN_WDG (1<<29) +#define NETX_DPMAS_INT_EN_PIO72 (1<<28) +#define NETX_DPMAS_INT_EN_PIO47 (1<<27) +#define NETX_DPMAS_INT_EN_PIO40 (1<<26) +#define NETX_DPMAS_INT_EN_PIO36 (1<<25) +#define NETX_DPMAS_INT_EN_PIO35 (1<<24) + +#define NETX_DPMAS_IF_CONF0_HIF_DISABLED (0<<28) +#define NETX_DPMAS_IF_CONF0_HIF_EXT_BUS (1<<28) +#define NETX_DPMAS_IF_CONF0_HIF_UP_8BIT (2<<28) +#define NETX_DPMAS_IF_CONF0_HIF_UP_16BIT (3<<28) +#define NETX_DPMAS_IF_CONF0_HIF_IO (4<<28) +#define NETX_DPMAS_IF_CONF0_WAIT_DRV_PP (1<<14) +#define NETX_DPMAS_IF_CONF0_WAIT_DRV_OD (2<<14) +#define NETX_DPMAS_IF_CONF0_WAIT_DRV_TRI (3<<14) + +#define NETX_DPMAS_IF_CONF1_IRQ_POL_PIO35 (1<<26) +#define NETX_DPMAS_IF_CONF1_IRQ_POL_PIO36 (1<<27) +#define NETX_DPMAS_IF_CONF1_IRQ_POL_PIO40 (1<<28) +#define NETX_DPMAS_IF_CONF1_IRQ_POL_PIO47 (1<<29) +#define NETX_DPMAS_IF_CONF1_IRQ_POL_PIO72 (1<<30) + +#define NETX_EXT_CONFIG_TALEWIDTH(x) (((x) & 0x7) << 29) +#define NETX_EXT_CONFIG_TADRHOLD(x) (((x) & 0x7) << 26) +#define NETX_EXT_CONFIG_TCSON(x) (((x) & 0x7) << 23) +#define NETX_EXT_CONFIG_TRDON(x) (((x) & 0x7) << 20) +#define NETX_EXT_CONFIG_TWRON(x) (((x) & 0x7) << 17) +#define NETX_EXT_CONFIG_TWROFF(x) (((x) & 0x1f) << 12) +#define NETX_EXT_CONFIG_TRDWRCYC(x) (((x) & 0x1f) << 7) +#define NETX_EXT_CONFIG_WAIT_POL (1<<6) +#define NETX_EXT_CONFIG_WAIT_EN (1<<5) +#define NETX_EXT_CONFIG_NRD_MODE (1<<4) +#define NETX_EXT_CONFIG_DS_MODE (1<<3) +#define NETX_EXT_CONFIG_NWR_MODE (1<<2) +#define NETX_EXT_CONFIG_16BIT (1<<1) +#define NETX_EXT_CONFIG_CS_ENABLE (1<<0) + +#define NETX_DPMAS_IO_MODE0_WRL (1<<13) +#define NETX_DPMAS_IO_MODE0_WAIT (1<<14) +#define NETX_DPMAS_IO_MODE0_READY (1<<15) +#define NETX_DPMAS_IO_MODE0_CS0 (1<<19) +#define NETX_DPMAS_IO_MODE0_EXTRD (1<<20) + +#define NETX_DPMAS_IO_MODE1_CS2 (1<<15) +#define NETX_DPMAS_IO_MODE1_CS1 (1<<16) +#define NETX_DPMAS_IO_MODE1_SAMPLE_NPOR (0<<30) +#define NETX_DPMAS_IO_MODE1_SAMPLE_100MHZ (1<<30) +#define NETX_DPMAS_IO_MODE1_SAMPLE_NPIO36 (2<<30) +#define NETX_DPMAS_IO_MODE1_SAMPLE_PIO36 (3<<30) + +/******************************* + * I2C * + *******************************/ +#define NETX_I2C_REG(ofs) __io(NETX_VA_I2C, (ofs)) +#define NETX_I2C_CTRL NETX_I2C_REG(0x0) +#define NETX_I2C_DATA NETX_I2C_REG(0x4) + +#endif /* __ASM_ARCH_NETX_REGS_H */ diff --git a/include/asm-arm/arch-netx/param.h b/include/asm-arm/arch-netx/param.h new file mode 100644 index 0000000..7a80c26 --- /dev/null +++ b/include/asm-arm/arch-netx/param.h @@ -0,0 +1,18 @@ +/* + * linux/include/asm-arm/arch-netx/param.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ diff --git a/include/asm-arm/arch-netx/pfifo.h b/include/asm-arm/arch-netx/pfifo.h new file mode 100644 index 0000000..4af2ee4 --- /dev/null +++ b/include/asm-arm/arch-netx/pfifo.h @@ -0,0 +1,54 @@ +/* + * include/asm-arm/arch-netx/pfifo.h + * + * Copyright (c) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef ASM_ARCH_PFIFO_H +#define ASM_ARCH_PFIFO_H + +static inline int pfifo_push(int no, unsigned int pointer) +{ + writel(pointer, NETX_PFIFO_BASE(no)); + return 0; +} + +static inline unsigned int pfifo_pop(int no) +{ + return readl(NETX_PFIFO_BASE(no)); +} + +static inline int pfifo_fill_level(int no) +{ + + return readl(NETX_PFIFO_FILL_LEVEL(no)); +} + +static inline int pfifo_full(int no) +{ + return readl(NETX_PFIFO_FULL) & (1<, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +#include +#include +#include "netx-regs.h" + +static inline void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode) +{ + writel(NETX_SYSTEM_RES_CR_FIRMW_RES_EN | NETX_SYSTEM_RES_CR_FIRMW_RES, + NETX_SYSTEM_RES_CR); +} + +#endif + diff --git a/include/asm-arm/arch-netx/timex.h b/include/asm-arm/arch-netx/timex.h new file mode 100644 index 0000000..7fdb42d --- /dev/null +++ b/include/asm-arm/arch-netx/timex.h @@ -0,0 +1,20 @@ +/* + * include/asm-arm/arch-netx/timex.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define CLOCK_TICK_RATE 100000000 diff --git a/include/asm-arm/arch-netx/uncompress.h b/include/asm-arm/arch-netx/uncompress.h new file mode 100644 index 0000000..f894345 --- /dev/null +++ b/include/asm-arm/arch-netx/uncompress.h @@ -0,0 +1,76 @@ +/* + * include/asm-arm/arch-netx/uncompress.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The following code assumes the serial port has already been + * initialized by the bootloader. We search for the first enabled + * port in the most probable order. If you didn't setup a port in + * your bootloader then nothing will appear (which might be desired). + * + * This does not append a newline + */ + +#define REG(x) (*(volatile unsigned long *)(x)) + +#define UART1_BASE 0x100a00 +#define UART2_BASE 0x100a80 + +#define UART_DR 0x0 + +#define UART_CR 0x14 +#define CR_UART_EN (1<<0) + +#define UART_FR 0x18 +#define FR_BUSY (1<<3) +#define FR_TXFF (1<<5) + +static void putc(char c) +{ + unsigned long base; + + if (REG(UART1_BASE + UART_CR) & CR_UART_EN) + base = UART1_BASE; + else if (REG(UART2_BASE + UART_CR) & CR_UART_EN) + base = UART2_BASE; + else + return; + + while (REG(base + UART_FR) & FR_TXFF); + REG(base + UART_DR) = c; +} + +static inline void flush(void) +{ + unsigned long base; + + if (REG(UART1_BASE + UART_CR) & CR_UART_EN) + base = UART1_BASE; + else if (REG(UART2_BASE + UART_CR) & CR_UART_EN) + base = UART2_BASE; + else + return; + + while (REG(base + UART_FR) & FR_BUSY); +} + +/* + * nothing to do + */ +#define arch_decomp_setup() +#define arch_decomp_wdog() diff --git a/include/asm-arm/arch-netx/vmalloc.h b/include/asm-arm/arch-netx/vmalloc.h new file mode 100644 index 0000000..da2da5a --- /dev/null +++ b/include/asm-arm/arch-netx/vmalloc.h @@ -0,0 +1,19 @@ +/* + * linux/include/asm-arm/arch-netx/vmalloc.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#define VMALLOC_END (PAGE_OFFSET + 0x10000000) diff --git a/include/asm-arm/arch-netx/xc.h b/include/asm-arm/arch-netx/xc.h new file mode 100644 index 0000000..659af19 --- /dev/null +++ b/include/asm-arm/arch-netx/xc.h @@ -0,0 +1,42 @@ +/* + * linux/include/asm-arm/arch-netx/xc.h + * + * Copyright (C) 2005 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_ARCH_XC_H +#define __ASM_ARCH_XC_H + +struct xc { + int no; + unsigned int type; + unsigned int version; + void __iomem *xpec_base; + void __iomem *xmac_base; + void __iomem *sram_base; + int irq; + struct device *dev; +}; + +int xc_reset(struct xc *x); +int xc_stop(struct xc* x); +int xc_start(struct xc *x); +int xc_running(struct xc *x); +int xc_request_firmware(struct xc* x); +struct xc* request_xc(int xcno, struct device *dev); +void free_xc(struct xc *x); + +#endif /* __ASM_ARCH_XC_H */ diff --git a/include/asm-arm/arch-omap/board-fsample.h b/include/asm-arm/arch-omap/board-fsample.h new file mode 100644 index 0000000..89a1e52 --- /dev/null +++ b/include/asm-arm/arch-omap/board-fsample.h @@ -0,0 +1,51 @@ +/* + * linux/include/asm-arm/arch-omap/board-fsample.h + * + * Board-specific goodies for TI F-Sample. + * + * Copyright (C) 2006 Google, Inc. + * Author: Brian Swetland + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_OMAP_FSAMPLE_H +#define __ASM_ARCH_OMAP_FSAMPLE_H + +/* fsample is pretty close to p2-sample */ +#include + +#define fsample_cpld_read(reg) __raw_readb(reg) +#define fsample_cpld_write(val, reg) __raw_writeb(val, reg) + +#define FSAMPLE_CPLD_BASE 0xE8100000 +#define FSAMPLE_CPLD_SIZE SZ_4K +#define FSAMPLE_CPLD_START 0x05080000 + +#define FSAMPLE_CPLD_REG_A (FSAMPLE_CPLD_BASE + 0x00) +#define FSAMPLE_CPLD_SWITCH (FSAMPLE_CPLD_BASE + 0x02) +#define FSAMPLE_CPLD_UART (FSAMPLE_CPLD_BASE + 0x02) +#define FSAMPLE_CPLD_REG_B (FSAMPLE_CPLD_BASE + 0x04) +#define FSAMPLE_CPLD_VERSION (FSAMPLE_CPLD_BASE + 0x06) +#define FSAMPLE_CPLD_SET_CLR (FSAMPLE_CPLD_BASE + 0x06) + +#define FSAMPLE_CPLD_BIT_BT_RESET 0 +#define FSAMPLE_CPLD_BIT_LCD_RESET 1 +#define FSAMPLE_CPLD_BIT_CAM_PWDN 2 +#define FSAMPLE_CPLD_BIT_CHARGER_ENABLE 3 +#define FSAMPLE_CPLD_BIT_SD_MMC_EN 4 +#define FSAMPLE_CPLD_BIT_aGPS_PWREN 5 +#define FSAMPLE_CPLD_BIT_BACKLIGHT 6 +#define FSAMPLE_CPLD_BIT_aGPS_EN_RESET 7 +#define FSAMPLE_CPLD_BIT_aGPS_SLEEPx_N 8 +#define FSAMPLE_CPLD_BIT_OTG_RESET 9 + +#define fsample_cpld_set(bit) \ + fsample_cpld_write((((bit) & 15) << 4) | 0x0f, FSAMPLE_CPLD_SET_CLR) + +#define fsample_cpld_clear(bit) \ + fsample_cpld_write(0xf0 | ((bit) & 15), FSAMPLE_CPLD_SET_CLR) + +#endif diff --git a/include/asm-arm/arch-omap/board.h b/include/asm-arm/arch-omap/board.h index 6d6240a..edf1dc6 100644 --- a/include/asm-arm/arch-omap/board.h +++ b/include/asm-arm/arch-omap/board.h @@ -10,7 +10,6 @@ #ifndef _OMAP_BOARD_H #define _OMAP_BOARD_H -#include #include /* Different peripheral ids */ @@ -23,6 +22,7 @@ #define OMAP_TAG_GPIO_SWITCH 0x4f06 #define OMAP_TAG_UART 0x4f07 #define OMAP_TAG_FBMEM 0x4f08 #define OMAP_TAG_STI_CONSOLE 0x4f09 +#define OMAP_TAG_CAMERA_SENSOR 0x4f0a #define OMAP_TAG_BOOT_REASON 0x4f80 #define OMAP_TAG_FLASH_PART 0x4f81 @@ -62,6 +62,12 @@ struct omap_sti_console_config { u8 channel; }; +struct omap_camera_sensor_config { + u16 reset_gpio; + int (*power_on)(void * data); + int (*power_off)(void * data); +}; + struct omap_usb_config { /* Configure drivers according to the connectors on your board: * - "A" connector (rectagular) diff --git a/include/asm-arm/arch-omap/dma.h b/include/asm-arm/arch-omap/dma.h index ca12023..1b1b023 100644 --- a/include/asm-arm/arch-omap/dma.h +++ b/include/asm-arm/arch-omap/dma.h @@ -185,8 +185,8 @@ #define OMAP_DMA_CRYPTO_DES_OUT 56 /* DMA channels for 24xx */ #define OMAP24XX_DMA_NO_DEVICE 0 #define OMAP24XX_DMA_XTI_DMA 1 /* S_DMA_0 */ -#define OMAP24XX_DMA_EXT_NDMA_REQ0 2 /* S_DMA_1 */ -#define OMAP24XX_DMA_EXT_NDMA_REQ1 3 /* S_DMA_2 */ +#define OMAP24XX_DMA_EXT_DMAREQ0 2 /* S_DMA_1 */ +#define OMAP24XX_DMA_EXT_DMAREQ1 3 /* S_DMA_2 */ #define OMAP24XX_DMA_GPMC 4 /* S_DMA_3 */ #define OMAP24XX_DMA_GFX 5 /* S_DMA_4 */ #define OMAP24XX_DMA_DSS 6 /* S_DMA_5 */ @@ -197,7 +197,9 @@ #define OMAP24XX_DMA_AES_RX 10 /* S_DMA #define OMAP24XX_DMA_DES_TX 11 /* S_DMA_10 */ #define OMAP24XX_DMA_DES_RX 12 /* S_DMA_11 */ #define OMAP24XX_DMA_SHA1MD5_RX 13 /* S_DMA_12 */ - +#define OMAP24XX_DMA_EXT_DMAREQ2 14 /* S_DMA_13 */ +#define OMAP24XX_DMA_EXT_DMAREQ3 15 /* S_DMA_14 */ +#define OMAP24XX_DMA_EXT_DMAREQ4 16 /* S_DMA_15 */ #define OMAP24XX_DMA_EAC_AC_RD 17 /* S_DMA_16 */ #define OMAP24XX_DMA_EAC_AC_WR 18 /* S_DMA_17 */ #define OMAP24XX_DMA_EAC_MD_UL_RD 19 /* S_DMA_18 */ @@ -244,6 +246,7 @@ #define OMAP24XX_DMA_USB_W2FC_RX2 60 /* #define OMAP24XX_DMA_MMC1_TX 61 /* SDMA_60 */ #define OMAP24XX_DMA_MMC1_RX 62 /* SDMA_61 */ #define OMAP24XX_DMA_MS 63 /* SDMA_62 */ +#define OMAP24XX_DMA_EXT_DMAREQ5 64 /* S_DMA_63 */ /*----------------------------------------------------------------------------*/ @@ -274,7 +277,7 @@ #define OMAP1610_DMA_LCD_SRC_FN_B1 (OMAP #define OMAP1610_DMA_LCD_LCH_CTRL (OMAP1610_DMA_LCD_BASE + 0xea) #define OMAP1610_DMA_LCD_SRC_FI_B1_U (OMAP1610_DMA_LCD_BASE + 0xf4) -#define OMAP_DMA_TOUT_IRQ (1 << 0) /* Only on omap1 */ +#define OMAP1_DMA_TOUT_IRQ (1 << 0) #define OMAP_DMA_DROP_IRQ (1 << 1) #define OMAP_DMA_HALF_IRQ (1 << 2) #define OMAP_DMA_FRAME_IRQ (1 << 3) @@ -315,11 +318,11 @@ enum { OMAP_LCD_DMA_B2_BOTTOM }; -/* REVISIT: Check if BURST_4 is really 1 (or 2) */ enum omap_dma_burst_mode { OMAP_DMA_DATA_BURST_DIS = 0, OMAP_DMA_DATA_BURST_4, - OMAP_DMA_DATA_BURST_8 + OMAP_DMA_DATA_BURST_8, + OMAP_DMA_DATA_BURST_16, }; enum omap_dma_color_mode { diff --git a/include/asm-arm/arch-omap/dmtimer.h b/include/asm-arm/arch-omap/dmtimer.h index e6522e6..7a289ff 100644 --- a/include/asm-arm/arch-omap/dmtimer.h +++ b/include/asm-arm/arch-omap/dmtimer.h @@ -5,6 +5,7 @@ * * Copyright (C) 2005 Nokia Corporation * Author: Lauri Leukkunen + * PWM and clock framwork support by Timo Teras. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -25,69 +26,56 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef __ASM_ARCH_TIMER_H -#define __ASM_ARCH_TIMER_H - -#include - -#define OMAP_TIMER_SRC_ARMXOR 0x00 -#define OMAP_TIMER_SRC_32_KHZ 0x01 -#define OMAP_TIMER_SRC_EXT_CLK 0x02 - -/* timer control reg bits */ -#define OMAP_TIMER_CTRL_CAPTMODE (1 << 13) -#define OMAP_TIMER_CTRL_PT (1 << 12) -#define OMAP_TIMER_CTRL_TRG_OVERFLOW (0x1 << 10) -#define OMAP_TIMER_CTRL_TRG_OFANDMATCH (0x2 << 10) -#define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8) -#define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8) -#define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8) -#define OMAP_TIMER_CTRL_SCPWM (1 << 7) -#define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */ -#define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */ -#define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* how much to shift the prescaler value */ -#define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */ -#define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */ +#ifndef __ASM_ARCH_DMTIMER_H +#define __ASM_ARCH_DMTIMER_H -/* timer interrupt enable bits */ -#define OMAP_TIMER_INT_CAPTURE (1 << 2) -#define OMAP_TIMER_INT_OVERFLOW (1 << 1) -#define OMAP_TIMER_INT_MATCH (1 << 0) +/* clock sources */ +#define OMAP_TIMER_SRC_SYS_CLK 0x00 +#define OMAP_TIMER_SRC_32_KHZ 0x01 +#define OMAP_TIMER_SRC_EXT_CLK 0x02 +/* timer interrupt enable bits */ +#define OMAP_TIMER_INT_CAPTURE (1 << 2) +#define OMAP_TIMER_INT_OVERFLOW (1 << 1) +#define OMAP_TIMER_INT_MATCH (1 << 0) -struct omap_dm_timer { - struct list_head timer_list; +/* trigger types */ +#define OMAP_TIMER_TRIGGER_NONE 0x00 +#define OMAP_TIMER_TRIGGER_OVERFLOW 0x01 +#define OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02 - u32 base; - unsigned int irq; -}; +struct omap_dm_timer; +struct clk; -u32 omap_dm_timer_read_reg(struct omap_dm_timer *timer, int reg); -void omap_dm_timer_write_reg(struct omap_dm_timer *timer, int reg, u32 value); +int omap_dm_timer_init(void); -struct omap_dm_timer * omap_dm_timer_request(void); +struct omap_dm_timer *omap_dm_timer_request(void); +struct omap_dm_timer *omap_dm_timer_request_specific(int timer_id); void omap_dm_timer_free(struct omap_dm_timer *timer); -void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source); -void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value); -void omap_dm_timer_set_trigger(struct omap_dm_timer *timer, unsigned int value); -void omap_dm_timer_enable_compare(struct omap_dm_timer *timer); -void omap_dm_timer_enable_autoreload(struct omap_dm_timer *timer); +int omap_dm_timer_get_irq(struct omap_dm_timer *timer); + +u32 omap_dm_timer_modify_idlect_mask(u32 inputmask); +struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer); void omap_dm_timer_trigger(struct omap_dm_timer *timer); void omap_dm_timer_start(struct omap_dm_timer *timer); void omap_dm_timer_stop(struct omap_dm_timer *timer); -void omap_dm_timer_set_load(struct omap_dm_timer *timer, unsigned int load); -void omap_dm_timer_set_match(struct omap_dm_timer *timer, unsigned int match); +void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source); +void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, unsigned int value); +void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, unsigned int match); +void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, int toggle, int trigger); +void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler); + +void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, unsigned int value); unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer); void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value); - unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer); -void omap_dm_timer_reset_counter(struct omap_dm_timer *timer); +void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value); int omap_dm_timers_active(void); -u32 omap_dm_timer_modify_idlect_mask(u32 inputmask); -#endif /* __ASM_ARCH_TIMER_H */ + +#endif /* __ASM_ARCH_DMTIMER_H */ diff --git a/include/asm-arm/arch-omap/gpmc.h b/include/asm-arm/arch-omap/gpmc.h new file mode 100644 index 0000000..1a0a520 --- /dev/null +++ b/include/asm-arm/arch-omap/gpmc.h @@ -0,0 +1,91 @@ +/* + * General-Purpose Memory Controller for OMAP2 + * + * Copyright (C) 2005-2006 Nokia Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __OMAP2_GPMC_H +#define __OMAP2_GPMC_H + +#define GPMC_CS_CONFIG1 0x00 +#define GPMC_CS_CONFIG2 0x04 +#define GPMC_CS_CONFIG3 0x08 +#define GPMC_CS_CONFIG4 0x0c +#define GPMC_CS_CONFIG5 0x10 +#define GPMC_CS_CONFIG6 0x14 +#define GPMC_CS_CONFIG7 0x18 +#define GPMC_CS_NAND_COMMAND 0x1c +#define GPMC_CS_NAND_ADDRESS 0x20 +#define GPMC_CS_NAND_DATA 0x24 + +#define GPMC_CONFIG1_WRAPBURST_SUPP (1 << 31) +#define GPMC_CONFIG1_READMULTIPLE_SUPP (1 << 20) +#define GPMC_CONFIG1_READTYPE_ASYNC (0 << 29) +#define GPMC_CONFIG1_READTYPE_SYNC (1 << 29) +#define GPMC_CONFIG1_WRITETYPE_ASYNC (0 << 27) +#define GPMC_CONFIG1_WRITETYPE_SYNC (1 << 27) +#define GPMC_CONFIG1_CLKACTIVATIONTIME(val) ((val & 3) << 25) +#define GPMC_CONFIG1_PAGE_LEN(val) ((val & 3) << 23) +#define GPMC_CONFIG1_WAIT_READ_MON (1 << 22) +#define GPMC_CONFIG1_WAIT_WRITE_MON (1 << 21) +#define GPMC_CONFIG1_WAIT_MON_IIME(val) ((val & 3) << 18) +#define GPMC_CONFIG1_WAIT_PIN_SEL(val) ((val & 3) << 16) +#define GPMC_CONFIG1_DEVICESIZE(val) ((val & 3) << 12) +#define GPMC_CONFIG1_DEVICESIZE_16 GPMC_CONFIG1_DEVICESIZE(1) +#define GPMC_CONFIG1_DEVICETYPE(val) ((val & 3) << 10) +#define GPMC_CONFIG1_DEVICETYPE_NOR GPMC_CONFIG1_DEVICETYPE(0) +#define GPMC_CONFIG1_DEVICETYPE_NAND GPMC_CONFIG1_DEVICETYPE(1) +#define GPMC_CONFIG1_MUXADDDATA (1 << 9) +#define GPMC_CONFIG1_TIME_PARA_GRAN (1 << 4) +#define GPMC_CONFIG1_FCLK_DIV(val) (val & 3) +#define GPMC_CONFIG1_FCLK_DIV2 (GPMC_CONFIG1_FCLK_DIV(1)) +#define GPMC_CONFIG1_FCLK_DIV3 (GPMC_CONFIG1_FCLK_DIV(2)) +#define GPMC_CONFIG1_FCLK_DIV4 (GPMC_CONFIG1_FCLK_DIV(3)) + +/* + * Note that all values in this struct are in nanoseconds, while + * the register values are in gpmc_fck cycles. + */ +struct gpmc_timings { + /* Minimum clock period for synchronous mode */ + u16 sync_clk; + + /* Chip-select signal timings corresponding to GPMC_CS_CONFIG2 */ + u16 cs_on; /* Assertion time */ + u16 cs_rd_off; /* Read deassertion time */ + u16 cs_wr_off; /* Write deassertion time */ + + /* ADV signal timings corresponding to GPMC_CONFIG3 */ + u16 adv_on; /* Assertion time */ + u16 adv_rd_off; /* Read deassertion time */ + u16 adv_wr_off; /* Write deassertion time */ + + /* WE signals timings corresponding to GPMC_CONFIG4 */ + u16 we_on; /* WE assertion time */ + u16 we_off; /* WE deassertion time */ + + /* OE signals timings corresponding to GPMC_CONFIG4 */ + u16 oe_on; /* OE assertion time */ + u16 oe_off; /* OE deassertion time */ + + /* Access time and cycle time timings corresponding to GPMC_CONFIG5 */ + u16 page_burst_access; /* Multiple access word delay */ + u16 access; /* Start-cycle to first data valid delay */ + u16 rd_cycle; /* Total read cycle time */ + u16 wr_cycle; /* Total write cycle time */ +}; + +extern unsigned int gpmc_ns_to_ticks(unsigned int time_ns); + +extern void gpmc_cs_write_reg(int cs, int idx, u32 val); +extern u32 gpmc_cs_read_reg(int cs, int idx); +extern int gpmc_cs_calc_divider(int cs, unsigned int sync_clk); +extern int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t); +extern unsigned long gpmc_cs_get_base_addr(int cs); + + +#endif diff --git a/include/asm-arm/arch-omap/hardware.h b/include/asm-arm/arch-omap/hardware.h index 7909b72..481048d 100644 --- a/include/asm-arm/arch-omap/hardware.h +++ b/include/asm-arm/arch-omap/hardware.h @@ -37,7 +37,6 @@ #ifndef __ASM_ARCH_OMAP_HARDWARE_H #define __ASM_ARCH_OMAP_HARDWARE_H #include -#include #ifndef __ASSEMBLER__ #include #include @@ -298,6 +297,10 @@ #ifdef CONFIG_MACH_OMAP_PERSEUS2 #include "board-perseus2.h" #endif +#ifdef CONFIG_MACH_OMAP_FSAMPLE +#include "board-fsample.h" +#endif + #ifdef CONFIG_MACH_OMAP_H3 #include "board-h3.h" #endif diff --git a/include/asm-arm/arch-omap/io.h b/include/asm-arm/arch-omap/io.h index b726acf..78f68e6 100644 --- a/include/asm-arm/arch-omap/io.h +++ b/include/asm-arm/arch-omap/io.h @@ -44,7 +44,6 @@ #define IO_SPACE_LIMIT 0xffffffff */ #define __io(a) ((void __iomem *)(PCIO_BASE + (a))) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) /* * ---------------------------------------------------------------------------- diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h index 42098d9..2542495 100644 --- a/include/asm-arm/arch-omap/irqs.h +++ b/include/asm-arm/arch-omap/irqs.h @@ -242,10 +242,24 @@ #define INT_24XX_GPIO_BANK1 29 #define INT_24XX_GPIO_BANK2 30 #define INT_24XX_GPIO_BANK3 31 #define INT_24XX_GPIO_BANK4 32 +#define INT_24XX_GPTIMER1 37 +#define INT_24XX_GPTIMER2 38 +#define INT_24XX_GPTIMER3 39 +#define INT_24XX_GPTIMER4 40 +#define INT_24XX_GPTIMER5 41 +#define INT_24XX_GPTIMER6 42 +#define INT_24XX_GPTIMER7 43 +#define INT_24XX_GPTIMER8 44 +#define INT_24XX_GPTIMER9 45 +#define INT_24XX_GPTIMER10 46 +#define INT_24XX_GPTIMER11 47 +#define INT_24XX_GPTIMER12 48 #define INT_24XX_MCBSP1_IRQ_TX 59 #define INT_24XX_MCBSP1_IRQ_RX 60 #define INT_24XX_MCBSP2_IRQ_TX 62 #define INT_24XX_MCBSP2_IRQ_RX 63 +#define INT_24XX_UART1_IRQ 72 +#define INT_24XX_UART2_IRQ 73 #define INT_24XX_UART3_IRQ 74 /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730) and diff --git a/include/asm-arm/arch-omap/mux.h b/include/asm-arm/arch-omap/mux.h index 0dc24d4..679869c 100644 --- a/include/asm-arm/arch-omap/mux.h +++ b/include/asm-arm/arch-omap/mux.h @@ -410,6 +410,12 @@ enum omap24xx_index { /* 24xx clock */ W14_24XX_SYS_CLKOUT, + /* 24xx GPMC wait pin monitoring */ + L3_GPMC_WAIT0, + N7_GPMC_WAIT1, + M1_GPMC_WAIT2, + P1_GPMC_WAIT3, + /* 242X McBSP */ Y15_24XX_MCBSP2_CLKX, R14_24XX_MCBSP2_FSX, @@ -429,6 +435,26 @@ enum omap24xx_index { M15_24XX_GPIO92, V14_24XX_GPIO117, + /* 242x DBG GPIO */ + V4_242X_GPIO49, + W2_242X_GPIO50, + U4_242X_GPIO51, + V3_242X_GPIO52, + V2_242X_GPIO53, + V6_242X_GPIO53, + T4_242X_GPIO54, + Y4_242X_GPIO54, + T3_242X_GPIO55, + U2_242X_GPIO56, + + /* 24xx external DMA requests */ + AA10_242X_DMAREQ0, + AA6_242X_DMAREQ1, + E4_242X_DMAREQ2, + G4_242X_DMAREQ3, + D3_242X_DMAREQ4, + E3_242X_DMAREQ5, + P20_24XX_TSC_IRQ, /* UART3 */ diff --git a/include/asm-arm/arch-omap/pm.h b/include/asm-arm/arch-omap/pm.h index 05b003f..e46623c 100644 --- a/include/asm-arm/arch-omap/pm.h +++ b/include/asm-arm/arch-omap/pm.h @@ -299,10 +299,43 @@ enum omap24xx_save_state { OMAP24XX_SLEEP_SAVE_INTC_MIR0, OMAP24XX_SLEEP_SAVE_INTC_MIR1, OMAP24XX_SLEEP_SAVE_INTC_MIR2, + + OMAP24XX_SLEEP_SAVE_CM_CLKSTCTRL_MPU, + OMAP24XX_SLEEP_SAVE_CM_CLKSTCTRL_CORE, + OMAP24XX_SLEEP_SAVE_CM_CLKSTCTRL_GFX, + OMAP24XX_SLEEP_SAVE_CM_CLKSTCTRL_DSP, + OMAP24XX_SLEEP_SAVE_CM_CLKSTCTRL_MDM, + + OMAP24XX_SLEEP_SAVE_PM_PWSTCTRL_MPU, + OMAP24XX_SLEEP_SAVE_PM_PWSTCTRL_CORE, + OMAP24XX_SLEEP_SAVE_PM_PWSTCTRL_GFX, + OMAP24XX_SLEEP_SAVE_PM_PWSTCTRL_DSP, + OMAP24XX_SLEEP_SAVE_PM_PWSTCTRL_MDM, + + OMAP24XX_SLEEP_SAVE_CM_IDLEST1_CORE, + OMAP24XX_SLEEP_SAVE_CM_IDLEST2_CORE, + OMAP24XX_SLEEP_SAVE_CM_IDLEST3_CORE, + OMAP24XX_SLEEP_SAVE_CM_IDLEST4_CORE, + OMAP24XX_SLEEP_SAVE_CM_IDLEST_GFX, + OMAP24XX_SLEEP_SAVE_CM_IDLEST_WKUP, + OMAP24XX_SLEEP_SAVE_CM_IDLEST_CKGEN, + OMAP24XX_SLEEP_SAVE_CM_IDLEST_DSP, + OMAP24XX_SLEEP_SAVE_CM_IDLEST_MDM, + + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE1_CORE, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE2_CORE, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE3_CORE, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE4_CORE, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE_WKUP, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE_PLL, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE_DSP, + OMAP24XX_SLEEP_SAVE_CM_AUTOIDLE_MDM, + OMAP24XX_SLEEP_SAVE_CM_FCLKEN1_CORE, OMAP24XX_SLEEP_SAVE_CM_FCLKEN2_CORE, OMAP24XX_SLEEP_SAVE_CM_ICLKEN1_CORE, OMAP24XX_SLEEP_SAVE_CM_ICLKEN2_CORE, + OMAP24XX_SLEEP_SAVE_CM_ICLKEN3_CORE, OMAP24XX_SLEEP_SAVE_CM_ICLKEN4_CORE, OMAP24XX_SLEEP_SAVE_GPIO1_IRQENABLE1, OMAP24XX_SLEEP_SAVE_GPIO2_IRQENABLE1, diff --git a/include/asm-arm/arch-omap/system.h b/include/asm-arm/arch-omap/system.h index 67970d1..ac2bfa4 100644 --- a/include/asm-arm/arch-omap/system.h +++ b/include/asm-arm/arch-omap/system.h @@ -4,7 +4,6 @@ */ #ifndef __ASM_ARCH_SYSTEM_H #define __ASM_ARCH_SYSTEM_H -#include #include #include diff --git a/include/asm-arm/arch-omap/uncompress.h b/include/asm-arm/arch-omap/uncompress.h index ca2c8be..aca0adf 100644 --- a/include/asm-arm/arch-omap/uncompress.h +++ b/include/asm-arm/arch-omap/uncompress.h @@ -17,7 +17,6 @@ * kind, whether express or implied. */ -#include #include #include #include diff --git a/include/asm-arm/arch-pnx4008/clock.h b/include/asm-arm/arch-pnx4008/clock.h new file mode 100644 index 0000000..91ae003 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/clock.h @@ -0,0 +1,61 @@ +/* + * include/asm-arm/arch-pnx4008/clock.h + * + * Clock control driver for PNX4008 - header file + * + * Authors: Vitaly Wool, Dmitry Chigirev + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __PNX4008_CLOCK_H__ +#define __PNX4008_CLOCK_H__ + +struct module; +struct clk; + +#define PWRMAN_VA_BASE IO_ADDRESS(PNX4008_PWRMAN_BASE) +#define HCLKDIVCTRL_REG (PWRMAN_VA_BASE + 0x40) +#define PWRCTRL_REG (PWRMAN_VA_BASE + 0x44) +#define PLLCTRL_REG (PWRMAN_VA_BASE + 0x48) +#define OSC13CTRL_REG (PWRMAN_VA_BASE + 0x4c) +#define SYSCLKCTRL_REG (PWRMAN_VA_BASE + 0x50) +#define HCLKPLLCTRL_REG (PWRMAN_VA_BASE + 0x58) +#define USBCTRL_REG (PWRMAN_VA_BASE + 0x64) +#define SDRAMCLKCTRL_REG (PWRMAN_VA_BASE + 0x68) +#define MSCTRL_REG (PWRMAN_VA_BASE + 0x80) +#define BTCLKCTRL (PWRMAN_VA_BASE + 0x84) +#define DUMCLKCTRL_REG (PWRMAN_VA_BASE + 0x90) +#define I2CCLKCTRL_REG (PWRMAN_VA_BASE + 0xac) +#define KEYCLKCTRL_REG (PWRMAN_VA_BASE + 0xb0) +#define TSCLKCTRL_REG (PWRMAN_VA_BASE + 0xb4) +#define PWMCLKCTRL_REG (PWRMAN_VA_BASE + 0xb8) +#define SPICTRL_REG (PWRMAN_VA_BASE + 0xc4) +#define FLASHCLKCTRL_REG (PWRMAN_VA_BASE + 0xc8) +#define UART3CLK_REG (PWRMAN_VA_BASE + 0xd0) +#define UARTCLKCTRL_REG (PWRMAN_VA_BASE + 0xe4) +#define DMACLKCTRL_REG (PWRMAN_VA_BASE + 0xe8) +#define AUTOCLK_CTRL (PWRMAN_VA_BASE + 0xec) +#define JPEGCLKCTRL_REG (PWRMAN_VA_BASE + 0xfc) + +#define AUDIOCONFIG_VA_BASE IO_ADDRESS(PNX4008_AUDIOCONFIG_BASE) +#define DSPPLLCTRL_REG (AUDIOCONFIG_VA_BASE + 0x60) +#define DSPCLKCTRL_REG (AUDIOCONFIG_VA_BASE + 0x64) +#define AUDIOCLKCTRL_REG (AUDIOCONFIG_VA_BASE + 0x68) +#define AUDIOPLLCTRL_REG (AUDIOCONFIG_VA_BASE + 0x6C) + +#define USB_OTG_CLKCTRL_REG IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xff4) + +#define VFP9CLKCTRL_REG IO_ADDRESS(PNX4008_DEBUG_BASE) + +#define CLK_RATE_13MHZ 13000 +#define CLK_RATE_1MHZ 1000 +#define CLK_RATE_208MHZ 208000 +#define CLK_RATE_48MHZ 48000 +#define CLK_RATE_32KHZ 32 + +#define PNX4008_UART_CLK CLK_RATE_13MHZ * 1000 /* in MHz */ + +#endif diff --git a/include/asm-arm/arch-pnx4008/debug-macro.S b/include/asm-arm/arch-pnx4008/debug-macro.S new file mode 100644 index 0000000..67d18a2 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/debug-macro.S @@ -0,0 +1,23 @@ +/* linux/include/asm-arm/arch-pnx4008/debug-macro.S + * + * Debugging macro include header + * + * Copyright (C) 1994-1999 Russell King + * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +*/ + + .macro addruart,rx + mrc p15, 0, \rx, c1, c0 + tst \rx, #1 @ MMU enabled? + mov \rx, #0x00090000 + addeq \rx, \rx, #0x40000000 + addne \rx, \rx, #0xf4000000 + .endm + +#define UART_SHIFT 2 +#include diff --git a/include/asm-arm/arch-pnx4008/dma.h b/include/asm-arm/arch-pnx4008/dma.h new file mode 100644 index 0000000..3aee120 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/dma.h @@ -0,0 +1,162 @@ +/* + * linux/include/asm-arm/arch-pnx4008/dma.h + * + * PNX4008 DMA header file + * + * Author: Vitaly Wool + * Copyright: MontaVista Software Inc. (c) 2005 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_ARCH_DMA_H +#define __ASM_ARCH_DMA_H + +#include "platform.h" + +#define MAX_DMA_ADDRESS 0xffffffff + +#define MAX_DMA_CHANNELS 8 + +#define DMAC_BASE IO_ADDRESS(PNX4008_DMA_CONFIG_BASE) +#define DMAC_INT_STAT (DMAC_BASE + 0x0000) +#define DMAC_INT_TC_STAT (DMAC_BASE + 0x0004) +#define DMAC_INT_TC_CLEAR (DMAC_BASE + 0x0008) +#define DMAC_INT_ERR_STAT (DMAC_BASE + 0x000c) +#define DMAC_INT_ERR_CLEAR (DMAC_BASE + 0x0010) +#define DMAC_SOFT_SREQ (DMAC_BASE + 0x0024) +#define DMAC_CONFIG (DMAC_BASE + 0x0030) +#define DMAC_Cx_SRC_ADDR(c) (DMAC_BASE + 0x0100 + (c) * 0x20) +#define DMAC_Cx_DEST_ADDR(c) (DMAC_BASE + 0x0104 + (c) * 0x20) +#define DMAC_Cx_LLI(c) (DMAC_BASE + 0x0108 + (c) * 0x20) +#define DMAC_Cx_CONTROL(c) (DMAC_BASE + 0x010c + (c) * 0x20) +#define DMAC_Cx_CONFIG(c) (DMAC_BASE + 0x0110 + (c) * 0x20) + +enum { + WIDTH_BYTE = 0, + WIDTH_HWORD, + WIDTH_WORD +}; + +enum { + FC_MEM2MEM_DMA, + FC_MEM2PER_DMA, + FC_PER2MEM_DMA, + FC_PER2PER_DMA, + FC_PER2PER_DPER, + FC_MEM2PER_PER, + FC_PER2MEM_PER, + FC_PER2PER_SPER +}; + +enum { + DMA_INT_UNKNOWN = 0, + DMA_ERR_INT = 1, + DMA_TC_INT = 2, +}; + +enum { + DMA_BUFFER_ALLOCATED = 1, + DMA_HAS_LL = 2, +}; + +enum { + PER_CAM_DMA_1 = 0, + PER_NDF_FLASH = 1, + PER_MBX_SLAVE_FIFO = 2, + PER_SPI2_REC_XMIT = 3, + PER_MS_SD_RX_XMIT = 4, + PER_HS_UART_1_XMIT = 5, + PER_HS_UART_1_RX = 6, + PER_HS_UART_2_XMIT = 7, + PER_HS_UART_2_RX = 8, + PER_HS_UART_7_XMIT = 9, + PER_HS_UART_7_RX = 10, + PER_SPI1_REC_XMIT = 11, + PER_MLC_NDF_SREC = 12, + PER_CAM_DMA_2 = 13, + PER_PRNG_INFIFO = 14, + PER_PRNG_OUTFIFO = 15, +}; + +struct pnx4008_dma_ch_ctrl { + int tc_mask; + int cacheable; + int bufferable; + int priv_mode; + int di; + int si; + int dest_ahb1; + int src_ahb1; + int dwidth; + int swidth; + int dbsize; + int sbsize; + int tr_size; +}; + +struct pnx4008_dma_ch_config { + int halt; + int active; + int lock; + int itc; + int ie; + int flow_cntrl; + int dest_per; + int src_per; +}; + +struct pnx4008_dma_ll { + unsigned long src_addr; + unsigned long dest_addr; + u32 next_dma; + unsigned long ch_ctrl; + struct pnx4008_dma_ll *next; + int flags; + void *alloc_data; + int (*free) (void *); +}; + +struct pnx4008_dma_config { + int is_ll; + unsigned long src_addr; + unsigned long dest_addr; + unsigned long ch_ctrl; + unsigned long ch_cfg; + struct pnx4008_dma_ll *ll; + u32 ll_dma; + int flags; + void *alloc_data; + int (*free) (void *); +}; + +extern struct pnx4008_dma_ll *pnx4008_alloc_ll_entry(dma_addr_t *); +extern void pnx4008_free_ll_entry(struct pnx4008_dma_ll *, dma_addr_t); +extern void pnx4008_free_ll(u32 ll_dma, struct pnx4008_dma_ll *); + +extern int pnx4008_request_channel(char *, int, + void (*)(int, int, void *, struct pt_regs *), + void *); +extern void pnx4008_free_channel(int); +extern int pnx4008_config_dma(int, int, int); +extern int pnx4008_dma_pack_control(const struct pnx4008_dma_ch_ctrl *, + unsigned long *); +extern int pnx4008_dma_parse_control(unsigned long, + struct pnx4008_dma_ch_ctrl *); +extern int pnx4008_dma_pack_config(const struct pnx4008_dma_ch_config *, + unsigned long *); +extern int pnx4008_dma_parse_config(unsigned long, + struct pnx4008_dma_ch_config *); +extern int pnx4008_config_channel(int, struct pnx4008_dma_config *); +extern int pnx4008_channel_get_config(int, struct pnx4008_dma_config *); +extern int pnx4008_dma_ch_enable(int); +extern int pnx4008_dma_ch_disable(int); +extern int pnx4008_dma_ch_enabled(int); +extern void pnx4008_dma_split_head_entry(struct pnx4008_dma_config *, + struct pnx4008_dma_ch_ctrl *); +extern void pnx4008_dma_split_ll_entry(struct pnx4008_dma_ll *, + struct pnx4008_dma_ch_ctrl *); + +#endif /* _ASM_ARCH_DMA_H */ diff --git a/include/asm-arm/arch-pnx4008/entry-macro.S b/include/asm-arm/arch-pnx4008/entry-macro.S new file mode 100644 index 0000000..c1c198e --- /dev/null +++ b/include/asm-arm/arch-pnx4008/entry-macro.S @@ -0,0 +1,121 @@ +/* + * include/asm-arm/arch-pnx4008/entry-macro.S + * + * Low-level IRQ helper macros for PNX4008-based platforms + * + * 2005-2006 (c) MontaVista Software, Inc. + * Author: Vitaly Wool + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include "platform.h" + +#define IO_BASE 0xF0000000 +#define IO_ADDRESS(x) (((((x) & 0xff000000) >> 4) | ((x) & 0xfffff)) | IO_BASE) + +#define INTRC_MASK 0x00 +#define INTRC_RAW_STAT 0x04 +#define INTRC_STAT 0x08 +#define INTRC_POLAR 0x0C +#define INTRC_ACT_TYPE 0x10 +#define INTRC_TYPE 0x14 + +#define SIC1_BASE_INT 32 +#define SIC2_BASE_INT 64 + + .macro disable_fiq + .endm + + .macro get_irqnr_and_base, irqnr, irqstat, base, tmp +/* decode the MIC interrupt numbers */ + ldr \base, =IO_ADDRESS(PNX4008_INTCTRLMIC_BASE) + ldr \irqstat, [\base, #INTRC_STAT] + + cmp \irqstat,#1<<16 + movhs \irqnr,#16 + movlo \irqnr,#0 + movhs \irqstat,\irqstat,lsr#16 + cmp \irqstat,#1<<8 + addhs \irqnr,\irqnr,#8 + movhs \irqstat,\irqstat,lsr#8 + cmp \irqstat,#1<<4 + addhs \irqnr,\irqnr,#4 + movhs \irqstat,\irqstat,lsr#4 + cmp \irqstat,#1<<2 + addhs \irqnr,\irqnr,#2 + movhs \irqstat,\irqstat,lsr#2 + cmp \irqstat,#1<<1 + addhs \irqnr,\irqnr,#1 + +/* was there an interrupt ? if not then drop out with EQ status */ + teq \irqstat,#0 + beq 1003f + +/* and now check for extended IRQ reasons */ + cmp \irqnr,#1 + bls 1003f + cmp \irqnr,#30 + blo 1002f + +/* IRQ 31,30 : High priority cascade IRQ handle */ +/* read the correct SIC */ +/* decoding status after compare : eq is 30 (SIC1) , ne is 31 (SIC2) */ +/* set the base IRQ number */ + ldreq \base, =IO_ADDRESS(PNX4008_INTCTRLSIC1_BASE) + moveq \irqnr,#SIC1_BASE_INT + ldrne \base, =IO_ADDRESS(PNX4008_INTCTRLSIC2_BASE) + movne \irqnr,#SIC2_BASE_INT + ldr \irqstat, [\base, #INTRC_STAT] + ldr \tmp, [\base, #INTRC_TYPE] +/* and with inverted mask : low priority interrupts */ + and \irqstat,\irqstat,\tmp + b 1004f + +1003: +/* IRQ 1,0 : Low priority cascade IRQ handle */ +/* read the correct SIC */ +/* decoding status after compare : eq is 1 (SIC2) , ne is 0 (SIC1)*/ +/* read the correct SIC */ +/* set the base IRQ number */ + ldrne \base, =IO_ADDRESS(PNX4008_INTCTRLSIC1_BASE) + movne \irqnr,#SIC1_BASE_INT + ldreq \base, =IO_ADDRESS(PNX4008_INTCTRLSIC2_BASE) + moveq \irqnr,#SIC2_BASE_INT + ldr \irqstat, [\base, #INTRC_STAT] + ldr \tmp, [\base, #INTRC_TYPE] +/* and with inverted mask : low priority interrupts */ + bic \irqstat,\irqstat,\tmp + +1004: + + cmp \irqstat,#1<<16 + addhs \irqnr,\irqnr,#16 + movhs \irqstat,\irqstat,lsr#16 + cmp \irqstat,#1<<8 + addhs \irqnr,\irqnr,#8 + movhs \irqstat,\irqstat,lsr#8 + cmp \irqstat,#1<<4 + addhs \irqnr,\irqnr,#4 + movhs \irqstat,\irqstat,lsr#4 + cmp \irqstat,#1<<2 + addhs \irqnr,\irqnr,#2 + movhs \irqstat,\irqstat,lsr#2 + cmp \irqstat,#1<<1 + addhs \irqnr,\irqnr,#1 + + +/* is irqstat not zero */ + +1002: +/* we assert that irqstat is not equal to zero and return ne status if true*/ + teq \irqstat,#0 +1003: + .endm + + + .macro irq_prio_table + .endm + + diff --git a/include/asm-arm/arch-pnx4008/gpio.h b/include/asm-arm/arch-pnx4008/gpio.h new file mode 100644 index 0000000..d01bf83 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/gpio.h @@ -0,0 +1,241 @@ +/* + * include/asm-arm/arch-pnx4008/gpio.h + * + * PNX4008 GPIO driver - header file + * + * Author: Dmitry Chigirev + * + * Based on reference code by Iwo Mergler and Z.Tabaaloute from Philips: + * Copyright (c) 2005 Koninklijke Philips Electronics N.V. + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef _PNX4008_GPIO_H_ +#define _PNX4008_GPIO_H_ + + +/* Block numbers */ +#define GPIO_IN (0) +#define GPIO_OUT (0x100) +#define GPIO_BID (0x200) +#define GPIO_RAM (0x300) +#define GPIO_MUX (0x400) + +#define GPIO_TYPE_MASK(K) ((K) & 0x700) + +/* INPUT GPIOs */ +/* GPI */ +#define GPI_00 (GPIO_IN | 0) +#define GPI_01 (GPIO_IN | 1) +#define GPI_02 (GPIO_IN | 2) +#define GPI_03 (GPIO_IN | 3) +#define GPI_04 (GPIO_IN | 4) +#define GPI_05 (GPIO_IN | 5) +#define GPI_06 (GPIO_IN | 6) +#define GPI_07 (GPIO_IN | 7) +#define GPI_08 (GPIO_IN | 8) +#define GPI_09 (GPIO_IN | 9) +#define U1_RX (GPIO_IN | 15) +#define U2_HTCS (GPIO_IN | 16) +#define U2_RX (GPIO_IN | 17) +#define U3_RX (GPIO_IN | 18) +#define U4_RX (GPIO_IN | 19) +#define U5_RX (GPIO_IN | 20) +#define U6_IRRX (GPIO_IN | 21) +#define U7_HCTS (GPIO_IN | 22) +#define U7_RX (GPIO_IN | 23) +/* MISC IN */ +#define SPI1_DATIN (GPIO_IN | 25) +#define DISP_SYNC (GPIO_IN | 26) +#define SPI2_DATIN (GPIO_IN | 27) +#define GPI_11 (GPIO_IN | 28) + +#define GPIO_IN_MASK 0x1eff83ff + +/* OUTPUT GPIOs */ +/* GPO */ +#define GPO_00 (GPIO_OUT | 0) +#define GPO_01 (GPIO_OUT | 1) +#define GPO_02 (GPIO_OUT | 2) +#define GPO_03 (GPIO_OUT | 3) +#define GPO_04 (GPIO_OUT | 4) +#define GPO_05 (GPIO_OUT | 5) +#define GPO_06 (GPIO_OUT | 6) +#define GPO_07 (GPIO_OUT | 7) +#define GPO_08 (GPIO_OUT | 8) +#define GPO_09 (GPIO_OUT | 9) +#define GPO_10 (GPIO_OUT | 10) +#define GPO_11 (GPIO_OUT | 11) +#define GPO_12 (GPIO_OUT | 12) +#define GPO_13 (GPIO_OUT | 13) +#define GPO_14 (GPIO_OUT | 14) +#define GPO_15 (GPIO_OUT | 15) +#define GPO_16 (GPIO_OUT | 16) +#define GPO_17 (GPIO_OUT | 17) +#define GPO_18 (GPIO_OUT | 18) +#define GPO_19 (GPIO_OUT | 19) +#define GPO_20 (GPIO_OUT | 20) +#define GPO_21 (GPIO_OUT | 21) +#define GPO_22 (GPIO_OUT | 22) +#define GPO_23 (GPIO_OUT | 23) + +#define GPIO_OUT_MASK 0xffffff + +/* BIDIRECTIONAL GPIOs */ +/* RAM pins */ +#define RAM_D19 (GPIO_RAM | 0) +#define RAM_D20 (GPIO_RAM | 1) +#define RAM_D21 (GPIO_RAM | 2) +#define RAM_D22 (GPIO_RAM | 3) +#define RAM_D23 (GPIO_RAM | 4) +#define RAM_D24 (GPIO_RAM | 5) +#define RAM_D25 (GPIO_RAM | 6) +#define RAM_D26 (GPIO_RAM | 7) +#define RAM_D27 (GPIO_RAM | 8) +#define RAM_D28 (GPIO_RAM | 9) +#define RAM_D29 (GPIO_RAM | 10) +#define RAM_D30 (GPIO_RAM | 11) +#define RAM_D31 (GPIO_RAM | 12) + +#define GPIO_RAM_MASK 0x1fff + +/* I/O pins */ +#define GPIO_00 (GPIO_BID | 25) +#define GPIO_01 (GPIO_BID | 26) +#define GPIO_02 (GPIO_BID | 27) +#define GPIO_03 (GPIO_BID | 28) +#define GPIO_04 (GPIO_BID | 29) +#define GPIO_05 (GPIO_BID | 30) + +#define GPIO_BID_MASK 0x7e000000 + +/* Non-GPIO multiplexed PIOs. For multiplexing with GPIO, please use GPIO macros */ +#define GPIO_SDRAM_SEL (GPIO_MUX | 3) + +#define GPIO_MUX_MASK 0x8 + +/* Extraction/assembly macros */ +#define GPIO_BIT_MASK(K) ((K) & 0x1F) +#define GPIO_BIT(K) (1 << GPIO_BIT_MASK(K)) +#define GPIO_ISMUX(K) ((GPIO_TYPE_MASK(K) == GPIO_MUX) && (GPIO_BIT(K) & GPIO_MUX_MASK)) +#define GPIO_ISRAM(K) ((GPIO_TYPE_MASK(K) == GPIO_RAM) && (GPIO_BIT(K) & GPIO_RAM_MASK)) +#define GPIO_ISBID(K) ((GPIO_TYPE_MASK(K) == GPIO_BID) && (GPIO_BIT(K) & GPIO_BID_MASK)) +#define GPIO_ISOUT(K) ((GPIO_TYPE_MASK(K) == GPIO_OUT) && (GPIO_BIT(K) & GPIO_OUT_MASK)) +#define GPIO_ISIN(K) ((GPIO_TYPE_MASK(K) == GPIO_IN) && (GPIO_BIT(K) & GPIO_IN_MASK)) + +/* Start Enable Pin Interrupts - table 58 page 66 */ + +#define SE_PIN_BASE_INT 32 + +#define SE_U7_RX_INT 63 +#define SE_U7_HCTS_INT 62 +#define SE_BT_CLKREQ_INT 61 +#define SE_U6_IRRX_INT 60 +/*59 unused*/ +#define SE_U5_RX_INT 58 +#define SE_GPI_11_INT 57 +#define SE_U3_RX_INT 56 +#define SE_U2_HCTS_INT 55 +#define SE_U2_RX_INT 54 +#define SE_U1_RX_INT 53 +#define SE_DISP_SYNC_INT 52 +/*51 unused*/ +#define SE_SDIO_INT_N 50 +#define SE_MSDIO_START_INT 49 +#define SE_GPI_06_INT 48 +#define SE_GPI_05_INT 47 +#define SE_GPI_04_INT 46 +#define SE_GPI_03_INT 45 +#define SE_GPI_02_INT 44 +#define SE_GPI_01_INT 43 +#define SE_GPI_00_INT 42 +#define SE_SYSCLKEN_PIN_INT 41 +#define SE_SPI1_DATAIN_INT 40 +#define SE_GPI_07_INT 39 +#define SE_SPI2_DATAIN_INT 38 +#define SE_GPI_10_INT 37 +#define SE_GPI_09_INT 36 +#define SE_GPI_08_INT 35 +/*34-32 unused*/ + +/* Start Enable Internal Interrupts - table 57 page 65 */ + +#define SE_INT_BASE_INT 0 + +#define SE_TS_IRQ 31 +#define SE_TS_P_INT 30 +#define SE_TS_AUX_INT 29 +/*27-28 unused*/ +#define SE_USB_AHB_NEED_CLK_INT 26 +#define SE_MSTIMER_INT 25 +#define SE_RTC_INT 24 +#define SE_USB_NEED_CLK_INT 23 +#define SE_USB_INT 22 +#define SE_USB_I2C_INT 21 +#define SE_USB_OTG_TIMER_INT 20 +#define SE_USB_OTG_ATX_INT_N 19 +/*18 unused*/ +#define SE_DSP_GPIO4_INT 17 +#define SE_KEY_IRQ 16 +#define SE_DSP_SLAVEPORT_INT 15 +#define SE_DSP_GPIO1_INT 14 +#define SE_DSP_GPIO0_INT 13 +#define SE_DSP_AHB_INT 12 +/*11-6 unused*/ +#define SE_GPIO_05_INT 5 +#define SE_GPIO_04_INT 4 +#define SE_GPIO_03_INT 3 +#define SE_GPIO_02_INT 2 +#define SE_GPIO_01_INT 1 +#define SE_GPIO_00_INT 0 + +#define START_INT_REG_BIT(irq) (1<<((irq)&0x1F)) + +#define START_INT_ER_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x20 + (((irq)&(0x1<<5))>>1))) +#define START_INT_RSR_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x24 + (((irq)&(0x1<<5))>>1))) +#define START_INT_SR_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x28 + (((irq)&(0x1<<5))>>1))) +#define START_INT_APR_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x2C + (((irq)&(0x1<<5))>>1))) + +extern int pnx4008_gpio_register_pin(unsigned short pin); +extern int pnx4008_gpio_unregister_pin(unsigned short pin); +extern unsigned long pnx4008_gpio_read_pin(unsigned short pin); +extern int pnx4008_gpio_write_pin(unsigned short pin, int output); +extern int pnx4008_gpio_set_pin_direction(unsigned short pin, int output); +extern int pnx4008_gpio_read_pin_direction(unsigned short pin); +extern int pnx4008_gpio_set_pin_mux(unsigned short pin, int output); +extern int pnx4008_gpio_read_pin_mux(unsigned short pin); + +static inline void start_int_umask(u8 irq) +{ + __raw_writel(__raw_readl(START_INT_ER_REG(irq)) | + START_INT_REG_BIT(irq), START_INT_ER_REG(irq)); +} + +static inline void start_int_mask(u8 irq) +{ + __raw_writel(__raw_readl(START_INT_ER_REG(irq)) & + ~START_INT_REG_BIT(irq), START_INT_ER_REG(irq)); +} + +static inline void start_int_ack(u8 irq) +{ + __raw_writel(START_INT_REG_BIT(irq), START_INT_RSR_REG(irq)); +} + +static inline void start_int_set_falling_edge(u8 irq) +{ + __raw_writel(__raw_readl(START_INT_APR_REG(irq)) & + ~START_INT_REG_BIT(irq), START_INT_APR_REG(irq)); +} + +static inline void start_int_set_rising_edge(u8 irq) +{ + __raw_writel(__raw_readl(START_INT_APR_REG(irq)) | + START_INT_REG_BIT(irq), START_INT_APR_REG(irq)); +} + +#endif /* _PNX4008_GPIO_H_ */ diff --git a/include/asm-arm/arch-pnx4008/hardware.h b/include/asm-arm/arch-pnx4008/hardware.h new file mode 100644 index 0000000..a441039 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/hardware.h @@ -0,0 +1,32 @@ +/* + * linux/include/asm-arm/arch-pnx4008/hardware.h + * + * Copyright (c) 2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ASM_ARCH_HARDWARE_H +#define __ASM_ARCH_HARDWARE_H + +#include +#include + +/* Start of virtual addresses for IO devices */ +#define IO_BASE 0xF0000000 + +/* This macro relies on fact that for all HW i/o addresses bits 20-23 are 0 */ +#define IO_ADDRESS(x) (((((x) & 0xff000000) >> 4) | ((x) & 0xfffff)) | IO_BASE) + +#endif diff --git a/include/asm-arm/arch-pnx4008/io.h b/include/asm-arm/arch-pnx4008/io.h new file mode 100644 index 0000000..29ee439 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/io.h @@ -0,0 +1,21 @@ + +/* + * include/asm-arm/arch-pnx4008/io.h + * + * Author: Dmitry Chigirev + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __ASM_ARM_ARCH_IO_H +#define __ASM_ARM_ARCH_IO_H + +#define IO_SPACE_LIMIT 0xffffffff + +#define __io(a) ((void __iomem *)(a)) +#define __mem_pci(a) (a) + +#endif diff --git a/include/asm-arm/arch-pnx4008/irq.h b/include/asm-arm/arch-pnx4008/irq.h new file mode 100644 index 0000000..fabff5d --- /dev/null +++ b/include/asm-arm/arch-pnx4008/irq.h @@ -0,0 +1,42 @@ +/* + * include/asm-arm/arch-pnx4008/irq.h + * + * PNX4008 IRQ controller driver - header file + * this one is used in entry-arnv.S as well so it cannot contain C code + * + * Copyright (c) 2005 Philips Semiconductors + * Copyright (c) 2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#ifndef __PNX4008_IRQ_H__ +#define __PNX4008_IRQ_H__ + +#define MIC_VA_BASE IO_ADDRESS(PNX4008_INTCTRLMIC_BASE) +#define SIC1_VA_BASE IO_ADDRESS(PNX4008_INTCTRLSIC1_BASE) +#define SIC2_VA_BASE IO_ADDRESS(PNX4008_INTCTRLSIC2_BASE) + +/* Manual: Chapter 20, page 195 */ + +#define INTC_BIT(irq) (1<< ((irq) & 0x1F)) + +#define INTC_ER(irq) IO_ADDRESS((PNX4008_INTCTRLMIC_BASE + 0x0 + (((irq)&(0x3<<5))<<9))) +#define INTC_RSR(irq) IO_ADDRESS((PNX4008_INTCTRLMIC_BASE + 0x4 + (((irq)&(0x3<<5))<<9))) +#define INTC_SR(irq) IO_ADDRESS((PNX4008_INTCTRLMIC_BASE + 0x8 + (((irq)&(0x3<<5))<<9))) +#define INTC_APR(irq) IO_ADDRESS((PNX4008_INTCTRLMIC_BASE + 0xC + (((irq)&(0x3<<5))<<9))) +#define INTC_ATR(irq) IO_ADDRESS((PNX4008_INTCTRLMIC_BASE + 0x10 + (((irq)&(0x3<<5))<<9))) +#define INTC_ITR(irq) IO_ADDRESS((PNX4008_INTCTRLMIC_BASE + 0x14 + (((irq)&(0x3<<5))<<9))) + +#define START_INT_REG_BIT(irq) (1<<((irq)&0x1F)) + +#define START_INT_ER_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x20 + (((irq)&(0x1<<5))>>1))) +#define START_INT_RSR_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x24 + (((irq)&(0x1<<5))>>1))) +#define START_INT_SR_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x28 + (((irq)&(0x1<<5))>>1))) +#define START_INT_APR_REG(irq) IO_ADDRESS((PNX4008_PWRMAN_BASE + 0x2C + (((irq)&(0x1<<5))>>1))) + +extern void __init pnx4008_init_irq(void); + +#endif /* __PNX4008_IRQ_H__ */ diff --git a/include/asm-arm/arch-pnx4008/irqs.h b/include/asm-arm/arch-pnx4008/irqs.h new file mode 100644 index 0000000..13ec7ed --- /dev/null +++ b/include/asm-arm/arch-pnx4008/irqs.h @@ -0,0 +1,215 @@ +/* + * include/asm-arm/arch-pnx4008/irqs.h + * + * PNX4008 IRQ controller driver - header file + * + * Author: Dmitry Chigirev + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __PNX4008_IRQS_h__ +#define __PNX4008_IRQS_h__ + +#define NR_IRQS 96 + +/*Manual: table 259, page 199*/ + +/*SUB2 Interrupt Routing (SIC2)*/ + +#define SIC2_BASE_INT 64 + +#define CLK_SWITCH_ARM_INT 95 /*manual: Clkswitch ARM */ +#define CLK_SWITCH_DSP_INT 94 /*manual: ClkSwitch DSP */ +#define CLK_SWITCH_AUD_INT 93 /*manual: Clkswitch AUD */ +#define GPI_06_INT 92 +#define GPI_05_INT 91 +#define GPI_04_INT 90 +#define GPI_03_INT 89 +#define GPI_02_INT 88 +#define GPI_01_INT 87 +#define GPI_00_INT 86 +#define BT_CLKREQ_INT 85 +#define SPI1_DATIN_INT 84 +#define U5_RX_INT 83 +#define SDIO_INT_N 82 +#define CAM_HS_INT 81 +#define CAM_VS_INT 80 +#define GPI_07_INT 79 +#define DISP_SYNC_INT 78 +#define DSP_INT8 77 +#define U7_HCTS_INT 76 +#define GPI_10_INT 75 +#define GPI_09_INT 74 +#define GPI_08_INT 73 +#define DSP_INT7 72 +#define U2_HCTS_INT 71 +#define SPI2_DATIN_INT 70 +#define GPIO_05_INT 69 +#define GPIO_04_INT 68 +#define GPIO_03_INT 67 +#define GPIO_02_INT 66 +#define GPIO_01_INT 65 +#define GPIO_00_INT 64 + +/*Manual: table 258, page 198*/ + +/*SUB1 Interrupt Routing (SIC1)*/ + +#define SIC1_BASE_INT 32 + +#define USB_I2C_INT 63 +#define USB_DEV_HP_INT 62 +#define USB_DEV_LP_INT 61 +#define USB_DEV_DMA_INT 60 +#define USB_HOST_INT 59 +#define USB_OTG_ATX_INT_N 58 +#define USB_OTG_TIMER_INT 57 +#define SW_INT 56 +#define SPI1_INT 55 +#define KEY_IRQ 54 +#define DSP_M_INT 53 +#define RTC_INT 52 +#define I2C_1_INT 51 +#define I2C_2_INT 50 +#define PLL1_LOCK_INT 49 +#define PLL2_LOCK_INT 48 +#define PLL3_LOCK_INT 47 +#define PLL4_LOCK_INT 46 +#define PLL5_LOCK_INT 45 +#define SPI2_INT 44 +#define DSP_INT1 43 +#define DSP_INT2 42 +#define DSP_TDM_INT2 41 +#define TS_AUX_INT 40 +#define TS_IRQ 39 +#define TS_P_INT 38 +#define UOUT1_TO_PAD_INT 37 +#define GPI_11_INT 36 +#define DSP_INT4 35 +#define JTAG_COMM_RX_INT 34 +#define JTAG_COMM_TX_INT 33 +#define DSP_INT3 32 + +/*Manual: table 257, page 197*/ + +/*MAIN Interrupt Routing*/ + +#define MAIN_BASE_INT 0 + +#define SUB2_FIQ_N 31 /*active low */ +#define SUB1_FIQ_N 30 /*active low */ +#define JPEG_INT 29 +#define DMA_INT 28 +#define MSTIMER_INT 27 +#define IIR1_INT 26 +#define IIR2_INT 25 +#define IIR7_INT 24 +#define DSP_TDM_INT0 23 +#define DSP_TDM_INT1 22 +#define DSP_P_INT 21 +#define DSP_INT0 20 +#define DUM_INT 19 +#define UOUT0_TO_PAD_INT 18 +#define MP4_ENC_INT 17 +#define MP4_DEC_INT 16 +#define SD0_INT 15 +#define MBX_INT 14 +#define SD1_INT 13 +#define MS_INT_N 12 +#define FLASH_INT 11 /*NAND*/ +#define IIR6_INT 10 +#define IIR5_INT 9 +#define IIR4_INT 8 +#define IIR3_INT 7 +#define WATCH_INT 6 +#define HSTIMER_INT 5 +#define ARCH_TIMER_IRQ HSTIMER_INT +#define CAM_INT 4 +#define PRNG_INT 3 +#define CRYPTO_INT 2 +#define SUB2_IRQ_N 1 /*active low */ +#define SUB1_IRQ_N 0 /*active low */ + +#define PNX4008_IRQ_TYPES \ +{ /*IRQ #'s: */ \ +IRQT_LOW, IRQT_LOW, IRQT_LOW, IRQT_HIGH, /* 0, 1, 2, 3 */ \ +IRQT_LOW, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 4, 5, 6, 7 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 8, 9,10,11 */ \ +IRQT_LOW, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 12,13,14,15 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 16,17,18,19 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 20,21,22,23 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 24,25,26,27 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_LOW, IRQT_LOW, /* 28,29,30,31 */ \ +IRQT_HIGH, IRQT_LOW, IRQT_HIGH, IRQT_HIGH, /* 32,33,34,35 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_FALLING, IRQT_HIGH, /* 36,37,38,39 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 40,41,42,43 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 44,45,46,47 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_LOW, IRQT_LOW, /* 48,49,50,51 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 52,53,54,55 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_LOW, IRQT_HIGH, /* 56,57,58,59 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 60,61,62,63 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 64,65,66,67 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 68,69,70,71 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 72,73,74,75 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 76,77,78,79 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 80,81,82,83 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 84,85,86,87 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 88,89,90,91 */ \ +IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, IRQT_HIGH, /* 92,93,94,95 */ \ +} + +/* Start Enable Pin Interrupts - table 58 page 66 */ + +#define SE_PIN_BASE_INT 32 + +#define SE_U7_RX_INT 63 +#define SE_U7_HCTS_INT 62 +#define SE_BT_CLKREQ_INT 61 +#define SE_U6_IRRX_INT 60 +/*59 unused*/ +#define SE_U5_RX_INT 58 +#define SE_GPI_11_INT 57 +#define SE_U3_RX_INT 56 +#define SE_U2_HCTS_INT 55 +#define SE_U2_RX_INT 54 +#define SE_U1_RX_INT 53 +#define SE_DISP_SYNC_INT 52 +/*51 unused*/ +#define SE_SDIO_INT_N 50 +#define SE_MSDIO_START_INT 49 +#define SE_GPI_06_INT 48 +#define SE_GPI_05_INT 47 +#define SE_GPI_04_INT 46 +#define SE_GPI_03_INT 45 +#define SE_GPI_02_INT 44 +#define SE_GPI_01_INT 43 +#define SE_GPI_00_INT 42 +#define SE_SYSCLKEN_PIN_INT 41 +#define SE_SPI1_DATAIN_INT 40 +#define SE_GPI_07_INT 39 +#define SE_SPI2_DATAIN_INT 38 +#define SE_GPI_10_INT 37 +#define SE_GPI_09_INT 36 +#define SE_GPI_08_INT 35 +/*34-32 unused*/ + +/* Start Enable Internal Interrupts - table 57 page 65 */ + +#define SE_INT_BASE_INT 0 + +#define SE_TS_IRQ 31 +#define SE_TS_P_INT 30 +#define SE_TS_AUX_INT 29 +/*27-28 unused*/ +#define SE_USB_AHB_NEED_CLK_INT 26 +#define SE_MSTIMER_INT 25 +#define SE_RTC_INT 24 +#define SE_USB_NEED_CLK_INT 23 +#define SE_USB_INT 22 +#define SE_USB_I2C_INT 21 +#define SE_USB_OTG_TIMER_INT 20 + +#endif /* __PNX4008_IRQS_h__ */ diff --git a/include/asm-arm/arch-pnx4008/memory.h b/include/asm-arm/arch-pnx4008/memory.h new file mode 100644 index 0000000..0d8268a --- /dev/null +++ b/include/asm-arm/arch-pnx4008/memory.h @@ -0,0 +1,24 @@ +/* + * linux/include/asm-arm/arch-pnx4008/memory.h + * + * Copyright (c) 2005 Philips Semiconductors + * Copyright (c) 2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __ASM_ARCH_MEMORY_H +#define __ASM_ARCH_MEMORY_H + +/* + * Physical DRAM offset. + */ +#define PHYS_OFFSET (0x80000000) + +#define __virt_to_bus(x) ((x) - PAGE_OFFSET + PHYS_OFFSET) +#define __bus_to_virt(x) ((x) + PAGE_OFFSET - PHYS_OFFSET) + +#endif diff --git a/include/asm-arm/arch-pnx4008/param.h b/include/asm-arm/arch-pnx4008/param.h new file mode 100644 index 0000000..95d5f54 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/param.h @@ -0,0 +1,21 @@ +/* + * linux/include/asm-arm/arch-pnx4008/param.h + * + * Copyright (C) 1999 ARM Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define HZ 100 diff --git a/include/asm-arm/arch-pnx4008/platform.h b/include/asm-arm/arch-pnx4008/platform.h new file mode 100644 index 0000000..485a365 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/platform.h @@ -0,0 +1,69 @@ +/* + * include/asm-arm/arch-pnx4008/platfrom.h + * + * PNX4008 Base addresses - header file + * + * Author: Dmitry Chigirev + * + * Based on reference code received from Philips: + * Copyright (C) 2003 Philips Semiconductors + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + + +#ifndef __ASM_ARCH_PLATFORM_H__ +#define __ASM_ARCH_PLATFORM_H__ + +#define PNX4008_IRAM_BASE 0x08000000 +#define PNX4008_IRAM_SIZE 0x00010000 +#define PNX4008_YUV_SLAVE_BASE 0x10000000 +#define PNX4008_DUM_SLAVE_BASE 0x18000000 +#define PNX4008_NDF_FLASH_BASE 0x20020000 +#define PNX4008_SPI1_BASE 0x20088000 +#define PNX4008_SPI2_BASE 0x20090000 +#define PNX4008_SD_CONFIG_BASE 0x20098000 +#define PNX4008_FLASH_DATA 0x200B0000 +#define PNX4008_MLC_FLASH_BASE 0x200B8000 +#define PNX4008_JPEG_CONFIG_BASE 0x300A0000 +#define PNX4008_DMA_CONFIG_BASE 0x31000000 +#define PNX4008_USB_CONFIG_BASE 0x31020000 +#define PNX4008_SDRAM_CFG_BASE 0x31080000 +#define PNX4008_AHB2FAB_BASE 0x40000000 +#define PNX4008_PWRMAN_BASE 0x40004000 +#define PNX4008_INTCTRLMIC_BASE 0x40008000 +#define PNX4008_INTCTRLSIC1_BASE 0x4000C000 +#define PNX4008_INTCTRLSIC2_BASE 0x40010000 +#define PNX4008_HSUART1_BASE 0x40014000 +#define PNX4008_HSUART2_BASE 0x40018000 +#define PNX4008_HSUART7_BASE 0x4001C000 +#define PNX4008_RTC_BASE 0x40024000 +#define PNX4008_PIO_BASE 0x40028000 +#define PNX4008_MSTIMER_BASE 0x40034000 +#define PNX4008_HSTIMER_BASE 0x40038000 +#define PNX4008_WDOG_BASE 0x4003C000 +#define PNX4008_DEBUG_BASE 0x40040000 +#define PNX4008_TOUCH1_BASE 0x40048000 +#define PNX4008_KEYSCAN_BASE 0x40050000 +#define PNX4008_UARTCTRL_BASE 0x40054000 +#define PNX4008_PWM_BASE 0x4005C000 +#define PNX4008_UART3_BASE 0x40080000 +#define PNX4008_UART4_BASE 0x40088000 +#define PNX4008_UART5_BASE 0x40090000 +#define PNX4008_UART6_BASE 0x40098000 +#define PNX4008_I2C1_BASE 0x400A0000 +#define PNX4008_I2C2_BASE 0x400A8000 +#define PNX4008_MAGICGATE_BASE 0x400B0000 +#define PNX4008_DUMCONF_BASE 0x400B8000 +#define PNX4008_DUM_MAINCFG_BASE 0x400BC000 +#define PNX4008_DSP_BASE 0x400C0000 +#define PNX4008_PROFCOUNTER_BASE 0x400C8000 +#define PNX4008_CRYPTO_BASE 0x400D0000 +#define PNX4008_CAMIFCONF_BASE 0x400D8000 +#define PNX4008_YUV2RGB_BASE 0x400E0000 +#define PNX4008_AUDIOCONFIG_BASE 0x400E8000 + +#endif diff --git a/include/asm-arm/arch-pnx4008/pm.h b/include/asm-arm/arch-pnx4008/pm.h new file mode 100644 index 0000000..bac1634 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/pm.h @@ -0,0 +1,33 @@ +/* + * include/asm-arm/arch-pnx4008/pm.h + * + * PNX4008 Power Management Routiness - header file + * + * Authors: Vitaly Wool, Dmitry Chigirev + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __ASM_ARCH_PNX4008_PM_H +#define __ASM_ARCH_PNX4008_PM_H + +#ifndef __ASSEMBLER__ +#include "irq.h" +#include "irqs.h" +#include "clock.h" + +extern void pnx4008_pm_idle(void); +extern void pnx4008_pm_suspend(void); +extern unsigned int pnx4008_cpu_suspend_sz; +extern void pnx4008_cpu_suspend(void); +extern unsigned int pnx4008_cpu_standby_sz; +extern void pnx4008_cpu_standby(void); + +extern int pnx4008_startup_pll(struct clk *); +extern int pnx4008_shutdown_pll(struct clk *); + +#endif /* ASSEMBLER */ +#endif /* __ASM_ARCH_PNX4008_PM_H */ diff --git a/include/asm-arm/arch-pnx4008/system.h b/include/asm-arm/arch-pnx4008/system.h new file mode 100644 index 0000000..6e3da70 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/system.h @@ -0,0 +1,38 @@ +/* + * linux/include/asm-arm/arch-pnx4008/system.h + * + * Copyright (C) 2003 Philips Semiconductors + * Copyright (C) 2005 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ASM_ARCH_SYSTEM_H +#define __ASM_ARCH_SYSTEM_H + +#include +#include +#include + +static void arch_idle(void) +{ + cpu_do_idle(); +} + +static inline void arch_reset(char mode) +{ + cpu_reset(0); +} + +#endif diff --git a/include/asm-arm/arch-pnx4008/timex.h b/include/asm-arm/arch-pnx4008/timex.h new file mode 100644 index 0000000..ee470a3 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/timex.h @@ -0,0 +1,73 @@ +/* + * include/asm-arm/arch-pnx4008/timex.h + * + * PNX4008 timers header file + * + * Author: Dmitry Chigirev + * + * 2005 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef __PNX4008_TIMEX_H +#define __PNX4008_TIMEX_H + +#include +#include + +#define CLOCK_TICK_RATE 1000000 + +#define TICKS2USECS(x) (x) + +/* MilliSecond Timer - Chapter 21 Page 202 */ + +#define MSTIM_INT IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x0)) +#define MSTIM_CTRL IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x4)) +#define MSTIM_COUNTER IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x8)) +#define MSTIM_MCTRL IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x14)) +#define MSTIM_MATCH0 IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x18)) +#define MSTIM_MATCH1 IO_ADDRESS((PNX4008_MSTIMER_BASE + 0x1c)) + +/* High Speed Timer - Chpater 22, Page 205 */ + +#define HSTIM_INT IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x0)) +#define HSTIM_CTRL IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x4)) +#define HSTIM_COUNTER IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x8)) +#define HSTIM_PMATCH IO_ADDRESS((PNX4008_HSTIMER_BASE + 0xC)) +#define HSTIM_PCOUNT IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x10)) +#define HSTIM_MCTRL IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x14)) +#define HSTIM_MATCH0 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x18)) +#define HSTIM_MATCH1 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x1c)) +#define HSTIM_MATCH2 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x20)) +#define HSTIM_CCR IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x28)) +#define HSTIM_CR0 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x2C)) +#define HSTIM_CR1 IO_ADDRESS((PNX4008_HSTIMER_BASE + 0x30)) + +/* IMPORTANT: both timers are UPCOUNTING */ + +/* xSTIM_MCTRL bit definitions */ +#define MR0_INT 1 +#define RESET_COUNT0 (1<<1) +#define STOP_COUNT0 (1<<2) +#define MR1_INT (1<<3) +#define RESET_COUNT1 (1<<4) +#define STOP_COUNT1 (1<<5) +#define MR2_INT (1<<6) +#define RESET_COUNT2 (1<<7) +#define STOP_COUNT2 (1<<8) + +/* xSTIM_CTRL bit definitions */ +#define COUNT_ENAB 1 +#define RESET_COUNT (1<<1) +#define DEBUG_EN (1<<2) + +/* xSTIM_INT bit definitions */ +#define MATCH0_INT 1 +#define MATCH1_INT (1<<1) +#define MATCH2_INT (1<<2) +#define RTC_TICK0 (1<<4) +#define RTC_TICK1 (1<<5) + +#endif diff --git a/include/asm-arm/arch-pnx4008/uncompress.h b/include/asm-arm/arch-pnx4008/uncompress.h new file mode 100644 index 0000000..8fa4d24 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/uncompress.h @@ -0,0 +1,46 @@ +/* + * linux/include/asm-arm/arch-pnx4008/uncompress.h + * + * Copyright (C) 1999 ARM Limited + * Copyright (C) 2006 MontaVista Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define UART5_BASE 0x40090000 + +#define UART5_DR (*(volatile unsigned char *) (UART5_BASE)) +#define UART5_FR (*(volatile unsigned char *) (UART5_BASE + 18)) + +static __inline__ void putc(char c) +{ + while (UART5_FR & (1 << 5)) + barrier(); + + UART5_DR = c; +} + +/* + * This does not append a newline + */ +static inline void flush(void) +{ +} + +/* + * nothing to do + */ +#define arch_decomp_setup() +#define arch_decomp_wdog() diff --git a/include/asm-arm/arch-pnx4008/vmalloc.h b/include/asm-arm/arch-pnx4008/vmalloc.h new file mode 100644 index 0000000..140d925 --- /dev/null +++ b/include/asm-arm/arch-pnx4008/vmalloc.h @@ -0,0 +1,20 @@ +/* + * include/asm-arm/arch-pnx4008/vmalloc.h + * + * Author: Vitaly Wool + * + * 2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * Just any arbitrary offset to the start of the vmalloc VM area: the + * current 8MB value just means that there will be a 8MB "hole" after the + * physical memory until the kernel virtual memory starts. That means that + * any out-of-bounds memory accesses will hopefully be caught. + * The vmalloc() routines leaves a hole of 4kB between each vmalloced + * area for the same reason. ;) + */ +#define VMALLOC_END (PAGE_OFFSET + 0x10000000) diff --git a/include/asm-arm/arch-pxa/idp.h b/include/asm-arm/arch-pxa/idp.h index e7ef497..b695253 100644 --- a/include/asm-arm/arch-pxa/idp.h +++ b/include/asm-arm/arch-pxa/idp.h @@ -15,7 +15,6 @@ * Changes for 2.6 kernel. */ -#include /* * Note: this file must be safe to include in assembly files diff --git a/include/asm-arm/arch-pxa/io.h b/include/asm-arm/arch-pxa/io.h index eb2dd58..7f8d817 100644 --- a/include/asm-arm/arch-pxa/io.h +++ b/include/asm-arm/arch-pxa/io.h @@ -16,6 +16,5 @@ #define IO_SPACE_LIMIT 0xffffffff */ #define __io(a) ((void __iomem *)(a)) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif diff --git a/include/asm-arm/arch-pxa/irqs.h b/include/asm-arm/arch-pxa/irqs.h index 67af238..f3bc70e 100644 --- a/include/asm-arm/arch-pxa/irqs.h +++ b/include/asm-arm/arch-pxa/irqs.h @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #ifdef CONFIG_PXA27x #define PXA_IRQ_SKIP 0 diff --git a/include/asm-arm/arch-pxa/poodle.h b/include/asm-arm/arch-pxa/poodle.h index 6b5ac51..4d6a403 100644 --- a/include/asm-arm/arch-pxa/poodle.h +++ b/include/asm-arm/arch-pxa/poodle.h @@ -31,6 +31,7 @@ #define POODLE_GPIO_CF_IRQ (17) #define POODLE_GPIO_CF_CD (14) #define POODLE_GPIO_CF_STSCHG (14) #define POODLE_GPIO_SD_PWR (33) +#define POODLE_GPIO_SD_PWR1 (3) #define POODLE_GPIO_nSD_CLK (6) #define POODLE_GPIO_nSD_WP (7) #define POODLE_GPIO_nSD_INT (8) @@ -42,6 +43,7 @@ #define POODLE_GPIO_ADC_TEMP_ON (21) #define POODLE_GPIO_BYPASS_ON (36) #define POODLE_GPIO_CHRG_ON (38) #define POODLE_GPIO_CHRG_FULL (16) +#define POODLE_GPIO_DISCHARGE_ON (42) /* Enable battery discharge */ /* PXA GPIOs */ #define POODLE_IRQ_GPIO_ON_KEY IRQ_GPIO(0) @@ -68,4 +70,6 @@ #define POODLE_SCOOP_HS_OUT SCOOP_GPCR_P #define POODLE_SCOOP_IO_DIR ( POODLE_SCOOP_VPEN | POODLE_SCOOP_HS_OUT ) #define POODLE_SCOOP_IO_OUT ( 0 ) +extern struct platform_device poodle_locomo_device; + #endif /* __ASM_ARCH_POODLE_H */ diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h index c8f53a7..f5cc65d 100644 --- a/include/asm-arm/arch-pxa/pxa-regs.h +++ b/include/asm-arm/arch-pxa/pxa-regs.h @@ -13,7 +13,6 @@ #ifndef __PXA_REGS_H #define __PXA_REGS_H -#include /* * PXA Chip selects @@ -1330,6 +1329,7 @@ #define GPIO83_NSTXD 83 /* NSSP transmi #define GPIO84_NSRXD 84 /* NSSP receive */ #define GPIO85_nPCE_1 85 /* Card Enable for Card Space (PXA27x) */ #define GPIO92_MMCDAT0 92 /* MMC DAT0 (PXA27x) */ +#define GPIO102_nPCE_1 102 /* PCMCIA (PXA27x) */ #define GPIO109_MMCDAT1 109 /* MMC DAT1 (PXA27x) */ #define GPIO110_MMCDAT2 110 /* MMC DAT2 (PXA27x) */ #define GPIO110_MMCCS0 110 /* MMC Chip Select 0 (PXA27x) */ @@ -1472,6 +1472,7 @@ #define GPIO84_NSSP_TX (84 | GPIO_ #define GPIO84_NSSP_RX (84 | GPIO_ALT_FN_2_IN) #define GPIO85_nPCE_1_MD (85 | GPIO_ALT_FN_1_OUT) #define GPIO92_MMCDAT0_MD (92 | GPIO_ALT_FN_1_OUT) +#define GPIO102_nPCE_1_MD (102 | GPIO_ALT_FN_1_OUT) #define GPIO104_pSKTSEL_MD (104 | GPIO_ALT_FN_1_OUT) #define GPIO109_MMCDAT1_MD (109 | GPIO_ALT_FN_1_OUT) #define GPIO110_MMCDAT2_MD (110 | GPIO_ALT_FN_1_OUT) @@ -1626,7 +1627,7 @@ #define SSCR0_NCS (1 << 21) /* Network c #define SSCR0_RIM (1 << 22) /* Receive FIFO overrrun interrupt mask */ #define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun interrupt mask */ #define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */ -#define SSCR0_SlotsPerFrm(c) ((x) - 1) /* Time slots per frame [1..8] */ +#define SSCR0_SlotsPerFrm(x) ((x) - 1) /* Time slots per frame [1..8] */ #define SSCR0_ADC (1 << 30) /* Audio clock select */ #define SSCR0_MOD (1 << 31) /* Mode (normal or network) */ #endif @@ -1707,6 +1708,10 @@ #define SSDR SSDR_P1 /* (Write / Read #if defined (CONFIG_PXA27x) #define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ #define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSTSA_P1 __REG(0x41000030) /* SSP Port 1 Tx Timeslot Active */ +#define SSRSA_P1 __REG(0x41000034) /* SSP Port 1 Rx Timeslot Active */ +#define SSTSS_P1 __REG(0x41000038) /* SSP Port 1 Timeslot Status */ +#define SSACD_P1 __REG(0x4100003C) /* SSP Port 1 Audio Clock Divider */ #define SSCR0_P2 __REG(0x41700000) /* SSP Port 2 Control Register 0 */ #define SSCR1_P2 __REG(0x41700004) /* SSP Port 2 Control Register 1 */ #define SSSR_P2 __REG(0x41700008) /* SSP Port 2 Status Register */ @@ -1714,6 +1719,10 @@ #define SSITR_P2 __REG(0x4170000C) /* S #define SSDR_P2 __REG(0x41700010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ #define SSTO_P2 __REG(0x41700028) /* SSP Port 2 Time Out Register */ #define SSPSP_P2 __REG(0x4170002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSTSA_P2 __REG(0x41700030) /* SSP Port 2 Tx Timeslot Active */ +#define SSRSA_P2 __REG(0x41700034) /* SSP Port 2 Rx Timeslot Active */ +#define SSTSS_P2 __REG(0x41700038) /* SSP Port 2 Timeslot Status */ +#define SSACD_P2 __REG(0x4170003C) /* SSP Port 2 Audio Clock Divider */ #define SSCR0_P3 __REG(0x41900000) /* SSP Port 3 Control Register 0 */ #define SSCR1_P3 __REG(0x41900004) /* SSP Port 3 Control Register 1 */ #define SSSR_P3 __REG(0x41900008) /* SSP Port 3 Status Register */ @@ -1721,6 +1730,10 @@ #define SSITR_P3 __REG(0x4190000C) /* S #define SSDR_P3 __REG(0x41900010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ #define SSTO_P3 __REG(0x41900028) /* SSP Port 3 Time Out Register */ #define SSPSP_P3 __REG(0x4190002C) /* SSP Port 3 Programmable Serial Protocol */ +#define SSTSA_P3 __REG(0x41900030) /* SSP Port 3 Tx Timeslot Active */ +#define SSRSA_P3 __REG(0x41900034) /* SSP Port 3 Rx Timeslot Active */ +#define SSTSS_P3 __REG(0x41900038) /* SSP Port 3 Timeslot Status */ +#define SSACD_P3 __REG(0x4190003C) /* SSP Port 3 Audio Clock Divider */ #else /* PXA255 (only port 2) and PXA26x ports*/ #define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ #define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ @@ -1747,6 +1760,10 @@ #define SSITR_P(x) (*(((x) == 1) ? &SSIT #define SSDR_P(x) (*(((x) == 1) ? &SSDR_P1 : ((x) == 2) ? &SSDR_P2 : ((x) == 3) ? &SSDR_P3 : NULL)) #define SSTO_P(x) (*(((x) == 1) ? &SSTO_P1 : ((x) == 2) ? &SSTO_P2 : ((x) == 3) ? &SSTO_P3 : NULL)) #define SSPSP_P(x) (*(((x) == 1) ? &SSPSP_P1 : ((x) == 2) ? &SSPSP_P2 : ((x) == 3) ? &SSPSP_P3 : NULL)) +#define SSTSA_P(x) (*(((x) == 1) ? &SSTSA_P1 : ((x) == 2) ? &SSTSA_P2 : ((x) == 3) ? &SSTSA_P3 : NULL)) +#define SSRSA_P(x) (*(((x) == 1) ? &SSRSA_P1 : ((x) == 2) ? &SSRSA_P2 : ((x) == 3) ? &SSRSA_P3 : NULL)) +#define SSTSS_P(x) (*(((x) == 1) ? &SSTSS_P1 : ((x) == 2) ? &SSTSS_P2 : ((x) == 3) ? &SSTSS_P3 : NULL)) +#define SSACD_P(x) (*(((x) == 1) ? &SSACD_P1 : ((x) == 2) ? &SSACD_P2 : ((x) == 3) ? &SSACD_P3 : NULL)) /* * MultiMediaCard (MMC) controller diff --git a/include/asm-arm/arch-pxa/system.h b/include/asm-arm/arch-pxa/system.h index 840a46b..1d56a3e 100644 --- a/include/asm-arm/arch-pxa/system.h +++ b/include/asm-arm/arch-pxa/system.h @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include "hardware.h" #include "pxa-regs.h" diff --git a/include/asm-arm/arch-pxa/timex.h b/include/asm-arm/arch-pxa/timex.h index aa125ec..2473bb5 100644 --- a/include/asm-arm/arch-pxa/timex.h +++ b/include/asm-arm/arch-pxa/timex.h @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #if defined(CONFIG_PXA25x) /* PXA250/210 timer base */ diff --git a/include/asm-arm/arch-pxa/trizeps4.h b/include/asm-arm/arch-pxa/trizeps4.h new file mode 100644 index 0000000..641d0ec --- /dev/null +++ b/include/asm-arm/arch-pxa/trizeps4.h @@ -0,0 +1,106 @@ +/************************************************************************ + * Include file for TRIZEPS4 SoM and ConXS eval-board + * Copyright (c) JÃŒrgen Schindele + * 2006 + ************************************************************************/ + +/* + * Includes/Defines + */ +#ifndef _TRIPEPS4_H_ +#define _TRIPEPS4_H_ + +/* physical memory regions */ +#define TRIZEPS4_FLASH_PHYS (PXA_CS0_PHYS) /* Flash region */ +#define TRIZEPS4_DISK_PHYS (PXA_CS1_PHYS) /* Disk On Chip region */ +#define TRIZEPS4_ETH_PHYS (PXA_CS2_PHYS) /* Ethernet DM9000 region */ +#define TRIZEPS4_PIC_PHYS (PXA_CS3_PHYS) /* Logic chip on ConXS-Board */ +#define TRIZEPS4_SDRAM_BASE 0xa0000000 /* SDRAM region */ + +#define TRIZEPS4_CFSR_PHYS (PXA_CS3_PHYS) /* Logic chip on ConXS-Board CSFR register */ +#define TRIZEPS4_BOCR_PHYS (PXA_CS3_PHYS+0x02000000) /* Logic chip on ConXS-Board BOCR register */ +#define TRIZEPS4_IRCR_PHYS (PXA_CS3_PHYS+0x02400000) /* Logic chip on ConXS-Board IRCR register*/ +#define TRIZEPS4_UPSR_PHYS (PXA_CS3_PHYS+0x02800000) /* Logic chip on ConXS-Board UPSR register*/ +#define TRIZEPS4_DICR_PHYS (PXA_CS3_PHYS+0x03800000) /* Logic chip on ConXS-Board DICR register*/ + +/* virtual memory regions */ +#define TRIZEPS4_DISK_VIRT 0xF0000000 /* Disk On Chip region */ + +#define TRIZEPS4_PIC_VIRT 0xF0100000 /* not used */ +#define TRIZEPS4_CFSR_VIRT 0xF0100000 +#define TRIZEPS4_BOCR_VIRT 0xF0200000 +#define TRIZEPS4_DICR_VIRT 0xF0300000 +#define TRIZEPS4_IRCR_VIRT 0xF0400000 +#define TRIZEPS4_UPSR_VIRT 0xF0500000 + +/* size of flash */ +#define TRIZEPS4_FLASH_SIZE 0x02000000 /* Flash size 32 MB */ + +/* Ethernet Controller Davicom DM9000 */ +#define GPIO_DM9000 101 +#define TRIZEPS4_ETH_IRQ IRQ_GPIO(GPIO_DM9000) + +/* UCB1400 audio / TS-controller */ +#define GPIO_UCB1400 1 +#define TRIZEPS4_UCB1400_IRQ IRQ_GPIO(GPIO_UCB1400) + +/* PCMCIA socket Compact Flash */ +#define GPIO_PCD 11 /* PCMCIA Card Detect */ +#define TRIZEPS4_CD_IRQ IRQ_GPIO(GPIO_PCD) +#define GPIO_PRDY 13 /* READY / nINT */ +#define TRIZEPS4_READY_NINT IRQ_GPIO(GPIO_PRDY) + +/* MMC socket */ +#define GPIO_MMC_DET 12 +#define TRIZEPS4_MMC_IRQ IRQ_GPIO(GPIO_MMC_DET) + +/* LEDS using tx2 / rx2 */ +#define GPIO_SYS_BUSY_LED 46 +#define GPIO_HEARTBEAT_LED 47 + +/* Off-module PIC on ConXS board */ +#define GPIO_PIC 0 +#define TRIZEPS4_PIC_IRQ IRQ_GPIO(GPIO_PIC) + +#define CFSR_P2V(x) ((x) - TRIZEPS4_CFSR_PHYS + TRIZEPS4_CFSR_VIRT) +#define CFSR_V2P(x) ((x) - TRIZEPS4_CFSR_VIRT + TRIZEPS4_CFSR_PHYS) + +#define BCR_P2V(x) ((x) - TRIZEPS4_BOCR_PHYS + TRIZEPS4_BOCR_VIRT) +#define BCR_V2P(x) ((x) - TRIZEPS4_BOCR_VIRT + TRIZEPS4_BOCR_PHYS) + +#define DCR_P2V(x) ((x) - TRIZEPS4_DICR_PHYS + TRIZEPS4_DICR_VIRT) +#define DCR_V2P(x) ((x) - TRIZEPS4_DICR_VIRT + TRIZEPS4_DICR_PHYS) + +#ifndef __ASSEMBLY__ +#define ConXS_CFSR (*((volatile unsigned short *)CFSR_P2V(0x0C000000))) +#define ConXS_BCR (*((volatile unsigned short *)BCR_P2V(0x0E000000))) +#define ConXS_DCR (*((volatile unsigned short *)DCR_P2V(0x0F800000))) +#else +#define ConXS_CFSR CFSR_P2V(0x0C000000) +#define ConXS_BCR BCR_P2V(0x0E000000) +#define ConXS_DCR DCR_P2V(0x0F800000) +#endif + +#define ConXS_CFSR_BVD_MASK 0x0003 +#define ConXS_CFSR_BVD1 (1 << 0) +#define ConXS_CFSR_BVD2 (1 << 1) +#define ConXS_CFSR_VS_MASK 0x000C +#define ConXS_CFSR_VS1 (1 << 2) +#define ConXS_CFSR_VS2 (1 << 3) +#define ConXS_CFSR_VS_5V (0x3 << 2) +#define ConXS_CFSR_VS_3V3 0x0 + +#define ConXS_BCR_S0_POW_EN0 (1 << 0) +#define ConXS_BCR_S0_POW_EN1 (1 << 1) +#define ConXS_BCR_L_DISP (1 << 4) +#define ConXS_BCR_CF_BUF_EN (1 << 5) +#define ConXS_BCR_CF_RESET (1 << 7) +#define ConXS_BCR_S0_VCC_3V3 0x1 +#define ConXS_BCR_S0_VCC_5V0 0x2 +#define ConXS_BCR_S0_VPP_12V 0x4 +#define ConXS_BCR_S0_VPP_3V3 0x8 + +#define ConXS_IRCR_MODE (1 << 0) +#define ConXS_IRCR_SD (1 << 1) + +#endif /* _TRIPEPS4_H_ */ diff --git a/include/asm-arm/arch-realview/io.h b/include/asm-arm/arch-realview/io.h index d444a68..c70f1df 100644 --- a/include/asm-arm/arch-realview/io.h +++ b/include/asm-arm/arch-realview/io.h @@ -29,6 +29,5 @@ static inline void __iomem *__io(unsigne #define __io(a) __io(a) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif diff --git a/include/asm-arm/arch-realview/smp.h b/include/asm-arm/arch-realview/smp.h index fc87783..515819e 100644 --- a/include/asm-arm/arch-realview/smp.h +++ b/include/asm-arm/arch-realview/smp.h @@ -1,7 +1,6 @@ #ifndef ASMARM_ARCH_SMP_H #define ASMARM_ARCH_SMP_H -#include #include diff --git a/include/asm-arm/arch-s3c2410/debug-macro.S b/include/asm-arm/arch-s3c2410/debug-macro.S index 5f8223e..b7d15d1 100644 --- a/include/asm-arm/arch-s3c2410/debug-macro.S +++ b/include/asm-arm/arch-s3c2410/debug-macro.S @@ -33,7 +33,7 @@ #endif .endm .macro senduart,rd,rx - str \rd, [\rx, # S3C2410_UTXH ] + strb \rd, [\rx, # S3C2410_UTXH ] .endm .macro busyuart, rd, rx @@ -42,6 +42,12 @@ #endif beq 1001f @ @ FIFO enabled... 1003: + @ check for arm920 vs arm926. currently assume all arm926 + @ devices have an 64 byte FIFO identical to the s3c2440 + mrc p15, 0, \rd, c0, c0 + and \rd, \rd, #0xff0 + teq \rd, #0x260 + beq 1004f mrc p15, 0, \rd, c1, c0 tst \rd, #1 addeq \rd, \rx, #(S3C24XX_PA_GPIO - S3C24XX_PA_UART) @@ -50,7 +56,7 @@ #endif ldr \rd, [ \rd, # S3C2410_GSTATUS1 - S3C2410_GPIOREG(0) ] and \rd, \rd, #0x00ff0000 teq \rd, #0x00440000 @ is it 2440? - +1004: ldr \rd, [ \rx, # S3C2410_UFSTAT ] moveq \rd, \rd, lsr #SHIFT_2440TXF tst \rd, #S3C2410_UFSTAT_TXFULL diff --git a/include/asm-arm/arch-s3c2410/dma.h b/include/asm-arm/arch-s3c2410/dma.h index b011e14..72964f9 100644 --- a/include/asm-arm/arch-s3c2410/dma.h +++ b/include/asm-arm/arch-s3c2410/dma.h @@ -18,7 +18,6 @@ #ifndef __ASM_ARCH_DMA_H #define __ASM_ARCH_DMA_H __FILE__ -#include #include #include "hardware.h" diff --git a/include/asm-arm/arch-s3c2410/entry-macro.S b/include/asm-arm/arch-s3c2410/entry-macro.S index 894c35c..e09a6b8 100644 --- a/include/asm-arm/arch-s3c2410/entry-macro.S +++ b/include/asm-arm/arch-s3c2410/entry-macro.S @@ -18,8 +18,6 @@ #define INTPND (0x10) #define INTOFFSET (0x14) -#define EXTINTPEND (0xa8) -#define EXTINTMASK (0xa4) #include #include @@ -28,37 +26,23 @@ #include mov \base, #S3C24XX_VA_IRQ - ldr \irqstat, [ \base, #INTPND] - bics \irqnr, \irqstat, #3<<4 @@ only an GPIO IRQ - beq 2000f - @@ try the interrupt offset register, since it is there + ldr \irqstat, [ \base, #INTPND ] + teq \irqstat, #0 + beq 1002f ldr \irqnr, [ \base, #INTOFFSET ] mov \tmp, #1 tst \irqstat, \tmp, lsl \irqnr - addne \irqnr, \irqnr, #IRQ_EINT0 bne 1001f @@ the number specified is not a valid irq, so try @@ and work it out for ourselves - mov \irqnr, #IRQ_EINT0 @@ start here - b 3000f - -2000: - @@ load the GPIO interrupt register, and check it - - add \tmp, \base, #S3C24XX_VA_GPIO - S3C24XX_VA_IRQ - ldr \irqstat, [ \tmp, # EXTINTPEND ] - ldr \irqnr, [ \tmp, # EXTINTMASK ] - bics \irqstat, \irqstat, \irqnr - beq 1001f - - mov \irqnr, #(IRQ_EINT4 - 4) + mov \irqnr, #0 @@ start here @@ work out which irq (if any) we got -3000: + movs \tmp, \irqstat, lsl#16 addeq \irqnr, \irqnr, #16 moveq \irqstat, \irqstat, lsr#16 @@ -75,9 +59,9 @@ #include addeq \irqnr, \irqnr, #1 @@ we have the value - movs \irqnr, \irqnr - 1001: + adds \irqnr, \irqnr, #IRQ_EINT0 +1002: @@ exit here, Z flag unset if IRQ .endm diff --git a/include/asm-arm/arch-s3c2410/map.h b/include/asm-arm/arch-s3c2410/map.h index c380d26..fae2766 100644 --- a/include/asm-arm/arch-s3c2410/map.h +++ b/include/asm-arm/arch-s3c2410/map.h @@ -126,9 +126,18 @@ #define S3C2410_PA_IIS (0x55000000) #define S3C24XX_SZ_IIS SZ_1M /* GPIO ports */ -#define S3C24XX_VA_GPIO S3C2410_ADDR(0x00E00000) + +/* the calculation for the VA of this must ensure that + * it is the same distance apart from the UART in the + * phsyical address space, as the initial mapping for the IO + * is done as a 1:1 maping. This puts it (currently) at + * 0xF6800000, which is not in the way of any current mapping + * by the base system. +*/ + #define S3C2400_PA_GPIO (0x15600000) #define S3C2410_PA_GPIO (0x56000000) +#define S3C24XX_VA_GPIO ((S3C2410_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART) #define S3C24XX_SZ_GPIO SZ_1M /* RTC */ @@ -227,4 +236,20 @@ #define S3C24XX_PA_ADC S3C2410_PA_A #define S3C24XX_PA_SPI S3C2410_PA_SPI #endif +/* deal with the registers that move under the 2412/2413 */ + +#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) +#ifndef __ASSEMBLY__ +extern void __iomem *s3c24xx_va_gpio2; +#endif +#ifdef CONFIG_CPU_S3C2412_ONLY +#define S3C24XX_VA_GPIO2 (S3C24XX_VA_GPIO + 0x10) +#else +#define S3C24XX_VA_GPIO2 s3c24xx_va_gpio2 +#endif +#else +#define s3c24xx_va_gpio2 S3C24XX_VA_GPIO +#define S3C24XX_VA_GPIO2 S3C24XX_VA_GPIO +#endif + #endif /* __ASM_ARCH_MAP_H */ diff --git a/include/asm-arm/arch-s3c2410/regs-clock.h b/include/asm-arm/arch-s3c2410/regs-clock.h index 3436070..a7c61fe 100644 --- a/include/asm-arm/arch-s3c2410/regs-clock.h +++ b/include/asm-arm/arch-s3c2410/regs-clock.h @@ -1,6 +1,6 @@ /* linux/include/asm/arch-s3c2410/regs-clock.h * - * Copyright (c) 2003,2004,2005 Simtec Electronics + * Copyright (c) 2003,2004,2005,2006 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -114,7 +114,7 @@ s3c2410_get_pll(unsigned int pllval, uns #endif /* __ASSEMBLY__ */ -#ifdef CONFIG_CPU_S3C2440 +#if defined(CONFIG_CPU_S3C2440) || defined(CONFIG_CPU_S3C2442) /* extra registers */ #define S3C2440_CAMDIVN S3C2410_CLKREG(0x18) @@ -136,7 +136,70 @@ #define S3C2440_CAMDIVN_HCLK3_HALF (1< #define S3C2440_CAMDIVN_HCLK4_HALF (1<<9) #define S3C2440_CAMDIVN_DVSEN (1<<12) -#endif /* CONFIG_CPU_S3C2440 */ - +#define S3C2442_CAMDIVN_CAMCLK_DIV3 (1<<5) + +#endif /* CONFIG_CPU_S3C2440 or CONFIG_CPU_S3C2442 */ + +#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) + +#define S3C2412_OSCSET S3C2410_CLKREG(0x18) +#define S3C2412_CLKSRC S3C2410_CLKREG(0x1C) + +#define S3C2412_PLLCON_OFF (1<<20) + +#define S3C2412_CLKDIVN_PDIVN (1<<2) +#define S3C2412_CLKDIVN_HDIVN_MASK (3<<0) +#define S3C2421_CLKDIVN_ARMDIVN (1<<3) +#define S3C2412_CLKDIVN_USB48DIV (1<<6) +#define S3C2412_CLKDIVN_UARTDIV_MASK (15<<8) +#define S3C2412_CLKDIVN_UARTDIV_SHIFT (8) +#define S3C2412_CLKDIVN_I2SDIV_MASK (15<<12) +#define S3C2412_CLKDIVN_I2SDIV_SHIFT (12) +#define S3C2412_CLKDIVN_CAMDIV_MASK (15<<16) +#define S3C2412_CLKDIVN_CAMDIV_SHIFT (16) + +#define S3C2412_CLKCON_WDT (1<<28) +#define S3C2412_CLKCON_SPI (1<<27) +#define S3C2412_CLKCON_IIS (1<<26) +#define S3C2412_CLKCON_IIC (1<<25) +#define S3C2412_CLKCON_ADC (1<<24) +#define S3C2412_CLKCON_RTC (1<<23) +#define S3C2412_CLKCON_GPIO (1<<22) +#define S3C2412_CLKCON_UART2 (1<<21) +#define S3C2412_CLKCON_UART1 (1<<20) +#define S3C2412_CLKCON_UART0 (1<<19) +#define S3C2412_CLKCON_SDI (1<<18) +#define S3C2412_CLKCON_PWMT (1<<17) +#define S3C2412_CLKCON_USBD (1<<16) +#define S3C2412_CLKCON_CAMCLK (1<<15) +#define S3C2412_CLKCON_UARTCLK (1<<14) +/* missing 13 */ +#define S3C2412_CLKCON_USB_HOST48 (1<<12) +#define S3C2412_CLKCON_USB_DEV48 (1<<11) +#define S3C2412_CLKCON_HCLKdiv2 (1<<10) +#define S3C2412_CLKCON_HCLKx2 (1<<9) +#define S3C2412_CLKCON_SDRAM (1<<8) +/* missing 7 */ +#define S3C2412_CLKCON_USBH S3C2410_CLKCON_USBH +#define S3C2412_CLKCON_LCDC S3C2410_CLKCON_LCDC +#define S3C2412_CLKCON_NAND S3C2410_CLKCON_NAND +#define S3C2412_CLKCON_DMA3 (1<<3) +#define S3C2412_CLKCON_DMA2 (1<<2) +#define S3C2412_CLKCON_DMA1 (1<<1) +#define S3C2412_CLKCON_DMA0 (1<<0) + +/* clock sourec controls */ + +#define S3C2412_CLKSRC_EXTCLKDIV_MASK (7 << 0) +#define S3C2412_CLKSRC_EXTCLKDIV_SHIFT (0) +#define S3C2412_CLKSRC_MDIVCLK_EXTCLKDIV (1<<3) +#define S3C2412_CLKSRC_MSYSCLK_MPLL (1<<4) +#define S3C2412_CLKSRC_USYSCLK_UPLL (1<<5) +#define S3C2412_CLKSRC_UARTCLK_MPLL (1<<8) +#define S3C2412_CLKSRC_I2SCLK_MPLL (1<<9) +#define S3C2412_CLKSRC_USBCLK_HCLK (1<<10) +#define S3C2412_CLKSRC_CAMCLK_HCLK (1<<11) + +#endif /* CONFIG_CPU_S3C2412 | CONFIG_CPU_S3C2413 */ #endif /* __ASM_ARM_REGS_CLOCK */ diff --git a/include/asm-arm/arch-s3c2410/regs-dsc.h b/include/asm-arm/arch-s3c2410/regs-dsc.h index a023b04..a0a1248 100644 --- a/include/asm-arm/arch-s3c2410/regs-dsc.h +++ b/include/asm-arm/arch-s3c2410/regs-dsc.h @@ -7,18 +7,19 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * S3C2440 Signal Drive Strength Control - * - * Changelog: - * 11-Aug-2004 BJD Created file - * 25-Aug-2004 BJD Added the _SELECT_* defs for using with functions + * S3C2440/S3C2412 Signal Drive Strength Control */ #ifndef __ASM_ARCH_REGS_DSC_H #define __ASM_ARCH_REGS_DSC_H "2440-dsc" -#ifdef CONFIG_CPU_S3C2440 +#if defined(CONFIG_CPU_S3C2412) +#define S3C2412_DSC0 S3C2410_GPIOREG(0xdc) +#define S3C2412_DSC1 S3C2410_GPIOREG(0xe0) +#endif + +#if defined(CONFIG_CPU_S3C2440) #define S3C2440_DSC0 S3C2410_GPIOREG(0xc4) #define S3C2440_DSC1 S3C2410_GPIOREG(0xc8) @@ -170,7 +171,7 @@ #define S3C2440_DSC1_CS1_6mA (2<<2) #define S3C2440_DSC1_CS1_4mA (3<<2) #define S3C2440_DSC1_CS1_MASK (3<<2) -#define S3C2440_DSC1_CS0 (S3C2440_SELECT_DSC1 | 0 +#define S3C2440_DSC1_CS0 (S3C2440_SELECT_DSC1 | 0) #define S3C2440_DSC1_CS0_10mA (0<<0) #define S3C2440_DSC1_CS0_8mA (1<<0) #define S3C2440_DSC1_CS0_6mA (2<<0) diff --git a/include/asm-arm/arch-s3c2410/regs-gpio.h b/include/asm-arm/arch-s3c2410/regs-gpio.h index d257408..6dd17f0 100644 --- a/include/asm-arm/arch-s3c2410/regs-gpio.h +++ b/include/asm-arm/arch-s3c2410/regs-gpio.h @@ -45,7 +45,7 @@ #define S3C24XX_GPIO_BASE(x) S3C2400_GP #define S3C24XX_MISCCR S3C2400_MISCCR #else #define S3C24XX_GPIO_BASE(x) S3C2410_GPIO_BASE(x) -#define S3C24XX_MISCCR S3C2410_MISCCR +#define S3C24XX_MISCCR S3C24XX_GPIOREG2(0x80) #endif /* CONFIG_CPU_S3C2400 */ @@ -73,9 +73,15 @@ #define S3C2410_GPIO_IRQ (0xFFFFFFF2 #define S3C2410_GPIO_SFN2 (0xFFFFFFF2) /* not available on A */ #define S3C2410_GPIO_SFN3 (0xFFFFFFF3) /* not available on A */ -/* configure GPIO ports A..G */ +/* register address for the GPIO registers. + * S3C24XX_GPIOREG2 is for the second set of registers in the + * GPIO which move between s3c2410 and s3c2412 type systems */ #define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO) +#define S3C24XX_GPIOREG2(x) ((x) + S3C24XX_VA_GPIO2) + + +/* configure GPIO ports A..G */ /* port A - S3C2410: 22bits, zero in bit X makes pin X output * S3C2400: 18bits, zero in bit X makes pin X output @@ -450,12 +456,14 @@ #define S3C2410_GPD0_INP (0x00 << 0) #define S3C2410_GPD0_OUTP (0x01 << 0) #define S3C2410_GPD0_VD8 (0x02 << 0) #define S3C2400_GPD0_VFRAME (0x02 << 0) +#define S3C2442_GPD0_nSPICS1 (0x03 << 0) #define S3C2410_GPD1 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 1) #define S3C2410_GPD1_INP (0x00 << 2) #define S3C2410_GPD1_OUTP (0x01 << 2) #define S3C2410_GPD1_VD9 (0x02 << 2) #define S3C2400_GPD1_VM (0x02 << 2) +#define S3C2442_GPD1_SPICLK1 (0x03 << 2) #define S3C2410_GPD2 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 2) #define S3C2410_GPD2_INP (0x00 << 4) @@ -858,6 +866,7 @@ #define S3C2410_GPG12_INP (0x00 << 2 #define S3C2410_GPG12_OUTP (0x01 << 24) #define S3C2410_GPG12_EINT20 (0x02 << 24) #define S3C2410_GPG12_XMON (0x03 << 24) +#define S3C2442_GPG12_nSPICS0 (0x03 << 24) #define S3C2410_GPG13 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 13) #define S3C2410_GPG13_INP (0x00 << 26) @@ -943,17 +952,25 @@ #define S3C2410_GPH9 S3C2410_GP #define S3C2410_GPH9_INP (0x00 << 18) #define S3C2410_GPH9_OUTP (0x01 << 18) #define S3C2410_GPH9_CLKOUT0 (0x02 << 18) +#define S3C2442_GPH9_nSPICS0 (0x03 << 18) #define S3C2410_GPH10 S3C2410_GPIONO(S3C2410_GPIO_BANKH, 10) #define S3C2410_GPH10_INP (0x00 << 20) #define S3C2410_GPH10_OUTP (0x01 << 20) #define S3C2410_GPH10_CLKOUT1 (0x02 << 20) +/* The S3C2412 and S3C2413 move the GPJ register set to after + * GPH, which means all registers after 0x80 are now offset by 0x10 + * for the 2412/2413 from the 2410/2440/2442 +*/ + /* miscellaneous control */ #define S3C2400_MISCCR S3C2410_GPIOREG(0x54) #define S3C2410_MISCCR S3C2410_GPIOREG(0x80) #define S3C2410_DCLKCON S3C2410_GPIOREG(0x84) +#define S3C24XX_DCLKCON S3C24XX_GPIOREG2(0x84) + /* see clock.h for dclk definitions */ /* pullup control on databus */ @@ -981,6 +998,8 @@ #define S3C2410_MISCCR_CLK0_PCLK (4<< #define S3C2410_MISCCR_CLK0_DCLK0 (5<<4) #define S3C2410_MISCCR_CLK0_MASK (7<<4) +#define S3C2412_MISCCR_CLK0_RTC (2<<4) + #define S3C2410_MISCCR_CLK1_MPLL (0<<8) #define S3C2410_MISCCR_CLK1_UPLL (1<<8) #define S3C2410_MISCCR_CLK1_FCLK (2<<8) @@ -989,6 +1008,8 @@ #define S3C2410_MISCCR_CLK1_PCLK (4<< #define S3C2410_MISCCR_CLK1_DCLK1 (5<<8) #define S3C2410_MISCCR_CLK1_MASK (7<<8) +#define S3C2412_MISCCR_CLK1_CLKsrc (0<<8) + #define S3C2410_MISCCR_USBSUSPND0 (1<<12) #define S3C2410_MISCCR_USBSUSPND1 (1<<13) @@ -996,7 +1017,7 @@ #define S3C2410_MISCCR_nRSTCON (1<<1 #define S3C2410_MISCCR_nEN_SCLK0 (1<<17) #define S3C2410_MISCCR_nEN_SCLK1 (1<<18) -#define S3C2410_MISCCR_nEN_SCLKE (1<<19) +#define S3C2410_MISCCR_nEN_SCLKE (1<<19) /* not 2412 */ #define S3C2410_MISCCR_SDSLEEP (7<<17) /* external interrupt control... */ @@ -1013,6 +1034,10 @@ #define S3C2410_EXTINT0 S3C2410_GPIOR #define S3C2410_EXTINT1 S3C2410_GPIOREG(0x8C) #define S3C2410_EXTINT2 S3C2410_GPIOREG(0x90) +#define S3C24XX_EXTINT0 S3C24XX_GPIOREG2(0x88) +#define S3C24XX_EXTINT1 S3C24XX_GPIOREG2(0x8C) +#define S3C24XX_EXTINT2 S3C24XX_GPIOREG2(0x90) + /* values for S3C2410_EXTINT0/1/2 */ #define S3C2410_EXTINT_LOWLEV (0x00) #define S3C2410_EXTINT_HILEV (0x01) @@ -1026,6 +1051,11 @@ #define S3C2410_EINFLT1 S3C2410_GPIOR #define S3C2410_EINFLT2 S3C2410_GPIOREG(0x9C) #define S3C2410_EINFLT3 S3C2410_GPIOREG(0xA0) +#define S3C24XX_EINFLT0 S3C24XX_GPIOREG2(0x94) +#define S3C24XX_EINFLT1 S3C24XX_GPIOREG2(0x98) +#define S3C24XX_EINFLT2 S3C24XX_GPIOREG2(0x9C) +#define S3C24XX_EINFLT3 S3C24XX_GPIOREG2(0xA0) + /* values for interrupt filtering */ #define S3C2410_EINTFLT_PCLK (0x00) #define S3C2410_EINTFLT_EXTCLK (1<<7) @@ -1035,6 +1065,7 @@ #define S3C2410_EINTFLT_WIDTHMSK(x) ((x) /* GSTATUS have miscellaneous information in them * + * These move between s3c2410 and s3c2412 style systems. */ #define S3C2410_GSTATUS0 S3C2410_GPIOREG(0x0AC) @@ -1043,6 +1074,18 @@ #define S3C2410_GSTATUS2 S3C2410_GPIOR #define S3C2410_GSTATUS3 S3C2410_GPIOREG(0x0B8) #define S3C2410_GSTATUS4 S3C2410_GPIOREG(0x0BC) +#define S3C2412_GSTATUS0 S3C2410_GPIOREG(0x0BC) +#define S3C2412_GSTATUS1 S3C2410_GPIOREG(0x0C0) +#define S3C2412_GSTATUS2 S3C2410_GPIOREG(0x0C4) +#define S3C2412_GSTATUS3 S3C2410_GPIOREG(0x0C8) +#define S3C2412_GSTATUS4 S3C2410_GPIOREG(0x0CC) + +#define S3C24XX_GSTATUS0 S3C24XX_GPIOREG2(0x0AC) +#define S3C24XX_GSTATUS1 S3C24XX_GPIOREG2(0x0B0) +#define S3C24XX_GSTATUS2 S3C24XX_GPIOREG2(0x0B4) +#define S3C24XX_GSTATUS3 S3C24XX_GPIOREG2(0x0B8) +#define S3C24XX_GSTATUS4 S3C24XX_GPIOREG2(0x0BC) + #define S3C2410_GSTATUS0_nWAIT (1<<3) #define S3C2410_GSTATUS0_NCON (1<<2) #define S3C2410_GSTATUS0_RnB (1<<1) @@ -1050,7 +1093,9 @@ #define S3C2410_GSTATUS0_nBATTFLT (1<<0 #define S3C2410_GSTATUS1_IDMASK (0xffff0000) #define S3C2410_GSTATUS1_2410 (0x32410000) +#define S3C2410_GSTATUS1_2412 (0x32412001) #define S3C2410_GSTATUS1_2440 (0x32440000) +#define S3C2410_GSTATUS1_2442 (0x32440aaa) #define S3C2410_GSTATUS2_WTRESET (1<<2) #define S3C2410_GSTATUS2_OFFRESET (1<<1) @@ -1072,5 +1117,22 @@ #define S3C2400_OPENCR_OPC_MISOEN (1<< #define S3C2400_OPENCR_OPC_MOSIDIS (0<<5) #define S3C2400_OPENCR_OPC_MOSIEN (1<<5) +/* 2412/2413 sleep configuration registers */ + +#define S3C2412_GPBSLPCON S3C2410_GPIOREG(0x1C) +#define S3C2412_GPCSLPCON S3C2410_GPIOREG(0x2C) +#define S3C2412_GPDSLPCON S3C2410_GPIOREG(0x3C) +#define S3C2412_GPESLPCON S3C2410_GPIOREG(0x4C) +#define S3C2412_GPFSLPCON S3C2410_GPIOREG(0x5C) +#define S3C2412_GPGSLPCON S3C2410_GPIOREG(0x6C) +#define S3C2412_GPHSLPCON S3C2410_GPIOREG(0x7C) + +/* definitions for each pin bit */ +#define S3C2412_SLPCON_LOW(x) ( 0x00 << ((x) * 2)) +#define S3C2412_SLPCON_HI(x) ( 0x01 << ((x) * 2)) +#define S3C2412_SLPCON_IN(x) ( 0x02 << ((x) * 2)) +#define S3C2412_SLPCON_PDWN(x) ( 0x03 << ((x) * 2)) +#define S3C2412_SLPCON_MASK(x) ( 0x03 << ((x) * 2)) + #endif /* __ASM_ARCH_REGS_GPIO_H */ diff --git a/include/asm-arm/arch-s3c2410/regs-gpioj.h b/include/asm-arm/arch-s3c2410/regs-gpioj.h index 3ad2324..18edae5 100644 --- a/include/asm-arm/arch-s3c2410/regs-gpioj.h +++ b/include/asm-arm/arch-s3c2410/regs-gpioj.h @@ -32,6 +32,11 @@ #define S3C2440_GPJCON S3C2410_GPIOR #define S3C2440_GPJDAT S3C2410_GPIOREG(0xd4) #define S3C2440_GPJUP S3C2410_GPIOREG(0xd8) +#define S3C2413_GPJCON S3C2410_GPIOREG(0x80) +#define S3C2413_GPJDAT S3C2410_GPIOREG(0x84) +#define S3C2413_GPJUP S3C2410_GPIOREG(0x88) +#define S3C2413_GPJSLPCON S3C2410_GPIOREG(0x8C) + #define S3C2440_GPJ0 S3C2410_GPIONO(S3C2440_GPIO_BANKJ, 0) #define S3C2440_GPJ0_INP (0x00 << 0) #define S3C2440_GPJ0_OUTP (0x01 << 0) diff --git a/include/asm-arm/arch-s3c2410/regs-irq.h b/include/asm-arm/arch-s3c2410/regs-irq.h index 24b7292..572fca5 100644 --- a/include/asm-arm/arch-s3c2410/regs-irq.h +++ b/include/asm-arm/arch-s3c2410/regs-irq.h @@ -23,6 +23,7 @@ #define ___ASM_ARCH_REGS_IRQ_H "$Id: irq #define S3C2410_IRQREG(x) ((x) + S3C24XX_VA_IRQ) #define S3C2410_EINTREG(x) ((x) + S3C24XX_VA_GPIO) +#define S3C24XX_EINTREG(x) ((x) + S3C24XX_VA_GPIO2) #define S3C2410_SRCPND S3C2410_IRQREG(0x000) #define S3C2410_INTMOD S3C2410_IRQREG(0x004) @@ -40,5 +41,10 @@ #define S3C2410_INTSUBMSK S3C2410_I #define S3C2410_EINTMASK S3C2410_EINTREG(0x0A4) #define S3C2410_EINTPEND S3C2410_EINTREG(0X0A8) +#define S3C2412_EINTMASK S3C2410_EINTREG(0x0B4) +#define S3C2412_EINTPEND S3C2410_EINTREG(0X0B8) + +#define S3C24XX_EINTMASK S3C24XX_EINTREG(0x0A4) +#define S3C24XX_EINTPEND S3C24XX_EINTREG(0X0A8) #endif /* ___ASM_ARCH_REGS_IRQ_H */ diff --git a/include/asm-arm/arch-s3c2410/regs-nand.h b/include/asm-arm/arch-s3c2410/regs-nand.h index 7cff235..c1470c6 100644 --- a/include/asm-arm/arch-s3c2410/regs-nand.h +++ b/include/asm-arm/arch-s3c2410/regs-nand.h @@ -39,10 +39,19 @@ #define S3C2440_NFESTAT0 S3C2410_NFREG(0 #define S3C2440_NFESTAT1 S3C2410_NFREG(0x28) #define S3C2440_NFMECC0 S3C2410_NFREG(0x2C) #define S3C2440_NFMECC1 S3C2410_NFREG(0x30) -#define S3C2440_NFSECC S3C2410_NFREG(0x34) +#define S3C2440_NFSECC S3C24E10_NFREG(0x34) #define S3C2440_NFSBLK S3C2410_NFREG(0x38) #define S3C2440_NFEBLK S3C2410_NFREG(0x3C) +#define S3C2412_NFSBLK S3C2410_NFREG(0x20) +#define S3C2412_NFEBLK S3C2410_NFREG(0x24) +#define S3C2412_NFSTAT S3C2410_NFREG(0x28) +#define S3C2412_NFMECC_ERR0 S3C2410_NFREG(0x2C) +#define S3C2412_NFMECC_ERR1 S3C2410_NFREG(0x30) +#define S3C2412_NFMECC0 S3C2410_NFREG(0x34) +#define S3C2412_NFMECC1 S3C2410_NFREG(0x38) +#define S3C2412_NFSECC S3C2410_NFREG(0x3C) + #define S3C2410_NFCONF_EN (1<<15) #define S3C2410_NFCONF_512BYTE (1<<14) #define S3C2410_NFCONF_4STEP (1<<13) @@ -77,5 +86,42 @@ #define S3C2440_NFSTAT_nCE (1<<1) #define S3C2440_NFSTAT_RnB_CHANGE (1<<2) #define S3C2440_NFSTAT_ILLEGAL_ACCESS (1<<3) +#define S3C2412_NFCONF_NANDBOOT (1<<31) +#define S3C2412_NFCONF_ECCCLKCON (1<<30) +#define S3C2412_NFCONF_ECC_MLC (1<<24) +#define S3C2412_NFCONF_TACLS_MASK (7<<12) /* 1 extra bit of Tacls */ + +#define S3C2412_NFCONT_ECC4_DIRWR (1<<18) +#define S3C2412_NFCONT_LOCKTIGHT (1<<17) +#define S3C2412_NFCONT_SOFTLOCK (1<<16) +#define S3C2412_NFCONT_ECC4_ENCINT (1<<13) +#define S3C2412_NFCONT_ECC4_DECINT (1<<12) +#define S3C2412_NFCONT_MAIN_ECC_LOCK (1<<7) +#define S3C2412_NFCONT_INIT_MAIN_ECC (1<<5) +#define S3C2412_NFCONT_nFCE1 (1<<2) +#define S3C2412_NFCONT_nFCE0 (1<<1) + +#define S3C2412_NFSTAT_ECC_ENCDONE (1<<7) +#define S3C2412_NFSTAT_ECC_DECDONE (1<<6) +#define S3C2412_NFSTAT_ILLEGAL_ACCESS (1<<5) +#define S3C2412_NFSTAT_RnB_CHANGE (1<<4) +#define S3C2412_NFSTAT_nFCE1 (1<<3) +#define S3C2412_NFSTAT_nFCE0 (1<<2) +#define S3C2412_NFSTAT_Res1 (1<<1) +#define S3C2412_NFSTAT_READY (1<<0) + +#define S3C2412_NFECCERR_SERRDATA(x) (((x) >> 21) & 0xf) +#define S3C2412_NFECCERR_SERRBIT(x) (((x) >> 18) & 0x7) +#define S3C2412_NFECCERR_MERRDATA(x) (((x) >> 7) & 0x3ff) +#define S3C2412_NFECCERR_MERRBIT(x) (((x) >> 4) & 0x7) +#define S3C2412_NFECCERR_SPARE_ERR(x) (((x) >> 2) & 0x3) +#define S3C2412_NFECCERR_MAIN_ERR(x) (((x) >> 2) & 0x3) +#define S3C2412_NFECCERR_NONE (0) +#define S3C2412_NFECCERR_1BIT (1) +#define S3C2412_NFECCERR_MULTIBIT (2) +#define S3C2412_NFECCERR_ECCAREA (3) + + + #endif /* __ASM_ARM_REGS_NAND */ diff --git a/include/asm-arm/arch-s3c2410/regs-serial.h b/include/asm-arm/arch-s3c2410/regs-serial.h index 83b0125..93f651a 100644 --- a/include/asm-arm/arch-s3c2410/regs-serial.h +++ b/include/asm-arm/arch-s3c2410/regs-serial.h @@ -82,6 +82,12 @@ #define S3C2440_UCON1_DIVMASK (15 << 1 #define S3C2440_UCON2_DIVMASK (7 << 12) #define S3C2440_UCON_DIVSHIFT (12) +#define S3C2412_UCON_CLKMASK (3<<10) +#define S3C2412_UCON_UCLK (1<<10) +#define S3C2412_UCON_USYSCLK (3<<10) +#define S3C2412_UCON_PCLK (0<<10) +#define S3C2412_UCON_PCLK2 (2<<10) + #define S3C2410_UCON_UCLK (1<<10) #define S3C2410_UCON_SBREAK (1<<4) @@ -124,6 +130,15 @@ #define S3C2410_UFCON_DEFAULT (S3C2410 #define S3C2410_UMCOM_AFC (1<<4) #define S3C2410_UMCOM_RTS_LOW (1<<0) +#define S3C2412_UMCON_AFC_63 (0<<5) +#define S3C2412_UMCON_AFC_56 (1<<5) +#define S3C2412_UMCON_AFC_48 (2<<5) +#define S3C2412_UMCON_AFC_40 (3<<5) +#define S3C2412_UMCON_AFC_32 (4<<5) +#define S3C2412_UMCON_AFC_24 (5<<5) +#define S3C2412_UMCON_AFC_16 (6<<5) +#define S3C2412_UMCON_AFC_8 (7<<5) + #define S3C2410_UFSTAT_TXFULL (1<<9) #define S3C2410_UFSTAT_RXFULL (1<<8) #define S3C2410_UFSTAT_TXMASK (15<<4) diff --git a/include/asm-arm/arch-s3c2410/uncompress.h b/include/asm-arm/arch-s3c2410/uncompress.h index a6f6a0e..8e152a0 100644 --- a/include/asm-arm/arch-s3c2410/uncompress.h +++ b/include/asm-arm/arch-s3c2410/uncompress.h @@ -22,7 +22,6 @@ #ifndef __ASM_ARCH_UNCOMPRESS_H #define __ASM_ARCH_UNCOMPRESS_H -#include /* defines for UART registers */ #include "asm/arch/regs-serial.h" @@ -82,7 +81,8 @@ #endif while (1) { level = uart_rd(S3C2410_UFSTAT); - if (cpuid == S3C2410_GSTATUS1_2440) { + if (cpuid == S3C2410_GSTATUS1_2440 || + cpuid == S3C2410_GSTATUS1_2442) { level &= S3C2440_UFSTAT_TXMASK; level >>= S3C2440_UFSTAT_TXSHIFT; } else { @@ -130,7 +130,7 @@ static void arch_decomp_wdog_start(void) { __raw_writel(WDOG_COUNT, S3C2410_WTDAT); __raw_writel(WDOG_COUNT, S3C2410_WTCNT); - __raw_writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x40), S3C2410_WTCON); + __raw_writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128 | S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x80), S3C2410_WTCON); } #else diff --git a/include/asm-arm/arch-sa1100/assabet.h b/include/asm-arm/arch-sa1100/assabet.h index 1f59b36..d6a1bb5 100644 --- a/include/asm-arm/arch-sa1100/assabet.h +++ b/include/asm-arm/arch-sa1100/assabet.h @@ -12,7 +12,6 @@ #ifndef __ASM_ARCH_ASSABET_H #define __ASM_ARCH_ASSABET_H -#include /* System Configuration Register flags */ diff --git a/include/asm-arm/arch-sa1100/cerf.h b/include/asm-arm/arch-sa1100/cerf.h index 356d5ba..9a19c3d 100644 --- a/include/asm-arm/arch-sa1100/cerf.h +++ b/include/asm-arm/arch-sa1100/cerf.h @@ -10,7 +10,6 @@ #ifndef _INCLUDE_CERF_H_ #define _INCLUDE_CERF_H_ -#include #define CERF_ETH_IO 0xf0000000 #define CERF_ETH_IRQ IRQ_GPIO26 diff --git a/include/asm-arm/arch-sa1100/collie.h b/include/asm-arm/arch-sa1100/collie.h index d49e5ff..14a344a 100644 --- a/include/asm-arm/arch-sa1100/collie.h +++ b/include/asm-arm/arch-sa1100/collie.h @@ -13,7 +13,6 @@ #ifndef __ASM_ARCH_COLLIE_H #define __ASM_ARCH_COLLIE_H -#include #define COLLIE_SCP_CHARGE_ON SCOOP_GPCR_PA11 #define COLLIE_SCP_DIAG_BOOT1 SCOOP_GPCR_PA12 diff --git a/include/asm-arm/arch-sa1100/dma.h b/include/asm-arm/arch-sa1100/dma.h index 02575d7..6b7917a 100644 --- a/include/asm-arm/arch-sa1100/dma.h +++ b/include/asm-arm/arch-sa1100/dma.h @@ -10,7 +10,6 @@ #ifndef __ASM_ARCH_DMA_H #define __ASM_ARCH_DMA_H -#include #include "hardware.h" diff --git a/include/asm-arm/arch-sa1100/hardware.h b/include/asm-arm/arch-sa1100/hardware.h index ee008a5..1abd7cf 100644 --- a/include/asm-arm/arch-sa1100/hardware.h +++ b/include/asm-arm/arch-sa1100/hardware.h @@ -12,7 +12,6 @@ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H -#include #define UNCACHEABLE_ADDR 0xfa050000 diff --git a/include/asm-arm/arch-sa1100/ide.h b/include/asm-arm/arch-sa1100/ide.h index 2153538..98b10bc 100644 --- a/include/asm-arm/arch-sa1100/ide.h +++ b/include/asm-arm/arch-sa1100/ide.h @@ -9,7 +9,6 @@ * architectures. */ -#include #include #include #include diff --git a/include/asm-arm/arch-sa1100/io.h b/include/asm-arm/arch-sa1100/io.h index 040ccde..0756269 100644 --- a/include/asm-arm/arch-sa1100/io.h +++ b/include/asm-arm/arch-sa1100/io.h @@ -22,6 +22,5 @@ static inline void __iomem *__io(unsigne } #define __io(a) __io(a) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif diff --git a/include/asm-arm/arch-sa1100/irqs.h b/include/asm-arm/arch-sa1100/irqs.h index eabd3be..d794068 100644 --- a/include/asm-arm/arch-sa1100/irqs.h +++ b/include/asm-arm/arch-sa1100/irqs.h @@ -7,7 +7,6 @@ * * 2001/11/14 RMK Cleaned up and standardised a lot of the IRQs. */ -#include #define IRQ_GPIO0 0 #define IRQ_GPIO1 1 diff --git a/include/asm-arm/arch-sa1100/memory.h b/include/asm-arm/arch-sa1100/memory.h index a29fac1..1ff172d 100644 --- a/include/asm-arm/arch-sa1100/memory.h +++ b/include/asm-arm/arch-sa1100/memory.h @@ -7,7 +7,6 @@ #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H -#include #include /* diff --git a/include/asm-arm/arch-sa1100/system.h b/include/asm-arm/arch-sa1100/system.h index 0f0612f..aef91e3 100644 --- a/include/asm-arm/arch-sa1100/system.h +++ b/include/asm-arm/arch-sa1100/system.h @@ -3,7 +3,6 @@ * * Copyright (c) 1999 Nicolas Pitre */ -#include #include static inline void arch_idle(void) diff --git a/include/asm-arm/arch-versatile/io.h b/include/asm-arm/arch-versatile/io.h index 47e904c..c4d0194 100644 --- a/include/asm-arm/arch-versatile/io.h +++ b/include/asm-arm/arch-versatile/io.h @@ -28,6 +28,5 @@ static inline void __iomem *__io(unsigne } #define __io(a) __io(a) #define __mem_pci(a) (a) -#define __mem_isa(a) (a) #endif diff --git a/include/asm-arm/arch-versatile/system.h b/include/asm-arm/arch-versatile/system.h index 8889a18..71c6254 100644 --- a/include/asm-arm/arch-versatile/system.h +++ b/include/asm-arm/arch-versatile/system.h @@ -36,16 +36,14 @@ static inline void arch_idle(void) static inline void arch_reset(char mode) { - unsigned int hdr_ctrl = (IO_ADDRESS(VERSATILE_SYS_BASE) + VERSATILE_SYS_RESETCTL_OFFSET); - unsigned int val; + u32 val; - /* - * To reset, we hit the on-board reset register - * in the system FPGA - */ - val = __raw_readl(hdr_ctrl); - val |= VERSATILE_SYS_CTRL_RESET_CONFIGCLR; - __raw_writel(val, hdr_ctrl); + val = __raw_readl(IO_ADDRESS(VERSATILE_SYS_RESETCTL)) & ~0x7; + val |= 0x105; + + __raw_writel(0xa05f, IO_ADDRESS(VERSATILE_SYS_LOCK)); + __raw_writel(val, IO_ADDRESS(VERSATILE_SYS_RESETCTL)); + __raw_writel(0, IO_ADDRESS(VERSATILE_SYS_LOCK)); } #endif diff --git a/include/asm-arm/assembler.h b/include/asm-arm/assembler.h index d53bafa..fce8328 100644 --- a/include/asm-arm/assembler.h +++ b/include/asm-arm/assembler.h @@ -55,30 +55,6 @@ #else #define PLD(code...) #endif -#define MODE_USR USR_MODE -#define MODE_FIQ FIQ_MODE -#define MODE_IRQ IRQ_MODE -#define MODE_SVC SVC_MODE - -#define DEFAULT_FIQ MODE_FIQ - -/* - * LOADREGS - ldm with PC in register list (eg, ldmfd sp!, {pc}) - */ -#ifdef __STDC__ -#define LOADREGS(cond, base, reglist...)\ - ldm##cond base,reglist -#else -#define LOADREGS(cond, base, reglist...)\ - ldm/**/cond base,reglist -#endif - -/* - * Build a return instruction for this processor type. - */ -#define RETINSTR(instr, regs...)\ - instr regs - /* * Enable and disable interrupts */ @@ -117,18 +93,6 @@ #endif msr cpsr_c, \oldcpsr .endm -/* - * These two are used to save LR/restore PC over a user-based access. - * The old 26-bit architecture requires that we do. On 32-bit - * architecture, we can safely ignore this requirement. - */ - .macro save_lr - .endm - - .macro restore_pc - mov pc, lr - .endm - #define USER(x...) \ 9999: x; \ .section __ex_table,"a"; \ diff --git a/include/asm-arm/atomic.h b/include/asm-arm/atomic.h index 3d7283d..4b0ce3e 100644 --- a/include/asm-arm/atomic.h +++ b/include/asm-arm/atomic.h @@ -11,7 +11,6 @@ #ifndef __ASM_ARM_ATOMIC_H #define __ASM_ARM_ATOMIC_H -#include #include typedef struct { volatile int counter; } atomic_t; diff --git a/include/asm-arm/bug.h b/include/asm-arm/bug.h index 5ab8216..0e36fd5 100644 --- a/include/asm-arm/bug.h +++ b/include/asm-arm/bug.h @@ -1,8 +1,6 @@ #ifndef _ASMARM_BUG_H #define _ASMARM_BUG_H -#include -#include #ifdef CONFIG_BUG #ifdef CONFIG_DEBUG_BUGVERBOSE diff --git a/include/asm-arm/bugs.h b/include/asm-arm/bugs.h index 4c80ec5..ca54eb0 100644 --- a/include/asm-arm/bugs.h +++ b/include/asm-arm/bugs.h @@ -10,8 +10,12 @@ #ifndef __ASM_BUGS_H #define __ASM_BUGS_H +#ifdef CONFIG_MMU extern void check_writebuffer_bugs(void); #define check_bugs() check_writebuffer_bugs() +#else +#define check_bugs() do { } while (0) +#endif #endif diff --git a/include/asm-arm/byteorder.h b/include/asm-arm/byteorder.h index 17eaf8b..e6f7fcd 100644 --- a/include/asm-arm/byteorder.h +++ b/include/asm-arm/byteorder.h @@ -22,16 +22,18 @@ static inline __attribute_const__ __u32 { __u32 t; - if (__builtin_constant_p(x)) { - t = x ^ ((x << 16) | (x >> 16)); /* eor r1,r0,r0,ror #16 */ - } else { +#ifndef __thumb__ + if (!__builtin_constant_p(x)) { /* * The compiler needs a bit of a hint here to always do the * right thing and not screw it up to different degrees * depending on the gcc version. */ asm ("eor\t%0, %1, %1, ror #16" : "=r" (t) : "r" (x)); - } + } else +#endif + t = x ^ ((x << 16) | (x >> 16)); /* eor r1,r0,r0,ror #16 */ + x = (x << 24) | (x >> 8); /* mov r0,r0,ror #8 */ t &= ~0x00FF0000; /* bic r1,r1,#0x00FF0000 */ x ^= (t >> 8); /* eor r0,r0,r1,lsr #8 */ diff --git a/include/asm-arm/cacheflush.h b/include/asm-arm/cacheflush.h index 746be56..fe0c744 100644 --- a/include/asm-arm/cacheflush.h +++ b/include/asm-arm/cacheflush.h @@ -10,7 +10,6 @@ #ifndef _ASMARM_CACHEFLUSH_H #define _ASMARM_CACHEFLUSH_H -#include #include #include diff --git a/include/asm-arm/cpu.h b/include/asm-arm/cpu.h index 751bc74..715426b 100644 --- a/include/asm-arm/cpu.h +++ b/include/asm-arm/cpu.h @@ -10,7 +10,6 @@ #ifndef __ASM_ARM_CPU_H #define __ASM_ARM_CPU_H -#include #include struct cpuinfo_arm { diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h index 63ca741..55eb4dc 100644 --- a/include/asm-arm/dma-mapping.h +++ b/include/asm-arm/dma-mapping.h @@ -3,7 +3,6 @@ #define ASMARM_DMA_MAPPING_H #ifdef __KERNEL__ -#include #include /* need struct page */ #include diff --git a/include/asm-arm/dma.h b/include/asm-arm/dma.h index 49c01e2..9f2c530 100644 --- a/include/asm-arm/dma.h +++ b/include/asm-arm/dma.h @@ -3,7 +3,6 @@ #define __ASM_ARM_DMA_H typedef unsigned int dmach_t; -#include #include #include #include diff --git a/include/asm-arm/domain.h b/include/asm-arm/domain.h index f8ea2de..4c2885a 100644 --- a/include/asm-arm/domain.h +++ b/include/asm-arm/domain.h @@ -50,6 +50,8 @@ #define DOMAIN_MANAGER 3 #define domain_val(dom,type) ((type) << (2*(dom))) #ifndef __ASSEMBLY__ + +#ifdef CONFIG_MMU #define set_domain(x) \ do { \ __asm__ __volatile__( \ @@ -66,5 +68,10 @@ #define modify_domain(dom,type) \ set_domain(thread->cpu_domain); \ } while (0) +#else +#define set_domain(x) do { } while (0) +#define modify_domain(dom,type) do { } while (0) +#endif + #endif #endif /* !__ASSEMBLY__ */ diff --git a/include/asm-arm/dyntick.h b/include/asm-arm/dyntick.h new file mode 100644 index 0000000..19fab2d --- /dev/null +++ b/include/asm-arm/dyntick.h @@ -0,0 +1,6 @@ +#ifndef _ASMARM_DYNTICK_H +#define _ASMARM_DYNTICK_H + +#include + +#endif /* _ASMARM_DYNTICK_H */ diff --git a/include/asm-arm/elf.h b/include/asm-arm/elf.h index 2d44b42..71061ca 100644 --- a/include/asm-arm/elf.h +++ b/include/asm-arm/elf.h @@ -1,7 +1,6 @@ #ifndef __ASMARM_ELF_H #define __ASMARM_ELF_H -#include /* * ELF register definitions.. diff --git a/include/asm-arm/floppy.h b/include/asm-arm/floppy.h index 6ea657c..54b5ae4 100644 --- a/include/asm-arm/floppy.h +++ b/include/asm-arm/floppy.h @@ -25,7 +25,7 @@ #define fd_outb(val,port) \ #define fd_inb(port) inb((port)) #define fd_request_irq() request_irq(IRQ_FLOPPYDISK,floppy_interrupt,\ - SA_INTERRUPT|SA_SAMPLE_RANDOM,"floppy",NULL) + IRQF_DISABLED,"floppy",NULL) #define fd_free_irq() free_irq(IRQ_FLOPPYDISK,NULL) #define fd_disable_irq() disable_irq(IRQ_FLOPPYDISK) #define fd_enable_irq() enable_irq(IRQ_FLOPPYDISK) diff --git a/include/asm-arm/fpstate.h b/include/asm-arm/fpstate.h index 52bae08..6af4e6b 100644 --- a/include/asm-arm/fpstate.h +++ b/include/asm-arm/fpstate.h @@ -11,7 +11,6 @@ #ifndef __ASM_ARM_FPSTATE_H #define __ASM_ARM_FPSTATE_H -#include #ifndef __ASSEMBLY__ @@ -73,6 +72,14 @@ #endif #define FP_SIZE (sizeof(union fp_state) / sizeof(int)) +struct crunch_state { + unsigned int mvdx[16][2]; + unsigned int mvax[4][3]; + unsigned int dspsc[2]; +}; + +#define CRUNCH_SIZE sizeof(struct crunch_state) + #endif #endif diff --git a/include/asm-arm/glue.h b/include/asm-arm/glue.h index 223e0d6..0cc5d3b 100644 --- a/include/asm-arm/glue.h +++ b/include/asm-arm/glue.h @@ -15,7 +15,6 @@ */ #ifdef __KERNEL__ -#include #ifdef __STDC__ #define ____glue(name,fn) name##fn diff --git a/include/asm-arm/hardirq.h b/include/asm-arm/hardirq.h index 1cbb173..182310b 100644 --- a/include/asm-arm/hardirq.h +++ b/include/asm-arm/hardirq.h @@ -1,7 +1,6 @@ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include #include diff --git a/include/asm-arm/hardware/dec21285.h b/include/asm-arm/hardware/dec21285.h index 6685e3f..546f707 100644 --- a/include/asm-arm/hardware/dec21285.h +++ b/include/asm-arm/hardware/dec21285.h @@ -18,7 +18,6 @@ #define DC21285_FLASH 0x41000000 #define DC21285_PCI_IO 0x7c000000 #define DC21285_PCI_MEM 0x80000000 -#include #ifndef __ASSEMBLY__ #include #define DC21285_IO(x) ((volatile unsigned long *)(ARMCSR_BASE+(x))) diff --git a/include/asm-arm/hardware/iomd.h b/include/asm-arm/hardware/iomd.h index 82fa2c2..396e55a 100644 --- a/include/asm-arm/hardware/iomd.h +++ b/include/asm-arm/hardware/iomd.h @@ -13,7 +13,6 @@ #ifndef __ASMARM_HARDWARE_IOMD_H #define __ASMARM_HARDWARE_IOMD_H -#include #ifndef __ASSEMBLY__ diff --git a/include/asm-arm/hardware/locomo.h b/include/asm-arm/hardware/locomo.h index 5f10048..22dfb17 100644 --- a/include/asm-arm/hardware/locomo.h +++ b/include/asm-arm/hardware/locomo.h @@ -111,6 +111,8 @@ #define LOCOMO_FRONTLIGHT 0xc8 #define LOCOMO_ALS 0x00 /* Adjust light cycle */ #define LOCOMO_ALD 0x04 /* Adjust light duty */ +#define LOCOMO_ALC_EN 0x8000 + /* Backlight controller: TFT signal */ #define LOCOMO_BACKLIGHT 0x38 #define LOCOMO_TC 0x00 /* TFT control signal */ @@ -203,4 +205,7 @@ void locomo_gpio_write(struct locomo_dev /* M62332 control function */ void locomo_m62332_senddata(struct locomo_dev *ldev, unsigned int dac_data, int channel); +/* Frontlight control */ +void locomo_frontlight_set(struct locomo_dev *dev, int duty, int vr, int bpwf); + #endif diff --git a/include/asm-arm/hardware/sharpsl_pm.h b/include/asm-arm/hardware/sharpsl_pm.h index 36983e5..ecf15b8 100644 --- a/include/asm-arm/hardware/sharpsl_pm.h +++ b/include/asm-arm/hardware/sharpsl_pm.h @@ -16,6 +16,7 @@ struct sharpsl_charger_machinfo { void (*exit)(void); int gpio_acin; int gpio_batfull; + int batfull_irq; int gpio_batlock; int gpio_fatal; void (*discharge)(int); @@ -34,9 +35,19 @@ #define SHARPSL_STATUS_CHRGFULL 6 #define SHARPSL_STATUS_FATAL 7 unsigned long (*charger_wakeup)(void); int (*should_wakeup)(unsigned int resume_on_alarm); + void (*backlight_limit)(int); + int (*backlight_get_status) (void); + int charge_on_volt; + int charge_on_temp; + int charge_acin_high; + int charge_acin_low; + int fatal_acin_volt; + int fatal_noacin_volt; int bat_levels; struct battery_thresh *bat_levels_noac; struct battery_thresh *bat_levels_acin; + struct battery_thresh *bat_levels_noac_bl; + struct battery_thresh *bat_levels_acin_bl; int status_high_acin; int status_low_acin; int status_high_noac; diff --git a/include/asm-arm/hw_irq.h b/include/asm-arm/hw_irq.h new file mode 100644 index 0000000..ea85697 --- /dev/null +++ b/include/asm-arm/hw_irq.h @@ -0,0 +1,20 @@ +/* + * Nothing to see here yet + */ +#ifndef _ARCH_ARM_HW_IRQ_H +#define _ARCH_ARM_HW_IRQ_H + +#include + +#if defined(CONFIG_NO_IDLE_HZ) +# include +# define handle_dynamic_tick(action) \ + if (!(action->flags & IRQF_TIMER) && system_timer->dyn_tick) { \ + write_seqlock(&xtime_lock); \ + if (system_timer->dyn_tick->state & DYN_TICK_ENABLED) \ + system_timer->dyn_tick->handler(irq, 0, regs); \ + write_sequnlock(&xtime_lock); \ + } +#endif + +#endif diff --git a/include/asm-arm/irq.h b/include/asm-arm/irq.h index 60b5105..283af50 100644 --- a/include/asm-arm/irq.h +++ b/include/asm-arm/irq.h @@ -21,18 +21,13 @@ #endif struct irqaction; -extern void disable_irq_nosync(unsigned int); -extern void disable_irq(unsigned int); -extern void enable_irq(unsigned int); - /* - * These correspond with the SA_TRIGGER_* defines, and therefore the - * IORESOURCE_IRQ_* defines. + * Migration helpers */ -#define __IRQT_RISEDGE (1 << 0) -#define __IRQT_FALEDGE (1 << 1) -#define __IRQT_HIGHLVL (1 << 2) -#define __IRQT_LOWLVL (1 << 3) +#define __IRQT_FALEDGE IRQ_TYPE_EDGE_FALLING +#define __IRQT_RISEDGE IRQ_TYPE_EDGE_RISING +#define __IRQT_LOWLVL IRQ_TYPE_LEVEL_LOW +#define __IRQT_HIGHLVL IRQ_TYPE_LEVEL_HIGH #define IRQT_NOEDGE (0) #define IRQT_RISING (__IRQT_RISEDGE) @@ -40,16 +35,7 @@ #define IRQT_FALLING (__IRQT_FALEDGE) #define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE) #define IRQT_LOW (__IRQT_LOWLVL) #define IRQT_HIGH (__IRQT_HIGHLVL) -#define IRQT_PROBE (1 << 4) - -int set_irq_type(unsigned int irq, unsigned int type); -void disable_irq_wake(unsigned int irq); -void enable_irq_wake(unsigned int irq); -int setup_irq(unsigned int, struct irqaction *); - -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); +#define IRQT_PROBE IRQ_TYPE_PROBE extern void migrate_irqs(void); #endif diff --git a/include/asm-arm/leds.h b/include/asm-arm/leds.h index 88ce412..12290ea 100644 --- a/include/asm-arm/leds.h +++ b/include/asm-arm/leds.h @@ -13,7 +13,6 @@ #ifndef ASM_ARM_LEDS_H #define ASM_ARM_LEDS_H -#include typedef enum { led_idle_start, diff --git a/include/asm-arm/mach/irq.h b/include/asm-arm/mach/irq.h index d4d420e..131f337 100644 --- a/include/asm-arm/mach/irq.h +++ b/include/asm-arm/mach/irq.h @@ -10,95 +10,9 @@ #ifndef __ASM_ARM_MACH_IRQ_H #define __ASM_ARM_MACH_IRQ_H -struct irqdesc; -struct pt_regs; -struct seq_file; - -typedef void (*irq_handler_t)(unsigned int, struct irqdesc *, struct pt_regs *); -typedef void (*irq_control_t)(unsigned int); - -struct irqchip { - /* - * Acknowledge the IRQ. - * If this is a level-based IRQ, then it is expected to mask the IRQ - * as well. - */ - void (*ack)(unsigned int); - /* - * Mask the IRQ in hardware. - */ - void (*mask)(unsigned int); - /* - * Unmask the IRQ in hardware. - */ - void (*unmask)(unsigned int); - /* - * Ask the hardware to re-trigger the IRQ. - * Note: This method _must_ _not_ call the interrupt handler. - * If you are unable to retrigger the interrupt, do not - * provide a function, or if you do, return non-zero. - */ - int (*retrigger)(unsigned int); - /* - * Set the type of the IRQ. - */ - int (*set_type)(unsigned int, unsigned int); - /* - * Set wakeup-enable on the selected IRQ - */ - int (*set_wake)(unsigned int, unsigned int); - -#ifdef CONFIG_SMP - /* - * Route an interrupt to a CPU - */ - void (*set_cpu)(struct irqdesc *desc, unsigned int irq, unsigned int cpu); -#endif -}; - -struct irqdesc { - irq_handler_t handle; - struct irqchip *chip; - struct irqaction *action; - struct list_head pend; - void __iomem *base; - void *data; - unsigned int disable_depth; - - unsigned int triggered: 1; /* IRQ has occurred */ - unsigned int running : 1; /* IRQ is running */ - unsigned int pending : 1; /* IRQ is pending */ - unsigned int probing : 1; /* IRQ in use for a probe */ - unsigned int probe_ok : 1; /* IRQ can be used for probe */ - unsigned int valid : 1; /* IRQ claimable */ - unsigned int noautoenable : 1; /* don't automatically enable IRQ */ - unsigned int unused :25; - - unsigned int irqs_unhandled; - struct proc_dir_entry *procdir; - -#ifdef CONFIG_SMP - cpumask_t affinity; - unsigned int cpu; -#endif - - /* - * IRQ lock detection - */ - unsigned int lck_cnt; - unsigned int lck_pc; - unsigned int lck_jif; -}; - -extern struct irqdesc irq_desc[]; +#include -/* - * Helpful inline function for calling irq descriptor handlers. - */ -static inline void desc_handle_irq(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) -{ - desc->handle(irq, desc, regs); -} +struct seq_file; /* * This is internal. Do not use it. @@ -106,18 +20,22 @@ static inline void desc_handle_irq(unsig extern void (*init_arch_irq)(void); extern void init_FIQ(void); extern int show_fiq_list(struct seq_file *, void *); -void __set_irq_handler(unsigned int irq, irq_handler_t, int); /* - * External stuff. + * Function wrappers + */ +#define set_irq_chipdata(irq, d) set_irq_chip_data(irq, d) +#define get_irq_chipdata(irq) get_irq_chip_data(irq) + +/* + * Obsolete inline function for calling irq descriptor handlers. */ -#define set_irq_handler(irq,handler) __set_irq_handler(irq,handler,0) -#define set_irq_chained_handler(irq,handler) __set_irq_handler(irq,handler,1) -#define set_irq_data(irq,d) do { irq_desc[irq].data = d; } while (0) -#define set_irq_chipdata(irq,d) do { irq_desc[irq].base = d; } while (0) -#define get_irq_chipdata(irq) (irq_desc[irq].base) +static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc, + struct pt_regs *regs) +{ + desc->handle_irq(irq, desc, regs); +} -void set_irq_chip(unsigned int irq, struct irqchip *); void set_irq_flags(unsigned int irq, unsigned int flags); #define IRQF_VALID (1 << 0) @@ -125,12 +43,25 @@ #define IRQF_PROBE (1 << 1) #define IRQF_NOAUTOEN (1 << 2) /* - * Built-in IRQ handlers. + * This is for easy migration, but should be changed in the source */ -void do_level_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); -void do_edge_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); -void do_simple_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); -void do_bad_IRQ(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs); -void dummy_mask_unmask_irq(unsigned int irq); +#define do_level_IRQ handle_level_irq +#define do_edge_IRQ handle_edge_irq +#define do_simple_IRQ handle_simple_irq +#define irqdesc irq_desc +#define irqchip irq_chip + +#define do_bad_IRQ(irq,desc,regs) \ +do { \ + spin_lock(&desc->lock); \ + handle_bad_irq(irq, desc, regs); \ + spin_unlock(&desc->lock); \ +} while(0) + +extern unsigned long irq_err_count; +static inline void ack_bad_irq(int irq) +{ + irq_err_count++; +} #endif diff --git a/include/asm-arm/mach/map.h b/include/asm-arm/mach/map.h index e8ea67c..cef5364 100644 --- a/include/asm-arm/mach/map.h +++ b/include/asm-arm/mach/map.h @@ -16,8 +16,6 @@ struct map_desc { unsigned int type; }; -struct meminfo; - #define MT_DEVICE 0 #define MT_CACHECLEAN 1 #define MT_MINICLEAN 2 @@ -28,7 +26,8 @@ #define MT_ROM 6 #define MT_IXP2000_DEVICE 7 #define MT_NONSHARED_DEVICE 8 -extern void create_memmap_holes(struct meminfo *); -extern void memtable_init(struct meminfo *); +#ifdef CONFIG_MMU extern void iotable_init(struct map_desc *, int); -extern void setup_io_desc(void); +#else +#define iotable_init(map,num) do { } while (0) +#endif diff --git a/include/asm-arm/mach/pci.h b/include/asm-arm/mach/pci.h index 25d540e..923e0ca 100644 --- a/include/asm-arm/mach/pci.h +++ b/include/asm-arm/mach/pci.h @@ -28,7 +28,7 @@ struct hw_pci { struct pci_sys_data { struct list_head node; int busnr; /* primary bus number */ - unsigned long mem_offset; /* bus->cpu memory mapping offset */ + u64 mem_offset; /* bus->cpu memory mapping offset */ unsigned long io_offset; /* bus->cpu IO mapping offset */ struct pci_bus *bus; /* PCI bus */ struct resource *resource[3]; /* Primary PCI bus resources */ diff --git a/include/asm-arm/mach/serial_at91.h b/include/asm-arm/mach/serial_at91.h new file mode 100644 index 0000000..1290bb3 --- /dev/null +++ b/include/asm-arm/mach/serial_at91.h @@ -0,0 +1,33 @@ +/* + * linux/include/asm-arm/mach/serial_at91.h + * + * Based on serial_sa1100.h by Nicolas Pitre + * + * Copyright (C) 2002 ATMEL Rousset + * + * Low level machine dependent UART functions. + */ + +struct uart_port; + +/* + * This is a temporary structure for registering these + * functions; it is intended to be discarded after boot. + */ +struct at91_port_fns { + void (*set_mctrl)(struct uart_port *, u_int); + u_int (*get_mctrl)(struct uart_port *); + void (*enable_ms)(struct uart_port *); + void (*pm)(struct uart_port *, u_int, u_int); + int (*set_wake)(struct uart_port *, u_int); + int (*open)(struct uart_port *); + void (*close)(struct uart_port *); +}; + +#if defined(CONFIG_SERIAL_AT91) +void at91_register_uart_fns(struct at91_port_fns *fns); +#else +#define at91_register_uart_fns(fns) do { } while (0) +#endif + + diff --git a/include/asm-arm/mach/serial_at91rm9200.h b/include/asm-arm/mach/serial_at91rm9200.h deleted file mode 100644 index 98f4b0c..0000000 --- a/include/asm-arm/mach/serial_at91rm9200.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * linux/include/asm-arm/mach/serial_at91rm9200.h - * - * Based on serial_sa1100.h by Nicolas Pitre - * - * Copyright (C) 2002 ATMEL Rousset - * - * Low level machine dependent UART functions. - */ -#include - -struct uart_port; - -/* - * This is a temporary structure for registering these - * functions; it is intended to be discarded after boot. - */ -struct at91rm9200_port_fns { - void (*set_mctrl)(struct uart_port *, u_int); - u_int (*get_mctrl)(struct uart_port *); - void (*enable_ms)(struct uart_port *); - void (*pm)(struct uart_port *, u_int, u_int); - int (*set_wake)(struct uart_port *, u_int); - int (*open)(struct uart_port *); - void (*close)(struct uart_port *); -}; - -#if defined(CONFIG_SERIAL_AT91) -void at91_register_uart_fns(struct at91rm9200_port_fns *fns); -void at91_register_uart(int idx, int port); -#else -#define at91_register_uart_fns(fns) do { } while (0) -#define at91_register_uart(idx,port) do { } while (0) -#endif - - diff --git a/include/asm-arm/mach/serial_sa1100.h b/include/asm-arm/mach/serial_sa1100.h index 9162018..20c22bb 100644 --- a/include/asm-arm/mach/serial_sa1100.h +++ b/include/asm-arm/mach/serial_sa1100.h @@ -7,7 +7,6 @@ * * Low level machine dependent UART functions. */ -#include struct uart_port; struct uart_info; diff --git a/include/asm-arm/mach/time.h b/include/asm-arm/mach/time.h index 96c6db7..dee0bc3 100644 --- a/include/asm-arm/mach/time.h +++ b/include/asm-arm/mach/time.h @@ -50,6 +50,7 @@ #ifdef CONFIG_NO_IDLE_HZ #define DYN_TICK_ENABLED (1 << 1) struct dyn_tick_timer { + spinlock_t lock; unsigned int state; /* Current state */ int (*enable)(void); /* Enables dynamic tick */ int (*disable)(void); /* Disables dynamic tick */ @@ -68,6 +69,7 @@ extern void timer_tick(struct pt_regs *) /* * Kernel time keeping support. */ +struct timespec; extern int (*set_rtc)(void); extern void save_time_delta(struct timespec *delta, struct timespec *rtc); extern void restore_time_delta(struct timespec *delta, struct timespec *rtc); diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h index 2092894..91d536c 100644 --- a/include/asm-arm/memory.h +++ b/include/asm-arm/memory.h @@ -2,6 +2,7 @@ * linux/include/asm-arm/memory.h * * Copyright (C) 2000-2002 Russell King + * modification for nommu, Hyok S. Choi, 2004 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,11 +23,12 @@ #else #define UL(x) (x) #endif -#include #include #include #include +#ifdef CONFIG_MMU + #ifndef TASK_SIZE /* * TASK_SIZE - the maximum size of a user space task. @@ -49,6 +51,65 @@ #define PAGE_OFFSET UL(0xc0000000) #endif /* + * The module space lives between the addresses given by TASK_SIZE + * and PAGE_OFFSET - it must be within 32MB of the kernel text. + */ +#define MODULE_END (PAGE_OFFSET) +#define MODULE_START (MODULE_END - 16*1048576) + +#if TASK_SIZE > MODULE_START +#error Top of user space clashes with start of module space +#endif + +/* + * The XIP kernel gets mapped at the bottom of the module vm area. + * Since we use sections to map it, this macro replaces the physical address + * with its virtual address while keeping offset from the base section. + */ +#define XIP_VIRT_ADDR(physaddr) (MODULE_START + ((physaddr) & 0x000fffff)) + +/* + * Allow 16MB-aligned ioremap pages + */ +#define IOREMAP_MAX_ORDER 24 + +#else /* CONFIG_MMU */ + +/* + * The limitation of user task size can grow up to the end of free ram region. + * It is difficult to define and perhaps will never meet the original meaning + * of this define that was meant to. + * Fortunately, there is no reference for this in noMMU mode, for now. + */ +#ifndef TASK_SIZE +#define TASK_SIZE (CONFIG_DRAM_SIZE) +#endif + +#ifndef TASK_UNMAPPED_BASE +#define TASK_UNMAPPED_BASE UL(0x00000000) +#endif + +#ifndef PHYS_OFFSET +#define PHYS_OFFSET (CONFIG_DRAM_BASE) +#endif + +#ifndef END_MEM +#define END_MEM (CONFIG_DRAM_BASE + CONFIG_DRAM_SIZE) +#endif + +#ifndef PAGE_OFFSET +#define PAGE_OFFSET (PHYS_OFFSET) +#endif + +/* + * The module can be at any place in ram in nommu mode. + */ +#define MODULE_END (END_MEM) +#define MODULE_START (PHYS_OFFSET) + +#endif /* !CONFIG_MMU */ + +/* * Size of DMA-consistent memory region. Must be multiple of 2M, * between 2MB and 14MB inclusive. */ @@ -72,24 +133,6 @@ #endif #define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT) #define __pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT) -/* - * The module space lives between the addresses given by TASK_SIZE - * and PAGE_OFFSET - it must be within 32MB of the kernel text. - */ -#define MODULE_END (PAGE_OFFSET) -#define MODULE_START (MODULE_END - 16*1048576) - -#if TASK_SIZE > MODULE_START -#error Top of user space clashes with start of module space -#endif - -/* - * The XIP kernel gets mapped at the bottom of the module vm area. - * Since we use sections to map it, this macro replaces the physical address - * with its virtual address while keeping offset from the base section. - */ -#define XIP_VIRT_ADDR(physaddr) (MODULE_START + ((physaddr) & 0x000fffff)) - #ifndef __ASSEMBLY__ /* diff --git a/include/asm-arm/mmu.h b/include/asm-arm/mmu.h index a457cb7..fe2a23b 100644 --- a/include/asm-arm/mmu.h +++ b/include/asm-arm/mmu.h @@ -1,10 +1,13 @@ #ifndef __ARM_MMU_H #define __ARM_MMU_H +#ifdef CONFIG_MMU + typedef struct { #if __LINUX_ARM_ARCH__ >= 6 unsigned int id; #endif + unsigned int kvm_seq; } mm_context_t; #if __LINUX_ARM_ARCH__ >= 6 @@ -13,4 +16,18 @@ #else #define ASID(mm) (0) #endif +#else + +/* + * From nommu.h: + * Copyright (C) 2002, David McCullough + * modified for 2.6 by Hyok S. Choi + */ +typedef struct { + struct vm_list_struct *vmlist; + unsigned long end_brk; +} mm_context_t; + +#endif + #endif diff --git a/include/asm-arm/mmu_context.h b/include/asm-arm/mmu_context.h index 81c59fa..d1a65b1 100644 --- a/include/asm-arm/mmu_context.h +++ b/include/asm-arm/mmu_context.h @@ -17,6 +17,8 @@ #include #include #include +void __check_kvm_seq(struct mm_struct *mm); + #if __LINUX_ARM_ARCH__ >= 6 /* @@ -45,13 +47,21 @@ static inline void check_context(struct { if (unlikely((mm->context.id ^ cpu_last_asid) >> ASID_BITS)) __new_context(mm); + + if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) + __check_kvm_seq(mm); } #define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0) #else -#define check_context(mm) do { } while (0) +static inline void check_context(struct mm_struct *mm) +{ + if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq)) + __check_kvm_seq(mm); +} + #define init_new_context(tsk,mm) 0 #endif @@ -82,6 +92,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) { +#ifdef CONFIG_MMU unsigned int cpu = smp_processor_id(); if (prev != next) { @@ -91,6 +102,7 @@ switch_mm(struct mm_struct *prev, struct if (cache_is_vivt()) cpu_clear(cpu, prev->cpu_vm_mask); } +#endif } #define deactivate_mm(tsk,mm) do { } while (0) diff --git a/include/asm-arm/page-nommu.h b/include/asm-arm/page-nommu.h new file mode 100644 index 0000000..a1bcad0 --- /dev/null +++ b/include/asm-arm/page-nommu.h @@ -0,0 +1,51 @@ +/* + * linux/include/asm-arm/page-nommu.h + * + * Copyright (C) 2004 Hyok S. Choi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ASMARM_PAGE_NOMMU_H +#define _ASMARM_PAGE_NOMMU_H + +#if !defined(CONFIG_SMALL_TASKS) && PAGE_SHIFT < 13 +#define KTHREAD_SIZE (8192) +#else +#define KTHREAD_SIZE PAGE_SIZE +#endif + +#define get_user_page(vaddr) __get_free_page(GFP_KERNEL) +#define free_user_page(page, addr) free_page(addr) + +#define clear_page(page) memset((page), 0, PAGE_SIZE) +#define copy_page(to,from) memcpy((to), (from), PAGE_SIZE) + +#define clear_user_page(page, vaddr, pg) clear_page(page) +#define copy_user_page(to, from, vaddr, pg) copy_page(to, from) + +/* + * These are used to make use of C type-checking.. + */ +typedef unsigned long pte_t; +typedef unsigned long pmd_t; +typedef unsigned long pgd_t[2]; +typedef unsigned long pgprot_t; + +#define pte_val(x) (x) +#define pmd_val(x) (x) +#define pgd_val(x) ((x)[0]) +#define pgprot_val(x) (x) + +#define __pte(x) (x) +#define __pmd(x) (x) +#define __pgprot(x) (x) + +/* to align the pointer to the (next) page boundary */ +#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK) + +extern unsigned long memory_start; +extern unsigned long memory_end; + +#endif diff --git a/include/asm-arm/page.h b/include/asm-arm/page.h index a404d2b..63d12f0 100644 --- a/include/asm-arm/page.h +++ b/include/asm-arm/page.h @@ -10,7 +10,6 @@ #ifndef _ASMARM_PAGE_H #define _ASMARM_PAGE_H -#include /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 @@ -24,6 +23,12 @@ #define PAGE_ALIGN(addr) (((addr)+PAGE_S #ifndef __ASSEMBLY__ +#ifndef CONFIG_MMU + +#include "page-nommu.h" + +#else + #include /* @@ -172,6 +177,8 @@ #endif /* STRICT_MM_TYPECHECKS */ /* the upper-most page table pointer */ extern pmd_t *top_pmd; +#endif /* CONFIG_MMU */ + #include #endif /* !__ASSEMBLY__ */ diff --git a/include/asm-arm/pci.h b/include/asm-arm/pci.h index ead3ced..f21abd4 100644 --- a/include/asm-arm/pci.h +++ b/include/asm-arm/pci.h @@ -2,7 +2,6 @@ #ifndef ASMARM_PCI_H #define ASMARM_PCI_H #ifdef __KERNEL__ -#include #include #include /* for PCIBIOS_MIN_* */ diff --git a/include/asm-arm/pgalloc.h b/include/asm-arm/pgalloc.h index c4ac2e6..4d43945 100644 --- a/include/asm-arm/pgalloc.h +++ b/include/asm-arm/pgalloc.h @@ -16,6 +16,10 @@ #include #include #include +#define check_pgt_cache() do { } while (0) + +#ifdef CONFIG_MMU + #define _PAGE_USER_TABLE (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER)) #define _PAGE_KERNEL_TABLE (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_KERNEL)) @@ -32,8 +36,6 @@ extern void free_pgd_slow(pgd_t *pgd); #define pgd_alloc(mm) get_pgd_slow(mm) #define pgd_free(pgd) free_pgd_slow(pgd) -#define check_pgt_cache() do { } while (0) - /* * Allocate one PTE table. * @@ -126,4 +128,6 @@ pmd_populate(struct mm_struct *mm, pmd_t __pmd_populate(pmdp, page_to_pfn(ptep) << PAGE_SHIFT | _PAGE_USER_TABLE); } +#endif /* CONFIG_MMU */ + #endif diff --git a/include/asm-arm/pgtable-hwdef.h b/include/asm-arm/pgtable-hwdef.h index 1bc1f99..f3b5120 100644 --- a/include/asm-arm/pgtable-hwdef.h +++ b/include/asm-arm/pgtable-hwdef.h @@ -28,6 +28,7 @@ #define PMD_PROTECTION (1 << 9) /* v5 * */ #define PMD_SECT_BUFFERABLE (1 << 2) #define PMD_SECT_CACHEABLE (1 << 3) +#define PMD_SECT_XN (1 << 4) /* v6 */ #define PMD_SECT_AP_WRITE (1 << 10) #define PMD_SECT_AP_READ (1 << 11) #define PMD_SECT_TEX(x) ((x) << 12) /* v5 */ diff --git a/include/asm-arm/pgtable-nommu.h b/include/asm-arm/pgtable-nommu.h new file mode 100644 index 0000000..b13322d --- /dev/null +++ b/include/asm-arm/pgtable-nommu.h @@ -0,0 +1,123 @@ +/* + * linux/include/asm-arm/pgtable-nommu.h + * + * Copyright (C) 1995-2002 Russell King + * Copyright (C) 2004 Hyok S. Choi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ASMARM_PGTABLE_NOMMU_H +#define _ASMARM_PGTABLE_NOMMU_H + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include +#include + +/* + * Trivial page table functions. + */ +#define pgd_present(pgd) (1) +#define pgd_none(pgd) (0) +#define pgd_bad(pgd) (0) +#define pgd_clear(pgdp) +#define kern_addr_valid(addr) (1) +#define pmd_offset(a, b) ((void *)0) +/* FIXME */ +/* + * PMD_SHIFT determines the size of the area a second-level page table can map + * PGDIR_SHIFT determines what a third-level page table entry can map + */ +#define PGDIR_SHIFT 21 + +#define PGDIR_SIZE (1UL << PGDIR_SHIFT) +#define PGDIR_MASK (~(PGDIR_SIZE-1)) +/* FIXME */ + +#define PAGE_NONE __pgprot(0) +#define PAGE_SHARED __pgprot(0) +#define PAGE_COPY __pgprot(0) +#define PAGE_READONLY __pgprot(0) +#define PAGE_KERNEL __pgprot(0) + +//extern void paging_init(struct meminfo *, struct machine_desc *); +#define swapper_pg_dir ((pgd_t *) 0) + +#define __swp_type(x) (0) +#define __swp_offset(x) (0) +#define __swp_entry(typ,off) ((swp_entry_t) { ((typ) | ((off) << 7)) }) +#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) +#define __swp_entry_to_pte(x) ((pte_t) { (x).val }) + + +typedef pte_t *pte_addr_t; + +static inline int pte_file(pte_t pte) { return 0; } + +/* + * ZERO_PAGE is a global shared page that is always zero: used + * for zero-mapped memory areas etc.. + */ +#define ZERO_PAGE(vaddr) (virt_to_page(0)) + +/* + * Mark the prot value as uncacheable and unbufferable. + */ +#define pgprot_noncached(prot) __pgprot(0) +#define pgprot_writecombine(prot) __pgprot(0) + + +/* + * These would be in other places but having them here reduces the diffs. + */ +extern unsigned int kobjsize(const void *objp); +extern int is_in_rom(unsigned long); + +/* + * No page table caches to initialise. + */ +#define pgtable_cache_init() do { } while (0) +#define io_remap_page_range remap_page_range +#define io_remap_pfn_range remap_pfn_range + +#define MK_IOSPACE_PFN(space, pfn) (pfn) +#define GET_IOSPACE(pfn) 0 +#define GET_PFN(pfn) (pfn) + + +/* + * All 32bit addresses are effectively valid for vmalloc... + * Sort of meaningless for non-VM targets. + */ +#define VMALLOC_START 0 +#define VMALLOC_END 0xffffffff + +#define FIRST_USER_ADDRESS (0) + +#else + +/* + * dummy tlb and user structures. + */ +#define v3_tlb_fns (0) +#define v4_tlb_fns (0) +#define v4wb_tlb_fns (0) +#define v4wbi_tlb_fns (0) +#define v6_tlb_fns (0) + +#define v3_user_fns (0) +#define v4_user_fns (0) +#define v4_mc_user_fns (0) +#define v4wb_user_fns (0) +#define v4wt_user_fns (0) +#define v6_user_fns (0) +#define xscale_mc_user_fns (0) + +#endif /*__ASSEMBLY__*/ + +#endif /* _ASMARM_PGTABLE_H */ diff --git a/include/asm-arm/pgtable.h b/include/asm-arm/pgtable.h index e85c08d..8d3919c 100644 --- a/include/asm-arm/pgtable.h +++ b/include/asm-arm/pgtable.h @@ -11,9 +11,15 @@ #ifndef _ASMARM_PGTABLE_H #define _ASMARM_PGTABLE_H #include +#include + +#ifndef CONFIG_MMU + +#include "pgtable-nommu.h" + +#else #include -#include #include /* @@ -378,4 +384,6 @@ #define pgtable_cache_init() do { } whil #endif /* !__ASSEMBLY__ */ +#endif /* CONFIG_MMU */ + #endif /* _ASMARM_PGTABLE_H */ diff --git a/include/asm-arm/proc-fns.h b/include/asm-arm/proc-fns.h index 106045e..1bde92c 100644 --- a/include/asm-arm/proc-fns.h +++ b/include/asm-arm/proc-fns.h @@ -13,7 +13,6 @@ #define __ASM_PROCFNS_H #ifdef __KERNEL__ -#include /* * Work out if we need multiple CPU support @@ -166,6 +165,8 @@ #endif #include +#ifdef CONFIG_MMU + #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm) #define cpu_get_pgd() \ @@ -177,6 +178,8 @@ #define cpu_get_pgd() \ (pgd_t *)phys_to_virt(pg); \ }) +#endif + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* __ASM_PROCFNS_H */ diff --git a/include/asm-arm/procinfo.h b/include/asm-arm/procinfo.h index 8425260..edb7b65 100644 --- a/include/asm-arm/procinfo.h +++ b/include/asm-arm/procinfo.h @@ -29,7 +29,8 @@ struct processor; struct proc_info_list { unsigned int cpu_val; unsigned int cpu_mask; - unsigned long __cpu_mmu_flags; /* used by head.S */ + unsigned long __cpu_mm_mmu_flags; /* used by head.S */ + unsigned long __cpu_io_mmu_flags; /* used by head.S */ unsigned long __cpu_flush; /* used by head.S */ const char *arch_name; const char *elf_name; diff --git a/include/asm-arm/ptrace.h b/include/asm-arm/ptrace.h index 77adb7f..5a8ef78 100644 --- a/include/asm-arm/ptrace.h +++ b/include/asm-arm/ptrace.h @@ -10,7 +10,6 @@ #ifndef __ASM_ARM_PTRACE_H #define __ASM_ARM_PTRACE_H -#include #define PTRACE_GETREGS 12 #define PTRACE_SETREGS 13 @@ -26,6 +25,11 @@ #define PTRACE_GET_THREAD_AREA 22 #define PTRACE_SET_SYSCALL 23 +/* PTRACE_SYSCALL is 24 */ + +#define PTRACE_GETCRUNCHREGS 25 +#define PTRACE_SETCRUNCHREGS 26 + /* * PSR bits */ diff --git a/include/asm-arm/signal.h b/include/asm-arm/signal.h index ced6916..d0fb487 100644 --- a/include/asm-arm/signal.h +++ b/include/asm-arm/signal.h @@ -82,7 +82,6 @@ #define SIGSWI 32 * is running in 26-bit. * SA_ONSTACK allows alternate signal stacks (see sigaltstack(2)). * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_NODEFER prevents the current signal from being masked in the handler. * SA_RESETHAND clears the handler when the signal is delivered. * @@ -101,7 +100,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ /* @@ -113,10 +111,6 @@ #define SS_DISABLE 2 #define MINSIGSTKSZ 2048 #define SIGSTKSZ 8192 -#ifdef __KERNEL__ -#define SA_TIMER 0x40000000 -#endif - #include #ifdef __KERNEL__ diff --git a/include/asm-arm/smp.h b/include/asm-arm/smp.h index fe45f7f..f67acce 100644 --- a/include/asm-arm/smp.h +++ b/include/asm-arm/smp.h @@ -10,7 +10,6 @@ #ifndef __ASM_ARM_SMP_H #define __ASM_ARM_SMP_H -#include #include #include #include diff --git a/include/asm-arm/socket.h b/include/asm-arm/socket.h index 3c51da6..19f7df7 100644 --- a/include/asm-arm/socket.h +++ b/include/asm-arm/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-arm/system.h b/include/asm-arm/system.h index 7c9568d..6001feb 100644 --- a/include/asm-arm/system.h +++ b/include/asm-arm/system.h @@ -3,7 +3,6 @@ #define __ASM_ARM_SYSTEM_H #ifdef __KERNEL__ -#include #define CPU_ARCH_UNKNOWN 0 #define CPU_ARCH_ARMv3 1 @@ -108,6 +107,9 @@ extern void __show_regs(struct pt_regs * extern int cpu_architecture(void); extern void cpu_init(void); +void arm_machine_restart(char mode); +extern void (*arm_pm_restart)(char str); + /* * Intel's XScale3 core supports some v6 features (supersections, L2) * but advertises itself as v5 as it does not support the v6 ISA. For diff --git a/include/asm-arm/thread_info.h b/include/asm-arm/thread_info.h index cfbccb6..f28b236 100644 --- a/include/asm-arm/thread_info.h +++ b/include/asm-arm/thread_info.h @@ -59,6 +59,7 @@ struct thread_info { struct cpu_context_save cpu_context; /* cpu context */ __u8 used_cp[16]; /* thread used copro */ unsigned long tp_value; + struct crunch_state crunchstate; union fp_state fpstate __attribute__((aligned(8))); union vfp_state vfpstate; struct restart_block restart_block; @@ -101,16 +102,22 @@ #define thread_saved_pc(tsk) \ #define thread_saved_fp(tsk) \ ((unsigned long)(task_thread_info(tsk)->cpu_context.fp)) +extern void crunch_task_disable(struct thread_info *); +extern void crunch_task_copy(struct thread_info *, void *); +extern void crunch_task_restore(struct thread_info *, void *); +extern void crunch_task_release(struct thread_info *); + extern void iwmmxt_task_disable(struct thread_info *); extern void iwmmxt_task_copy(struct thread_info *, void *); extern void iwmmxt_task_restore(struct thread_info *, void *); extern void iwmmxt_task_release(struct thread_info *); +extern void iwmmxt_task_switch(struct thread_info *); #endif /* * We use bit 30 of the preempt_count to indicate that kernel - * preemption is occuring. See include/asm-arm/hardirq.h. + * preemption is occurring. See include/asm-arm/hardirq.h. */ #define PREEMPT_ACTIVE 0x40000000 diff --git a/include/asm-arm/thread_notify.h b/include/asm-arm/thread_notify.h new file mode 100644 index 0000000..8866e52 --- /dev/null +++ b/include/asm-arm/thread_notify.h @@ -0,0 +1,48 @@ +/* + * linux/include/asm-arm/thread_notify.h + * + * Copyright (C) 2006 Russell King. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ASMARM_THREAD_NOTIFY_H +#define ASMARM_THREAD_NOTIFY_H + +#ifdef __KERNEL__ + +#ifndef __ASSEMBLY__ + +#include +#include + +static inline int thread_register_notifier(struct notifier_block *n) +{ + extern struct atomic_notifier_head thread_notify_head; + return atomic_notifier_chain_register(&thread_notify_head, n); +} + +static inline void thread_unregister_notifier(struct notifier_block *n) +{ + extern struct atomic_notifier_head thread_notify_head; + atomic_notifier_chain_unregister(&thread_notify_head, n); +} + +static inline void thread_notify(unsigned long rc, struct thread_info *thread) +{ + extern struct atomic_notifier_head thread_notify_head; + atomic_notifier_call_chain(&thread_notify_head, rc, thread); +} + +#endif + +/* + * These are the reason codes for the thread notifier. + */ +#define THREAD_NOTIFY_FLUSH 0 +#define THREAD_NOTIFY_RELEASE 1 +#define THREAD_NOTIFY_SWITCH 2 + +#endif +#endif diff --git a/include/asm-arm/tlbflush.h b/include/asm-arm/tlbflush.h index 7289924..d97fc76 100644 --- a/include/asm-arm/tlbflush.h +++ b/include/asm-arm/tlbflush.h @@ -10,7 +10,6 @@ #ifndef _ASMARM_TLBFLUSH_H #define _ASMARM_TLBFLUSH_H -#include #ifndef CONFIG_MMU diff --git a/include/asm-arm/uaccess.h b/include/asm-arm/uaccess.h index 064f0f5..87aba57 100644 --- a/include/asm-arm/uaccess.h +++ b/include/asm-arm/uaccess.h @@ -41,15 +41,24 @@ struct exception_table_entry extern int fixup_exception(struct pt_regs *regs); /* + * These two are intentionally not defined anywhere - if the kernel + * code generates any references to them, that's a bug. + */ +extern int __get_user_bad(void); +extern int __put_user_bad(void); + +/* * Note that this is actually 0x1,0000,0000 */ #define KERNEL_DS 0x00000000 -#define USER_DS TASK_SIZE - #define get_ds() (KERNEL_DS) + +#ifdef CONFIG_MMU + +#define USER_DS TASK_SIZE #define get_fs() (current_thread_info()->addr_limit) -static inline void set_fs (mm_segment_t fs) +static inline void set_fs(mm_segment_t fs) { current_thread_info()->addr_limit = fs; modify_domain(DOMAIN_KERNEL, fs ? DOMAIN_CLIENT : DOMAIN_MANAGER); @@ -75,8 +84,6 @@ #define __range_ok(addr,size) ({ \ : "cc"); \ flag; }) -#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) - /* * Single-value transfer routines. They automatically use the right * size if we just have the right pointer type. Note that the functions @@ -87,20 +94,10 @@ #define access_ok(type,addr,size) (__ran * fixup code, but there are a few places where it intrudes on the * main code path. When we only write to user space, there is no * problem. - * - * The "__xxx" versions of the user access functions do not verify the - * address space - it must have been done previously with a separate - * "access_ok()" call. - * - * The "xxx_error" versions set the third argument to EFAULT if an - * error occurs, and leave it unchanged on success. Note that these - * versions are void (ie, don't return a value as such). */ - extern int __get_user_1(void *); extern int __get_user_2(void *); extern int __get_user_4(void *); -extern int __get_user_bad(void); #define __get_user_x(__r2,__p,__e,__s,__i...) \ __asm__ __volatile__ ( \ @@ -131,6 +128,74 @@ #define get_user(x,p) \ __e; \ }) +extern int __put_user_1(void *, unsigned int); +extern int __put_user_2(void *, unsigned int); +extern int __put_user_4(void *, unsigned int); +extern int __put_user_8(void *, unsigned long long); + +#define __put_user_x(__r2,__p,__e,__s) \ + __asm__ __volatile__ ( \ + __asmeq("%0", "r0") __asmeq("%2", "r2") \ + "bl __put_user_" #__s \ + : "=&r" (__e) \ + : "0" (__p), "r" (__r2) \ + : "ip", "lr", "cc") + +#define put_user(x,p) \ + ({ \ + const register typeof(*(p)) __r2 asm("r2") = (x); \ + const register typeof(*(p)) __user *__p asm("r0") = (p);\ + register int __e asm("r0"); \ + switch (sizeof(*(__p))) { \ + case 1: \ + __put_user_x(__r2, __p, __e, 1); \ + break; \ + case 2: \ + __put_user_x(__r2, __p, __e, 2); \ + break; \ + case 4: \ + __put_user_x(__r2, __p, __e, 4); \ + break; \ + case 8: \ + __put_user_x(__r2, __p, __e, 8); \ + break; \ + default: __e = __put_user_bad(); break; \ + } \ + __e; \ + }) + +#else /* CONFIG_MMU */ + +/* + * uClinux has only one addr space, so has simplified address limits. + */ +#define USER_DS KERNEL_DS + +#define segment_eq(a,b) (1) +#define __addr_ok(addr) (1) +#define __range_ok(addr,size) (0) +#define get_fs() (KERNEL_DS) + +static inline void set_fs(mm_segment_t fs) +{ +} + +#define get_user(x,p) __get_user(x,p) +#define put_user(x,p) __put_user(x,p) + +#endif /* CONFIG_MMU */ + +#define access_ok(type,addr,size) (__range_ok(addr,size) == 0) + +/* + * The "__xxx" versions of the user access functions do not verify the + * address space - it must have been done previously with a separate + * "access_ok()" call. + * + * The "xxx_error" versions set the third argument to EFAULT if an + * error occurs, and leave it unchanged on success. Note that these + * versions are void (ie, don't return a value as such). + */ #define __get_user(x,ptr) \ ({ \ long __gu_err = 0; \ @@ -212,43 +277,6 @@ #define __get_user_asm_word(x,addr,err) : "r" (addr), "i" (-EFAULT) \ : "cc") -extern int __put_user_1(void *, unsigned int); -extern int __put_user_2(void *, unsigned int); -extern int __put_user_4(void *, unsigned int); -extern int __put_user_8(void *, unsigned long long); -extern int __put_user_bad(void); - -#define __put_user_x(__r2,__p,__e,__s) \ - __asm__ __volatile__ ( \ - __asmeq("%0", "r0") __asmeq("%2", "r2") \ - "bl __put_user_" #__s \ - : "=&r" (__e) \ - : "0" (__p), "r" (__r2) \ - : "ip", "lr", "cc") - -#define put_user(x,p) \ - ({ \ - const register typeof(*(p)) __r2 asm("r2") = (x); \ - const register typeof(*(p)) __user *__p asm("r0") = (p);\ - register int __e asm("r0"); \ - switch (sizeof(*(__p))) { \ - case 1: \ - __put_user_x(__r2, __p, __e, 1); \ - break; \ - case 2: \ - __put_user_x(__r2, __p, __e, 2); \ - break; \ - case 4: \ - __put_user_x(__r2, __p, __e, 4); \ - break; \ - case 8: \ - __put_user_x(__r2, __p, __e, 8); \ - break; \ - default: __e = __put_user_bad(); break; \ - } \ - __e; \ - }) - #define __put_user(x,ptr) \ ({ \ long __pu_err = 0; \ @@ -353,66 +381,54 @@ #define __put_user_asm_dword(x,__pu_addr : "r" (x), "i" (-EFAULT) \ : "cc") -extern unsigned long __arch_copy_from_user(void *to, const void __user *from, unsigned long n); -extern unsigned long __arch_copy_to_user(void __user *to, const void *from, unsigned long n); -extern unsigned long __arch_clear_user(void __user *addr, unsigned long n); -extern unsigned long __arch_strncpy_from_user(char *to, const char __user *from, unsigned long count); -extern unsigned long __arch_strnlen_user(const char __user *s, long n); + +#ifdef CONFIG_MMU +extern unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n); +extern unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n); +extern unsigned long __clear_user(void __user *addr, unsigned long n); +#else +#define __copy_from_user(to,from,n) (memcpy(to, (void __force *)from, n), 0) +#define __copy_to_user(to,from,n) (memcpy((void __force *)to, from, n), 0) +#define __clear_user(addr,n) (memset((void __force *)addr, 0, n), 0) +#endif + +extern unsigned long __strncpy_from_user(char *to, const char __user *from, unsigned long count); +extern unsigned long __strnlen_user(const char __user *s, long n); static inline unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) { if (access_ok(VERIFY_READ, from, n)) - n = __arch_copy_from_user(to, from, n); + n = __copy_from_user(to, from, n); else /* security hole - plug it */ memzero(to, n); return n; } -static inline unsigned long __copy_from_user(void *to, const void __user *from, unsigned long n) -{ - return __arch_copy_from_user(to, from, n); -} - static inline unsigned long copy_to_user(void __user *to, const void *from, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) - n = __arch_copy_to_user(to, from, n); + n = __copy_to_user(to, from, n); return n; } -static inline unsigned long __copy_to_user(void __user *to, const void *from, unsigned long n) -{ - return __arch_copy_to_user(to, from, n); -} - #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user -static inline unsigned long clear_user (void __user *to, unsigned long n) +static inline unsigned long clear_user(void __user *to, unsigned long n) { if (access_ok(VERIFY_WRITE, to, n)) - n = __arch_clear_user(to, n); + n = __clear_user(to, n); return n; } -static inline unsigned long __clear_user (void __user *to, unsigned long n) -{ - return __arch_clear_user(to, n); -} - -static inline long strncpy_from_user (char *dst, const char __user *src, long count) +static inline long strncpy_from_user(char *dst, const char __user *src, long count) { long res = -EFAULT; if (access_ok(VERIFY_READ, src, 1)) - res = __arch_strncpy_from_user(dst, src, count); + res = __strncpy_from_user(dst, src, count); return res; } -static inline long __strncpy_from_user (char *dst, const char __user *src, long count) -{ - return __arch_strncpy_from_user(dst, src, count); -} - #define strlen_user(s) strnlen_user(s, ~0UL >> 1) static inline long strnlen_user(const char __user *s, long n) @@ -420,7 +436,7 @@ static inline long strnlen_user(const ch unsigned long res = 0; if (__addr_ok(s)) - res = __arch_strnlen_user(s, n); + res = __strnlen_user(s, n); return res; } diff --git a/include/asm-arm/ucontext.h b/include/asm-arm/ucontext.h index f853130..bf65e9f 100644 --- a/include/asm-arm/ucontext.h +++ b/include/asm-arm/ucontext.h @@ -1,12 +1,103 @@ #ifndef _ASMARM_UCONTEXT_H #define _ASMARM_UCONTEXT_H +#include + +/* + * struct sigcontext only has room for the basic registers, but struct + * ucontext now has room for all registers which need to be saved and + * restored. Coprocessor registers are stored in uc_regspace. Each + * coprocessor's saved state should start with a documented 32-bit magic + * number, followed by a 32-bit word giving the coproccesor's saved size. + * uc_regspace may be expanded if necessary, although this takes some + * coordination with glibc. + */ + struct ucontext { unsigned long uc_flags; struct ucontext *uc_link; stack_t uc_stack; struct sigcontext uc_mcontext; - sigset_t uc_sigmask; /* mask last for extensibility */ + sigset_t uc_sigmask; + /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ + int __unused[32 - (sizeof (sigset_t) / sizeof (int))]; + /* Last for extensibility. Eight byte aligned because some + coprocessors require eight byte alignment. */ + unsigned long uc_regspace[128] __attribute__((__aligned__(8))); }; +#ifdef __KERNEL__ + +/* + * Coprocessor save state. The magic values and specific + * coprocessor's layouts are part of the userspace ABI. Each one of + * these should be a multiple of eight bytes and aligned to eight + * bytes, to prevent unpredictable padding in the signal frame. + */ + +#ifdef CONFIG_CRUNCH +#define CRUNCH_MAGIC 0x5065cf03 +#define CRUNCH_STORAGE_SIZE (CRUNCH_SIZE + 8) + +struct crunch_sigframe { + unsigned long magic; + unsigned long size; + struct crunch_state storage; +} __attribute__((__aligned__(8))); +#endif + +#ifdef CONFIG_IWMMXT +/* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */ +#define IWMMXT_MAGIC 0x12ef842a +#define IWMMXT_STORAGE_SIZE (IWMMXT_SIZE + 8) + +struct iwmmxt_sigframe { + unsigned long magic; + unsigned long size; + struct iwmmxt_struct storage; +} __attribute__((__aligned__(8))); +#endif /* CONFIG_IWMMXT */ + +#ifdef CONFIG_VFP +#if __LINUX_ARM_ARCH__ < 6 +/* For ARM pre-v6, we use fstmiax and fldmiax. This adds one extra + * word after the registers, and a word of padding at the end for + * alignment. */ +#define VFP_MAGIC 0x56465001 +#define VFP_STORAGE_SIZE 152 +#else +#define VFP_MAGIC 0x56465002 +#define VFP_STORAGE_SIZE 144 +#endif + +struct vfp_sigframe +{ + unsigned long magic; + unsigned long size; + union vfp_state storage; +}; +#endif /* CONFIG_VFP */ + +/* + * Auxiliary signal frame. This saves stuff like FP state. + * The layout of this structure is not part of the user ABI, + * because the config options aren't. uc_regspace is really + * one of these. + */ +struct aux_sigframe { +#ifdef CONFIG_CRUNCH + struct crunch_sigframe crunch; +#endif +#ifdef CONFIG_IWMMXT + struct iwmmxt_sigframe iwmmxt; +#endif +#if 0 && defined CONFIG_VFP /* Not yet saved. */ + struct vfp_sigframe vfp; +#endif + /* Something that isn't a valid magic number for any coprocessor. */ + unsigned long end_magic; +} __attribute__((__aligned__(8))); + +#endif + #endif /* !_ASMARM_UCONTEXT_H */ diff --git a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h index cbf39a5..1e891f8 100644 --- a/include/asm-arm/unistd.h +++ b/include/asm-arm/unistd.h @@ -13,8 +13,6 @@ #ifndef __ASM_ARM_UNISTD_H #define __ASM_ARM_UNISTD_H -#include - #define __NR_OABI_SYSCALL_BASE 0x900000 #if defined(__thumb__) || defined(__ARM_EABI__) @@ -378,6 +376,9 @@ #undef __NR_syscall #undef __NR_ipc #endif +#ifdef __KERNEL__ +#include + #define __sys2(x) #x #define __sys1(x) __sys2(x) @@ -526,7 +527,6 @@ type name(type1 arg1, type2 arg2, type3 __syscall_return(type,__res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_GETHOSTNAME @@ -547,7 +547,6 @@ #define __ARCH_WANT_SYS_OLD_GETRLIMIT #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_SOCKETCALL #endif -#endif #ifdef __KERNEL_SYSCALLS__ @@ -571,7 +570,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -581,4 +580,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* __ASM_ARM_UNISTD_H */ diff --git a/include/asm-arm/vga.h b/include/asm-arm/vga.h index 926e5ee..1e0b913 100644 --- a/include/asm-arm/vga.h +++ b/include/asm-arm/vga.h @@ -4,7 +4,7 @@ #define ASMARM_VGA_H #include #include -#define VGA_MAP_MEM(x) (PCIMEM_BASE + (x)) +#define VGA_MAP_MEM(x,s) (PCIMEM_BASE + (x)) #define vga_readb(x) (*((volatile unsigned char *)x)) #define vga_writeb(x,y) (*((volatile unsigned char *)y) = (x)) diff --git a/include/asm-arm26/Kbuild b/include/asm-arm26/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-arm26/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-arm26/atomic.h b/include/asm-arm26/atomic.h index 1552c86..97e944f 100644 --- a/include/asm-arm26/atomic.h +++ b/include/asm-arm26/atomic.h @@ -20,7 +20,6 @@ #ifndef __ASM_ARM_ATOMIC_H #define __ASM_ARM_ATOMIC_H -#include #ifdef CONFIG_SMP #error SMP is NOT supported diff --git a/include/asm-arm26/bug.h b/include/asm-arm26/bug.h index 7177c73..8545d58 100644 --- a/include/asm-arm26/bug.h +++ b/include/asm-arm26/bug.h @@ -1,7 +1,6 @@ #ifndef _ASMARM_BUG_H #define _ASMARM_BUG_H -#include #ifdef CONFIG_BUG #ifdef CONFIG_DEBUG_BUGVERBOSE diff --git a/include/asm-arm26/dma.h b/include/asm-arm26/dma.h index 995e223..4326ba8 100644 --- a/include/asm-arm26/dma.h +++ b/include/asm-arm26/dma.h @@ -3,7 +3,6 @@ #define __ASM_ARM_DMA_H typedef unsigned int dmach_t; -#include #include #include #include diff --git a/include/asm-arm26/floppy.h b/include/asm-arm26/floppy.h index 9e090ad..efb7321 100644 --- a/include/asm-arm26/floppy.h +++ b/include/asm-arm26/floppy.h @@ -22,7 +22,7 @@ #define fd_outb(val,port) \ #define fd_inb(port) inb((port)) #define fd_request_irq() request_irq(IRQ_FLOPPYDISK,floppy_interrupt,\ - SA_INTERRUPT|SA_SAMPLE_RANDOM,"floppy",NULL) + IRQF_DISABLED,"floppy",NULL) #define fd_free_irq() free_irq(IRQ_FLOPPYDISK,NULL) #define fd_disable_irq() disable_irq(IRQ_FLOPPYDISK) #define fd_enable_irq() enable_irq(IRQ_FLOPPYDISK) diff --git a/include/asm-arm26/hardirq.h b/include/asm-arm26/hardirq.h index 87c19d2..e717742 100644 --- a/include/asm-arm26/hardirq.h +++ b/include/asm-arm26/hardirq.h @@ -1,7 +1,6 @@ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include #include diff --git a/include/asm-arm26/hardware.h b/include/asm-arm26/hardware.h index 82fc55e..801df0b 100644 --- a/include/asm-arm26/hardware.h +++ b/include/asm-arm26/hardware.h @@ -16,7 +16,6 @@ #ifndef __ASM_HARDWARE_H #define __ASM_HARDWARE_H -#include /* diff --git a/include/asm-arm26/io.h b/include/asm-arm26/io.h index 02f94d8..2aa033b 100644 --- a/include/asm-arm26/io.h +++ b/include/asm-arm26/io.h @@ -22,7 +22,6 @@ #define __ASM_ARM_IO_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-arm26/irq.h b/include/asm-arm26/irq.h index 06bd5a5..9aaac87 100644 --- a/include/asm-arm26/irq.h +++ b/include/asm-arm26/irq.h @@ -44,9 +44,5 @@ #define IRQT_PROBE (1 << 4) int set_irq_type(unsigned int irq, unsigned int type); -int setup_irq(unsigned int, struct irqaction *); -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif diff --git a/include/asm-arm26/leds.h b/include/asm-arm26/leds.h index 88ce412..12290ea 100644 --- a/include/asm-arm26/leds.h +++ b/include/asm-arm26/leds.h @@ -13,7 +13,6 @@ #ifndef ASM_ARM_LEDS_H #define ASM_ARM_LEDS_H -#include typedef enum { led_idle_start, diff --git a/include/asm-arm26/mach-types.h b/include/asm-arm26/mach-types.h index b34045b..0aeaedc 100644 --- a/include/asm-arm26/mach-types.h +++ b/include/asm-arm26/mach-types.h @@ -6,7 +6,6 @@ #ifndef __ASM_ARM_MACH_TYPE_H #define __ASM_ARM_MACH_TYPE_H -#include #ifndef __ASSEMBLY__ extern unsigned int __machine_arch_type; diff --git a/include/asm-arm26/page.h b/include/asm-arm26/page.h index d3f23ac..fa19de2 100644 --- a/include/asm-arm26/page.h +++ b/include/asm-arm26/page.h @@ -1,7 +1,6 @@ #ifndef _ASMARM_PAGE_H #define _ASMARM_PAGE_H -#include #ifdef __KERNEL__ #ifndef __ASSEMBLY__ diff --git a/include/asm-arm26/pgtable.h b/include/asm-arm26/pgtable.h index a590250..19ac910 100644 --- a/include/asm-arm26/pgtable.h +++ b/include/asm-arm26/pgtable.h @@ -13,7 +13,6 @@ #define _ASMARM_PGTABLE_H #include -#include #include /* diff --git a/include/asm-arm26/serial.h b/include/asm-arm26/serial.h index 5fc747d..dd86a71 100644 --- a/include/asm-arm26/serial.h +++ b/include/asm-arm26/serial.h @@ -14,7 +14,6 @@ #ifndef __ASM_SERIAL_H #define __ASM_SERIAL_H -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-arm26/signal.h b/include/asm-arm26/signal.h index 37ad253..967ba49 100644 --- a/include/asm-arm26/signal.h +++ b/include/asm-arm26/signal.h @@ -82,7 +82,6 @@ #define SIGSWI 32 * is running in 26-bit. * SA_ONSTACK allows alternate signal stacks (see sigaltstack(2)). * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_NODEFER prevents the current signal from being masked in the handler. * SA_RESETHAND clears the handler when the signal is delivered. * @@ -101,7 +100,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ /* diff --git a/include/asm-arm26/smp.h b/include/asm-arm26/smp.h index 5ca7716..38349ec 100644 --- a/include/asm-arm26/smp.h +++ b/include/asm-arm26/smp.h @@ -1,7 +1,6 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H -#include #ifdef CONFIG_SMP #error SMP not supported diff --git a/include/asm-arm26/socket.h b/include/asm-arm26/socket.h index 3c51da6..19f7df7 100644 --- a/include/asm-arm26/socket.h +++ b/include/asm-arm26/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-arm26/sysirq.h b/include/asm-arm26/sysirq.h index cad250c..81dca90 100644 --- a/include/asm-arm26/sysirq.h +++ b/include/asm-arm26/sysirq.h @@ -11,7 +11,6 @@ * 04-04-1998 PJB Merged arc and a5k versions */ -#include #if defined(CONFIG_ARCH_A5K) #define IRQ_PRINTER 0 diff --git a/include/asm-arm26/system.h b/include/asm-arm26/system.h index 7028849..d1f69d7 100644 --- a/include/asm-arm26/system.h +++ b/include/asm-arm26/system.h @@ -3,7 +3,6 @@ #define __ASM_ARM_SYSTEM_H #ifdef __KERNEL__ -#include /* * This is used to ensure the compiler did actually allocate the register we diff --git a/include/asm-arm26/unistd.h b/include/asm-arm26/unistd.h index be4c2fb..70eb6d9 100644 --- a/include/asm-arm26/unistd.h +++ b/include/asm-arm26/unistd.h @@ -14,8 +14,6 @@ #ifndef __ASM_ARM_UNISTD_H #define __ASM_ARM_UNISTD_H -#include - #define __NR_SYSCALL_BASE 0x900000 /* @@ -312,6 +310,9 @@ #define __ARM_NR_breakpoint (__ARM_NR_B #define __ARM_NR_cacheflush (__ARM_NR_BASE+2) #define __ARM_NR_usr26 (__ARM_NR_BASE+3) +#ifdef __KERNEL__ +#include + #define __sys2(x) #x #define __sys1(x) __sys2(x) @@ -443,7 +444,6 @@ type name(type1 arg1, type2 arg2, type3 __syscall_return(type,__res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 @@ -462,7 +462,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -486,7 +485,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -496,4 +495,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* __ASM_ARM_UNISTD_H */ diff --git a/include/asm-cris/Kbuild b/include/asm-cris/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-cris/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-cris/arch-v10/io.h b/include/asm-cris/arch-v10/io.h index dd39198..11ef5b5 100644 --- a/include/asm-cris/arch-v10/io.h +++ b/include/asm-cris/arch-v10/io.h @@ -2,7 +2,6 @@ #ifndef _ASM_ARCH_CRIS_IO_H #define _ASM_ARCH_CRIS_IO_H #include -#include /* Etrax shadow registers - which live in arch/cris/kernel/shadows.c */ diff --git a/include/asm-cris/arch-v10/irq.h b/include/asm-cris/arch-v10/irq.h index 4fa8945..b1128a9 100644 --- a/include/asm-cris/arch-v10/irq.h +++ b/include/asm-cris/arch-v10/irq.h @@ -141,7 +141,7 @@ __asm__ ( \ * it here, we would not get the multiple_irq at all. * * The non-blocking here is based on the knowledge that the timer interrupt is - * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not + * registred as a fast interrupt (IRQF_DISABLED) so that we _know_ there will not * be an sti() before the timer irq handler is run to acknowledge the interrupt. */ diff --git a/include/asm-cris/arch-v10/page.h b/include/asm-cris/arch-v10/page.h index 407e6e6..7d8307a 100644 --- a/include/asm-cris/arch-v10/page.h +++ b/include/asm-cris/arch-v10/page.h @@ -1,7 +1,6 @@ #ifndef _CRIS_ARCH_PAGE_H #define _CRIS_ARCH_PAGE_H -#include #ifdef __KERNEL__ diff --git a/include/asm-cris/arch-v10/system.h b/include/asm-cris/arch-v10/system.h index 1ac7b63..4a9cd36 100644 --- a/include/asm-cris/arch-v10/system.h +++ b/include/asm-cris/arch-v10/system.h @@ -1,7 +1,6 @@ #ifndef __ASM_CRIS_ARCH_SYSTEM_H #define __ASM_CRIS_ARCH_SYSTEM_H -#include /* read the CPU version register */ diff --git a/include/asm-cris/arch-v32/arbiter.h b/include/asm-cris/arch-v32/arbiter.h index dba3c28..081a911 100644 --- a/include/asm-cris/arch-v32/arbiter.h +++ b/include/asm-cris/arch-v32/arbiter.h @@ -20,8 +20,8 @@ enum arbiter_all_accesses = 0xff }; -int crisv32_arbiter_allocate_bandwith(int client, int region, - unsigned long bandwidth); +int crisv32_arbiter_allocate_bandwidth(int client, int region, + unsigned long bandwidth); int crisv32_arbiter_watch(unsigned long start, unsigned long size, unsigned long clients, unsigned long accesses, watch_callback* cb); diff --git a/include/asm-cris/arch-v32/io.h b/include/asm-cris/arch-v32/io.h index 043c9ce..5efe4d9 100644 --- a/include/asm-cris/arch-v32/io.h +++ b/include/asm-cris/arch-v32/io.h @@ -4,7 +4,6 @@ #define _ASM_ARCH_CRIS_IO_H #include #include #include -#include enum crisv32_io_dir { diff --git a/include/asm-cris/arch-v32/irq.h b/include/asm-cris/arch-v32/irq.h index d35aa81..bac94ee 100644 --- a/include/asm-cris/arch-v32/irq.h +++ b/include/asm-cris/arch-v32/irq.h @@ -1,7 +1,6 @@ #ifndef _ASM_ARCH_IRQ_H #define _ASM_ARCH_IRQ_H -#include #include "hwregs/intr_vect.h" /* Number of non-cpu interrupts. */ @@ -99,7 +98,7 @@ __asm__ ( \ * if we had BLOCK'edit here, we would not get the multiple_irq at all. * * The non-blocking here is based on the knowledge that the timer interrupt is - * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not + * registred as a fast interrupt (IRQF_DISABLED) so that we _know_ there will not * be an sti() before the timer irq handler is run to acknowledge the interrupt. */ #define BUILD_TIMER_IRQ(nr, mask) \ diff --git a/include/asm-cris/arch-v32/page.h b/include/asm-cris/arch-v32/page.h index 77827bc..fa454fe 100644 --- a/include/asm-cris/arch-v32/page.h +++ b/include/asm-cris/arch-v32/page.h @@ -1,7 +1,6 @@ #ifndef _ASM_CRIS_ARCH_PAGE_H #define _ASM_CRIS_ARCH_PAGE_H -#include #ifdef __KERNEL__ diff --git a/include/asm-cris/arch-v32/processor.h b/include/asm-cris/arch-v32/processor.h index 32bf2e5..5553b0c 100644 --- a/include/asm-cris/arch-v32/processor.h +++ b/include/asm-cris/arch-v32/processor.h @@ -1,7 +1,6 @@ #ifndef _ASM_CRIS_ARCH_PROCESSOR_H #define _ASM_CRIS_ARCH_PROCESSOR_H -#include /* Return current instruction pointer. */ #define current_text_addr() \ diff --git a/include/asm-cris/arch-v32/system.h b/include/asm-cris/arch-v32/system.h index a3d75d5..d20e2d6 100644 --- a/include/asm-cris/arch-v32/system.h +++ b/include/asm-cris/arch-v32/system.h @@ -1,7 +1,6 @@ #ifndef _ASM_CRIS_ARCH_SYSTEM_H #define _ASM_CRIS_ARCH_SYSTEM_H -#include /* Read the CPU version register. */ static inline unsigned long rdvr(void) diff --git a/include/asm-cris/eshlibld.h b/include/asm-cris/eshlibld.h index 2b577cd..10ce36c 100644 --- a/include/asm-cris/eshlibld.h +++ b/include/asm-cris/eshlibld.h @@ -32,7 +32,6 @@ #define _cris_relocate_h /* We have dependencies all over the place for the host system for xsim being a linux system, so let's not pretend anything else with #ifdef:s here until fixed. */ -#include #include /* Maybe do sanity checking if file input. */ diff --git a/include/asm-cris/etraxgpio.h b/include/asm-cris/etraxgpio.h index 80ee10f..5d0028d 100644 --- a/include/asm-cris/etraxgpio.h +++ b/include/asm-cris/etraxgpio.h @@ -25,7 +25,6 @@ #ifndef _ASM_ETRAXGPIO_H #define _ASM_ETRAXGPIO_H -#include /* etraxgpio _IOC_TYPE, bits 8 to 15 in ioctl cmd */ #ifdef CONFIG_ETRAX_ARCH_V10 #define ETRAXGPIO_IOCTYPE 43 diff --git a/include/asm-cris/fasttimer.h b/include/asm-cris/fasttimer.h index 6952202..a3a7713 100644 --- a/include/asm-cris/fasttimer.h +++ b/include/asm-cris/fasttimer.h @@ -5,7 +5,6 @@ * This may be useful in other OS than Linux so use 2 space indentation... * Copyright (C) 2000, 2002 Axis Communications AB */ -#include #include /* struct timeval */ #include diff --git a/include/asm-cris/hw_irq.h b/include/asm-cris/hw_irq.h index 341536a..2980660 100644 --- a/include/asm-cris/hw_irq.h +++ b/include/asm-cris/hw_irq.h @@ -1,7 +1,5 @@ #ifndef _ASM_HW_IRQ_H #define _ASM_HW_IRQ_H -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} - #endif diff --git a/include/asm-cris/irq.h b/include/asm-cris/irq.h index 4b33879..998cce9 100644 --- a/include/asm-cris/irq.h +++ b/include/asm-cris/irq.h @@ -1,11 +1,6 @@ #ifndef _ASM_IRQ_H #define _ASM_IRQ_H -/* - * IRQ line status macro IRQ_PER_CPU is used - */ -#define ARCH_HAS_IRQ_PER_CPU - #include static inline int irq_canonicalize(int irq) diff --git a/include/asm-cris/page.h b/include/asm-cris/page.h index 3787633..81832e9 100644 --- a/include/asm-cris/page.h +++ b/include/asm-cris/page.h @@ -1,7 +1,6 @@ #ifndef _CRIS_PAGE_H #define _CRIS_PAGE_H -#include #include /* PAGE_SHIFT determines the page size */ diff --git a/include/asm-cris/pci.h b/include/asm-cris/pci.h index 2064bc1..b2ac8a3 100644 --- a/include/asm-cris/pci.h +++ b/include/asm-cris/pci.h @@ -1,7 +1,6 @@ #ifndef __ASM_CRIS_PCI_H #define __ASM_CRIS_PCI_H -#include #ifdef __KERNEL__ #include /* for struct page */ diff --git a/include/asm-cris/pgtable.h b/include/asm-cris/pgtable.h index 70a8325..5d76c1c 100644 --- a/include/asm-cris/pgtable.h +++ b/include/asm-cris/pgtable.h @@ -9,7 +9,6 @@ #include #include #ifndef __ASSEMBLY__ -#include #include #include #endif diff --git a/include/asm-cris/processor.h b/include/asm-cris/processor.h index 961e2bc..568da1d 100644 --- a/include/asm-cris/processor.h +++ b/include/asm-cris/processor.h @@ -10,7 +10,6 @@ #ifndef __ASM_CRIS_PROCESSOR_H #define __ASM_CRIS_PROCESSOR_H -#include #include #include #include diff --git a/include/asm-cris/rtc.h b/include/asm-cris/rtc.h index 97c1303..cb4bf92 100644 --- a/include/asm-cris/rtc.h +++ b/include/asm-cris/rtc.h @@ -4,7 +4,6 @@ #ifndef __RTC_H__ #define __RTC_H__ -#include #ifdef CONFIG_ETRAX_DS1302 /* Dallas DS1302 clock/calendar register numbers. */ diff --git a/include/asm-cris/signal.h b/include/asm-cris/signal.h index dfe0395..349ae68 100644 --- a/include/asm-cris/signal.h +++ b/include/asm-cris/signal.h @@ -74,7 +74,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -95,7 +94,6 @@ #define SA_RESETHAND 0x80000000u #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-cris/socket.h b/include/asm-cris/socket.h index 8b1da3e..01cfdf1 100644 --- a/include/asm-cris/socket.h +++ b/include/asm-cris/socket.h @@ -50,6 +50,7 @@ #define SCM_TIMESTAMP SO_TIMEST #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-cris/tlbflush.h b/include/asm-cris/tlbflush.h index c522380..0569612 100644 --- a/include/asm-cris/tlbflush.h +++ b/include/asm-cris/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _CRIS_TLBFLUSH_H #define _CRIS_TLBFLUSH_H -#include #include #include #include diff --git a/include/asm-cris/unistd.h b/include/asm-cris/unistd.h index bb2dfe4..c2954e9 100644 --- a/include/asm-cris/unistd.h +++ b/include/asm-cris/unistd.h @@ -295,11 +295,11 @@ #define __NR_add_key 286 #define __NR_request_key 287 #define __NR_keyctl 288 -#define NR_syscalls 289 +#ifdef __KERNEL__ +#define NR_syscalls 289 -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -379,12 +379,10 @@ asmlinkage long sys_rt_sigaction(int sig * complaints. We don't want to use -fno-builtin, so just use a * different name when in the kernel. */ -#ifdef __KERNEL__ #define _exit kernel_syscall_exit -#endif static inline _syscall1(int,_exit,int,exitcode) static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* @@ -395,4 +393,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ASM_CRIS_UNISTD_H_ */ diff --git a/include/asm-frv/Kbuild b/include/asm-frv/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-frv/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-frv/atomic.h b/include/asm-frv/atomic.h index 5d9f84b..066386a 100644 --- a/include/asm-frv/atomic.h +++ b/include/asm-frv/atomic.h @@ -14,7 +14,6 @@ #ifndef _ASM_ATOMIC_H #define _ASM_ATOMIC_H -#include #include #include @@ -228,7 +227,7 @@ ({ \ break; \ \ default: \ - __xg_orig = 0; \ + __xg_orig = (__typeof__(__xg_orig))0; \ asm volatile("break"); \ break; \ } \ @@ -248,7 +247,7 @@ ({ \ switch (sizeof(__xg_orig)) { \ case 4: __xg_orig = (__typeof__(*(ptr))) __xchg_32((uint32_t) x, __xg_ptr); break; \ default: \ - __xg_orig = 0; \ + __xg_orig = (__typeof__(__xg_orig))0; \ asm volatile("break"); \ break; \ } \ diff --git a/include/asm-frv/bitops.h b/include/asm-frv/bitops.h index 6344d06..980ae1b 100644 --- a/include/asm-frv/bitops.h +++ b/include/asm-frv/bitops.h @@ -14,7 +14,6 @@ #ifndef _ASM_BITOPS_H #define _ASM_BITOPS_H -#include #include #include #include diff --git a/include/asm-frv/bug.h b/include/asm-frv/bug.h index 451712c..6b1b44d 100644 --- a/include/asm-frv/bug.h +++ b/include/asm-frv/bug.h @@ -11,7 +11,6 @@ #ifndef _ASM_BUG_H #define _ASM_BUG_H -#include #include #ifdef CONFIG_BUG diff --git a/include/asm-frv/cache.h b/include/asm-frv/cache.h index cf69b63..2797163 100644 --- a/include/asm-frv/cache.h +++ b/include/asm-frv/cache.h @@ -12,7 +12,6 @@ #ifndef __ASM_CACHE_H #define __ASM_CACHE_H -#include /* bytes per L1 cache line */ #define L1_CACHE_SHIFT (CONFIG_FRV_L1_CACHE_SHIFT) diff --git a/include/asm-frv/checksum.h b/include/asm-frv/checksum.h index 10236f6..42bf0db 100644 --- a/include/asm-frv/checksum.h +++ b/include/asm-frv/checksum.h @@ -43,7 +43,7 @@ unsigned int csum_partial_copy(const cha * here even more important to align src and dst on a 32-bit (or even * better 64-bit) boundary */ -extern unsigned int csum_partial_copy_from_user(const char *src, char *dst, +extern unsigned int csum_partial_copy_from_user(const char __user *src, char *dst, int len, int sum, int *csum_err); #define csum_partial_copy_nocheck(src, dst, len, sum) \ diff --git a/include/asm-frv/dma.h b/include/asm-frv/dma.h index d8f9a2f..18d6bb8 100644 --- a/include/asm-frv/dma.h +++ b/include/asm-frv/dma.h @@ -14,7 +14,6 @@ #define _ASM_DMA_H //#define DMA_DEBUG 1 -#include #include #undef MAX_DMA_CHANNELS /* don't use kernel/dma.c */ diff --git a/include/asm-frv/elf.h b/include/asm-frv/elf.h index 7d2098f..38656da 100644 --- a/include/asm-frv/elf.h +++ b/include/asm-frv/elf.h @@ -12,7 +12,6 @@ #ifndef __ASM_ELF_H #define __ASM_ELF_H -#include #include #include diff --git a/include/asm-frv/fpu.h b/include/asm-frv/fpu.h index b1178f8..d73c60b 100644 --- a/include/asm-frv/fpu.h +++ b/include/asm-frv/fpu.h @@ -1,7 +1,6 @@ #ifndef __ASM_FPU_H #define __ASM_FPU_H -#include /* * MAX floating point unit state size (FSAVE/FRESTORE) diff --git a/include/asm-frv/hardirq.h b/include/asm-frv/hardirq.h index 6851239..7581b5a 100644 --- a/include/asm-frv/hardirq.h +++ b/include/asm-frv/hardirq.h @@ -12,7 +12,6 @@ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include diff --git a/include/asm-frv/highmem.h b/include/asm-frv/highmem.h index 295f74a..e2247c2 100644 --- a/include/asm-frv/highmem.h +++ b/include/asm-frv/highmem.h @@ -17,7 +17,6 @@ #define _ASM_HIGHMEM_H #ifdef __KERNEL__ -#include #include #include #include @@ -135,7 +134,7 @@ static inline void *kmap_atomic(struct p default: BUG(); - return 0; + return NULL; } } diff --git a/include/asm-frv/ide.h b/include/asm-frv/ide.h index ae031ea..f0bd2cb 100644 --- a/include/asm-frv/ide.h +++ b/include/asm-frv/ide.h @@ -14,7 +14,6 @@ #define _ASM_IDE_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-frv/io.h b/include/asm-frv/io.h index 01247cb..7765f55 100644 --- a/include/asm-frv/io.h +++ b/include/asm-frv/io.h @@ -17,7 +17,6 @@ #define _ASM_IO_H #ifdef __KERNEL__ -#include #include #include #include @@ -41,13 +40,13 @@ static inline unsigned long _swapl(unsig //#define __iormb() asm volatile("membar") //#define __iowmb() asm volatile("membar") -#define __raw_readb(addr) __builtin_read8((void *) (addr)) -#define __raw_readw(addr) __builtin_read16((void *) (addr)) -#define __raw_readl(addr) __builtin_read32((void *) (addr)) +#define __raw_readb __builtin_read8 +#define __raw_readw __builtin_read16 +#define __raw_readl __builtin_read32 -#define __raw_writeb(datum, addr) __builtin_write8((void *) (addr), datum) -#define __raw_writew(datum, addr) __builtin_write16((void *) (addr), datum) -#define __raw_writel(datum, addr) __builtin_write32((void *) (addr), datum) +#define __raw_writeb(datum, addr) __builtin_write8(addr, datum) +#define __raw_writew(datum, addr) __builtin_write16(addr, datum) +#define __raw_writel(datum, addr) __builtin_write32(addr, datum) static inline void io_outsb(unsigned int addr, const void *buf, int len) { @@ -117,7 +116,7 @@ static inline void memset_io(volatile vo memset((void __force *) addr, val, count); } -static inline void memcpy_fromio(void *dst, volatile void __iomem *src, int count) +static inline void memcpy_fromio(void *dst, const volatile void __iomem *src, int count) { memcpy(dst, (void __force *) src, count); } @@ -129,12 +128,12 @@ static inline void memcpy_toio(volatile static inline uint8_t inb(unsigned long addr) { - return __builtin_read8((void *)addr); + return __builtin_read8((void __iomem *)addr); } static inline uint16_t inw(unsigned long addr) { - uint16_t ret = __builtin_read16((void *)addr); + uint16_t ret = __builtin_read16((void __iomem *)addr); if (__is_PCI_IO(addr)) ret = _swapw(ret); @@ -144,7 +143,7 @@ static inline uint16_t inw(unsigned long static inline uint32_t inl(unsigned long addr) { - uint32_t ret = __builtin_read32((void *)addr); + uint32_t ret = __builtin_read32((void __iomem *)addr); if (__is_PCI_IO(addr)) ret = _swapl(ret); @@ -154,21 +153,21 @@ static inline uint32_t inl(unsigned long static inline void outb(uint8_t datum, unsigned long addr) { - __builtin_write8((void *)addr, datum); + __builtin_write8((void __iomem *)addr, datum); } static inline void outw(uint16_t datum, unsigned long addr) { if (__is_PCI_IO(addr)) datum = _swapw(datum); - __builtin_write16((void *)addr, datum); + __builtin_write16((void __iomem *)addr, datum); } static inline void outl(uint32_t datum, unsigned long addr) { if (__is_PCI_IO(addr)) datum = _swapl(datum); - __builtin_write32((void *)addr, datum); + __builtin_write32((void __iomem *)addr, datum); } #define inb_p(addr) inb(addr) @@ -190,12 +189,12 @@ #define IO_SPACE_LIMIT 0xffffffff static inline uint8_t readb(const volatile void __iomem *addr) { - return __builtin_read8((volatile uint8_t __force *) addr); + return __builtin_read8((__force void volatile __iomem *) addr); } static inline uint16_t readw(const volatile void __iomem *addr) { - uint16_t ret = __builtin_read16((volatile uint16_t __force *)addr); + uint16_t ret = __builtin_read16((__force void volatile __iomem *)addr); if (__is_PCI_MEM(addr)) ret = _swapw(ret); @@ -204,7 +203,7 @@ static inline uint16_t readw(const volat static inline uint32_t readl(const volatile void __iomem *addr) { - uint32_t ret = __builtin_read32((volatile uint32_t __force *)addr); + uint32_t ret = __builtin_read32((__force void volatile __iomem *)addr); if (__is_PCI_MEM(addr)) ret = _swapl(ret); @@ -218,7 +217,7 @@ #define readl_relaxed readl static inline void writeb(uint8_t datum, volatile void __iomem *addr) { - __builtin_write8((volatile uint8_t __force *) addr, datum); + __builtin_write8(addr, datum); if (__is_PCI_MEM(addr)) __flush_PCI_writes(); } @@ -228,7 +227,7 @@ static inline void writew(uint16_t datum if (__is_PCI_MEM(addr)) datum = _swapw(datum); - __builtin_write16((volatile uint16_t __force *) addr, datum); + __builtin_write16(addr, datum); if (__is_PCI_MEM(addr)) __flush_PCI_writes(); } @@ -238,7 +237,7 @@ static inline void writel(uint32_t datum if (__is_PCI_MEM(addr)) datum = _swapl(datum); - __builtin_write32((volatile uint32_t __force *) addr, datum); + __builtin_write32(addr, datum); if (__is_PCI_MEM(addr)) __flush_PCI_writes(); } @@ -272,7 +271,7 @@ static inline void __iomem *ioremap_full return __ioremap(physaddr, size, IOMAP_FULL_CACHING); } -extern void iounmap(void __iomem *addr); +extern void iounmap(void volatile __iomem *addr); static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) { diff --git a/include/asm-frv/irq-routing.h b/include/asm-frv/irq-routing.h index 686fb2b..ac3ab90 100644 --- a/include/asm-frv/irq-routing.h +++ b/include/asm-frv/irq-routing.h @@ -51,7 +51,7 @@ struct irq_source { struct irq_level { int usage; int disable_count; - unsigned long flags; /* current SA_INTERRUPT and SA_SHIRQ settings */ + unsigned long flags; /* current IRQF_DISABLED and IRQF_SHARED settings */ spinlock_t lock; struct irq_source *sources; }; diff --git a/include/asm-frv/irq.h b/include/asm-frv/irq.h index 2c16d8d..58b6192 100644 --- a/include/asm-frv/irq.h +++ b/include/asm-frv/irq.h @@ -12,7 +12,6 @@ #ifndef _ASM_IRQ_H_ #define _ASM_IRQ_H_ -#include /* * the system has an on-CPU PIC and another PIC on the FPGA and other PICs on other peripherals, diff --git a/include/asm-frv/mb-regs.h b/include/asm-frv/mb-regs.h index 93fa732..219e5f9 100644 --- a/include/asm-frv/mb-regs.h +++ b/include/asm-frv/mb-regs.h @@ -16,6 +16,17 @@ #include #include #include +#ifndef __ASSEMBLY__ +/* gcc builtins, annotated */ + +unsigned long __builtin_read8(volatile void __iomem *); +unsigned long __builtin_read16(volatile void __iomem *); +unsigned long __builtin_read32(volatile void __iomem *); +void __builtin_write8(volatile void __iomem *, unsigned char); +void __builtin_write16(volatile void __iomem *, unsigned short); +void __builtin_write32(volatile void __iomem *, unsigned long); +#endif + #define __region_IO KERNEL_IO_START /* the region from 0xe0000000 to 0xffffffff has suitable * protection laid over the top for use in memory-mapped * I/O @@ -59,7 +70,7 @@ #define __region_PCI_IO (__region_CS2 + #define __region_PCI_MEM (__region_CS2 + 0x08000000UL) #define __flush_PCI_writes() \ do { \ - __builtin_write8((volatile void *) __region_PCI_MEM, 0); \ + __builtin_write8((volatile void __iomem *) __region_PCI_MEM, 0); \ } while(0) #define __is_PCI_IO(addr) \ @@ -83,15 +94,15 @@ #ifdef CONFIG_MB93090_MB00 #define __set_LEDS(X) \ do { \ if (mb93090_mb00_detected) \ - __builtin_write32((void *) __addr_LEDS(), ~(X)); \ + __builtin_write32((void __iomem *) __addr_LEDS(), ~(X)); \ } while (0) #else #define __set_LEDS(X) #endif #define __addr_LCD() (__region_CS2 + 0x01200008UL) -#define __get_LCD(B) __builtin_read32((volatile void *) (B)) -#define __set_LCD(B,X) __builtin_write32((volatile void *) (B), (X)) +#define __get_LCD(B) __builtin_read32((volatile void __iomem *) (B)) +#define __set_LCD(B,X) __builtin_write32((volatile void __iomem *) (B), (X)) #define LCD_D 0x000000ff /* LCD data bus */ #define LCD_RW 0x00000100 /* LCD R/W signal */ @@ -161,11 +172,11 @@ #define __get_CLKSW() 0UL #define __get_CLKIN() 66000000UL #define __addr_LEDS() (__region_CS2 + 0x00000023UL) -#define __set_LEDS(X) __builtin_write8((volatile void *) __addr_LEDS(), (X)) +#define __set_LEDS(X) __builtin_write8((volatile void __iomem *) __addr_LEDS(), (X)) #define __addr_FPGATR() (__region_CS2 + 0x00000030UL) -#define __set_FPGATR(X) __builtin_write32((volatile void *) __addr_FPGATR(), (X)) -#define __get_FPGATR() __builtin_read32((volatile void *) __addr_FPGATR()) +#define __set_FPGATR(X) __builtin_write32((volatile void __iomem *) __addr_FPGATR(), (X)) +#define __get_FPGATR() __builtin_read32((volatile void __iomem *) __addr_FPGATR()) #define MB93093_FPGA_FPGATR_AUDIO_CLK 0x00000003 @@ -180,7 +191,7 @@ #define MB93093_FPGA_FPGATR_AUDIO_CLK_02 #define MB93093_FPGA_SWR_PUSHSWMASK (0x1F<<26) #define MB93093_FPGA_SWR_PUSHSW4 (1<<29) -#define __addr_FPGA_SWR ((volatile void *)(__region_CS2 + 0x28UL)) +#define __addr_FPGA_SWR ((volatile void __iomem *)(__region_CS2 + 0x28UL)) #define __get_FPGA_PUSHSW1_5() (__builtin_read32(__addr_FPGA_SWR) & MB93093_FPGA_SWR_PUSHSWMASK) diff --git a/include/asm-frv/mmu_context.h b/include/asm-frv/mmu_context.h index 4fb9ea3..72edcaa 100644 --- a/include/asm-frv/mmu_context.h +++ b/include/asm-frv/mmu_context.h @@ -12,7 +12,6 @@ #ifndef _ASM_MMU_CONTEXT_H #define _ASM_MMU_CONTEXT_H -#include #include #include #include diff --git a/include/asm-frv/page.h b/include/asm-frv/page.h index dc0f7e0..134cc0c 100644 --- a/include/asm-frv/page.h +++ b/include/asm-frv/page.h @@ -3,7 +3,6 @@ #define _ASM_PAGE_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-frv/pci.h b/include/asm-frv/pci.h index 598b0c6..f35a451 100644 --- a/include/asm-frv/pci.h +++ b/include/asm-frv/pci.h @@ -13,7 +13,6 @@ #ifndef ASM_PCI_H #define ASM_PCI_H -#include #include #include #include diff --git a/include/asm-frv/pgalloc.h b/include/asm-frv/pgalloc.h index 1bd28f4..ce982a6 100644 --- a/include/asm-frv/pgalloc.h +++ b/include/asm-frv/pgalloc.h @@ -15,7 +15,6 @@ #ifndef _ASM_PGALLOC_H #define _ASM_PGALLOC_H -#include #include #include diff --git a/include/asm-frv/pgtable.h b/include/asm-frv/pgtable.h index d1c3b18..7af7485 100644 --- a/include/asm-frv/pgtable.h +++ b/include/asm-frv/pgtable.h @@ -16,7 +16,6 @@ #ifndef _ASM_PGTABLE_H #define _ASM_PGTABLE_H -#include #include #include #include diff --git a/include/asm-frv/processor.h b/include/asm-frv/processor.h index 5228c18..1c4dba1 100644 --- a/include/asm-frv/processor.h +++ b/include/asm-frv/processor.h @@ -12,7 +12,6 @@ #ifndef _ASM_PROCESSOR_H #define _ASM_PROCESSOR_H -#include #include #ifndef __ASSEMBLY__ diff --git a/include/asm-frv/segment.h b/include/asm-frv/segment.h index 61222f0..e3616a6 100644 --- a/include/asm-frv/segment.h +++ b/include/asm-frv/segment.h @@ -12,7 +12,6 @@ #ifndef _ASM_SEGMENT_H #define _ASM_SEGMENT_H -#include #ifndef __ASSEMBLY__ diff --git a/include/asm-frv/serial.h b/include/asm-frv/serial.h index 6917d55..dbb8259 100644 --- a/include/asm-frv/serial.h +++ b/include/asm-frv/serial.h @@ -6,7 +6,6 @@ * * Based on linux/include/asm-i386/serial.h */ -#include #include /* diff --git a/include/asm-frv/signal.h b/include/asm-frv/signal.h index 6736689..2079197 100644 --- a/include/asm-frv/signal.h +++ b/include/asm-frv/signal.h @@ -74,7 +74,6 @@ #define SIGRTMAX (_NSIG-1) * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -94,7 +93,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 @@ -114,13 +112,13 @@ struct old_sigaction { __sighandler_t sa_handler; old_sigset_t sa_mask; unsigned long sa_flags; - void (*sa_restorer)(void); + __sigrestore_t sa_restorer; }; struct sigaction { __sighandler_t sa_handler; unsigned long sa_flags; - void (*sa_restorer)(void); + __sigrestore_t sa_restorer; sigset_t sa_mask; /* mask last for extensibility */ }; @@ -146,7 +144,7 @@ #define sa_sigaction _u._sa_sigaction #endif /* __KERNEL__ */ typedef struct sigaltstack { - void *ss_sp; + void __user *ss_sp; int ss_flags; size_t ss_size; } stack_t; diff --git a/include/asm-frv/smp.h b/include/asm-frv/smp.h index 5ca7716..38349ec 100644 --- a/include/asm-frv/smp.h +++ b/include/asm-frv/smp.h @@ -1,7 +1,6 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H -#include #ifdef CONFIG_SMP #error SMP not supported diff --git a/include/asm-frv/socket.h b/include/asm-frv/socket.h index 7177f8b..31db18f 100644 --- a/include/asm-frv/socket.h +++ b/include/asm-frv/socket.h @@ -48,6 +48,7 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-frv/system.h b/include/asm-frv/system.h index 1734ed9..351863d 100644 --- a/include/asm-frv/system.h +++ b/include/asm-frv/system.h @@ -12,7 +12,6 @@ #ifndef _ASM_SYSTEM_H #define _ASM_SYSTEM_H -#include /* get configuration macros */ #include #include diff --git a/include/asm-frv/tlbflush.h b/include/asm-frv/tlbflush.h index bc34626..da3a317 100644 --- a/include/asm-frv/tlbflush.h +++ b/include/asm-frv/tlbflush.h @@ -12,7 +12,6 @@ #ifndef _ASM_TLBFLUSH_H #define _ASM_TLBFLUSH_H -#include #include #include diff --git a/include/asm-frv/types.h b/include/asm-frv/types.h index 2560f59..1b6d192 100644 --- a/include/asm-frv/types.h +++ b/include/asm-frv/types.h @@ -46,7 +46,6 @@ #define BITS_PER_LONG 32 #ifndef __ASSEMBLY__ -#include typedef signed char s8; typedef unsigned char u8; diff --git a/include/asm-frv/uaccess.h b/include/asm-frv/uaccess.h index a1d1404..3d90e10 100644 --- a/include/asm-frv/uaccess.h +++ b/include/asm-frv/uaccess.h @@ -22,7 +22,7 @@ #include #define HAVE_ARCH_UNMAPPED_AREA /* we decide where to put mmaps */ -#define __ptr(x) ((unsigned long *)(x)) +#define __ptr(x) ((unsigned long __force *)(x)) #define VERIFY_READ 0 #define VERIFY_WRITE 1 @@ -64,7 +64,7 @@ #endif #define __range_ok(addr,size) ___range_ok((unsigned long) (addr), (unsigned long) (size)) -#define access_ok(type,addr,size) (__range_ok((addr), (size)) == 0) +#define access_ok(type,addr,size) (__range_ok((void __user *)(addr), (size)) == 0) #define __access_ok(addr,size) (__range_ok((addr), (size)) == 0) /* @@ -97,6 +97,7 @@ ({ \ int __pu_err = 0; \ \ typeof(*(ptr)) __pu_val = (x); \ + __chk_user_ptr(ptr); \ \ switch (sizeof (*(ptr))) { \ case 1: \ @@ -120,7 +121,7 @@ ({ \ #define put_user(x, ptr) \ ({ \ - typeof(&*ptr) _p = (ptr); \ + typeof(*(ptr)) __user *_p = (ptr); \ int _e; \ \ _e = __range_ok(_p, sizeof(*_p)); \ @@ -175,33 +176,44 @@ #endif */ #define __get_user(x, ptr) \ ({ \ - typeof(*(ptr)) __gu_val = 0; \ int __gu_err = 0; \ + __chk_user_ptr(ptr); \ \ switch (sizeof(*(ptr))) { \ - case 1: \ - __get_user_asm(__gu_err, *(u8*)&__gu_val, ptr, "ub", "=r"); \ + case 1: { \ + unsigned char __gu_val; \ + __get_user_asm(__gu_err, __gu_val, ptr, "ub", "=r"); \ + (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \ break; \ - case 2: \ - __get_user_asm(__gu_err, *(u16*)&__gu_val, ptr, "uh", "=r"); \ + } \ + case 2: { \ + unsigned short __gu_val; \ + __get_user_asm(__gu_err, __gu_val, ptr, "uh", "=r"); \ + (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \ break; \ - case 4: \ - __get_user_asm(__gu_err, *(u32*)&__gu_val, ptr, "", "=r"); \ + } \ + case 4: { \ + unsigned int __gu_val; \ + __get_user_asm(__gu_err, __gu_val, ptr, "", "=r"); \ + (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \ break; \ - case 8: \ - __get_user_asm(__gu_err, *(u64*)&__gu_val, ptr, "d", "=e"); \ + } \ + case 8: { \ + unsigned long long __gu_val; \ + __get_user_asm(__gu_err, __gu_val, ptr, "d", "=e"); \ + (x) = *(__force __typeof__(*(ptr)) *) &__gu_val; \ break; \ + } \ default: \ __gu_err = __get_user_bad(); \ break; \ } \ - (x) = __gu_val; \ __gu_err; \ }) #define get_user(x, ptr) \ ({ \ - typeof(&*ptr) _p = (ptr); \ + const typeof(*(ptr)) __user *_p = (ptr);\ int _e; \ \ _e = __range_ok(_p, sizeof(*_p)); \ @@ -248,19 +260,20 @@ #endif /* * */ +#define ____force(x) (__force void *)(void __user *)(x) #ifdef CONFIG_MMU extern long __memset_user(void *dst, unsigned long count); extern long __memcpy_user(void *dst, const void *src, unsigned long count); -#define clear_user(dst,count) __memset_user((dst), (count)) -#define __copy_from_user_inatomic(to, from, n) __memcpy_user((to), (from), (n)) -#define __copy_to_user_inatomic(to, from, n) __memcpy_user((to), (from), (n)) +#define clear_user(dst,count) __memset_user(____force(dst), (count)) +#define __copy_from_user_inatomic(to, from, n) __memcpy_user((to), ____force(from), (n)) +#define __copy_to_user_inatomic(to, from, n) __memcpy_user(____force(to), (from), (n)) #else -#define clear_user(dst,count) (memset((dst), 0, (count)), 0) -#define __copy_from_user_inatomic(to, from, n) (memcpy((to), (from), (n)), 0) -#define __copy_to_user_inatomic(to, from, n) (memcpy((to), (from), (n)), 0) +#define clear_user(dst,count) (memset(____force(dst), 0, (count)), 0) +#define __copy_from_user_inatomic(to, from, n) (memcpy((to), ____force(from), (n)), 0) +#define __copy_to_user_inatomic(to, from, n) (memcpy(____force(to), (from), (n)), 0) #endif @@ -278,7 +291,7 @@ __copy_from_user(void *to, const void __ return __copy_from_user_inatomic(to, from, n); } -static inline long copy_from_user(void *to, const void *from, unsigned long n) +static inline long copy_from_user(void *to, const void __user *from, unsigned long n) { unsigned long ret = n; @@ -291,16 +304,13 @@ static inline long copy_from_user(void * return ret; } -static inline long copy_to_user(void *to, const void *from, unsigned long n) +static inline long copy_to_user(void __user *to, const void *from, unsigned long n) { return likely(__access_ok(to, n)) ? __copy_to_user(to, from, n) : n; } -#define copy_to_user_ret(to,from,n,retval) ({ if (copy_to_user(to,from,n)) return retval; }) -#define copy_from_user_ret(to,from,n,retval) ({ if (copy_from_user(to,from,n)) return retval; }) - -extern long strncpy_from_user(char *dst, const char *src, long count); -extern long strnlen_user(const char *src, long count); +extern long strncpy_from_user(char *dst, const char __user *src, long count); +extern long strnlen_user(const char __user *src, long count); #define strlen_user(str) strnlen_user(str, 32767) diff --git a/include/asm-frv/unaligned.h b/include/asm-frv/unaligned.h index a0d199b..dc8e9c9 100644 --- a/include/asm-frv/unaligned.h +++ b/include/asm-frv/unaligned.h @@ -12,7 +12,6 @@ #ifndef _ASM_UNALIGNED_H #define _ASM_UNALIGNED_H -#include /* * Unaligned accesses on uClinux can't be performed in a fault handler - the diff --git a/include/asm-frv/unistd.h b/include/asm-frv/unistd.h index 2662a3e..b80dbd8 100644 --- a/include/asm-frv/unistd.h +++ b/include/asm-frv/unistd.h @@ -306,7 +306,7 @@ #define __NR_mkdirat 296 #define __NR_mknodat 297 #define __NR_fchownat 298 #define __NR_futimesat 299 -#define __NR_newfstatat 300 +#define __NR_fstatat64 300 #define __NR_unlinkat 301 #define __NR_renameat 302 #define __NR_linkat 303 @@ -317,6 +317,8 @@ #define __NR_faccessat 307 #define __NR_pselect6 308 #define __NR_ppoll 309 +#ifdef __KERNEL__ + #define NR_syscalls 310 /* @@ -458,28 +460,10 @@ #include * some others too. */ #define __NR__exit __NR_exit -static inline _syscall0(int,pause) -static inline _syscall0(int,sync) -static inline _syscall0(pid_t,setsid) -static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) -static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) -static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) -static inline _syscall1(int,dup,int,fd) static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) -static inline _syscall3(int,open,const char *,file,int,flag,int,mode) -static inline _syscall1(int,close,int,fd) -static inline _syscall1(int,_exit,int,exitcode) -static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) -static inline _syscall1(int,delete_module,const char *,name) -static inline pid_t wait(int * wait_stat) -{ - return waitpid(-1,wait_stat,0); -} +#endif /* __KERNEL_SYSCALLS__ */ -#endif - -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION /* #define __ARCH_WANT_OLD_READDIR */ #define __ARCH_WANT_OLD_STAT @@ -503,7 +487,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGSUSPEND -#endif /* * "Conditional" syscalls @@ -515,4 +498,5 @@ #ifndef cond_syscall #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") #endif +#endif /* __KERNEL__ */ #endif /* _ASM_UNISTD_H_ */ diff --git a/include/asm-frv/virtconvert.h b/include/asm-frv/virtconvert.h index a29a0ae..59788fa 100644 --- a/include/asm-frv/virtconvert.h +++ b/include/asm-frv/virtconvert.h @@ -17,7 +17,6 @@ #define _ASM_VIRTCONVERT_H #ifdef __KERNEL__ -#include #include #ifdef CONFIG_MMU diff --git a/include/asm-generic/Kbuild b/include/asm-generic/Kbuild new file mode 100644 index 0000000..70594b2 --- /dev/null +++ b/include/asm-generic/Kbuild @@ -0,0 +1,3 @@ +header-y += atomic.h errno-base.h errno.h fcntl.h ioctl.h ipc.h mman.h \ + signal.h statfs.h +unifdef-y := resource.h siginfo.h diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm new file mode 100644 index 0000000..d8d0bce --- /dev/null +++ b/include/asm-generic/Kbuild.asm @@ -0,0 +1,11 @@ +unifdef-y += a.out.h auxvec.h byteorder.h errno.h fcntl.h ioctl.h \ + ioctls.h ipcbuf.h irq.h mman.h msgbuf.h param.h poll.h \ + posix_types.h ptrace.h resource.h sembuf.h shmbuf.h shmparam.h \ + sigcontext.h siginfo.h signal.h socket.h sockios.h stat.h \ + statfs.h termbits.h termios.h timex.h types.h unistd.h user.h + +# These really shouldn't be exported +unifdef-y += atomic.h io.h + +# These probably shouldn't be exported +unifdef-y += elf.h page.h diff --git a/include/asm-generic/audit_change_attr.h b/include/asm-generic/audit_change_attr.h new file mode 100644 index 0000000..cb05bf6 --- /dev/null +++ b/include/asm-generic/audit_change_attr.h @@ -0,0 +1,18 @@ +__NR_chmod, +__NR_fchmod, +__NR_chown, +__NR_fchown, +__NR_lchown, +__NR_setxattr, +__NR_lsetxattr, +__NR_fsetxattr, +__NR_removexattr, +__NR_lremovexattr, +__NR_fremovexattr, +__NR_fchownat, +__NR_fchmodat, +#ifdef __NR_chown32 +__NR_chown32, +__NR_fchown32, +__NR_lchown32, +#endif diff --git a/include/asm-generic/audit_dir_write.h b/include/asm-generic/audit_dir_write.h new file mode 100644 index 0000000..161a7a5 --- /dev/null +++ b/include/asm-generic/audit_dir_write.h @@ -0,0 +1,14 @@ +__NR_rename, +__NR_mkdir, +__NR_rmdir, +__NR_creat, +__NR_link, +__NR_unlink, +__NR_symlink, +__NR_mknod, +__NR_mkdirat, +__NR_mknodat, +__NR_unlinkat, +__NR_renameat, +__NR_linkat, +__NR_symlinkat, diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index 1a565a9..8ceab7b 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h @@ -2,7 +2,6 @@ #ifndef _ASM_GENERIC_BUG_H #define _ASM_GENERIC_BUG_H #include -#include #ifdef CONFIG_BUG #ifndef HAVE_ARCH_BUG @@ -39,4 +38,23 @@ #define WARN_ON(condition) do { if (cond #endif #endif +#define WARN_ON_ONCE(condition) \ +({ \ + static int __warn_once = 1; \ + int __ret = 0; \ + \ + if (unlikely((condition) && __warn_once)) { \ + __warn_once = 0; \ + WARN_ON(1); \ + __ret = 1; \ + } \ + __ret; \ +}) + +#ifdef CONFIG_SMP +# define WARN_ON_SMP(x) WARN_ON(x) +#else +# define WARN_ON_SMP(x) do { } while (0) +#endif + #endif diff --git a/include/asm-generic/cputime.h b/include/asm-generic/cputime.h index 6f17856..09204e4 100644 --- a/include/asm-generic/cputime.h +++ b/include/asm-generic/cputime.h @@ -24,7 +24,9 @@ typedef u64 cputime64_t; #define cputime64_zero (0ULL) #define cputime64_add(__a, __b) ((__a) + (__b)) +#define cputime64_sub(__a, __b) ((__a) - (__b)) #define cputime64_to_jiffies64(__ct) (__ct) +#define jiffies64_to_cputime64(__jif) (__jif) #define cputime_to_cputime64(__ct) ((u64) __ct) diff --git a/include/asm-generic/dma-mapping.h b/include/asm-generic/dma-mapping.h index 1b35620..b541e48 100644 --- a/include/asm-generic/dma-mapping.h +++ b/include/asm-generic/dma-mapping.h @@ -7,7 +7,6 @@ #ifndef _ASM_GENERIC_DMA_MAPPING_H #define _ASM_GENERIC_DMA_MAPPING_H -#include #ifdef CONFIG_PCI diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index b663520..c154b9d 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h @@ -1,7 +1,6 @@ #ifndef _ASM_GENERIC_FCNTL_H #define _ASM_GENERIC_FCNTL_H -#include #include /* open/fcntl - O_SYNC is only implemented on blocks devices and on files diff --git a/include/asm-generic/local.h b/include/asm-generic/local.h index 9291c24..ab46929 100644 --- a/include/asm-generic/local.h +++ b/include/asm-generic/local.h @@ -1,7 +1,6 @@ #ifndef _ASM_GENERIC_LOCAL_H #define _ASM_GENERIC_LOCAL_H -#include #include #include #include diff --git a/include/asm-generic/memory_model.h b/include/asm-generic/memory_model.h index 0cfb086..8078cbd 100644 --- a/include/asm-generic/memory_model.h +++ b/include/asm-generic/memory_model.h @@ -23,29 +23,23 @@ #endif #endif /* CONFIG_DISCONTIGMEM */ -#ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE -struct page; -/* this is useful when inlined pfn_to_page is too big */ -extern struct page *pfn_to_page(unsigned long pfn); -extern unsigned long page_to_pfn(struct page *page); -#else /* * supports 3 memory models. */ #if defined(CONFIG_FLATMEM) -#define pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) -#define page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ +#define __pfn_to_page(pfn) (mem_map + ((pfn) - ARCH_PFN_OFFSET)) +#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + \ ARCH_PFN_OFFSET) #elif defined(CONFIG_DISCONTIGMEM) -#define pfn_to_page(pfn) \ +#define __pfn_to_page(pfn) \ ({ unsigned long __pfn = (pfn); \ unsigned long __nid = arch_pfn_to_nid(pfn); \ NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\ }) -#define page_to_pfn(pg) \ +#define __page_to_pfn(pg) \ ({ struct page *__pg = (pg); \ struct pglist_data *__pgdat = NODE_DATA(page_to_nid(__pg)); \ (unsigned long)(__pg - __pgdat->node_mem_map) + \ @@ -57,18 +51,27 @@ #elif defined(CONFIG_SPARSEMEM) * Note: section's mem_map is encorded to reflect its start_pfn. * section[i].section_mem_map == mem_map's address - start_pfn; */ -#define page_to_pfn(pg) \ +#define __page_to_pfn(pg) \ ({ struct page *__pg = (pg); \ int __sec = page_to_section(__pg); \ __pg - __section_mem_map_addr(__nr_to_section(__sec)); \ }) -#define pfn_to_page(pfn) \ +#define __pfn_to_page(pfn) \ ({ unsigned long __pfn = (pfn); \ struct mem_section *__sec = __pfn_to_section(__pfn); \ __section_mem_map_addr(__sec) + __pfn; \ }) #endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */ + +#ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE +struct page; +/* this is useful when inlined pfn_to_page is too big */ +extern struct page *pfn_to_page(unsigned long pfn); +extern unsigned long page_to_pfn(struct page *page); +#else +#define page_to_pfn __page_to_pfn +#define pfn_to_page __pfn_to_page #endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ #endif /* __ASSEMBLY__ */ diff --git a/include/asm-generic/mutex-null.h b/include/asm-generic/mutex-null.h index 5cf8b7c..254a126 100644 --- a/include/asm-generic/mutex-null.h +++ b/include/asm-generic/mutex-null.h @@ -10,15 +10,10 @@ #ifndef _ASM_GENERIC_MUTEX_NULL_H #define _ASM_GENERIC_MUTEX_NULL_H -/* extra parameter only needed for mutex debugging: */ -#ifndef __IP__ -# define __IP__ -#endif - -#define __mutex_fastpath_lock(count, fail_fn) fail_fn(count __RET_IP__) -#define __mutex_fastpath_lock_retval(count, fail_fn) fail_fn(count __RET_IP__) -#define __mutex_fastpath_unlock(count, fail_fn) fail_fn(count __RET_IP__) -#define __mutex_fastpath_trylock(count, fail_fn) fail_fn(count) -#define __mutex_slowpath_needs_to_unlock() 1 +#define __mutex_fastpath_lock(count, fail_fn) fail_fn(count) +#define __mutex_fastpath_lock_retval(count, fail_fn) fail_fn(count) +#define __mutex_fastpath_unlock(count, fail_fn) fail_fn(count) +#define __mutex_fastpath_trylock(count, fail_fn) fail_fn(count) +#define __mutex_slowpath_needs_to_unlock() 1 #endif diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h index c0caf43..e160e04 100644 --- a/include/asm-generic/percpu.h +++ b/include/asm-generic/percpu.h @@ -7,6 +7,8 @@ #ifdef CONFIG_SMP extern unsigned long __per_cpu_offset[NR_CPUS]; +#define per_cpu_offset(x) (__per_cpu_offset[x]) + /* Separate out the type, so (int[3], foo) works. */ #define DEFINE_PER_CPU(type, name) \ __attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name @@ -14,6 +16,7 @@ #define DEFINE_PER_CPU(type, name) \ /* var is in discarded region: offset to particular copy we want */ #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu])) #define __get_cpu_var(var) per_cpu(var, smp_processor_id()) +#define __raw_get_cpu_var(var) per_cpu(var, raw_smp_processor_id()) /* A macro to avoid #include hell... */ #define percpu_modcopy(pcpudst, src, size) \ @@ -30,6 +33,7 @@ #define DEFINE_PER_CPU(type, name) \ #define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var +#define __raw_get_cpu_var(var) per_cpu__##var #endif /* SMP */ diff --git a/include/asm-generic/rtc.h b/include/asm-generic/rtc.h index cef08db..4087037 100644 --- a/include/asm-generic/rtc.h +++ b/include/asm-generic/rtc.h @@ -114,6 +114,7 @@ #endif /* Set the current date and time in the real time clock. */ static inline int set_rtc_time(struct rtc_time *time) { + unsigned long flags; unsigned char mon, day, hrs, min, sec; unsigned char save_control, save_freq_select; unsigned int yrs; @@ -131,7 +132,7 @@ #endif if (yrs > 255) /* They are unsigned */ return -EINVAL; - spin_lock_irq(&rtc_lock); + spin_lock_irqsave(&rtc_lock, flags); #ifdef CONFIG_MACH_DECSTATION real_yrs = yrs; leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) || @@ -152,7 +153,7 @@ #endif * whether the chip is in binary mode or not. */ if (yrs > 169) { - spin_unlock_irq(&rtc_lock); + spin_unlock_irqrestore(&rtc_lock, flags); return -EINVAL; } @@ -187,7 +188,7 @@ #endif CMOS_WRITE(save_control, RTC_CONTROL); CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - spin_unlock_irq(&rtc_lock); + spin_unlock_irqrestore(&rtc_lock, flags); return 0; } diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h index 0b49f9e..962cad7 100644 --- a/include/asm-generic/sections.h +++ b/include/asm-generic/sections.h @@ -14,5 +14,6 @@ extern char _end[]; extern char __per_cpu_start[], __per_cpu_end[]; extern char __kprobes_text_start[], __kprobes_text_end[]; extern char __initdata_begin[], __initdata_end[]; +extern char __start_rodata[], __end_rodata[]; #endif /* _ASM_GENERIC_SECTIONS_H_ */ diff --git a/include/asm-generic/signal.h b/include/asm-generic/signal.h index 9418d6e..dae1d87 100644 --- a/include/asm-generic/signal.h +++ b/include/asm-generic/signal.h @@ -1,3 +1,8 @@ +#ifndef __ASM_GENERIC_SIGNAL_H +#define __ASM_GENERIC_SIGNAL_H + +#include + #ifndef SIG_BLOCK #define SIG_BLOCK 0 /* for blocking signals */ #endif @@ -19,3 +24,5 @@ #define SIG_DFL ((__force __sighandler_t #define SIG_IGN ((__force __sighandler_t)1) /* ignore signal */ #define SIG_ERR ((__force __sighandler_t)-1) /* error return from signal */ #endif + +#endif /* __ASM_GENERIC_SIGNAL_H */ diff --git a/include/asm-generic/tlb.h b/include/asm-generic/tlb.h index cdd4145..867d900 100644 --- a/include/asm-generic/tlb.h +++ b/include/asm-generic/tlb.h @@ -13,7 +13,6 @@ #ifndef _ASM_GENERIC__TLB_H #define _ASM_GENERIC__TLB_H -#include #include #include #include diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 9d11550..db5a373 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -58,6 +58,20 @@ #define RODATA \ VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .; \ } \ \ + /* Kernel symbol table: Normal unused symbols */ \ + __ksymtab_unused : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___ksymtab_unused) = .; \ + *(__ksymtab_unused) \ + VMLINUX_SYMBOL(__stop___ksymtab_unused) = .; \ + } \ + \ + /* Kernel symbol table: GPL-only unused symbols */ \ + __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .; \ + *(__ksymtab_unused_gpl) \ + VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .; \ + } \ + \ /* Kernel symbol table: GPL-future-only symbols */ \ __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .; \ @@ -79,6 +93,20 @@ #define RODATA \ VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .; \ } \ \ + /* Kernel symbol table: Normal unused symbols */ \ + __kcrctab_unused : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___kcrctab_unused) = .; \ + *(__kcrctab_unused) \ + VMLINUX_SYMBOL(__stop___kcrctab_unused) = .; \ + } \ + \ + /* Kernel symbol table: GPL-only unused symbols */ \ + __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .; \ + *(__kcrctab_unused_gpl) \ + VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .; \ + } \ + \ /* Kernel symbol table: GPL-future-only symbols */ \ __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .; \ diff --git a/include/asm-h8300/Kbuild b/include/asm-h8300/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-h8300/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-h8300/bitops.h b/include/asm-h8300/bitops.h index 574f57b..d76299c 100644 --- a/include/asm-h8300/bitops.h +++ b/include/asm-h8300/bitops.h @@ -6,7 +6,6 @@ #define _H8300_BITOPS_H * Copyright 2002, Yoshinori Sato */ -#include #include #include diff --git a/include/asm-h8300/dma.h b/include/asm-h8300/dma.h index 3708681..3edbaaa 100644 --- a/include/asm-h8300/dma.h +++ b/include/asm-h8300/dma.h @@ -1,7 +1,6 @@ #ifndef _H8300_DMA_H #define _H8300_DMA_H -#include /* * Set number of channels of DMA on ColdFire for different implementations. diff --git a/include/asm-h8300/elf.h b/include/asm-h8300/elf.h index f4af155..7ba6a0a 100644 --- a/include/asm-h8300/elf.h +++ b/include/asm-h8300/elf.h @@ -5,7 +5,6 @@ #define __ASMH8300_ELF_H * ELF register definitions.. */ -#include #include #include diff --git a/include/asm-h8300/hardirq.h b/include/asm-h8300/hardirq.h index e961bfe..18fa793 100644 --- a/include/asm-h8300/hardirq.h +++ b/include/asm-h8300/hardirq.h @@ -2,7 +2,6 @@ #ifndef __H8300_HARDIRQ_H #define __H8300_HARDIRQ_H #include -#include #include #include #include diff --git a/include/asm-h8300/io.h b/include/asm-h8300/io.h index 1773e37..91b7487 100644 --- a/include/asm-h8300/io.h +++ b/include/asm-h8300/io.h @@ -3,7 +3,6 @@ #define _H8300_IO_H #ifdef __KERNEL__ -#include #include #if defined(CONFIG_H83007) || defined(CONFIG_H83068) diff --git a/include/asm-h8300/irq.h b/include/asm-h8300/irq.h index 73065f5..42a3ac4 100644 --- a/include/asm-h8300/irq.h +++ b/include/asm-h8300/irq.h @@ -63,8 +63,4 @@ extern void enable_irq(unsigned int); extern void disable_irq(unsigned int); #define disable_irq_nosync(x) disable_irq(x) -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif /* _H8300_IRQ_H_ */ diff --git a/include/asm-h8300/keyboard.h b/include/asm-h8300/keyboard.h index b05d113..fbad65e 100644 --- a/include/asm-h8300/keyboard.h +++ b/include/asm-h8300/keyboard.h @@ -7,7 +7,6 @@ #ifndef _H8300_KEYBOARD_H #define _H8300_KEYBOARD_H -#include /* dummy i.e. no real keyboard */ #define kbd_setkeycode(x...) (-ENOSYS) diff --git a/include/asm-h8300/mmu_context.h b/include/asm-h8300/mmu_context.h index 23b555b..855721a 100644 --- a/include/asm-h8300/mmu_context.h +++ b/include/asm-h8300/mmu_context.h @@ -1,7 +1,6 @@ #ifndef __H8300_MMU_CONTEXT_H #define __H8300_MMU_CONTEXT_H -#include #include #include #include diff --git a/include/asm-h8300/page.h b/include/asm-h8300/page.h index 6472c9f..f9f9d3e 100644 --- a/include/asm-h8300/page.h +++ b/include/asm-h8300/page.h @@ -1,7 +1,6 @@ #ifndef _H8300_PAGE_H #define _H8300_PAGE_H -#include /* PAGE_SHIFT determines the page size */ diff --git a/include/asm-h8300/page_offset.h b/include/asm-h8300/page_offset.h index 8cc6e17..f870646 100644 --- a/include/asm-h8300/page_offset.h +++ b/include/asm-h8300/page_offset.h @@ -1,4 +1,3 @@ -#include #define PAGE_OFFSET_RAW 0x00000000 diff --git a/include/asm-h8300/param.h b/include/asm-h8300/param.h index 126dddf..c25806e 100644 --- a/include/asm-h8300/param.h +++ b/include/asm-h8300/param.h @@ -1,7 +1,6 @@ #ifndef _H8300_PARAM_H #define _H8300_PARAM_H -#include #ifndef HZ #define HZ 100 diff --git a/include/asm-h8300/pgtable.h b/include/asm-h8300/pgtable.h index f6e296f..8b7c685 100644 --- a/include/asm-h8300/pgtable.h +++ b/include/asm-h8300/pgtable.h @@ -3,7 +3,6 @@ #define _H8300_PGTABLE_H #include -#include #include #include #include diff --git a/include/asm-h8300/processor.h b/include/asm-h8300/processor.h index c6f0a71..c7e2f45 100644 --- a/include/asm-h8300/processor.h +++ b/include/asm-h8300/processor.h @@ -17,7 +17,6 @@ #define __ASM_H8300_PROCESSOR_H */ #define current_text_addr() ({ __label__ _l; _l: &&_l;}) -#include #include #include #include diff --git a/include/asm-h8300/semaphore-helper.h b/include/asm-h8300/semaphore-helper.h index 29e0fbf..4fea36b 100644 --- a/include/asm-h8300/semaphore-helper.h +++ b/include/asm-h8300/semaphore-helper.h @@ -10,7 +10,6 @@ #define _H8300_SEMAPHORE_HELPER_H * m68k version by Andreas Schwab */ -#include #include /* diff --git a/include/asm-h8300/shm.h b/include/asm-h8300/shm.h index bec7585..ed6623c 100644 --- a/include/asm-h8300/shm.h +++ b/include/asm-h8300/shm.h @@ -1,7 +1,6 @@ #ifndef _H8300_SHM_H #define _H8300_SHM_H -#include /* format of page table entries that correspond to shared memory pages currently out in swap space (see also mm/swap.c): diff --git a/include/asm-h8300/signal.h b/include/asm-h8300/signal.h index 8eccdc1..7bc1504 100644 --- a/include/asm-h8300/signal.h +++ b/include/asm-h8300/signal.h @@ -74,7 +74,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -94,7 +93,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-h8300/socket.h b/include/asm-h8300/socket.h index d98cf85..ebc830f 100644 --- a/include/asm-h8300/socket.h +++ b/include/asm-h8300/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-h8300/system.h b/include/asm-h8300/system.h index 8e81cf6..134e092 100644 --- a/include/asm-h8300/system.h +++ b/include/asm-h8300/system.h @@ -1,7 +1,6 @@ #ifndef _H8300_SYSTEM_H #define _H8300_SYSTEM_H -#include /* get configuration macros */ #include /* diff --git a/include/asm-h8300/unaligned.h b/include/asm-h8300/unaligned.h index 8a93961..ffb67f4 100644 --- a/include/asm-h8300/unaligned.h +++ b/include/asm-h8300/unaligned.h @@ -1,7 +1,6 @@ #ifndef __H8300_UNALIGNED_H #define __H8300_UNALIGNED_H -#include /* Use memmove here, so gcc does not insert a __builtin_memcpy. */ diff --git a/include/asm-h8300/unistd.h b/include/asm-h8300/unistd.h index adb0515..226dd59 100644 --- a/include/asm-h8300/unistd.h +++ b/include/asm-h8300/unistd.h @@ -292,6 +292,8 @@ #define __NR_add_key 286 #define __NR_request_key 287 #define __NR_keyctl 288 +#ifdef __KERNEL__ + #define NR_syscalls 289 @@ -460,7 +462,6 @@ type name(atype a, btype b, ctype c, dty __syscall_return(type, __res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -483,7 +484,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -534,7 +534,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -543,4 +543,5 @@ #define cond_syscall(name) \ asm (".weak\t_" #name "\n" \ ".set\t_" #name ",_sys_ni_syscall"); +#endif /* __KERNEL__ */ #endif /* _ASM_H8300_UNISTD_H_ */ diff --git a/include/asm-h8300/virtconvert.h b/include/asm-h8300/virtconvert.h index 3b344c1..ee7d5ea 100644 --- a/include/asm-h8300/virtconvert.h +++ b/include/asm-h8300/virtconvert.h @@ -7,7 +7,6 @@ #define __H8300_VIRT_CONVERT__ #ifdef __KERNEL__ -#include #include #include diff --git a/include/asm-i386/Kbuild b/include/asm-i386/Kbuild new file mode 100644 index 0000000..c064a8e --- /dev/null +++ b/include/asm-i386/Kbuild @@ -0,0 +1,5 @@ +include include/asm-generic/Kbuild.asm + +header-y += boot.h cpufeature.h debugreg.h ldt.h setup.h ucontext.h + +unifdef-y += mtrr.h vm86.h diff --git a/include/asm-i386/alternative.h b/include/asm-i386/alternative.h index e201dec..96adbab 100644 --- a/include/asm-i386/alternative.h +++ b/include/asm-i386/alternative.h @@ -3,6 +3,10 @@ #define _I386_ALTERNATIVE_H #ifdef __KERNEL__ +#include + +#include + struct alt_instr { u8 *instr; /* original instruction */ u8 *replacement; @@ -15,11 +19,19 @@ struct alt_instr { extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); struct module; +#ifdef CONFIG_SMP extern void alternatives_smp_module_add(struct module *mod, char *name, void *locks, void *locks_end, void *text, void *text_end); extern void alternatives_smp_module_del(struct module *mod); extern void alternatives_smp_switch(int smp); +#else +static inline void alternatives_smp_module_add(struct module *mod, char *name, + void *locks, void *locks_end, + void *text, void *text_end) {} +static inline void alternatives_smp_module_del(struct module *mod) {} +static inline void alternatives_smp_switch(int smp) {} +#endif #endif diff --git a/include/asm-i386/apic.h b/include/asm-i386/apic.h index 288233f..2c1e371 100644 --- a/include/asm-i386/apic.h +++ b/include/asm-i386/apic.h @@ -1,7 +1,6 @@ #ifndef __ASM_APIC_H #define __ASM_APIC_H -#include #include #include #include @@ -112,24 +111,12 @@ extern void init_apic_mappings (void); extern void smp_local_timer_interrupt (struct pt_regs * regs); extern void setup_boot_APIC_clock (void); extern void setup_secondary_APIC_clock (void); -extern void setup_apic_nmi_watchdog (void); -extern int reserve_lapic_nmi(void); -extern void release_lapic_nmi(void); -extern void disable_timer_nmi_watchdog(void); -extern void enable_timer_nmi_watchdog(void); -extern void nmi_watchdog_tick (struct pt_regs * regs); extern int APIC_init_uniprocessor (void); extern void disable_APIC_timer(void); extern void enable_APIC_timer(void); extern void enable_NMI_through_LVT0 (void * dummy); -extern unsigned int nmi_watchdog; -#define NMI_NONE 0 -#define NMI_IO_APIC 1 -#define NMI_LOCAL_APIC 2 -#define NMI_INVALID 3 - extern int disable_timer_pin_1; void smp_send_timer_broadcast_ipi(struct pt_regs *regs); @@ -139,8 +126,6 @@ #define ARCH_APICTIMER_STOPS_ON_C3 1 extern int timer_over_8254; -extern int modern_apic(void); - #else /* !CONFIG_X86_LOCAL_APIC */ static inline void lapic_shutdown(void) { } diff --git a/include/asm-i386/apicdef.h b/include/asm-i386/apicdef.h index 5e4a35a..9f69953 100644 --- a/include/asm-i386/apicdef.h +++ b/include/asm-i386/apicdef.h @@ -121,7 +121,6 @@ #define MAX_IO_APICS 64 */ #define u32 unsigned int -#define lapic ((volatile struct local_apic *)APIC_BASE) struct local_apic { diff --git a/include/asm-i386/atomic.h b/include/asm-i386/atomic.h index 4ddce52..4f061fa 100644 --- a/include/asm-i386/atomic.h +++ b/include/asm-i386/atomic.h @@ -1,7 +1,6 @@ #ifndef __ARCH_I386_ATOMIC__ #define __ARCH_I386_ATOMIC__ -#include #include #include diff --git a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h index 08deaee..1c780fa 100644 --- a/include/asm-i386/bitops.h +++ b/include/asm-i386/bitops.h @@ -5,7 +5,6 @@ #define _I386_BITOPS_H * Copyright 1992, Linus Torvalds. */ -#include #include #include diff --git a/include/asm-i386/bug.h b/include/asm-i386/bug.h index 8f79de1..8062cdb 100644 --- a/include/asm-i386/bug.h +++ b/include/asm-i386/bug.h @@ -1,7 +1,6 @@ #ifndef _I386_BUG_H #define _I386_BUG_H -#include /* * Tell the user there is some problem. diff --git a/include/asm-i386/bugs.h b/include/asm-i386/bugs.h index 50233e0..2a9e4ee 100644 --- a/include/asm-i386/bugs.h +++ b/include/asm-i386/bugs.h @@ -17,7 +17,6 @@ * void check_bugs(void); */ -#include #include #include #include diff --git a/include/asm-i386/byteorder.h b/include/asm-i386/byteorder.h index a0d73f4..a45470a 100644 --- a/include/asm-i386/byteorder.h +++ b/include/asm-i386/byteorder.h @@ -8,7 +8,6 @@ #ifdef __GNUC__ /* For avoiding bswap on i386 */ #ifdef __KERNEL__ -#include #endif static __inline__ __attribute_const__ __u32 ___arch__swab32(__u32 x) diff --git a/include/asm-i386/cache.h b/include/asm-i386/cache.h index ca15c9c..57c62f4 100644 --- a/include/asm-i386/cache.h +++ b/include/asm-i386/cache.h @@ -4,7 +4,6 @@ #ifndef __ARCH_I386_CACHE_H #define __ARCH_I386_CACHE_H -#include /* L1 cache line size */ #define L1_CACHE_SHIFT (CONFIG_X86_L1_CACHE_SHIFT) diff --git a/include/asm-i386/cpu.h b/include/asm-i386/cpu.h index e7252c2..b1bc7b1 100644 --- a/include/asm-i386/cpu.h +++ b/include/asm-i386/cpu.h @@ -7,8 +7,6 @@ #include #include #include -#include - struct i386_cpu { struct cpu cpu; }; diff --git a/include/asm-i386/cpufeature.h b/include/asm-i386/cpufeature.h index b44bfc6..d314ebb 100644 --- a/include/asm-i386/cpufeature.h +++ b/include/asm-i386/cpufeature.h @@ -72,6 +72,7 @@ #define X86_FEATURE_P4 (3*32+ 7) /* P4 #define X86_FEATURE_CONSTANT_TSC (3*32+ 8) /* TSC ticks at a constant rate */ #define X86_FEATURE_UP (3*32+ 9) /* smp kernel running on up */ #define X86_FEATURE_FXSAVE_LEAK (3*32+10) /* FXSAVE leaks FOP/FIP/FOP */ +#define X86_FEATURE_ARCH_PERFMON (3*32+11) /* Intel Architectural PerfMon */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ #define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ @@ -88,6 +89,12 @@ #define X86_FEATURE_XSTORE (5*32+ 2) /* #define X86_FEATURE_XSTORE_EN (5*32+ 3) /* on-CPU RNG enabled */ #define X86_FEATURE_XCRYPT (5*32+ 6) /* on-CPU crypto (xcrypt insn) */ #define X86_FEATURE_XCRYPT_EN (5*32+ 7) /* on-CPU crypto enabled */ +#define X86_FEATURE_ACE2 (5*32+ 8) /* Advanced Cryptography Engine v2 */ +#define X86_FEATURE_ACE2_EN (5*32+ 9) /* ACE v2 enabled */ +#define X86_FEATURE_PHE (5*32+ 10) /* PadLock Hash Engine */ +#define X86_FEATURE_PHE_EN (5*32+ 11) /* PHE enabled */ +#define X86_FEATURE_PMM (5*32+ 12) /* PadLock Montgomery Multiplier */ +#define X86_FEATURE_PMM_EN (5*32+ 13) /* PMM enabled */ /* More extended AMD flags: CPUID level 0x80000001, ecx, word 6 */ #define X86_FEATURE_LAHF_LM (6*32+ 0) /* LAHF/SAHF in long mode */ @@ -121,6 +128,12 @@ #define cpu_has_xstore boot_cpu_has(X86 #define cpu_has_xstore_enabled boot_cpu_has(X86_FEATURE_XSTORE_EN) #define cpu_has_xcrypt boot_cpu_has(X86_FEATURE_XCRYPT) #define cpu_has_xcrypt_enabled boot_cpu_has(X86_FEATURE_XCRYPT_EN) +#define cpu_has_ace2 boot_cpu_has(X86_FEATURE_ACE2) +#define cpu_has_ace2_enabled boot_cpu_has(X86_FEATURE_ACE2_EN) +#define cpu_has_phe boot_cpu_has(X86_FEATURE_PHE) +#define cpu_has_phe_enabled boot_cpu_has(X86_FEATURE_PHE_EN) +#define cpu_has_pmm boot_cpu_has(X86_FEATURE_PMM) +#define cpu_has_pmm_enabled boot_cpu_has(X86_FEATURE_PMM_EN) #endif /* __ASM_I386_CPUFEATURE_H */ diff --git a/include/asm-i386/delay.h b/include/asm-i386/delay.h index 456db85..b1c7650 100644 --- a/include/asm-i386/delay.h +++ b/include/asm-i386/delay.h @@ -23,4 +23,6 @@ #define ndelay(n) (__builtin_constant_p( ((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \ __ndelay(n)) +void use_tsc_delay(void); + #endif /* defined(_I386_DELAY_H) */ diff --git a/include/asm-i386/dma.h b/include/asm-i386/dma.h index f24b2bb..d23aac8 100644 --- a/include/asm-i386/dma.h +++ b/include/asm-i386/dma.h @@ -8,7 +8,6 @@ #ifndef _ASM_DMA_H #define _ASM_DMA_H -#include #include /* And spinlocks */ #include /* need byte IO */ #include diff --git a/include/asm-i386/dwarf2.h b/include/asm-i386/dwarf2.h new file mode 100644 index 0000000..2280f62 --- /dev/null +++ b/include/asm-i386/dwarf2.h @@ -0,0 +1,54 @@ +#ifndef _DWARF2_H +#define _DWARF2_H + +#include + +#ifndef __ASSEMBLY__ +#warning "asm/dwarf2.h should be only included in pure assembly files" +#endif + +/* + Macros for dwarf2 CFI unwind table entries. + See "as.info" for details on these pseudo ops. Unfortunately + they are only supported in very new binutils, so define them + away for older version. + */ + +#ifdef CONFIG_UNWIND_INFO + +#define CFI_STARTPROC .cfi_startproc +#define CFI_ENDPROC .cfi_endproc +#define CFI_DEF_CFA .cfi_def_cfa +#define CFI_DEF_CFA_REGISTER .cfi_def_cfa_register +#define CFI_DEF_CFA_OFFSET .cfi_def_cfa_offset +#define CFI_ADJUST_CFA_OFFSET .cfi_adjust_cfa_offset +#define CFI_OFFSET .cfi_offset +#define CFI_REL_OFFSET .cfi_rel_offset +#define CFI_REGISTER .cfi_register +#define CFI_RESTORE .cfi_restore +#define CFI_REMEMBER_STATE .cfi_remember_state +#define CFI_RESTORE_STATE .cfi_restore_state + +#else + +/* Due to the structure of pre-exisiting code, don't use assembler line + comment character # to ignore the arguments. Instead, use a dummy macro. */ +.macro ignore a=0, b=0, c=0, d=0 +.endm + +#define CFI_STARTPROC ignore +#define CFI_ENDPROC ignore +#define CFI_DEF_CFA ignore +#define CFI_DEF_CFA_REGISTER ignore +#define CFI_DEF_CFA_OFFSET ignore +#define CFI_ADJUST_CFA_OFFSET ignore +#define CFI_OFFSET ignore +#define CFI_REL_OFFSET ignore +#define CFI_REGISTER ignore +#define CFI_RESTORE ignore +#define CFI_REMEMBER_STATE ignore +#define CFI_RESTORE_STATE ignore + +#endif + +#endif diff --git a/include/asm-i386/elf.h b/include/asm-i386/elf.h index 4153d80..1eac92c 100644 --- a/include/asm-i386/elf.h +++ b/include/asm-i386/elf.h @@ -10,6 +10,7 @@ #include #include #include /* for savesegment */ #include +#include #include @@ -129,15 +130,41 @@ #define ELF_CORE_COPY_TASK_REGS(tsk, elf #define ELF_CORE_COPY_FPREGS(tsk, elf_fpregs) dump_task_fpu(tsk, elf_fpregs) #define ELF_CORE_COPY_XFPREGS(tsk, elf_xfpregs) dump_task_extended_fpu(tsk, elf_xfpregs) -#define VSYSCALL_BASE (__fix_to_virt(FIX_VSYSCALL)) -#define VSYSCALL_EHDR ((const struct elfhdr *) VSYSCALL_BASE) -#define VSYSCALL_ENTRY ((unsigned long) &__kernel_vsyscall) +#define VDSO_HIGH_BASE (__fix_to_virt(FIX_VDSO)) +#define VDSO_BASE ((unsigned long)current->mm->context.vdso) + +#ifdef CONFIG_COMPAT_VDSO +# define VDSO_COMPAT_BASE VDSO_HIGH_BASE +# define VDSO_PRELINK VDSO_HIGH_BASE +#else +# define VDSO_COMPAT_BASE VDSO_BASE +# define VDSO_PRELINK 0 +#endif + +#define VDSO_COMPAT_SYM(x) \ + (VDSO_COMPAT_BASE + (unsigned long)(x) - VDSO_PRELINK) + +#define VDSO_SYM(x) \ + (VDSO_BASE + (unsigned long)(x) - VDSO_PRELINK) + +#define VDSO_HIGH_EHDR ((const struct elfhdr *) VDSO_HIGH_BASE) +#define VDSO_EHDR ((const struct elfhdr *) VDSO_COMPAT_BASE) + extern void __kernel_vsyscall; +#define VDSO_ENTRY VDSO_SYM(&__kernel_vsyscall) + +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES +struct linux_binprm; +extern int arch_setup_additional_pages(struct linux_binprm *bprm, + int executable_stack); + +extern unsigned int vdso_enabled; + #define ARCH_DLINFO \ -do { \ - NEW_AUX_ENT(AT_SYSINFO, VSYSCALL_ENTRY); \ - NEW_AUX_ENT(AT_SYSINFO_EHDR, VSYSCALL_BASE); \ +do if (vdso_enabled) { \ + NEW_AUX_ENT(AT_SYSINFO, VDSO_ENTRY); \ + NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_COMPAT_BASE); \ } while (0) /* @@ -148,15 +175,15 @@ do { \ * Dumping its extra ELF program headers includes all the other information * a debugger needs to easily find how the vsyscall DSO was being used. */ -#define ELF_CORE_EXTRA_PHDRS (VSYSCALL_EHDR->e_phnum) +#define ELF_CORE_EXTRA_PHDRS (VDSO_HIGH_EHDR->e_phnum) #define ELF_CORE_WRITE_EXTRA_PHDRS \ do { \ const struct elf_phdr *const vsyscall_phdrs = \ - (const struct elf_phdr *) (VSYSCALL_BASE \ - + VSYSCALL_EHDR->e_phoff); \ + (const struct elf_phdr *) (VDSO_HIGH_BASE \ + + VDSO_HIGH_EHDR->e_phoff); \ int i; \ Elf32_Off ofs = 0; \ - for (i = 0; i < VSYSCALL_EHDR->e_phnum; ++i) { \ + for (i = 0; i < VDSO_HIGH_EHDR->e_phnum; ++i) { \ struct elf_phdr phdr = vsyscall_phdrs[i]; \ if (phdr.p_type == PT_LOAD) { \ BUG_ON(ofs != 0); \ @@ -174,10 +201,10 @@ do { \ #define ELF_CORE_WRITE_EXTRA_DATA \ do { \ const struct elf_phdr *const vsyscall_phdrs = \ - (const struct elf_phdr *) (VSYSCALL_BASE \ - + VSYSCALL_EHDR->e_phoff); \ + (const struct elf_phdr *) (VDSO_HIGH_BASE \ + + VDSO_HIGH_EHDR->e_phoff); \ int i; \ - for (i = 0; i < VSYSCALL_EHDR->e_phnum; ++i) { \ + for (i = 0; i < VDSO_HIGH_EHDR->e_phnum; ++i) { \ if (vsyscall_phdrs[i].p_type == PT_LOAD) \ DUMP_WRITE((void *) vsyscall_phdrs[i].p_vaddr, \ PAGE_ALIGN(vsyscall_phdrs[i].p_memsz)); \ diff --git a/include/asm-i386/fixmap.h b/include/asm-i386/fixmap.h index cfb1c61..a48cc3f 100644 --- a/include/asm-i386/fixmap.h +++ b/include/asm-i386/fixmap.h @@ -13,7 +13,6 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H -#include /* used by vmalloc.c, vsyscall.lds.S. * @@ -52,7 +51,7 @@ #endif */ enum fixed_addresses { FIX_HOLE, - FIX_VSYSCALL, + FIX_VDSO, #ifdef CONFIG_X86_LOCAL_APIC FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */ #endif @@ -116,14 +115,6 @@ #define FIXADDR_BOOT_START (FIXADDR_TOP #define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT)) #define __virt_to_fix(x) ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT) -/* - * This is the range that is readable by user mode, and things - * acting like user mode such as get_user_pages. - */ -#define FIXADDR_USER_START (__fix_to_virt(FIX_VSYSCALL)) -#define FIXADDR_USER_END (FIXADDR_USER_START + PAGE_SIZE) - - extern void __this_fixmap_does_not_exist(void); /* diff --git a/include/asm-i386/floppy.h b/include/asm-i386/floppy.h index 0340304..359ead6 100644 --- a/include/asm-i386/floppy.h +++ b/include/asm-i386/floppy.h @@ -144,12 +144,11 @@ static int vdma_get_dma_residue(unsigned static int fd_request_irq(void) { if(can_use_virtual_dma) - return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, - "floppy", NULL); + return request_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy", NULL); else return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT|SA_SAMPLE_RANDOM, - "floppy", NULL); + IRQF_DISABLED, "floppy", NULL); } diff --git a/include/asm-i386/hardirq.h b/include/asm-i386/hardirq.h index ee754d3..0e358dc 100644 --- a/include/asm-i386/hardirq.h +++ b/include/asm-i386/hardirq.h @@ -1,7 +1,6 @@ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include diff --git a/include/asm-i386/highmem.h b/include/asm-i386/highmem.h index 0fd3313..e9a34eb 100644 --- a/include/asm-i386/highmem.h +++ b/include/asm-i386/highmem.h @@ -20,7 +20,6 @@ #define _ASM_HIGHMEM_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-i386/hpet.h b/include/asm-i386/hpet.h index 7f1a8a6..af5d435 100644 --- a/include/asm-i386/hpet.h +++ b/include/asm-i386/hpet.h @@ -27,7 +27,6 @@ #include #include #include -#include #include diff --git a/include/asm-i386/hw_irq.h b/include/asm-i386/hw_irq.h index 622815b..87e5a35 100644 --- a/include/asm-i386/hw_irq.h +++ b/include/asm-i386/hw_irq.h @@ -12,7 +12,6 @@ #define _ASM_HW_IRQ_H * */ -#include #include #include #include @@ -20,6 +19,8 @@ #include struct hw_interrupt_type; +#define NMI_VECTOR 0x02 + /* * Various low-level irq details needed by irq.c, process.c, * time.c, io_apic.c and smp.c @@ -68,14 +69,4 @@ extern atomic_t irq_mis_count; #define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs)) -#if defined(CONFIG_X86_IO_APIC) -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) -{ - if (IO_APIC_IRQ(i)) - send_IPI_self(IO_APIC_VECTOR(i)); -} -#else -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} -#endif - #endif /* _ASM_HW_IRQ_H */ diff --git a/include/asm-i386/ide.h b/include/asm-i386/ide.h index 4544401..73465d2 100644 --- a/include/asm-i386/ide.h +++ b/include/asm-i386/ide.h @@ -13,7 +13,6 @@ #define __ASMi386_IDE_H #ifdef __KERNEL__ -#include #ifndef MAX_HWIFS # ifdef CONFIG_BLK_DEV_IDEPCI diff --git a/include/asm-i386/intel_arch_perfmon.h b/include/asm-i386/intel_arch_perfmon.h new file mode 100644 index 0000000..134ea9c --- /dev/null +++ b/include/asm-i386/intel_arch_perfmon.h @@ -0,0 +1,19 @@ +#ifndef X86_INTEL_ARCH_PERFMON_H +#define X86_INTEL_ARCH_PERFMON_H 1 + +#define MSR_ARCH_PERFMON_PERFCTR0 0xc1 +#define MSR_ARCH_PERFMON_PERFCTR1 0xc2 + +#define MSR_ARCH_PERFMON_EVENTSEL0 0x186 +#define MSR_ARCH_PERFMON_EVENTSEL1 0x187 + +#define ARCH_PERFMON_EVENTSEL0_ENABLE (1 << 22) +#define ARCH_PERFMON_EVENTSEL_INT (1 << 20) +#define ARCH_PERFMON_EVENTSEL_OS (1 << 17) +#define ARCH_PERFMON_EVENTSEL_USR (1 << 16) + +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL (0x3c) +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT (1 << 0) + +#endif /* X86_INTEL_ARCH_PERFMON_H */ diff --git a/include/asm-i386/io.h b/include/asm-i386/io.h index 79670bb..b3724fe 100644 --- a/include/asm-i386/io.h +++ b/include/asm-i386/io.h @@ -1,7 +1,6 @@ #ifndef _ASM_IO_H #define _ASM_IO_H -#include #include #include diff --git a/include/asm-i386/io_apic.h b/include/asm-i386/io_apic.h index d92e253..5092e81 100644 --- a/include/asm-i386/io_apic.h +++ b/include/asm-i386/io_apic.h @@ -1,7 +1,6 @@ #ifndef __ASM_IO_APIC_H #define __ASM_IO_APIC_H -#include #include #include diff --git a/include/asm-i386/irq.h b/include/asm-i386/irq.h index 5169d7a..331726b 100644 --- a/include/asm-i386/irq.h +++ b/include/asm-i386/irq.h @@ -10,7 +10,6 @@ #define _ASM_IRQ_H * */ -#include #include /* include comes from machine specific directory */ #include "irq_vectors.h" diff --git a/include/asm-i386/irqflags.h b/include/asm-i386/irqflags.h new file mode 100644 index 0000000..e1bdb97 --- /dev/null +++ b/include/asm-i386/irqflags.h @@ -0,0 +1,127 @@ +/* + * include/asm-i386/irqflags.h + * + * IRQ flags handling + * + * This file gets included from lowlevel asm headers too, to provide + * wrapped versions of the local_irq_*() APIs, based on the + * raw_local_irq_*() functions from the lowlevel headers. + */ +#ifndef _ASM_IRQFLAGS_H +#define _ASM_IRQFLAGS_H + +#ifndef __ASSEMBLY__ + +static inline unsigned long __raw_local_save_flags(void) +{ + unsigned long flags; + + __asm__ __volatile__( + "pushfl ; popl %0" + : "=g" (flags) + : /* no input */ + ); + + return flags; +} + +#define raw_local_save_flags(flags) \ + do { (flags) = __raw_local_save_flags(); } while (0) + +static inline void raw_local_irq_restore(unsigned long flags) +{ + __asm__ __volatile__( + "pushl %0 ; popfl" + : /* no output */ + :"g" (flags) + :"memory", "cc" + ); +} + +static inline void raw_local_irq_disable(void) +{ + __asm__ __volatile__("cli" : : : "memory"); +} + +static inline void raw_local_irq_enable(void) +{ + __asm__ __volatile__("sti" : : : "memory"); +} + +/* + * Used in the idle loop; sti takes one instruction cycle + * to complete: + */ +static inline void raw_safe_halt(void) +{ + __asm__ __volatile__("sti; hlt" : : : "memory"); +} + +/* + * Used when interrupts are already enabled or to + * shutdown the processor: + */ +static inline void halt(void) +{ + __asm__ __volatile__("hlt": : :"memory"); +} + +static inline int raw_irqs_disabled_flags(unsigned long flags) +{ + return !(flags & (1 << 9)); +} + +static inline int raw_irqs_disabled(void) +{ + unsigned long flags = __raw_local_save_flags(); + + return raw_irqs_disabled_flags(flags); +} + +/* + * For spinlocks, etc: + */ +static inline unsigned long __raw_local_irq_save(void) +{ + unsigned long flags = __raw_local_save_flags(); + + raw_local_irq_disable(); + + return flags; +} + +#define raw_local_irq_save(flags) \ + do { (flags) = __raw_local_irq_save(); } while (0) + +#endif /* __ASSEMBLY__ */ + +/* + * Do the CPU's IRQ-state tracing from assembly code. We call a + * C function, so save all the C-clobbered registers: + */ +#ifdef CONFIG_TRACE_IRQFLAGS + +# define TRACE_IRQS_ON \ + pushl %eax; \ + pushl %ecx; \ + pushl %edx; \ + call trace_hardirqs_on; \ + popl %edx; \ + popl %ecx; \ + popl %eax; + +# define TRACE_IRQS_OFF \ + pushl %eax; \ + pushl %ecx; \ + pushl %edx; \ + call trace_hardirqs_off; \ + popl %edx; \ + popl %ecx; \ + popl %eax; + +#else +# define TRACE_IRQS_ON +# define TRACE_IRQS_OFF +#endif + +#endif diff --git a/include/asm-i386/k8.h b/include/asm-i386/k8.h new file mode 100644 index 0000000..dfd88a6 --- /dev/null +++ b/include/asm-i386/k8.h @@ -0,0 +1 @@ +#include diff --git a/include/asm-i386/kdebug.h b/include/asm-i386/kdebug.h index 96d0828..d18cdb9 100644 --- a/include/asm-i386/kdebug.h +++ b/include/asm-i386/kdebug.h @@ -19,6 +19,8 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); extern struct atomic_notifier_head i386die_chain; diff --git a/include/asm-i386/kmap_types.h b/include/asm-i386/kmap_types.h index 6886a0c..806aae3 100644 --- a/include/asm-i386/kmap_types.h +++ b/include/asm-i386/kmap_types.h @@ -1,7 +1,6 @@ #ifndef _ASM_KMAP_TYPES_H #define _ASM_KMAP_TYPES_H -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-i386/kprobes.h b/include/asm-i386/kprobes.h index 57d157c..0730a20 100644 --- a/include/asm-i386/kprobes.h +++ b/include/asm-i386/kprobes.h @@ -44,6 +44,7 @@ #define MIN_STACK_SIZE(ADDR) (((MAX_STAC #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry #define ARCH_SUPPORTS_KRETPROBES +#define ARCH_INACTIVE_KPROBE_COUNT 0 void arch_remove_kprobe(struct kprobe *p); void kretprobe_trampoline(void); diff --git a/include/asm-i386/local.h b/include/asm-i386/local.h index e67fa08..3b4998c 100644 --- a/include/asm-i386/local.h +++ b/include/asm-i386/local.h @@ -55,12 +55,26 @@ #define __local_sub(i,l) local_sub((i),( * much more efficient than these naive implementations. Note they take * a variable, not an address. */ -#define cpu_local_read(v) local_read(&__get_cpu_var(v)) -#define cpu_local_set(v, i) local_set(&__get_cpu_var(v), (i)) -#define cpu_local_inc(v) local_inc(&__get_cpu_var(v)) -#define cpu_local_dec(v) local_dec(&__get_cpu_var(v)) -#define cpu_local_add(i, v) local_add((i), &__get_cpu_var(v)) -#define cpu_local_sub(i, v) local_sub((i), &__get_cpu_var(v)) + +/* Need to disable preemption for the cpu local counters otherwise we could + still access a variable of a previous CPU in a non atomic way. */ +#define cpu_local_wrap_v(v) \ + ({ local_t res__; \ + preempt_disable(); \ + res__ = (v); \ + preempt_enable(); \ + res__; }) +#define cpu_local_wrap(v) \ + ({ preempt_disable(); \ + v; \ + preempt_enable(); }) \ + +#define cpu_local_read(v) cpu_local_wrap_v(local_read(&__get_cpu_var(v))) +#define cpu_local_set(v, i) cpu_local_wrap(local_set(&__get_cpu_var(v), (i))) +#define cpu_local_inc(v) cpu_local_wrap(local_inc(&__get_cpu_var(v))) +#define cpu_local_dec(v) cpu_local_wrap(local_dec(&__get_cpu_var(v))) +#define cpu_local_add(i, v) cpu_local_wrap(local_add((i), &__get_cpu_var(v))) +#define cpu_local_sub(i, v) cpu_local_wrap(local_sub((i), &__get_cpu_var(v))) #define __cpu_local_inc(v) cpu_local_inc(v) #define __cpu_local_dec(v) cpu_local_dec(v) diff --git a/include/asm-i386/mach-default/mach_ipi.h b/include/asm-i386/mach-default/mach_ipi.h index a1d0072..0dba244 100644 --- a/include/asm-i386/mach-default/mach_ipi.h +++ b/include/asm-i386/mach-default/mach_ipi.h @@ -1,6 +1,9 @@ #ifndef __ASM_MACH_IPI_H #define __ASM_MACH_IPI_H +/* Avoid include hell */ +#define NMI_VECTOR 0x02 + void send_IPI_mask_bitmask(cpumask_t mask, int vector); void __send_IPI_shortcut(unsigned int shortcut, int vector); @@ -13,7 +16,7 @@ static inline void send_IPI_mask(cpumask static inline void __local_send_IPI_allbutself(int vector) { - if (no_broadcast) { + if (no_broadcast || vector == NMI_VECTOR) { cpumask_t mask = cpu_online_map; cpu_clear(smp_processor_id(), mask); @@ -24,7 +27,7 @@ static inline void __local_send_IPI_allb static inline void __local_send_IPI_all(int vector) { - if (no_broadcast) + if (no_broadcast || vector == NMI_VECTOR) send_IPI_mask(cpu_online_map, vector); else __send_IPI_shortcut(APIC_DEST_ALLINC, vector); diff --git a/include/asm-i386/mach-default/mach_timer.h b/include/asm-i386/mach-default/mach_timer.h index 4b9703b..807992f 100644 --- a/include/asm-i386/mach-default/mach_timer.h +++ b/include/asm-i386/mach-default/mach_timer.h @@ -15,7 +15,9 @@ #ifndef _MACH_TIMER_H #define _MACH_TIMER_H -#define CALIBRATE_LATCH (5 * LATCH) +#define CALIBRATE_TIME_MSEC 30 /* 30 msecs */ +#define CALIBRATE_LATCH \ + ((CLOCK_TICK_RATE * CALIBRATE_TIME_MSEC + 1000/2)/1000) static inline void mach_prepare_counter(void) { diff --git a/include/asm-i386/mach-default/setup_arch.h b/include/asm-i386/mach-default/setup_arch.h new file mode 100644 index 0000000..fb42099 --- /dev/null +++ b/include/asm-i386/mach-default/setup_arch.h @@ -0,0 +1,5 @@ +/* Hook to call BIOS initialisation function */ + +/* no action for generic */ + +#define ARCH_SETUP diff --git a/include/asm-i386/mach-default/setup_arch_post.h b/include/asm-i386/mach-default/setup_arch_post.h deleted file mode 100644 index 2fc4888..0000000 --- a/include/asm-i386/mach-default/setup_arch_post.h +++ /dev/null @@ -1,40 +0,0 @@ -/** - * machine_specific_memory_setup - Hook for machine specific memory setup. - * - * Description: - * This is included late in kernel/setup.c so that it can make - * use of all of the static functions. - **/ - -static char * __init machine_specific_memory_setup(void) -{ - char *who; - - - who = "BIOS-e820"; - - /* - * Try to copy the BIOS-supplied E820-map. - * - * Otherwise fake a memory map; one section from 0k->640k, - * the next section from 1mb->appropriate_mem_k - */ - sanitize_e820_map(E820_MAP, &E820_MAP_NR); - if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { - unsigned long mem_size; - - /* compare results from other methods and take the greater */ - if (ALT_MEM_K < EXT_MEM_K) { - mem_size = EXT_MEM_K; - who = "BIOS-88"; - } else { - mem_size = ALT_MEM_K; - who = "BIOS-e801"; - } - - e820.nr_map = 0; - add_memory_region(0, LOWMEMSIZE(), E820_RAM); - add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); - } - return who; -} diff --git a/include/asm-i386/mach-default/setup_arch_pre.h b/include/asm-i386/mach-default/setup_arch_pre.h deleted file mode 100644 index fb42099..0000000 --- a/include/asm-i386/mach-default/setup_arch_pre.h +++ /dev/null @@ -1,5 +0,0 @@ -/* Hook to call BIOS initialisation function */ - -/* no action for generic */ - -#define ARCH_SETUP diff --git a/include/asm-i386/mach-summit/mach_apic.h b/include/asm-i386/mach-summit/mach_apic.h index 3d6d129..9fd0732 100644 --- a/include/asm-i386/mach-summit/mach_apic.h +++ b/include/asm-i386/mach-summit/mach_apic.h @@ -1,7 +1,6 @@ #ifndef __ASM_MACH_APIC_H #define __ASM_MACH_APIC_H -#include #include #define esr_disable (1) diff --git a/include/asm-i386/mach-summit/mach_mpparse.h b/include/asm-i386/mach-summit/mach_mpparse.h index 1cce2b9..9426839 100644 --- a/include/asm-i386/mach-summit/mach_mpparse.h +++ b/include/asm-i386/mach-summit/mach_mpparse.h @@ -2,6 +2,7 @@ #ifndef __ASM_MACH_MPPARSE_H #define __ASM_MACH_MPPARSE_H #include +#include extern int use_cyclone; @@ -29,6 +30,7 @@ static inline int mps_oem_check(struct m (!strncmp(productid, "VIGIL SMP", 9) || !strncmp(productid, "EXA", 3) || !strncmp(productid, "RUTHLESS SMP", 12))){ + mark_tsc_unstable(); use_cyclone = 1; /*enable cyclone-timer*/ setup_summit(); return 1; @@ -42,6 +44,7 @@ static inline int acpi_madt_oem_check(ch if (!strncmp(oem_id, "IBM", 3) && (!strncmp(oem_table_id, "SERVIGIL", 8) || !strncmp(oem_table_id, "EXA", 3))){ + mark_tsc_unstable(); use_cyclone = 1; /*enable cyclone-timer*/ setup_summit(); return 1; diff --git a/include/asm-i386/mach-visws/setup_arch.h b/include/asm-i386/mach-visws/setup_arch.h new file mode 100644 index 0000000..33f700e --- /dev/null +++ b/include/asm-i386/mach-visws/setup_arch.h @@ -0,0 +1,8 @@ +/* Hook to call BIOS initialisation function */ + +extern unsigned long sgivwfb_mem_phys; +extern unsigned long sgivwfb_mem_size; + +/* no action for visws */ + +#define ARCH_SETUP diff --git a/include/asm-i386/mach-visws/setup_arch_post.h b/include/asm-i386/mach-visws/setup_arch_post.h deleted file mode 100644 index cdbd895..0000000 --- a/include/asm-i386/mach-visws/setup_arch_post.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Hook for machine specific memory setup. - * - * This is included late in kernel/setup.c so that it can make use of all of - * the static functions. */ - -#define MB (1024 * 1024) - -unsigned long sgivwfb_mem_phys; -unsigned long sgivwfb_mem_size; - -long long mem_size __initdata = 0; - -static char * __init machine_specific_memory_setup(void) -{ - long long gfx_mem_size = 8 * MB; - - mem_size = ALT_MEM_K; - - if (!mem_size) { - printk(KERN_WARNING "Bootloader didn't set memory size, upgrade it !\n"); - mem_size = 128 * MB; - } - - /* - * this hardcodes the graphics memory to 8 MB - * it really should be sized dynamically (or at least - * set as a boot param) - */ - if (!sgivwfb_mem_size) { - printk(KERN_WARNING "Defaulting to 8 MB framebuffer size\n"); - sgivwfb_mem_size = 8 * MB; - } - - /* - * Trim to nearest MB - */ - sgivwfb_mem_size &= ~((1 << 20) - 1); - sgivwfb_mem_phys = mem_size - gfx_mem_size; - - add_memory_region(0, LOWMEMSIZE(), E820_RAM); - add_memory_region(HIGH_MEMORY, mem_size - sgivwfb_mem_size - HIGH_MEMORY, E820_RAM); - add_memory_region(sgivwfb_mem_phys, sgivwfb_mem_size, E820_RESERVED); - - return "PROM"; - - /* Remove gcc warnings */ - (void) sanitize_e820_map(NULL, NULL); - (void) copy_e820_map(NULL, 0); -} diff --git a/include/asm-i386/mach-visws/setup_arch_pre.h b/include/asm-i386/mach-visws/setup_arch_pre.h deleted file mode 100644 index b92d6d9..0000000 --- a/include/asm-i386/mach-visws/setup_arch_pre.h +++ /dev/null @@ -1,5 +0,0 @@ -/* Hook to call BIOS initialisation function */ - -/* no action for visws */ - -#define ARCH_SETUP diff --git a/include/asm-i386/mach-voyager/setup_arch.h b/include/asm-i386/mach-voyager/setup_arch.h new file mode 100644 index 0000000..84d01ad --- /dev/null +++ b/include/asm-i386/mach-voyager/setup_arch.h @@ -0,0 +1,10 @@ +#include +#define VOYAGER_BIOS_INFO ((struct voyager_bios_info *)(PARAM+0x40)) + +/* Hook to call BIOS initialisation function */ + +/* for voyager, pass the voyager BIOS/SUS info area to the detection + * routines */ + +#define ARCH_SETUP voyager_detect(VOYAGER_BIOS_INFO); + diff --git a/include/asm-i386/mach-voyager/setup_arch_post.h b/include/asm-i386/mach-voyager/setup_arch_post.h deleted file mode 100644 index f6f6c2c..0000000 --- a/include/asm-i386/mach-voyager/setup_arch_post.h +++ /dev/null @@ -1,73 +0,0 @@ -/* Hook for machine specific memory setup. - * - * This is included late in kernel/setup.c so that it can make use of all of - * the static functions. */ - -static char * __init machine_specific_memory_setup(void) -{ - char *who; - - who = "NOT VOYAGER"; - - if(voyager_level == 5) { - __u32 addr, length; - int i; - - who = "Voyager-SUS"; - - e820.nr_map = 0; - for(i=0; voyager_memory_detect(i, &addr, &length); i++) { - add_memory_region(addr, length, E820_RAM); - } - return who; - } else if(voyager_level == 4) { - __u32 tom; - __u16 catbase = inb(VOYAGER_SSPB_RELOCATION_PORT)<<8; - /* select the DINO config space */ - outb(VOYAGER_DINO, VOYAGER_CAT_CONFIG_PORT); - /* Read DINO top of memory register */ - tom = ((inb(catbase + 0x4) & 0xf0) << 16) - + ((inb(catbase + 0x5) & 0x7f) << 24); - - if(inb(catbase) != VOYAGER_DINO) { - printk(KERN_ERR "Voyager: Failed to get DINO for L4, setting tom to EXT_MEM_K\n"); - tom = (EXT_MEM_K)<<10; - } - who = "Voyager-TOM"; - add_memory_region(0, 0x9f000, E820_RAM); - /* map from 1M to top of memory */ - add_memory_region(1*1024*1024, tom - 1*1024*1024, E820_RAM); - /* FIXME: Should check the ASICs to see if I need to - * take out the 8M window. Just do it at the moment - * */ - add_memory_region(8*1024*1024, 8*1024*1024, E820_RESERVED); - return who; - } - - who = "BIOS-e820"; - - /* - * Try to copy the BIOS-supplied E820-map. - * - * Otherwise fake a memory map; one section from 0k->640k, - * the next section from 1mb->appropriate_mem_k - */ - sanitize_e820_map(E820_MAP, &E820_MAP_NR); - if (copy_e820_map(E820_MAP, E820_MAP_NR) < 0) { - unsigned long mem_size; - - /* compare results from other methods and take the greater */ - if (ALT_MEM_K < EXT_MEM_K) { - mem_size = EXT_MEM_K; - who = "BIOS-88"; - } else { - mem_size = ALT_MEM_K; - who = "BIOS-e801"; - } - - e820.nr_map = 0; - add_memory_region(0, LOWMEMSIZE(), E820_RAM); - add_memory_region(HIGH_MEMORY, mem_size << 10, E820_RAM); - } - return who; -} diff --git a/include/asm-i386/mach-voyager/setup_arch_pre.h b/include/asm-i386/mach-voyager/setup_arch_pre.h deleted file mode 100644 index 48f7e6f..0000000 --- a/include/asm-i386/mach-voyager/setup_arch_pre.h +++ /dev/null @@ -1,10 +0,0 @@ -#include -#define VOYAGER_BIOS_INFO ((struct voyager_bios_info *)(PARAM+0x40)) - -/* Hook to call BIOS initialisation function */ - -/* for voyager, pass the voyager BIOS/SUS info area to the detection - * routines */ - -#define ARCH_SETUP voyager_detect(VOYAGER_BIOS_INFO); - diff --git a/include/asm-i386/mce.h b/include/asm-i386/mce.h new file mode 100644 index 0000000..7cc1a97 --- /dev/null +++ b/include/asm-i386/mce.h @@ -0,0 +1,5 @@ +#ifdef CONFIG_X86_MCE +extern void mcheck_init(struct cpuinfo_x86 *c); +#else +#define mcheck_init(c) do {} while(0) +#endif diff --git a/include/asm-i386/mmu.h b/include/asm-i386/mmu.h index f431a0b..8358dd3 100644 --- a/include/asm-i386/mmu.h +++ b/include/asm-i386/mmu.h @@ -12,6 +12,7 @@ typedef struct { int size; struct semaphore sem; void *ldt; + void *vdso; } mm_context_t; #endif diff --git a/include/asm-i386/mmu_context.h b/include/asm-i386/mmu_context.h index bf08218..62b7bf1 100644 --- a/include/asm-i386/mmu_context.h +++ b/include/asm-i386/mmu_context.h @@ -1,7 +1,6 @@ #ifndef __I386_SCHED_H #define __I386_SCHED_H -#include #include #include #include diff --git a/include/asm-i386/msi.h b/include/asm-i386/msi.h index f041d44..b11c4b7 100644 --- a/include/asm-i386/msi.h +++ b/include/asm-i386/msi.h @@ -9,7 +9,15 @@ #define ASM_MSI_H #include #include -#define LAST_DEVICE_VECTOR 232 +#define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) #define MSI_TARGET_CPU_SHIFT 12 +extern struct msi_ops msi_apic_ops; + +static inline int msi_arch_init(void) +{ + msi_register(&msi_apic_ops); + return 0; +} + #endif /* ASM_MSI_H */ diff --git a/include/asm-i386/mtrr.h b/include/asm-i386/mtrr.h index 64cf937..07f063a 100644 --- a/include/asm-i386/mtrr.h +++ b/include/asm-i386/mtrr.h @@ -23,7 +23,6 @@ #ifndef _LINUX_MTRR_H #define _LINUX_MTRR_H -#include #include #include @@ -77,6 +76,8 @@ extern int mtrr_add_page (unsigned long extern int mtrr_del (int reg, unsigned long base, unsigned long size); extern int mtrr_del_page (int reg, unsigned long base, unsigned long size); extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi); +extern void mtrr_ap_init(void); +extern void mtrr_bp_init(void); # else static __inline__ int mtrr_add (unsigned long base, unsigned long size, unsigned int type, char increment) @@ -101,6 +102,8 @@ static __inline__ int mtrr_del_page (int static __inline__ void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) {;} +#define mtrr_ap_init() do {} while (0) +#define mtrr_bp_init() do {} while (0) # endif #endif diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h index 21f1663..67d9947 100644 --- a/include/asm-i386/nmi.h +++ b/include/asm-i386/nmi.h @@ -5,24 +5,38 @@ #ifndef ASM_NMI_H #define ASM_NMI_H #include - + struct pt_regs; - + typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu); - -/** + +/** * set_nmi_callback * * Set a handler for an NMI. Only one handler may be * set. Return 1 if the NMI was handled. */ void set_nmi_callback(nmi_callback_t callback); - -/** + +/** * unset_nmi_callback * * Remove the handler previously set. */ void unset_nmi_callback(void); - + +extern void setup_apic_nmi_watchdog (void); +extern int reserve_lapic_nmi(void); +extern void release_lapic_nmi(void); +extern void disable_timer_nmi_watchdog(void); +extern void enable_timer_nmi_watchdog(void); +extern void nmi_watchdog_tick (struct pt_regs * regs); + +extern unsigned int nmi_watchdog; +#define NMI_DEFAULT -1 +#define NMI_NONE 0 +#define NMI_IO_APIC 1 +#define NMI_LOCAL_APIC 2 +#define NMI_INVALID 3 + #endif /* ASM_NMI_H */ diff --git a/include/asm-i386/node.h b/include/asm-i386/node.h deleted file mode 100644 index e13c6ff..0000000 --- a/include/asm-i386/node.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _ASM_I386_NODE_H_ -#define _ASM_I386_NODE_H_ - -#include -#include -#include -#include -#include - -struct i386_node { - struct node node; -}; -extern struct i386_node node_devices[MAX_NUMNODES]; - -static inline int arch_register_node(int num){ - int p_node; - struct node *parent = NULL; - - if (!node_online(num)) - return 0; - p_node = parent_node(num); - - if (p_node != num) - parent = &node_devices[p_node].node; - - return register_node(&node_devices[num].node, num, parent); -} - -#endif /* _ASM_I386_NODE_H_ */ diff --git a/include/asm-i386/page.h b/include/asm-i386/page.h index 30f52a2..f5bf544 100644 --- a/include/asm-i386/page.h +++ b/include/asm-i386/page.h @@ -12,7 +12,6 @@ #define LARGE_PAGE_SIZE (1UL << PMD_SHIF #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#include #ifdef CONFIG_X86_USE_3DNOW @@ -97,6 +96,8 @@ #define PAGE_ALIGN(addr) (((addr)+PAGE_S #ifndef __ASSEMBLY__ +struct vm_area_struct; + /* * This much address space is reserved for vmalloc() and iomap() * as well as fixmap mappings. @@ -137,9 +138,10 @@ #define VM_DATA_DEFAULT_FLAGS \ ((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0 ) | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) -#endif /* __KERNEL__ */ - #include #include +#define __HAVE_ARCH_GATE_AREA 1 +#endif /* __KERNEL__ */ + #endif /* _I386_PAGE_H */ diff --git a/include/asm-i386/param.h b/include/asm-i386/param.h index 095580f..745dc5b 100644 --- a/include/asm-i386/param.h +++ b/include/asm-i386/param.h @@ -2,7 +2,6 @@ #ifndef _ASMi386_PARAM_H #define _ASMi386_PARAM_H #ifdef __KERNEL__ -# include # define HZ CONFIG_HZ /* Internal kernel timer frequency */ # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ diff --git a/include/asm-i386/pci.h b/include/asm-i386/pci.h index 78c8598..64b6d0b 100644 --- a/include/asm-i386/pci.h +++ b/include/asm-i386/pci.h @@ -1,7 +1,6 @@ #ifndef __i386_PCI_H #define __i386_PCI_H -#include #ifdef __KERNEL__ #include /* for struct page */ diff --git a/include/asm-i386/pgalloc.h b/include/asm-i386/pgalloc.h index 0380c3d..4b1e613 100644 --- a/include/asm-i386/pgalloc.h +++ b/include/asm-i386/pgalloc.h @@ -1,7 +1,6 @@ #ifndef _I386_PGALLOC_H #define _I386_PGALLOC_H -#include #include #include #include /* for struct page */ diff --git a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h index 672c3f7..09697fe 100644 --- a/include/asm-i386/pgtable.h +++ b/include/asm-i386/pgtable.h @@ -1,7 +1,6 @@ #ifndef _I386_PGTABLE_H #define _I386_PGTABLE_H -#include /* * The Linux memory management assumes a three-level page table setup. On diff --git a/include/asm-i386/processor.h b/include/asm-i386/processor.h index 805f0dc..b32346d 100644 --- a/include/asm-i386/processor.h +++ b/include/asm-i386/processor.h @@ -17,7 +17,6 @@ #include #include #include #include -#include #include #include #include @@ -72,8 +71,12 @@ #ifdef CONFIG_SMP cpumask_t llc_shared_map; /* cpus sharing the last level cache */ #endif unsigned char x86_max_cores; /* cpuid returned max cores value */ - unsigned char booted_cores; /* number of cores as seen by OS */ unsigned char apicid; +#ifdef CONFIG_SMP + unsigned char booted_cores; /* number of cores as seen by OS */ + __u8 phys_proc_id; /* Physical processor id. */ + __u8 cpu_core_id; /* Core id */ +#endif } __attribute__((__aligned__(SMP_CACHE_BYTES))); #define X86_VENDOR_INTEL 0 @@ -105,14 +108,13 @@ #define cpu_data (&boot_cpu_data) #define current_cpu_data boot_cpu_data #endif -extern int phys_proc_id[NR_CPUS]; -extern int cpu_core_id[NR_CPUS]; extern int cpu_llc_id[NR_CPUS]; extern char ignore_fpu_irq; extern void identify_cpu(struct cpuinfo_x86 *); extern void print_cpu_info(struct cpuinfo_x86 *); extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); +extern unsigned short num_cache_leaves; #ifdef CONFIG_X86_HT extern void detect_ht(struct cpuinfo_x86 *c); @@ -555,7 +557,7 @@ extern void prepare_to_copy(struct task_ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); extern unsigned long thread_saved_pc(struct task_struct *tsk); -void show_trace(struct task_struct *task, unsigned long *stack); +void show_trace(struct task_struct *task, struct pt_regs *regs, unsigned long *stack); unsigned long get_wchan(struct task_struct *p); @@ -729,18 +731,4 @@ extern unsigned long boot_option_idle_ov extern void enable_sep_cpu(void); extern int sysenter_setup(void); -#ifdef CONFIG_MTRR -extern void mtrr_ap_init(void); -extern void mtrr_bp_init(void); -#else -#define mtrr_ap_init() do {} while (0) -#define mtrr_bp_init() do {} while (0) -#endif - -#ifdef CONFIG_X86_MCE -extern void mcheck_init(struct cpuinfo_x86 *c); -#else -#define mcheck_init(c) do {} while(0) -#endif - #endif /* __ASM_I386_PROCESSOR_H */ diff --git a/include/asm-i386/rwsem.h b/include/asm-i386/rwsem.h index be4ab85..2f07601 100644 --- a/include/asm-i386/rwsem.h +++ b/include/asm-i386/rwsem.h @@ -40,6 +40,7 @@ #ifdef __KERNEL__ #include #include +#include struct rwsem_waiter; @@ -61,36 +62,34 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_AC #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; #endif }; -/* - * initialisation - */ -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } #else -#define __RWSEM_DEBUG_INIT /* */ +# define __RWSEM_DEP_MAP_INIT(lockname) #endif + #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEBUG_INIT } + __RWSEM_DEP_MAP_INIT(name) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) -static inline void init_rwsem(struct rw_semaphore *sem) -{ - sem->count = RWSEM_UNLOCKED_VALUE; - spin_lock_init(&sem->wait_lock); - INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif -} +extern void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_rwsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_rwsem((sem), #sem, &__key); \ +} while (0) /* * lock for reading @@ -143,7 +142,7 @@ LOCK_PREFIX " cmpxchgl %2,%0\n\t" /* * lock for writing */ -static inline void __down_write(struct rw_semaphore *sem) +static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) { int tmp; @@ -167,6 +166,11 @@ LOCK_PREFIX " xadd %%edx,(%%eax)\n : "memory", "cc"); } +static inline void __down_write(struct rw_semaphore *sem) +{ + __down_write_nested(sem, 0); +} + /* * trylock for writing -- returns 1 if successful, 0 if contention */ diff --git a/include/asm-i386/serial.h b/include/asm-i386/serial.h index e1ecfcc..bd67480 100644 --- a/include/asm-i386/serial.h +++ b/include/asm-i386/serial.h @@ -2,7 +2,6 @@ * include/asm-i386/serial.h */ -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-i386/setup.h b/include/asm-i386/setup.h index ee94145..f737e42 100644 --- a/include/asm-i386/setup.h +++ b/include/asm-i386/setup.h @@ -59,6 +59,21 @@ #define EDD_MBR_SIG_NR (*(unsigned char #define EDD_MBR_SIGNATURE ((unsigned int *) (PARAM+EDD_MBR_SIG_BUF)) #define EDD_BUF ((struct edd_info *) (PARAM+EDDBUF)) +/* + * Do NOT EVER look at the BIOS memory size location. + * It does not work on many machines. + */ +#define LOWMEMSIZE() (0x9f000) + +struct e820entry; + +char * __init machine_specific_memory_setup(void); + +int __init copy_e820_map(struct e820entry * biosmap, int nr_map); +int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map); +void __init add_memory_region(unsigned long long start, + unsigned long long size, int type); + #endif /* __ASSEMBLY__ */ #endif /* _i386_SETUP_H */ diff --git a/include/asm-i386/signal.h b/include/asm-i386/signal.h index 026fd23..3824a50 100644 --- a/include/asm-i386/signal.h +++ b/include/asm-i386/signal.h @@ -77,7 +77,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -97,7 +96,6 @@ #define SA_RESETHAND 0x80000000u #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 61d3ab9..142d10e 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -5,7 +5,6 @@ #define __ASM_SMP_H * We need the APIC definitions automatically as part of 'smp.h' */ #ifndef __ASSEMBLY__ -#include #include #include #include diff --git a/include/asm-i386/socket.h b/include/asm-i386/socket.h index 802ae76..5755d57 100644 --- a/include/asm-i386/socket.h +++ b/include/asm-i386/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-i386/spinlock.h b/include/asm-i386/spinlock.h index d76b769..87c40f8 100644 --- a/include/asm-i386/spinlock.h +++ b/include/asm-i386/spinlock.h @@ -4,7 +4,6 @@ #define __ASM_SPINLOCK_H #include #include #include -#include #include /* @@ -32,6 +31,11 @@ #define __raw_spin_lock_string \ "jmp 1b\n" \ "3:\n\t" +/* + * NOTE: there's an irqs-on section here, which normally would have to be + * irq-traced, but on CONFIG_TRACE_IRQFLAGS we never use + * __raw_spin_lock_string_flags(). + */ #define __raw_spin_lock_string_flags \ "\n1:\t" \ "lock ; decb %0\n\t" \ @@ -64,6 +68,12 @@ static inline void __raw_spin_lock(raw_s "=m" (lock->slock) : : "memory"); } +/* + * It is easier for the lock validator if interrupts are not re-enabled + * in the middle of a lock-acquire. This is a performance feature anyway + * so we turn it off: + */ +#ifndef CONFIG_PROVE_LOCKING static inline void __raw_spin_lock_flags(raw_spinlock_t *lock, unsigned long flags) { alternative_smp( @@ -71,6 +81,7 @@ static inline void __raw_spin_lock_flags __raw_spin_lock_string_up, "=m" (lock->slock) : "r" (flags) : "memory"); } +#endif static inline int __raw_spin_trylock(raw_spinlock_t *lock) { diff --git a/include/asm-i386/string.h b/include/asm-i386/string.h index bb5f88a..b927736 100644 --- a/include/asm-i386/string.h +++ b/include/asm-i386/string.h @@ -2,7 +2,6 @@ #ifndef _I386_STRING_H_ #define _I386_STRING_H_ #ifdef __KERNEL__ -#include /* * On a 486 or Pentium, we are better off not using the * byte string operations. But on a 386 or a PPro the diff --git a/include/asm-i386/system.h b/include/asm-i386/system.h index 19cc79c..db398d8 100644 --- a/include/asm-i386/system.h +++ b/include/asm-i386/system.h @@ -1,7 +1,6 @@ #ifndef __ASM_SYSTEM_H #define __ASM_SYSTEM_H -#include #include #include #include @@ -428,7 +427,7 @@ #define rmb() alternative("lock; addl $0 * does not enforce ordering, since there is no data dependency between * the read of "a" and the read of "b". Therefore, on some CPUs, such * as Alpha, "y" could be set to 3 and "x" to 0. Use rmb() - * in cases like thiswhere there are no data dependencies. + * in cases like this where there are no data dependencies. **/ #define read_barrier_depends() do { } while(0) @@ -457,25 +456,7 @@ #endif #define set_wmb(var, value) do { var = value; wmb(); } while (0) -/* interrupt control.. */ -#define local_save_flags(x) do { typecheck(unsigned long,x); __asm__ __volatile__("pushfl ; popl %0":"=g" (x): /* no input */); } while (0) -#define local_irq_restore(x) do { typecheck(unsigned long,x); __asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"g" (x):"memory", "cc"); } while (0) -#define local_irq_disable() __asm__ __volatile__("cli": : :"memory") -#define local_irq_enable() __asm__ __volatile__("sti": : :"memory") -/* used in the idle loop; sti takes one instruction cycle to complete */ -#define safe_halt() __asm__ __volatile__("sti; hlt": : :"memory") -/* used when interrupts are already enabled or to shutdown the processor */ -#define halt() __asm__ __volatile__("hlt": : :"memory") - -#define irqs_disabled() \ -({ \ - unsigned long flags; \ - local_save_flags(flags); \ - !(flags & (1<<9)); \ -}) - -/* For spinlocks etc */ -#define local_irq_save(x) __asm__ __volatile__("pushfl ; popl %0 ; cli":"=g" (x): /* no input */ :"memory") +#include /* * disable hlt during certain critical i/o operations diff --git a/include/asm-i386/thread_info.h b/include/asm-i386/thread_info.h index 1f7d48c..2833fa2 100644 --- a/include/asm-i386/thread_info.h +++ b/include/asm-i386/thread_info.h @@ -9,7 +9,6 @@ #define _ASM_THREAD_INFO_H #ifdef __KERNEL__ -#include #include #include @@ -38,6 +37,7 @@ struct thread_info { 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ + void *sysenter_return; struct restart_block restart_block; unsigned long previous_esp; /* ESP of the previous stack in case @@ -84,17 +84,15 @@ #define init_thread_info (init_thread_un #define init_stack (init_thread_union.stack) +/* how to get the current stack pointer from C */ +register unsigned long current_stack_pointer asm("esp") __attribute_used__; + /* how to get the thread information struct from C */ static inline struct thread_info *current_thread_info(void) { - struct thread_info *ti; - __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1))); - return ti; + return (struct thread_info *)(current_stack_pointer & ~(THREAD_SIZE - 1)); } -/* how to get the current stack pointer from C */ -register unsigned long current_stack_pointer asm("esp") __attribute_used__; - /* thread information allocation */ #ifdef CONFIG_DEBUG_STACK_USAGE #define alloc_thread_info(tsk) \ @@ -141,8 +139,7 @@ #define TIF_SYSCALL_EMU 6 /* syscall em #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ #define TIF_SECCOMP 8 /* secure computing */ #define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */ -#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ -#define TIF_MEMDIE 17 +#define TIF_MEMDIE 16 #define _TIF_SYSCALL_TRACE (1<thread_info->status & TS_POLLING) #endif /* __KERNEL__ */ diff --git a/include/asm-i386/timer.h b/include/asm-i386/timer.h index aed1643..d0ebd05 100644 --- a/include/asm-i386/timer.h +++ b/include/asm-i386/timer.h @@ -3,68 +3,11 @@ #define _ASMi386_TIMER_H #include #include -/** - * struct timer_ops - used to define a timer source - * - * @name: name of the timer. - * @init: Probes and initializes the timer. Takes clock= override - * string as an argument. Returns 0 on success, anything else - * on failure. - * @mark_offset: called by the timer interrupt. - * @get_offset: called by gettimeofday(). Returns the number of microseconds - * since the last timer interupt. - * @monotonic_clock: returns the number of nanoseconds since the init of the - * timer. - * @delay: delays this many clock cycles. - */ -struct timer_opts { - char* name; - void (*mark_offset)(void); - unsigned long (*get_offset)(void); - unsigned long long (*monotonic_clock)(void); - void (*delay)(unsigned long); - unsigned long (*read_timer)(void); - int (*suspend)(pm_message_t state); - int (*resume)(void); -}; - -struct init_timer_opts { - int (*init)(char *override); - struct timer_opts *opts; -}; - #define TICK_SIZE (tick_nsec / 1000) - -extern struct timer_opts* __init select_timer(void); -extern void clock_fallback(void); void setup_pit_timer(void); - /* Modifiers for buggy PIT handling */ - extern int pit_latch_buggy; - -extern struct timer_opts *cur_timer; extern int timer_ack; - -/* list of externed timers */ -extern struct timer_opts timer_none; -extern struct timer_opts timer_pit; -extern struct init_timer_opts timer_pit_init; -extern struct init_timer_opts timer_tsc_init; -#ifdef CONFIG_X86_CYCLONE_TIMER -extern struct init_timer_opts timer_cyclone_init; -#endif - -extern unsigned long calibrate_tsc(void); -extern unsigned long read_timer_tsc(void); -extern void init_cpu_khz(void); extern int recalibrate_cpu_khz(void); -#ifdef CONFIG_HPET_TIMER -extern struct init_timer_opts timer_hpet_init; -extern unsigned long calibrate_tsc_hpet(unsigned long *tsc_hpet_quotient_ptr); -#endif -#ifdef CONFIG_X86_PM_TIMER -extern struct init_timer_opts timer_pmtmr_init; -#endif #endif diff --git a/include/asm-i386/timex.h b/include/asm-i386/timex.h index 292b5a6..3666044 100644 --- a/include/asm-i386/timex.h +++ b/include/asm-i386/timex.h @@ -6,8 +6,8 @@ #ifndef _ASMi386_TIMEX_H #define _ASMi386_TIMEX_H -#include #include +#include #ifdef CONFIG_X86_ELAN # define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ @@ -16,39 +16,6 @@ # define CLOCK_TICK_RATE 1193182 /* Und #endif -/* - * Standard way to access the cycle counter on i586+ CPUs. - * Currently only used on SMP. - * - * If you really have a SMP machine with i486 chips or older, - * compile for that, and this will just always return zero. - * That's ok, it just means that the nicer scheduling heuristics - * won't work for you. - * - * We only use the low 32 bits, and we'd simply better make sure - * that we reschedule before that wraps. Scheduling at least every - * four billion cycles just basically sounds like a good idea, - * regardless of how fast the machine is. - */ -typedef unsigned long long cycles_t; - -static inline cycles_t get_cycles (void) -{ - unsigned long long ret=0; - -#ifndef CONFIG_X86_TSC - if (!cpu_has_tsc) - return 0; -#endif - -#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC) - rdtscll(ret); -#endif - return ret; -} - -extern unsigned int cpu_khz; - extern int read_current_timer(unsigned long *timer_value); #define ARCH_HAS_READ_CURRENT_TIMER 1 diff --git a/include/asm-i386/tlbflush.h b/include/asm-i386/tlbflush.h index ab216e1..d57ca5c 100644 --- a/include/asm-i386/tlbflush.h +++ b/include/asm-i386/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _I386_TLBFLUSH_H #define _I386_TLBFLUSH_H -#include #include #include diff --git a/include/asm-i386/topology.h b/include/asm-i386/topology.h index b94e5ee..6adbd9b 100644 --- a/include/asm-i386/topology.h +++ b/include/asm-i386/topology.h @@ -28,10 +28,8 @@ #ifndef _ASM_I386_TOPOLOGY_H #define _ASM_I386_TOPOLOGY_H #ifdef CONFIG_X86_HT -#define topology_physical_package_id(cpu) \ - (phys_proc_id[cpu] == BAD_APICID ? -1 : phys_proc_id[cpu]) -#define topology_core_id(cpu) \ - (cpu_core_id[cpu] == BAD_APICID ? 0 : cpu_core_id[cpu]) +#define topology_physical_package_id(cpu) (cpu_data[cpu].phys_proc_id) +#define topology_core_id(cpu) (cpu_data[cpu].cpu_core_id) #define topology_core_siblings(cpu) (cpu_core_map[cpu]) #define topology_thread_siblings(cpu) (cpu_sibling_map[cpu]) #endif @@ -114,4 +112,9 @@ #endif /* CONFIG_NUMA */ extern cpumask_t cpu_coregroup_map(int cpu); +#ifdef CONFIG_SMP +#define mc_capable() (boot_cpu_data.x86_max_cores > 1) +#define smt_capable() (smp_num_siblings > 1) +#endif + #endif /* _ASM_I386_TOPOLOGY_H */ diff --git a/include/asm-i386/tsc.h b/include/asm-i386/tsc.h new file mode 100644 index 0000000..97b828c --- /dev/null +++ b/include/asm-i386/tsc.h @@ -0,0 +1,49 @@ +/* + * linux/include/asm-i386/tsc.h + * + * i386 TSC related functions + */ +#ifndef _ASM_i386_TSC_H +#define _ASM_i386_TSC_H + +#include +#include + +/* + * Standard way to access the cycle counter on i586+ CPUs. + * Currently only used on SMP. + * + * If you really have a SMP machine with i486 chips or older, + * compile for that, and this will just always return zero. + * That's ok, it just means that the nicer scheduling heuristics + * won't work for you. + * + * We only use the low 32 bits, and we'd simply better make sure + * that we reschedule before that wraps. Scheduling at least every + * four billion cycles just basically sounds like a good idea, + * regardless of how fast the machine is. + */ +typedef unsigned long long cycles_t; + +extern unsigned int cpu_khz; +extern unsigned int tsc_khz; + +static inline cycles_t get_cycles(void) +{ + unsigned long long ret = 0; + +#ifndef CONFIG_X86_TSC + if (!cpu_has_tsc) + return 0; +#endif + +#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC) + rdtscll(ret); +#endif + return ret; +} + +extern void tsc_init(void); +extern void mark_tsc_unstable(void); + +#endif diff --git a/include/asm-i386/types.h b/include/asm-i386/types.h index e50a08b..4b4b295 100644 --- a/include/asm-i386/types.h +++ b/include/asm-i386/types.h @@ -35,7 +35,6 @@ #define BITS_PER_LONG 32 #ifndef __ASSEMBLY__ -#include typedef signed char s8; typedef unsigned char u8; diff --git a/include/asm-i386/uaccess.h b/include/asm-i386/uaccess.h index 371457b..54d905e 100644 --- a/include/asm-i386/uaccess.h +++ b/include/asm-i386/uaccess.h @@ -4,7 +4,6 @@ #define __i386_UACCESS_H /* * User space memory access functions */ -#include #include #include #include @@ -59,7 +58,7 @@ #define __range_ok(addr,size) ({ \ __chk_user_ptr(addr); \ asm("addl %3,%1 ; sbbl %0,%0; cmpl %1,%4; sbbl $0,%0" \ :"=&r" (flag), "=r" (sum) \ - :"1" (addr),"g" ((int)(size)),"g" (current_thread_info()->addr_limit.seg)); \ + :"1" (addr),"g" ((int)(size)),"rm" (current_thread_info()->addr_limit.seg)); \ flag; }) /** @@ -391,6 +390,12 @@ unsigned long __must_check __copy_to_use const void *from, unsigned long n); unsigned long __must_check __copy_from_user_ll(void *to, const void __user *from, unsigned long n); +unsigned long __must_check __copy_from_user_ll_nozero(void *to, + const void __user *from, unsigned long n); +unsigned long __must_check __copy_from_user_ll_nocache(void *to, + const void __user *from, unsigned long n); +unsigned long __must_check __copy_from_user_ll_nocache_nozero(void *to, + const void __user *from, unsigned long n); /* * Here we special-case 1, 2 and 4-byte copy_*_user invocations. On a fault @@ -457,10 +462,41 @@ __copy_to_user(void __user *to, const vo * * If some data could not be copied, this function will pad the copied * data to the requested size using zero bytes. + * + * An alternate version - __copy_from_user_inatomic() - may be called from + * atomic context and will fail rather than sleep. In this case the + * uncopied bytes will *NOT* be padded with zeros. See fs/filemap.h + * for explanation of why this is needed. */ static __always_inline unsigned long __copy_from_user_inatomic(void *to, const void __user *from, unsigned long n) { + /* Avoid zeroing the tail if the copy fails.. + * If 'n' is constant and 1, 2, or 4, we do still zero on a failure, + * but as the zeroing behaviour is only significant when n is not + * constant, that shouldn't be a problem. + */ + if (__builtin_constant_p(n)) { + unsigned long ret; + + switch (n) { + case 1: + __get_user_size(*(u8 *)to, from, 1, ret, 1); + return ret; + case 2: + __get_user_size(*(u16 *)to, from, 2, ret, 2); + return ret; + case 4: + __get_user_size(*(u32 *)to, from, 4, ret, 4); + return ret; + } + } + return __copy_from_user_ll_nozero(to, from, n); +} +static __always_inline unsigned long +__copy_from_user(void *to, const void __user *from, unsigned long n) +{ + might_sleep(); if (__builtin_constant_p(n)) { unsigned long ret; @@ -479,12 +515,36 @@ __copy_from_user_inatomic(void *to, cons return __copy_from_user_ll(to, from, n); } +#define ARCH_HAS_NOCACHE_UACCESS + +static __always_inline unsigned long __copy_from_user_nocache(void *to, + const void __user *from, unsigned long n) +{ + might_sleep(); + if (__builtin_constant_p(n)) { + unsigned long ret; + + switch (n) { + case 1: + __get_user_size(*(u8 *)to, from, 1, ret, 1); + return ret; + case 2: + __get_user_size(*(u16 *)to, from, 2, ret, 2); + return ret; + case 4: + __get_user_size(*(u32 *)to, from, 4, ret, 4); + return ret; + } + } + return __copy_from_user_ll_nocache(to, from, n); +} + static __always_inline unsigned long -__copy_from_user(void *to, const void __user *from, unsigned long n) +__copy_from_user_inatomic_nocache(void *to, const void __user *from, unsigned long n) { - might_sleep(); - return __copy_from_user_inatomic(to, from, n); + return __copy_from_user_ll_nocache_nozero(to, from, n); } + unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n); unsigned long __must_check copy_from_user(void *to, diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index eb4b152..fc1c8dd 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -322,8 +322,11 @@ #define __NR_splice 313 #define __NR_sync_file_range 314 #define __NR_tee 315 #define __NR_vmsplice 316 +#define __NR_move_pages 317 -#define NR_syscalls 317 +#ifdef __KERNEL__ + +#define NR_syscalls 318 /* * user-visible error numbers are in the range -1 - -128: see @@ -422,7 +425,6 @@ __asm__ volatile ("push %%ebp ; push %%e __syscall_return(type,__res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -446,7 +448,6 @@ #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_RT_SIGSUSPEND -#endif #ifdef __KERNEL_SYSCALLS__ @@ -485,7 +486,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -497,4 +498,5 @@ #ifndef cond_syscall #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") #endif +#endif /* __KERNEL__ */ #endif /* _ASM_I386_UNISTD_H_ */ diff --git a/include/asm-i386/unwind.h b/include/asm-i386/unwind.h new file mode 100644 index 0000000..69f0f1d --- /dev/null +++ b/include/asm-i386/unwind.h @@ -0,0 +1,98 @@ +#ifndef _ASM_I386_UNWIND_H +#define _ASM_I386_UNWIND_H + +/* + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich + * This code is released under version 2 of the GNU GPL. + */ + +#ifdef CONFIG_STACK_UNWIND + +#include +#include +#include +#include + +struct unwind_frame_info +{ + struct pt_regs regs; + struct task_struct *task; +}; + +#define UNW_PC(frame) (frame)->regs.eip +#define UNW_SP(frame) (frame)->regs.esp +#ifdef CONFIG_FRAME_POINTER +#define UNW_FP(frame) (frame)->regs.ebp +#define FRAME_RETADDR_OFFSET 4 +#define FRAME_LINK_OFFSET 0 +#define STACK_BOTTOM(tsk) STACK_LIMIT((tsk)->thread.esp0) +#define STACK_TOP(tsk) ((tsk)->thread.esp0) +#endif +#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) + +#define UNW_REGISTER_INFO \ + PTREGS_INFO(eax), \ + PTREGS_INFO(ecx), \ + PTREGS_INFO(edx), \ + PTREGS_INFO(ebx), \ + PTREGS_INFO(esp), \ + PTREGS_INFO(ebp), \ + PTREGS_INFO(esi), \ + PTREGS_INFO(edi), \ + PTREGS_INFO(eip) + +static inline void arch_unw_init_frame_info(struct unwind_frame_info *info, + /*const*/ struct pt_regs *regs) +{ + if (user_mode_vm(regs)) + info->regs = *regs; + else { + memcpy(&info->regs, regs, offsetof(struct pt_regs, esp)); + info->regs.esp = (unsigned long)®s->esp; + info->regs.xss = __KERNEL_DS; + } +} + +static inline void arch_unw_init_blocked(struct unwind_frame_info *info) +{ + memset(&info->regs, 0, sizeof(info->regs)); + info->regs.eip = info->task->thread.eip; + info->regs.xcs = __KERNEL_CS; + __get_user(info->regs.ebp, (long *)info->task->thread.esp); + info->regs.esp = info->task->thread.esp; + info->regs.xss = __KERNEL_DS; + info->regs.xds = __USER_DS; + info->regs.xes = __USER_DS; +} + +extern asmlinkage int arch_unwind_init_running(struct unwind_frame_info *, + asmlinkage int (*callback)(struct unwind_frame_info *, + void *arg), + void *arg); + +static inline int arch_unw_user_mode(const struct unwind_frame_info *info) +{ +#if 0 /* This can only work when selector register and EFLAGS saves/restores + are properly annotated (and tracked in UNW_REGISTER_INFO). */ + return user_mode_vm(&info->regs); +#else + return info->regs.eip < PAGE_OFFSET + || (info->regs.eip >= __fix_to_virt(FIX_VDSO) + && info->regs.eip < __fix_to_virt(FIX_VDSO) + PAGE_SIZE) + || info->regs.esp < PAGE_OFFSET; +#endif +} + +#else + +#define UNW_PC(frame) ((void)(frame), 0) + +static inline int arch_unw_user_mode(const void *info) +{ + return 0; +} + +#endif + +#endif /* _ASM_I386_UNWIND_H */ diff --git a/include/asm-i386/vga.h b/include/asm-i386/vga.h index ef0c0e5..0ecf68a 100644 --- a/include/asm-i386/vga.h +++ b/include/asm-i386/vga.h @@ -12,7 +12,7 @@ #define _LINUX_ASM_VGA_H_ * access the videoram directly without any black magic. */ -#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) +#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x) #define vga_readb(x) (*(x)) #define vga_writeb(x,y) (*(y) = (x)) diff --git a/include/asm-ia64/Kbuild b/include/asm-ia64/Kbuild new file mode 100644 index 0000000..85d6f80 --- /dev/null +++ b/include/asm-ia64/Kbuild @@ -0,0 +1,7 @@ +include include/asm-generic/Kbuild.asm + +header-y += break.h fpu.h fpswa.h gcc_intrin.h ia64regs.h \ + intel_intrin.h intrinsics.h perfmon_default_smpl.h \ + ptrace_offsets.h rse.h setup.h ucontext.h + +unifdef-y += perfmon.h diff --git a/include/asm-ia64/asmmacro.h b/include/asm-ia64/asmmacro.h index edf2ceb..c22b465 100644 --- a/include/asm-ia64/asmmacro.h +++ b/include/asm-ia64/asmmacro.h @@ -6,7 +6,6 @@ #define _ASM_IA64_ASMMACRO_H * David Mosberger-Tang */ -#include #define ENTRY(name) \ .align 32; \ diff --git a/include/asm-ia64/cache.h b/include/asm-ia64/cache.h index f0a104d..e7482bd 100644 --- a/include/asm-ia64/cache.h +++ b/include/asm-ia64/cache.h @@ -1,7 +1,6 @@ #ifndef _ASM_IA64_CACHE_H #define _ASM_IA64_CACHE_H -#include /* * Copyright (C) 1998-2000 Hewlett-Packard Co diff --git a/include/asm-ia64/delay.h b/include/asm-ia64/delay.h index bba7020..a30a62f 100644 --- a/include/asm-ia64/delay.h +++ b/include/asm-ia64/delay.h @@ -12,7 +12,6 @@ #define _ASM_IA64_DELAY_H * Copyright (C) 1999 Don Dugger */ -#include #include #include #include diff --git a/include/asm-ia64/dma-mapping.h b/include/asm-ia64/dma-mapping.h index df67d40..99a8f8e 100644 --- a/include/asm-ia64/dma-mapping.h +++ b/include/asm-ia64/dma-mapping.h @@ -5,7 +5,6 @@ #define _ASM_IA64_DMA_MAPPING_H * Copyright (C) 2003-2004 Hewlett-Packard Co * David Mosberger-Tang */ -#include #include #define dma_alloc_coherent platform_dma_alloc_coherent diff --git a/include/asm-ia64/dma.h b/include/asm-ia64/dma.h index 3be1b49..dad3a73 100644 --- a/include/asm-ia64/dma.h +++ b/include/asm-ia64/dma.h @@ -6,7 +6,6 @@ #define _ASM_IA64_DMA_H * David Mosberger-Tang */ -#include #include /* need byte IO */ diff --git a/include/asm-ia64/elf.h b/include/asm-ia64/elf.h index 446fce0..25f9835 100644 --- a/include/asm-ia64/elf.h +++ b/include/asm-ia64/elf.h @@ -8,7 +8,6 @@ #define _ASM_IA64_ELF_H * David Mosberger-Tang */ -#include #include #include diff --git a/include/asm-ia64/hardirq.h b/include/asm-ia64/hardirq.h index 33ef8f0..140e495 100644 --- a/include/asm-ia64/hardirq.h +++ b/include/asm-ia64/hardirq.h @@ -6,7 +6,6 @@ #define _ASM_IA64_HARDIRQ_H * David Mosberger-Tang */ -#include #include #include diff --git a/include/asm-ia64/hw_irq.h b/include/asm-ia64/hw_irq.h index 0cf119b..27f9df6 100644 --- a/include/asm-ia64/hw_irq.h +++ b/include/asm-ia64/hw_irq.h @@ -47,9 +47,19 @@ #define IA64_CPE_VECTOR 0x1e /* correc #define IA64_CMC_VECTOR 0x1f /* corrected machine-check interrupt vector */ /* * Vectors 0x20-0x2f are reserved for legacy ISA IRQs. + * Use vectors 0x30-0xe7 as the default device vector range for ia64. + * Platforms may choose to reduce this range in platform_irq_setup, but the + * platform range must fall within + * [IA64_DEF_FIRST_DEVICE_VECTOR..IA64_DEF_LAST_DEVICE_VECTOR] */ -#define IA64_FIRST_DEVICE_VECTOR 0x30 -#define IA64_LAST_DEVICE_VECTOR 0xe7 +extern int ia64_first_device_vector; +extern int ia64_last_device_vector; + +#define IA64_DEF_FIRST_DEVICE_VECTOR 0x30 +#define IA64_DEF_LAST_DEVICE_VECTOR 0xe7 +#define IA64_FIRST_DEVICE_VECTOR ia64_first_device_vector +#define IA64_LAST_DEVICE_VECTOR ia64_last_device_vector +#define IA64_MAX_DEVICE_VECTORS (IA64_DEF_LAST_DEVICE_VECTOR - IA64_DEF_FIRST_DEVICE_VECTOR + 1) #define IA64_NUM_DEVICE_VECTORS (IA64_LAST_DEVICE_VECTOR - IA64_FIRST_DEVICE_VECTOR + 1) #define IA64_MCA_RENDEZ_VECTOR 0xe8 /* MCA rendez interrupt */ @@ -83,11 +93,11 @@ extern struct hw_interrupt_type irq_type extern int assign_irq_vector (int irq); /* allocate a free vector */ extern void free_irq_vector (int vector); +extern int reserve_irq_vector (int vector); extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect); extern void register_percpu_irq (ia64_vector vec, struct irqaction *action); -static inline void -hw_resend_irq (struct hw_interrupt_type *h, unsigned int vector) +static inline void ia64_resend_irq(unsigned int vector) { platform_send_ipi(smp_processor_id(), vector, IA64_IPI_DM_INT, 0); } diff --git a/include/asm-ia64/ia32.h b/include/asm-ia64/ia32.h index f8044a1..5ff8d74 100644 --- a/include/asm-ia64/ia32.h +++ b/include/asm-ia64/ia32.h @@ -1,7 +1,6 @@ #ifndef _ASM_IA64_IA32_H #define _ASM_IA64_IA32_H -#include #include #include diff --git a/include/asm-ia64/ide.h b/include/asm-ia64/ide.h index 93f45c5..e928675 100644 --- a/include/asm-ia64/ide.h +++ b/include/asm-ia64/ide.h @@ -13,7 +13,6 @@ #define __ASM_IA64_IDE_H #ifdef __KERNEL__ -#include #include diff --git a/include/asm-ia64/intrinsics.h b/include/asm-ia64/intrinsics.h index 8089f95..3a95aa4 100644 --- a/include/asm-ia64/intrinsics.h +++ b/include/asm-ia64/intrinsics.h @@ -9,7 +9,6 @@ #define _ASM_IA64_INTRINSICS_H */ #ifndef __ASSEMBLY__ -#include /* include compiler specific intrinsics */ #include diff --git a/include/asm-ia64/io.h b/include/asm-ia64/io.h index c2e3742..781ee2c 100644 --- a/include/asm-ia64/io.h +++ b/include/asm-ia64/io.h @@ -88,6 +88,7 @@ phys_to_virt (unsigned long address) } #define ARCH_HAS_VALID_PHYS_ADDR_RANGE +extern u64 kern_mem_attribute (unsigned long phys_addr, unsigned long size); extern int valid_phys_addr_range (unsigned long addr, size_t count); /* efi.c */ extern int valid_mmap_phys_addr_range (unsigned long addr, size_t count); diff --git a/include/asm-ia64/irq.h b/include/asm-ia64/irq.h index dbe86c0..79479e2 100644 --- a/include/asm-ia64/irq.h +++ b/include/asm-ia64/irq.h @@ -14,11 +14,6 @@ #define _ASM_IA64_IRQ_H #define NR_IRQS 256 #define NR_IRQ_VECTORS NR_IRQS -/* - * IRQ line status macro IRQ_PER_CPU is used - */ -#define ARCH_HAS_IRQ_PER_CPU - static __inline__ int irq_canonicalize (int irq) { diff --git a/include/asm-ia64/kdebug.h b/include/asm-ia64/kdebug.h index c195a9a..aed7142 100644 --- a/include/asm-ia64/kdebug.h +++ b/include/asm-ia64/kdebug.h @@ -40,6 +40,8 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); extern struct atomic_notifier_head ia64die_chain; enum die_val { diff --git a/include/asm-ia64/kmap_types.h b/include/asm-ia64/kmap_types.h index bc77752..5d1658a 100644 --- a/include/asm-ia64/kmap_types.h +++ b/include/asm-ia64/kmap_types.h @@ -1,7 +1,6 @@ #ifndef _ASM_IA64_KMAP_TYPES_H #define _ASM_IA64_KMAP_TYPES_H -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-ia64/kprobes.h b/include/asm-ia64/kprobes.h index 8c0fc22..2418a78 100644 --- a/include/asm-ia64/kprobes.h +++ b/include/asm-ia64/kprobes.h @@ -82,6 +82,7 @@ struct kprobe_ctlblk { #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry #define ARCH_SUPPORTS_KRETPROBES +#define ARCH_INACTIVE_KPROBE_COUNT 1 #define SLOT0_OPCODE_SHIFT (37) #define SLOT1_p1_OPCODE_SHIFT (37 - (64-46)) diff --git a/include/asm-ia64/machvec.h b/include/asm-ia64/machvec.h index a9c995a..15b545a 100644 --- a/include/asm-ia64/machvec.h +++ b/include/asm-ia64/machvec.h @@ -10,7 +10,6 @@ #ifndef _ASM_IA64_MACHVEC_H #define _ASM_IA64_MACHVEC_H -#include #include /* forward declarations: */ @@ -76,6 +75,7 @@ typedef unsigned char ia64_mv_readb_rela typedef unsigned short ia64_mv_readw_relaxed_t (const volatile void __iomem *); typedef unsigned int ia64_mv_readl_relaxed_t (const volatile void __iomem *); typedef unsigned long ia64_mv_readq_relaxed_t (const volatile void __iomem *); +typedef int ia64_mv_msi_init_t (void); static inline void machvec_noop (void) @@ -154,6 +154,7 @@ # define platform_readw_relaxed # define platform_readl_relaxed ia64_mv.readl_relaxed # define platform_readq_relaxed ia64_mv.readq_relaxed # define platform_migrate ia64_mv.migrate +# define platform_msi_init ia64_mv.msi_init # endif /* __attribute__((__aligned__(16))) is required to make size of the @@ -203,6 +204,7 @@ struct ia64_machine_vector { ia64_mv_readl_relaxed_t *readl_relaxed; ia64_mv_readq_relaxed_t *readq_relaxed; ia64_mv_migrate_t *migrate; + ia64_mv_msi_init_t *msi_init; } __attribute__((__aligned__(16))); /* align attrib? see above comment */ #define MACHVEC_INIT(name) \ @@ -248,6 +250,7 @@ #define MACHVEC_INIT(name) \ platform_readl_relaxed, \ platform_readq_relaxed, \ platform_migrate, \ + platform_msi_init, \ } extern struct ia64_machine_vector ia64_mv; @@ -401,5 +404,8 @@ #endif #ifndef platform_migrate # define platform_migrate machvec_noop_task #endif +#ifndef platform_msi_init +# define platform_msi_init ((ia64_mv_msi_init_t*)NULL) +#endif #endif /* _ASM_IA64_MACHVEC_H */ diff --git a/include/asm-ia64/machvec_sn2.h b/include/asm-ia64/machvec_sn2.h index da1d437..cf724dc 100644 --- a/include/asm-ia64/machvec_sn2.h +++ b/include/asm-ia64/machvec_sn2.h @@ -67,6 +67,8 @@ extern ia64_mv_dma_sync_sg_for_device sn extern ia64_mv_dma_mapping_error sn_dma_mapping_error; extern ia64_mv_dma_supported sn_dma_supported; extern ia64_mv_migrate_t sn_migrate; +extern ia64_mv_msi_init_t sn_msi_init; + /* * This stuff has dual use! @@ -117,6 +119,11 @@ #define platform_dma_sync_sg_for_device #define platform_dma_mapping_error sn_dma_mapping_error #define platform_dma_supported sn_dma_supported #define platform_migrate sn_migrate +#ifdef CONFIG_PCI_MSI +#define platform_msi_init sn_msi_init +#else +#define platform_msi_init ((ia64_mv_msi_init_t*)NULL) +#endif #include diff --git a/include/asm-ia64/mca.h b/include/asm-ia64/mca.h index 9c5389b..ee97f7c 100644 --- a/include/asm-ia64/mca.h +++ b/include/asm-ia64/mca.h @@ -69,14 +69,16 @@ typedef struct ia64_mc_info_s { */ struct ia64_sal_os_state { - /* SAL to OS, must be at offset 0 */ + + /* SAL to OS */ u64 os_gp; /* GP of the os registered with the SAL, physical */ u64 pal_proc; /* PAL_PROC entry point, physical */ u64 sal_proc; /* SAL_PROC entry point, physical */ u64 rv_rc; /* MCA - Rendezvous state, INIT - reason code */ u64 proc_state_param; /* from R18 */ u64 monarch; /* 1 for a monarch event, 0 for a slave */ - /* common, must follow SAL to OS */ + + /* common */ u64 sal_ra; /* Return address in SAL, physical */ u64 sal_gp; /* GP of the SAL - physical */ pal_min_state_area_t *pal_min_state; /* from R17. physical in asm, virtual in C */ @@ -98,7 +100,8 @@ struct ia64_sal_os_state { u64 iipa; u64 iim; u64 iha; - /* OS to SAL, must follow common */ + + /* OS to SAL */ u64 os_status; /* OS status to SAL, enum below */ u64 context; /* 0 if return to same context 1 if return to new context */ diff --git a/include/asm-ia64/meminit.h b/include/asm-ia64/meminit.h index 46501b0..894bc4d 100644 --- a/include/asm-ia64/meminit.h +++ b/include/asm-ia64/meminit.h @@ -7,7 +7,6 @@ #define meminit_h * for more details. */ -#include /* * Entries defined so far: diff --git a/include/asm-ia64/msi.h b/include/asm-ia64/msi.h index 97890f7..bb92b0d 100644 --- a/include/asm-ia64/msi.h +++ b/include/asm-ia64/msi.h @@ -14,4 +14,16 @@ #define IO_APIC_VECTOR(irq) (irq) #define ack_APIC_irq ia64_eoi #define MSI_TARGET_CPU_SHIFT 4 +extern struct msi_ops msi_apic_ops; + +static inline int msi_arch_init(void) +{ + if (platform_msi_init) + return platform_msi_init(); + + /* default ops for most ia64 platforms */ + msi_register(&msi_apic_ops); + return 0; +} + #endif /* ASM_MSI_H */ diff --git a/include/asm-ia64/nodedata.h b/include/asm-ia64/nodedata.h index 9978c7c..2fb337b 100644 --- a/include/asm-ia64/nodedata.h +++ b/include/asm-ia64/nodedata.h @@ -11,7 +11,6 @@ #ifndef _ASM_IA64_NODEDATA_H #define _ASM_IA64_NODEDATA_H -#include #include #include @@ -47,6 +46,18 @@ #define local_node_data (local_cpu_data */ #define NODE_DATA(nid) (local_node_data->pg_data_ptrs[nid]) +/* + * LOCAL_DATA_ADDR - This is to calculate the address of other node's + * "local_node_data" at hot-plug phase. The local_node_data + * is pointed by per_cpu_page. Kernel usually use it for + * just executing cpu. However, when new node is hot-added, + * the addresses of local data for other nodes are necessary + * to update all of them. + */ +#define LOCAL_DATA_ADDR(pgdat) \ + ((struct ia64_node_data *)((u64)(pgdat) + \ + L1_CACHE_ALIGN(sizeof(struct pglist_data)))) + #endif /* CONFIG_NUMA */ #endif /* _ASM_IA64_NODEDATA_H */ diff --git a/include/asm-ia64/numa.h b/include/asm-ia64/numa.h index dae6aeb..e5a8260 100644 --- a/include/asm-ia64/numa.h +++ b/include/asm-ia64/numa.h @@ -11,7 +11,6 @@ #ifndef _ASM_IA64_NUMA_H #define _ASM_IA64_NUMA_H -#include #ifdef CONFIG_NUMA diff --git a/include/asm-ia64/page.h b/include/asm-ia64/page.h index 2087825..f5a949e 100644 --- a/include/asm-ia64/page.h +++ b/include/asm-ia64/page.h @@ -7,7 +7,6 @@ #define _ASM_IA64_PAGE_H * David Mosberger-Tang */ -#include #include #include diff --git a/include/asm-ia64/param.h b/include/asm-ia64/param.h index 5e1e0d2..49c62dd 100644 --- a/include/asm-ia64/param.h +++ b/include/asm-ia64/param.h @@ -19,7 +19,6 @@ #endif #define MAXHOSTNAMELEN 64 /* max length of hostname */ #ifdef __KERNEL__ -# include /* mustn't include outside of #ifdef __KERNEL__ */ # ifdef CONFIG_IA64_HP_SIM /* * Yeah, simulating stuff is slow, so let us catch some breath between diff --git a/include/asm-ia64/percpu.h b/include/asm-ia64/percpu.h index 2b14dee..fbe5cf3 100644 --- a/include/asm-ia64/percpu.h +++ b/include/asm-ia64/percpu.h @@ -12,7 +12,6 @@ #ifdef __ASSEMBLY__ # define THIS_CPU(var) (per_cpu__##var) /* use this to mark accesses to per-CPU variables... */ #else /* !__ASSEMBLY__ */ -#include #include @@ -37,12 +36,14 @@ #define DEFINE_PER_CPU(type, name) \ #ifdef CONFIG_SMP extern unsigned long __per_cpu_offset[NR_CPUS]; +#define per_cpu_offset(x) (__per_cpu_offset(x)) /* Equal to __per_cpu_offset[smp_processor_id()], but faster to access: */ DECLARE_PER_CPU(unsigned long, local_per_cpu_offset); #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu])) #define __get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __ia64_per_cpu_var(local_per_cpu_offset))) +#define __raw_get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __ia64_per_cpu_var(local_per_cpu_offset))) extern void percpu_modcopy(void *pcpudst, const void *src, unsigned long size); extern void setup_per_cpu_areas (void); @@ -52,6 +53,7 @@ #else /* ! SMP */ #define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var +#define __raw_get_cpu_var(var) per_cpu__##var #define per_cpu_init() (__phys_per_cpu_start) #endif /* SMP */ diff --git a/include/asm-ia64/pgalloc.h b/include/asm-ia64/pgalloc.h index f2f2338..9cb68e9 100644 --- a/include/asm-ia64/pgalloc.h +++ b/include/asm-ia64/pgalloc.h @@ -13,7 +13,6 @@ #define _ASM_IA64_PGALLOC_H * Copyright (C) 2000, Goutham Rao */ -#include #include #include diff --git a/include/asm-ia64/pgtable.h b/include/asm-ia64/pgtable.h index c0f8144..228981c 100644 --- a/include/asm-ia64/pgtable.h +++ b/include/asm-ia64/pgtable.h @@ -12,7 +12,6 @@ #define _ASM_IA64_PGTABLE_H * David Mosberger-Tang */ -#include #include #include @@ -317,22 +316,20 @@ #define pte_mkdirty(pte) (__pte(pte_val( #define pte_mkhuge(pte) (__pte(pte_val(pte))) /* - * Macro to a page protection value as "uncacheable". Note that "protection" is really a - * misnomer here as the protection value contains the memory attribute bits, dirty bits, - * and various other bits as well. + * Make page protection values cacheable, uncacheable, or write- + * combining. Note that "protection" is really a misnomer here as the + * protection value contains the memory attribute bits, dirty bits, and + * various other bits as well. */ +#define pgprot_cacheable(prot) __pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_WB) #define pgprot_noncached(prot) __pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_UC) - -/* - * Macro to make mark a page protection value as "write-combining". - * Note that "protection" is really a misnomer here as the protection - * value contains the memory attribute bits, dirty bits, and various - * other bits as well. Accesses through a write-combining translation - * works bypasses the caches, but does allow for consecutive writes to - * be combined into single (but larger) write transactions. - */ #define pgprot_writecombine(prot) __pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_WC) +struct file; +extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, + unsigned long size, pgprot_t vma_prot); +#define __HAVE_PHYS_MEM_ACCESS_PROT + static inline unsigned long pgd_index (unsigned long address) { diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index b3bd58e..265f482 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -13,7 +13,6 @@ #define _ASM_IA64_PROCESSOR_H * 06/16/00 A. Mallick added csd/ssd/tssd for ia32 support */ -#include #include #include diff --git a/include/asm-ia64/ptrace.h b/include/asm-ia64/ptrace.h index 9471cdc..415abb2 100644 --- a/include/asm-ia64/ptrace.h +++ b/include/asm-ia64/ptrace.h @@ -54,7 +54,6 @@ #define _ASM_IA64_PTRACE_H * This is because ar.ec is saved as part of ar.pfs. */ -#include #include #ifndef ASM_OFFSETS_C diff --git a/include/asm-ia64/rwsem.h b/include/asm-ia64/rwsem.h index 1327c91..2d1640c 100644 --- a/include/asm-ia64/rwsem.h +++ b/include/asm-ia64/rwsem.h @@ -33,9 +33,6 @@ struct rw_semaphore { signed long count; spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; -#endif }; #define RWSEM_UNLOCKED_VALUE __IA64_UL_CONST(0x0000000000000000) @@ -45,19 +42,9 @@ #define RWSEM_WAITING_BIAS -__IA64_UL_C #define RWSEM_ACTIVE_READ_BIAS RWSEM_ACTIVE_BIAS #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) -/* - * initialization - */ -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 -#else -#define __RWSEM_DEBUG_INIT /* */ -#endif - #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEBUG_INIT } + LIST_HEAD_INIT((name).wait_list) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) @@ -73,9 +60,6 @@ init_rwsem (struct rw_semaphore *sem) sem->count = RWSEM_UNLOCKED_VALUE; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif } /* diff --git a/include/asm-ia64/signal.h b/include/asm-ia64/signal.h index 5e328ed..4f5ca56 100644 --- a/include/asm-ia64/signal.h +++ b/include/asm-ia64/signal.h @@ -56,7 +56,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -76,7 +75,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 @@ -114,8 +112,6 @@ #define _NSIG 64 #define _NSIG_BPW 64 #define _NSIG_WORDS (_NSIG / _NSIG_BPW) -#define SA_PERCPU_IRQ 0x02000000 - #endif /* __KERNEL__ */ #include diff --git a/include/asm-ia64/smp.h b/include/asm-ia64/smp.h index a391435..719ff30 100644 --- a/include/asm-ia64/smp.h +++ b/include/asm-ia64/smp.h @@ -10,7 +10,6 @@ #ifndef _ASM_IA64_SMP_H #define _ASM_IA64_SMP_H -#include #include #include #include diff --git a/include/asm-ia64/sn/intr.h b/include/asm-ia64/sn/intr.h index 60a51a4..12b54dd 100644 --- a/include/asm-ia64/sn/intr.h +++ b/include/asm-ia64/sn/intr.h @@ -10,6 +10,7 @@ #ifndef _ASM_IA64_SN_INTR_H #define _ASM_IA64_SN_INTR_H #include +#include #define SGI_UART_VECTOR 0xe9 @@ -40,6 +41,7 @@ struct sn_irq_info { int irq_cpuid; /* kernel logical cpuid */ int irq_irq; /* the IRQ number */ int irq_int_bit; /* Bridge interrupt pin */ + /* <0 means MSI */ u64 irq_xtalkaddr; /* xtalkaddr IRQ is sent to */ int irq_bridge_type;/* pciio asic type (pciio.h) */ void *irq_bridge; /* bridge generating irq */ @@ -53,6 +55,12 @@ struct sn_irq_info { }; extern void sn_send_IPI_phys(int, long, int, int); +extern u64 sn_intr_alloc(nasid_t, int, + struct sn_irq_info *, + int, nasid_t, int); +extern void sn_intr_free(nasid_t, int, struct sn_irq_info *); +extern struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *, nasid_t, int); +extern struct list_head **sn_irq_lh; #define CPU_VECTOR_TO_IRQ(cpuid,vector) (vector) diff --git a/include/asm-ia64/sn/pcibr_provider.h b/include/asm-ia64/sn/pcibr_provider.h index 51260ab..e3b0c3f 100644 --- a/include/asm-ia64/sn/pcibr_provider.h +++ b/include/asm-ia64/sn/pcibr_provider.h @@ -55,6 +55,7 @@ #define IS_PCI32_DIRECT(x) #define PCI32_ATE_V (0x1 << 0) #define PCI32_ATE_CO (0x1 << 1) #define PCI32_ATE_PREC (0x1 << 2) +#define PCI32_ATE_MSI (0x1 << 2) #define PCI32_ATE_PREF (0x1 << 3) #define PCI32_ATE_BAR (0x1 << 4) #define PCI32_ATE_ADDR_SHFT 12 @@ -117,8 +118,8 @@ struct pcibus_info { extern int pcibr_init_provider(void); extern void *pcibr_bus_fixup(struct pcibus_bussoft *, struct pci_controller *); -extern dma_addr_t pcibr_dma_map(struct pci_dev *, unsigned long, size_t); -extern dma_addr_t pcibr_dma_map_consistent(struct pci_dev *, unsigned long, size_t); +extern dma_addr_t pcibr_dma_map(struct pci_dev *, unsigned long, size_t, int type); +extern dma_addr_t pcibr_dma_map_consistent(struct pci_dev *, unsigned long, size_t, int type); extern void pcibr_dma_unmap(struct pci_dev *, dma_addr_t, int); /* diff --git a/include/asm-ia64/sn/pcibus_provider_defs.h b/include/asm-ia64/sn/pcibus_provider_defs.h index ce3f6c3..8f7c83d 100644 --- a/include/asm-ia64/sn/pcibus_provider_defs.h +++ b/include/asm-ia64/sn/pcibus_provider_defs.h @@ -3,7 +3,7 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 1992 - 1997, 2000-2005 Silicon Graphics, Inc. All rights reserved. */ #ifndef _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H #define _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H @@ -45,13 +45,24 @@ struct pci_controller; */ struct sn_pcibus_provider { - dma_addr_t (*dma_map)(struct pci_dev *, unsigned long, size_t); - dma_addr_t (*dma_map_consistent)(struct pci_dev *, unsigned long, size_t); + dma_addr_t (*dma_map)(struct pci_dev *, unsigned long, size_t, int flags); + dma_addr_t (*dma_map_consistent)(struct pci_dev *, unsigned long, size_t, int flags); void (*dma_unmap)(struct pci_dev *, dma_addr_t, int); void * (*bus_fixup)(struct pcibus_bussoft *, struct pci_controller *); void (*force_interrupt)(struct sn_irq_info *); void (*target_interrupt)(struct sn_irq_info *); }; +/* + * Flags used by the map interfaces + * bits 3:0 specifies format of passed in address + * bit 4 specifies that address is to be used for MSI + */ + +#define SN_DMA_ADDRTYPE(x) ((x) & 0xf) +#define SN_DMA_ADDR_PHYS 1 /* address is an xio address. */ +#define SN_DMA_ADDR_XIO 2 /* address is phys memory */ +#define SN_DMA_MSI 0x10 /* Bus address is to be used for MSI */ + extern struct sn_pcibus_provider *sn_pci_provider[]; #endif /* _ASM_IA64_SN_PCI_PCIBUS_PROVIDER_H */ diff --git a/include/asm-ia64/sn/simulator.h b/include/asm-ia64/sn/simulator.h index 16a48b5..c3fd3eb 100644 --- a/include/asm-ia64/sn/simulator.h +++ b/include/asm-ia64/sn/simulator.h @@ -8,7 +8,6 @@ #ifndef _ASM_IA64_SN_SIMULATOR_H #define _ASM_IA64_SN_SIMULATOR_H -#include #define SNMAGIC 0xaeeeeeee8badbeefL #define IS_MEDUSA() ({long sn; asm("mov %0=cpuid[%1]" : "=r"(sn) : "r"(2)); sn == SNMAGIC;}) diff --git a/include/asm-ia64/sn/sn_cpuid.h b/include/asm-ia64/sn/sn_cpuid.h index 749deb2..a676dd9 100644 --- a/include/asm-ia64/sn/sn_cpuid.h +++ b/include/asm-ia64/sn/sn_cpuid.h @@ -11,7 +11,6 @@ #ifndef _ASM_IA64_SN_SN_CPUID_H #define _ASM_IA64_SN_SN_CPUID_H -#include #include #include #include diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 51aca02..bd4452b 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h @@ -12,7 +12,6 @@ #define _ASM_IA64_SN_SN_SAL_H */ -#include #include #include #include @@ -86,6 +85,7 @@ #define SN_SAL_IOIF_GET_PCI_TOPOLOGY #define SN_SAL_GET_PROM_FEATURE_SET 0x02000065 #define SN_SAL_SET_OS_FEATURE_SET 0x02000066 #define SN_SAL_INJECT_ERROR 0x02000067 +#define SN_SAL_SET_CPU_NUMBER 0x02000068 /* * Service-specific constants @@ -346,7 +346,7 @@ ia64_sn_plat_set_error_handling_features ret_stuff.v1 = 0; ret_stuff.v2 = 0; SAL_CALL_REENTRANT(ret_stuff, SN_SAL_SET_ERROR_HANDLING_FEATURES, - (SAL_ERR_FEAT_MCA_SLV_TO_OS_INIT_SLV | SAL_ERR_FEAT_LOG_SBES), + SAL_ERR_FEAT_LOG_SBES, 0, 0, 0, 0, 0, 0); return ret_stuff.status; @@ -1151,4 +1151,13 @@ sn_inject_error(u64 paddr, u64 *data, u6 local_irq_restore(irq_flags); return ret_stuff.status; } + +static inline int +ia64_sn_set_cpu_number(int cpu) +{ + struct ia64_sal_retval rv; + + SAL_CALL_NOLOCK(rv, SN_SAL_SET_CPU_NUMBER, cpu, 0, 0, 0, 0, 0, 0); + return rv.status; +} #endif /* _ASM_IA64_SN_SN_SAL_H */ diff --git a/include/asm-ia64/sn/tioca_provider.h b/include/asm-ia64/sn/tioca_provider.h index ab7fe24..65cdd73 100644 --- a/include/asm-ia64/sn/tioca_provider.h +++ b/include/asm-ia64/sn/tioca_provider.h @@ -27,7 +27,7 @@ #define PV907908 (1 << 1) #define PV908234 (1 << 1) /* CA:AGPDMA write request data mismatch with ABC1CL merge */ #define PV895469 (1 << 1) - /* TIO:CA TLB invalidate of written GART entries possibly not occuring in CA*/ + /* TIO:CA TLB invalidate of written GART entries possibly not occurring in CA*/ #define PV910244 (1 << 1) struct tioca_dmamap{ diff --git a/include/asm-ia64/sn/tiocp.h b/include/asm-ia64/sn/tiocp.h index f47c08a..e8ad0bb 100644 --- a/include/asm-ia64/sn/tiocp.h +++ b/include/asm-ia64/sn/tiocp.h @@ -3,13 +3,14 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2003-2004 Silicon Graphics, Inc. All rights reserved. + * Copyright (C) 2003-2005 Silicon Graphics, Inc. All rights reserved. */ #ifndef _ASM_IA64_SN_PCI_TIOCP_H #define _ASM_IA64_SN_PCI_TIOCP_H #define TIOCP_HOST_INTR_ADDR 0x003FFFFFFFFFFFFFUL #define TIOCP_PCI64_CMDTYPE_MEM (0x1ull << 60) +#define TIOCP_PCI64_CMDTYPE_MSI (0x3ull << 60) /***************************************************************************** diff --git a/include/asm-ia64/sn/xpc.h b/include/asm-ia64/sn/xpc.h index aa3b8ac..8406f1e 100644 --- a/include/asm-ia64/sn/xpc.h +++ b/include/asm-ia64/sn/xpc.h @@ -15,7 +15,6 @@ #ifndef _ASM_IA64_SN_XPC_H #define _ASM_IA64_SN_XPC_H -#include #include #include #include diff --git a/include/asm-ia64/socket.h b/include/asm-ia64/socket.h index a255006..d638ef3 100644 --- a/include/asm-ia64/socket.h +++ b/include/asm-ia64/socket.h @@ -57,5 +57,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_IA64_SOCKET_H */ diff --git a/include/asm-ia64/string.h b/include/asm-ia64/string.h index 43502d3..85fd65c 100644 --- a/include/asm-ia64/string.h +++ b/include/asm-ia64/string.h @@ -9,7 +9,6 @@ #define _ASM_IA64_STRING_H * David Mosberger-Tang */ -#include /* remove this once we remove the A-step workaround... */ #define __HAVE_ARCH_STRLEN 1 /* see arch/ia64/lib/strlen.S */ #define __HAVE_ARCH_MEMSET 1 /* see arch/ia64/lib/memset.S */ diff --git a/include/asm-ia64/system.h b/include/asm-ia64/system.h index 2f36205..65db43c 100644 --- a/include/asm-ia64/system.h +++ b/include/asm-ia64/system.h @@ -12,7 +12,6 @@ #define _ASM_IA64_SYSTEM_H * Copyright (C) 1999 Asit Mallick * Copyright (C) 1999 Don Dugger */ -#include #include #include diff --git a/include/asm-ia64/thread_info.h b/include/asm-ia64/thread_info.h index e5392c4..8adcde0 100644 --- a/include/asm-ia64/thread_info.h +++ b/include/asm-ia64/thread_info.h @@ -27,6 +27,7 @@ struct thread_info { __u32 flags; /* thread_info flags (see TIF_*) */ __u32 cpu; /* current CPU */ __u32 last_cpu; /* Last CPU thread ran on */ + __u32 status; /* Thread synchronous flags */ mm_segment_t addr_limit; /* user-level address space limit */ int preempt_count; /* 0=premptable, <0=BUG; will also serve as bh-counter */ struct restart_block restart_block; @@ -67,7 +68,7 @@ #define setup_thread_stack(p, org) \ #define end_of_stack(p) (unsigned long *)((void *)(p) + IA64_RBS_OFFSET) #define __HAVE_ARCH_TASK_STRUCT_ALLOCATOR -#define alloc_task_struct() ((task_t *)__get_free_pages(GFP_KERNEL | __GFP_COMP, KERNEL_STACK_SIZE_ORDER)) +#define alloc_task_struct() ((struct task_struct *)__get_free_pages(GFP_KERNEL | __GFP_COMP, KERNEL_STACK_SIZE_ORDER)) #define free_task_struct(tsk) free_pages((unsigned long) (tsk), KERNEL_STACK_SIZE_ORDER) #endif /* !__ASSEMBLY */ @@ -103,4 +104,8 @@ #define TIF_ALLWORK_MASK (_TIF_NOTIFY_RE /* like TIF_ALLWORK_BITS but sans TIF_SYSCALL_TRACE or TIF_SYSCALL_AUDIT */ #define TIF_WORK_MASK (TIF_ALLWORK_MASK&~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)) +#define TS_POLLING 1 /* true if in idle loop and not sleeping */ + +#define tsk_is_polling(t) ((t)->thread_info->status & TS_POLLING) + #endif /* _ASM_IA64_THREAD_INFO_H */ diff --git a/include/asm-ia64/tlb.h b/include/asm-ia64/tlb.h index 834370b..26edcb7 100644 --- a/include/asm-ia64/tlb.h +++ b/include/asm-ia64/tlb.h @@ -37,7 +37,6 @@ #define _ASM_IA64_TLB_H * } * tlb_finish_mmu(tlb, start, end); // finish unmap for address space MM */ -#include #include #include #include diff --git a/include/asm-ia64/tlbflush.h b/include/asm-ia64/tlbflush.h index a35b323..cf9acb9 100644 --- a/include/asm-ia64/tlbflush.h +++ b/include/asm-ia64/tlbflush.h @@ -6,7 +6,6 @@ #define _ASM_IA64_TLBFLUSH_H * David Mosberger-Tang */ -#include #include diff --git a/include/asm-ia64/topology.h b/include/asm-ia64/topology.h index 616b5ed..937c212 100644 --- a/include/asm-ia64/topology.h +++ b/include/asm-ia64/topology.h @@ -112,6 +112,7 @@ #define topology_physical_package_id(cpu #define topology_core_id(cpu) (cpu_data(cpu)->core_id) #define topology_core_siblings(cpu) (cpu_core_map[cpu]) #define topology_thread_siblings(cpu) (cpu_sibling_map[cpu]) +#define smt_capable() (smp_num_siblings > 1) #endif #include diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index 7107763..bb0eb72 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -265,7 +265,7 @@ #define __NR_request_key 1272 #define __NR_keyctl 1273 #define __NR_ioprio_set 1274 #define __NR_ioprio_get 1275 -/* 1276 is available for reuse (was briefly sys_set_zone_reclaim) */ +#define __NR_move_pages 1276 #define __NR_inotify_init 1277 #define __NR_inotify_add_watch 1278 #define __NR_inotify_rm_watch 1279 @@ -294,7 +294,6 @@ #define __NR_vmsplice 1302 #ifdef __KERNEL__ -#include #define NR_syscalls 279 /* length of syscall table */ diff --git a/include/asm-ia64/vga.h b/include/asm-ia64/vga.h index 091177c..02184ec 100644 --- a/include/asm-ia64/vga.h +++ b/include/asm-ia64/vga.h @@ -17,7 +17,7 @@ #define __ASM_IA64_VGA_H_ extern unsigned long vga_console_iobase; extern unsigned long vga_console_membase; -#define VGA_MAP_MEM(x) ((unsigned long) ioremap_nocache(vga_console_membase + (x), 0)) +#define VGA_MAP_MEM(x,s) ((unsigned long) ioremap_nocache(vga_console_membase + (x), s)) #define vga_readb(x) (*(x)) #define vga_writeb(x,y) (*(y) = (x)) diff --git a/include/asm-m32r/Kbuild b/include/asm-m32r/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-m32r/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-m32r/assembler.h b/include/asm-m32r/assembler.h index 1a1aa17..47041d1 100644 --- a/include/asm-m32r/assembler.h +++ b/include/asm-m32r/assembler.h @@ -9,7 +9,6 @@ #define _ASM_M32R_ASSEMBLER_H * This file contains M32R architecture specific macro definitions. */ -#include #ifndef __STR #ifdef __ASSEMBLY__ diff --git a/include/asm-m32r/atomic.h b/include/asm-m32r/atomic.h index 3122fe1..f5a7d73 100644 --- a/include/asm-m32r/atomic.h +++ b/include/asm-m32r/atomic.h @@ -9,7 +9,6 @@ #define _ASM_M32R_ATOMIC_H * Copyright (C) 2004 Hirokazu Takata */ -#include #include #include diff --git a/include/asm-m32r/bitops.h b/include/asm-m32r/bitops.h index 902a366..66ab672 100644 --- a/include/asm-m32r/bitops.h +++ b/include/asm-m32r/bitops.h @@ -11,7 +11,6 @@ #define _ASM_M32R_BITOPS_H * Copyright (C) 2004 Hirokazu Takata */ -#include #include #include #include diff --git a/include/asm-m32r/cacheflush.h b/include/asm-m32r/cacheflush.h index e57427b..8b261b4 100644 --- a/include/asm-m32r/cacheflush.h +++ b/include/asm-m32r/cacheflush.h @@ -1,7 +1,6 @@ #ifndef _ASM_M32R_CACHEFLUSH_H #define _ASM_M32R_CACHEFLUSH_H -#include #include extern void _flush_cache_all(void); diff --git a/include/asm-m32r/hardirq.h b/include/asm-m32r/hardirq.h index 5da830e..cb8aa76 100644 --- a/include/asm-m32r/hardirq.h +++ b/include/asm-m32r/hardirq.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include diff --git a/include/asm-m32r/hw_irq.h b/include/asm-m32r/hw_irq.h index 8d7e9d0..7138537 100644 --- a/include/asm-m32r/hw_irq.h +++ b/include/asm-m32r/hw_irq.h @@ -1,9 +1,4 @@ #ifndef _ASM_M32R_HW_IRQ_H #define _ASM_M32R_HW_IRQ_H -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) -{ - /* Nothing to do */ -} - #endif /* _ASM_M32R_HW_IRQ_H */ diff --git a/include/asm-m32r/ide.h b/include/asm-m32r/ide.h index f7aa969..219a0f7 100644 --- a/include/asm-m32r/ide.h +++ b/include/asm-m32r/ide.h @@ -15,7 +15,6 @@ #define _ASM_M32R_IDE_H #ifdef __KERNEL__ -#include #ifndef MAX_HWIFS # ifdef CONFIG_BLK_DEV_IDEPCI diff --git a/include/asm-m32r/irq.h b/include/asm-m32r/irq.h index ca94395..2f93f47 100644 --- a/include/asm-m32r/irq.h +++ b/include/asm-m32r/irq.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef _ASM_M32R_IRQ_H #define _ASM_M32R_IRQ_H -#include #if defined(CONFIG_PLAT_M32700UT_Alpha) || defined(CONFIG_PLAT_USRV) /* diff --git a/include/asm-m32r/kmap_types.h b/include/asm-m32r/kmap_types.h index 7429591..0524d89 100644 --- a/include/asm-m32r/kmap_types.h +++ b/include/asm-m32r/kmap_types.h @@ -3,7 +3,6 @@ #define __M32R_KMAP_TYPES_H /* Dummy header just to define km_type. */ -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-m32r/m32104ut/m32104ut_pld.h b/include/asm-m32r/m32104ut/m32104ut_pld.h index a4eac20..6ba4ddf 100644 --- a/include/asm-m32r/m32104ut/m32104ut_pld.h +++ b/include/asm-m32r/m32104ut/m32104ut_pld.h @@ -15,7 +15,6 @@ #ifndef _M32104UT_M32104UT_PLD_H #define _M32104UT_M32104UT_PLD_H -#include #if defined(CONFIG_PLAT_M32104UT) #define PLD_PLAT_BASE 0x02c00000 diff --git a/include/asm-m32r/m32700ut/m32700ut_lan.h b/include/asm-m32r/m32700ut/m32700ut_lan.h index 50545ec..c050b19 100644 --- a/include/asm-m32r/m32700ut/m32700ut_lan.h +++ b/include/asm-m32r/m32700ut/m32700ut_lan.h @@ -15,7 +15,6 @@ #ifndef _M32700UT_M32700UT_LAN_H #define _M32700UT_M32700UT_LAN_H -#include #ifndef __ASSEMBLY__ /* diff --git a/include/asm-m32r/m32700ut/m32700ut_lcd.h b/include/asm-m32r/m32700ut/m32700ut_lcd.h index ede6c77..4da4e82 100644 --- a/include/asm-m32r/m32700ut/m32700ut_lcd.h +++ b/include/asm-m32r/m32700ut/m32700ut_lcd.h @@ -15,7 +15,6 @@ #ifndef _M32700UT_M32700UT_LCD_H #define _M32700UT_M32700UT_LCD_H -#include #ifndef __ASSEMBLY__ /* diff --git a/include/asm-m32r/m32700ut/m32700ut_pld.h b/include/asm-m32r/m32700ut/m32700ut_pld.h index f5e4794..f35f915 100644 --- a/include/asm-m32r/m32700ut/m32700ut_pld.h +++ b/include/asm-m32r/m32700ut/m32700ut_pld.h @@ -15,7 +15,6 @@ #ifndef _M32700UT_M32700UT_PLD_H #define _M32700UT_M32700UT_PLD_H -#include #if defined(CONFIG_PLAT_M32700UT_Alpha) #define PLD_PLAT_BASE 0x08c00000 diff --git a/include/asm-m32r/m32r.h b/include/asm-m32r/m32r.h index b133ca6..decfc59 100644 --- a/include/asm-m32r/m32r.h +++ b/include/asm-m32r/m32r.h @@ -7,7 +7,6 @@ #define _ASM_M32R_M32R_H_ * Copyright (C) 2003, 2004 Renesas Technology Corp. */ -#include /* Chip type */ #if defined(CONFIG_CHIP_XNUX_MP) || defined(CONFIG_CHIP_XNUX2_MP) diff --git a/include/asm-m32r/mmu.h b/include/asm-m32r/mmu.h index 9c00eb7..cf3f6d7 100644 --- a/include/asm-m32r/mmu.h +++ b/include/asm-m32r/mmu.h @@ -1,7 +1,6 @@ #ifndef _ASM_M32R_MMU_H #define _ASM_M32R_MMU_H -#include #if !defined(CONFIG_MMU) typedef struct { diff --git a/include/asm-m32r/mmu_context.h b/include/asm-m32r/mmu_context.h index 3634c53..542302e 100644 --- a/include/asm-m32r/mmu_context.h +++ b/include/asm-m32r/mmu_context.h @@ -3,7 +3,6 @@ #define _ASM_M32R_MMU_CONTEXT_H #ifdef __KERNEL__ -#include #include @@ -15,7 +14,6 @@ #define NO_CONTEXT (0x00 #ifndef __ASSEMBLY__ -#include #include #include #include diff --git a/include/asm-m32r/opsput/opsput_lan.h b/include/asm-m32r/opsput/opsput_lan.h index 7a2a839..6194829 100644 --- a/include/asm-m32r/opsput/opsput_lan.h +++ b/include/asm-m32r/opsput/opsput_lan.h @@ -15,7 +15,6 @@ #ifndef _OPSPUT_OPSPUT_LAN_H #define _OPSPUT_OPSPUT_LAN_H -#include #ifndef __ASSEMBLY__ /* diff --git a/include/asm-m32r/opsput/opsput_lcd.h b/include/asm-m32r/opsput/opsput_lcd.h index 3a883e3..44cfd7f 100644 --- a/include/asm-m32r/opsput/opsput_lcd.h +++ b/include/asm-m32r/opsput/opsput_lcd.h @@ -15,7 +15,6 @@ #ifndef _OPSPUT_OPSPUT_LCD_H #define _OPSPUT_OPSPUT_LCD_H -#include #ifndef __ASSEMBLY__ /* diff --git a/include/asm-m32r/opsput/opsput_pld.h b/include/asm-m32r/opsput/opsput_pld.h index 2018e69..46296fe 100644 --- a/include/asm-m32r/opsput/opsput_pld.h +++ b/include/asm-m32r/opsput/opsput_pld.h @@ -15,7 +15,6 @@ #ifndef _OPSPUT_OPSPUT_PLD_H #define _OPSPUT_OPSPUT_PLD_H -#include #define PLD_PLAT_BASE 0x1cc00000 diff --git a/include/asm-m32r/page.h b/include/asm-m32r/page.h index 9ddbc08..9688be0 100644 --- a/include/asm-m32r/page.h +++ b/include/asm-m32r/page.h @@ -1,7 +1,6 @@ #ifndef _ASM_M32R_PAGE_H #define _ASM_M32R_PAGE_H -#include /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 diff --git a/include/asm-m32r/pgalloc.h b/include/asm-m32r/pgalloc.h index 6da309b..e09a86c 100644 --- a/include/asm-m32r/pgalloc.h +++ b/include/asm-m32r/pgalloc.h @@ -3,7 +3,6 @@ #define _ASM_M32R_PGALLOC_H /* $Id$ */ -#include #include #include diff --git a/include/asm-m32r/pgtable-2level.h b/include/asm-m32r/pgtable-2level.h index 861727c..be0f167 100644 --- a/include/asm-m32r/pgtable-2level.h +++ b/include/asm-m32r/pgtable-2level.h @@ -3,7 +3,6 @@ #define _ASM_M32R_PGTABLE_2LEVEL_H #ifdef __KERNEL__ -#include /* * traditional M32R two-level paging structure: diff --git a/include/asm-m32r/pgtable.h b/include/asm-m32r/pgtable.h index 75740de..1983b7f 100644 --- a/include/asm-m32r/pgtable.h +++ b/include/asm-m32r/pgtable.h @@ -20,7 +20,6 @@ #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#include #include #include #include diff --git a/include/asm-m32r/processor.h b/include/asm-m32r/processor.h index 09fd181..32755bf 100644 --- a/include/asm-m32r/processor.h +++ b/include/asm-m32r/processor.h @@ -14,7 +14,6 @@ #define _ASM_M32R_PROCESSOR_H */ #include -#include #include #include /* pt_regs */ diff --git a/include/asm-m32r/ptrace.h b/include/asm-m32r/ptrace.h index 53c7924..a07fa90 100644 --- a/include/asm-m32r/ptrace.h +++ b/include/asm-m32r/ptrace.h @@ -12,7 +12,6 @@ #define _ASM_M32R_PTRACE_H * Copyright (C) 2001-2002, 2004 Hirokazu Takata */ -#include #include /* M32R_PSW_BSM, M32R_PSW_BPM */ /* 0 - 13 are integer registers (general purpose registers). */ diff --git a/include/asm-m32r/rtc.h b/include/asm-m32r/rtc.h index ec3cdf6..6b2b837 100644 --- a/include/asm-m32r/rtc.h +++ b/include/asm-m32r/rtc.h @@ -4,7 +4,6 @@ #ifndef __RTC_H__ #define __RTC_H__ -#include /* Dallas DS1302 clock/calendar register numbers. */ # define RTC_SECONDS 0 diff --git a/include/asm-m32r/semaphore.h b/include/asm-m32r/semaphore.h index 81750ed..41e45d7 100644 --- a/include/asm-m32r/semaphore.h +++ b/include/asm-m32r/semaphore.h @@ -12,7 +12,6 @@ #ifdef __KERNEL__ * Copyright (C) 2004, 2006 Hirokazu Takata */ -#include #include #include #include diff --git a/include/asm-m32r/serial.h b/include/asm-m32r/serial.h index 1bf480f..5ac244c 100644 --- a/include/asm-m32r/serial.h +++ b/include/asm-m32r/serial.h @@ -3,7 +3,6 @@ #define _ASM_M32R_SERIAL_H /* include/asm-m32r/serial.h */ -#include #define BASE_BAUD 115200 diff --git a/include/asm-m32r/sigcontext.h b/include/asm-m32r/sigcontext.h index 942b8a3..73025c0 100644 --- a/include/asm-m32r/sigcontext.h +++ b/include/asm-m32r/sigcontext.h @@ -3,7 +3,6 @@ #define _ASM_M32R_SIGCONTEXT_H /* $Id$ */ -#include struct sigcontext { /* CPU registers */ diff --git a/include/asm-m32r/signal.h b/include/asm-m32r/signal.h index 95f69b1..e750045 100644 --- a/include/asm-m32r/signal.h +++ b/include/asm-m32r/signal.h @@ -81,7 +81,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -101,7 +100,6 @@ #define SA_RESETHAND 0x80000000u #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-m32r/smp.h b/include/asm-m32r/smp.h index 1184293..650d255 100644 --- a/include/asm-m32r/smp.h +++ b/include/asm-m32r/smp.h @@ -3,7 +3,6 @@ #define _ASM_M32R_SMP_H /* $Id$ */ -#include #ifdef CONFIG_SMP #ifndef __ASSEMBLY__ diff --git a/include/asm-m32r/socket.h b/include/asm-m32r/socket.h index 8b6680f..acdf748 100644 --- a/include/asm-m32r/socket.h +++ b/include/asm-m32r/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_M32R_SOCKET_H */ diff --git a/include/asm-m32r/spinlock.h b/include/asm-m32r/spinlock.h index 7de7def..f94c1a6 100644 --- a/include/asm-m32r/spinlock.h +++ b/include/asm-m32r/spinlock.h @@ -9,7 +9,6 @@ #define _ASM_M32R_SPINLOCK_H * Copyright (C) 2004 Hirokazu Takata */ -#include /* CONFIG_DEBUG_SPINLOCK, CONFIG_SMP */ #include #include #include diff --git a/include/asm-m32r/system.h b/include/asm-m32r/system.h index e55013f..311cebf 100644 --- a/include/asm-m32r/system.h +++ b/include/asm-m32r/system.h @@ -10,7 +10,6 @@ #define _ASM_M32R_SYSTEM_H * Copyright (C) 2004, 2006 Hirokazu Takata */ -#include #include #ifdef __KERNEL__ @@ -19,7 +18,7 @@ #ifdef __KERNEL__ * switch_to(prev, next) should switch from task `prev' to `next' * `prev' will never be the same as `next'. * - * `next' and `prev' should be task_t, but it isn't always defined + * `next' and `prev' should be struct task_struct, but it isn't always defined */ #define switch_to(prev, next, last) do { \ @@ -319,7 +318,7 @@ #define wmb() mb() * does not enforce ordering, since there is no data dependency between * the read of "a" and the read of "b". Therefore, on some CPUs, such * as Alpha, "y" could be set to 3 and "x" to 0. Use rmb() - * in cases like thiswhere there are no data dependencies. + * in cases like this where there are no data dependencies. **/ #define read_barrier_depends() do { } while (0) diff --git a/include/asm-m32r/timex.h b/include/asm-m32r/timex.h index abf12e7..e89bfd1 100644 --- a/include/asm-m32r/timex.h +++ b/include/asm-m32r/timex.h @@ -9,7 +9,6 @@ #define _ASM_M32R_TIMEX_H * m32r architecture timex specifications */ -#include #define CLOCK_TICK_RATE (CONFIG_BUS_CLOCK / CONFIG_TIMER_DIVIDE) #define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */ diff --git a/include/asm-m32r/tlbflush.h b/include/asm-m32r/tlbflush.h index bc7c407..ae44949 100644 --- a/include/asm-m32r/tlbflush.h +++ b/include/asm-m32r/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _ASM_M32R_TLBFLUSH_H #define _ASM_M32R_TLBFLUSH_H -#include #include /* diff --git a/include/asm-m32r/uaccess.h b/include/asm-m32r/uaccess.h index 819cc28..26e978c 100644 --- a/include/asm-m32r/uaccess.h +++ b/include/asm-m32r/uaccess.h @@ -11,7 +11,6 @@ #define _ASM_M32R_UACCESS_H /* * User space memory access functions */ -#include #include #include #include diff --git a/include/asm-m32r/unistd.h b/include/asm-m32r/unistd.h index be0eb01..cc31790 100644 --- a/include/asm-m32r/unistd.h +++ b/include/asm-m32r/unistd.h @@ -295,6 +295,8 @@ #define __NR_mq_getsetattr (__NR_mq_open #define __NR_kexec_load 283 #define __NR_waitid 284 +#ifdef __KERNEL__ + #define NR_syscalls 285 /* user-visible error numbers are in the range -1 - -124: see @@ -405,7 +407,6 @@ __asm__ __volatile__ (\ __syscall_return(type,__res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM @@ -421,7 +422,6 @@ #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_OLD_GETRLIMIT /*will be unused*/ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -470,4 +470,5 @@ #ifndef cond_syscall #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") #endif +#endif /* __KERNEL__ */ #endif /* _ASM_M32R_UNISTD_H */ diff --git a/include/asm-m32r/vga.h b/include/asm-m32r/vga.h index d0f4b6e..5331634 100644 --- a/include/asm-m32r/vga.h +++ b/include/asm-m32r/vga.h @@ -14,7 +14,7 @@ #define _ASM_M32R_VGA_H * access the videoram directly without any black magic. */ -#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) +#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x) #define vga_readb(x) (*(x)) #define vga_writeb(x,y) (*(y) = (x)) diff --git a/include/asm-m68k/Kbuild b/include/asm-m68k/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-m68k/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-m68k/amigaints.h b/include/asm-m68k/amigaints.h index aa968d0..7c87134 100644 --- a/include/asm-m68k/amigaints.h +++ b/include/asm-m68k/amigaints.h @@ -13,6 +13,8 @@ #ifndef _ASMm68k_AMIGAINTS_H_ #define _ASMm68k_AMIGAINTS_H_ +#include + /* ** Amiga Interrupt sources. ** @@ -23,72 +25,52 @@ #define AMI_STD_IRQS (14) #define CIA_IRQS (5) #define AMI_IRQS (32) /* AUTO_IRQS+AMI_STD_IRQS+2*CIA_IRQS */ -/* vertical blanking interrupt */ -#define IRQ_AMIGA_VERTB 0 +/* builtin serial port interrupts */ +#define IRQ_AMIGA_TBE (IRQ_USER+0) +#define IRQ_AMIGA_RBF (IRQ_USER+11) -/* copper interrupt */ -#define IRQ_AMIGA_COPPER 1 +/* floppy disk interrupts */ +#define IRQ_AMIGA_DSKBLK (IRQ_USER+1) +#define IRQ_AMIGA_DSKSYN (IRQ_USER+12) -/* Audio interrupts */ -#define IRQ_AMIGA_AUD0 2 -#define IRQ_AMIGA_AUD1 3 -#define IRQ_AMIGA_AUD2 4 -#define IRQ_AMIGA_AUD3 5 +/* software interrupts */ +#define IRQ_AMIGA_SOFT (IRQ_USER+2) -/* Blitter done interrupt */ -#define IRQ_AMIGA_BLIT 6 +/* interrupts from external hardware */ +#define IRQ_AMIGA_PORTS IRQ_AUTO_2 +#define IRQ_AMIGA_EXTER IRQ_AUTO_6 -/* floppy disk interrupts */ -#define IRQ_AMIGA_DSKSYN 7 -#define IRQ_AMIGA_DSKBLK 8 +/* copper interrupt */ +#define IRQ_AMIGA_COPPER (IRQ_USER+4) -/* builtin serial port interrupts */ -#define IRQ_AMIGA_RBF 9 -#define IRQ_AMIGA_TBE 10 +/* vertical blanking interrupt */ +#define IRQ_AMIGA_VERTB (IRQ_USER+5) -/* software interrupts */ -#define IRQ_AMIGA_SOFT 11 +/* Blitter done interrupt */ +#define IRQ_AMIGA_BLIT (IRQ_USER+6) -/* interrupts from external hardware */ -#define IRQ_AMIGA_PORTS 12 -#define IRQ_AMIGA_EXTER 13 +/* Audio interrupts */ +#define IRQ_AMIGA_AUD0 (IRQ_USER+7) +#define IRQ_AMIGA_AUD1 (IRQ_USER+8) +#define IRQ_AMIGA_AUD2 (IRQ_USER+9) +#define IRQ_AMIGA_AUD3 (IRQ_USER+10) /* CIA interrupt sources */ -#define IRQ_AMIGA_CIAA 14 -#define IRQ_AMIGA_CIAA_TA 14 -#define IRQ_AMIGA_CIAA_TB 15 -#define IRQ_AMIGA_CIAA_ALRM 16 -#define IRQ_AMIGA_CIAA_SP 17 -#define IRQ_AMIGA_CIAA_FLG 18 -#define IRQ_AMIGA_CIAB 19 -#define IRQ_AMIGA_CIAB_TA 19 -#define IRQ_AMIGA_CIAB_TB 20 -#define IRQ_AMIGA_CIAB_ALRM 21 -#define IRQ_AMIGA_CIAB_SP 22 -#define IRQ_AMIGA_CIAB_FLG 23 - -/* auto-vector interrupts */ -#define IRQ_AMIGA_AUTO 24 -#define IRQ_AMIGA_AUTO_0 24 /* This is just a dummy */ -#define IRQ_AMIGA_AUTO_1 25 -#define IRQ_AMIGA_AUTO_2 26 -#define IRQ_AMIGA_AUTO_3 27 -#define IRQ_AMIGA_AUTO_4 28 -#define IRQ_AMIGA_AUTO_5 29 -#define IRQ_AMIGA_AUTO_6 30 -#define IRQ_AMIGA_AUTO_7 31 - -#define IRQ_FLOPPY IRQ_AMIGA_DSKBLK +#define IRQ_AMIGA_CIAA (IRQ_USER+14) +#define IRQ_AMIGA_CIAA_TA (IRQ_USER+14) +#define IRQ_AMIGA_CIAA_TB (IRQ_USER+15) +#define IRQ_AMIGA_CIAA_ALRM (IRQ_USER+16) +#define IRQ_AMIGA_CIAA_SP (IRQ_USER+17) +#define IRQ_AMIGA_CIAA_FLG (IRQ_USER+18) +#define IRQ_AMIGA_CIAB (IRQ_USER+19) +#define IRQ_AMIGA_CIAB_TA (IRQ_USER+19) +#define IRQ_AMIGA_CIAB_TB (IRQ_USER+20) +#define IRQ_AMIGA_CIAB_ALRM (IRQ_USER+21) +#define IRQ_AMIGA_CIAB_SP (IRQ_USER+22) +#define IRQ_AMIGA_CIAB_FLG (IRQ_USER+23) -/* INTREQR masks */ -#define IRQ1_MASK 0x0007 /* INTREQR mask for IRQ 1 */ -#define IRQ2_MASK 0x0008 /* INTREQR mask for IRQ 2 */ -#define IRQ3_MASK 0x0070 /* INTREQR mask for IRQ 3 */ -#define IRQ4_MASK 0x0780 /* INTREQR mask for IRQ 4 */ -#define IRQ5_MASK 0x1800 /* INTREQR mask for IRQ 5 */ -#define IRQ6_MASK 0x2000 /* INTREQR mask for IRQ 6 */ -#define IRQ7_MASK 0x4000 /* INTREQR mask for IRQ 7 */ +/* INTREQR masks */ #define IF_SETCLR 0x8000 /* set/clr bit */ #define IF_INTEN 0x4000 /* master interrupt bit in INT* registers */ #define IF_EXTER 0x2000 /* external level 6 and CIA B interrupt */ @@ -106,9 +88,6 @@ #define IF_SOFT 0x0004 /* software i #define IF_DSKBLK 0x0002 /* diskblock DMA finished */ #define IF_TBE 0x0001 /* serial transmit buffer empty interrupt */ -extern void amiga_do_irq(int irq, struct pt_regs *fp); -extern void amiga_do_irq_list(int irq, struct pt_regs *fp); - /* CIA interrupt control register bits */ #define CIA_ICR_TA 0x01 @@ -125,6 +104,7 @@ #define CIA_ICR_SETCLR 0x80 extern struct ciabase ciaa_base, ciab_base; +extern void cia_init_IRQ(struct ciabase *base); extern unsigned char cia_set_irq(struct ciabase *base, unsigned char mask); extern unsigned char cia_able_irq(struct ciabase *base, unsigned char mask); diff --git a/include/asm-m68k/apollohw.h b/include/asm-m68k/apollohw.h index 4304e1c..a1373b9 100644 --- a/include/asm-m68k/apollohw.h +++ b/include/asm-m68k/apollohw.h @@ -3,6 +3,8 @@ #ifndef _ASMm68k_APOLLOHW_H_ #define _ASMm68k_APOLLOHW_H_ +#include + /* apollo models */ @@ -101,4 +103,6 @@ #define addr_xlat_map ((unsigned short * #define isaIO2mem(x) (((((x) & 0x3f8) << 7) | (((x) & 0xfc00) >> 6) | ((x) & 0x7)) + 0x40000 + IO_BASE) +#define IRQ_APOLLO IRQ_USER + #endif diff --git a/include/asm-m68k/atari_stdma.h b/include/asm-m68k/atari_stdma.h index 64f9288..b4eadf8 100644 --- a/include/asm-m68k/atari_stdma.h +++ b/include/asm-m68k/atari_stdma.h @@ -3,7 +3,7 @@ #ifndef _atari_stdma_h #define _atari_stdma_h -#include +#include /***************************** Prototypes *****************************/ diff --git a/include/asm-m68k/atariints.h b/include/asm-m68k/atariints.h index 42952c8..0ed454f 100644 --- a/include/asm-m68k/atariints.h +++ b/include/asm-m68k/atariints.h @@ -45,17 +45,6 @@ #define IRQ_TYPE_SLOW 0 #define IRQ_TYPE_FAST 1 #define IRQ_TYPE_PRIO 2 -#define IRQ_SPURIOUS (0) - -/* auto-vector interrupts */ -#define IRQ_AUTO_1 (1) -#define IRQ_AUTO_2 (2) -#define IRQ_AUTO_3 (3) -#define IRQ_AUTO_4 (4) -#define IRQ_AUTO_5 (5) -#define IRQ_AUTO_6 (6) -#define IRQ_AUTO_7 (7) - /* ST-MFP interrupts */ #define IRQ_MFP_BUSY (8) #define IRQ_MFP_DCD (9) diff --git a/include/asm-m68k/atomic.h b/include/asm-m68k/atomic.h index 732d696..d5eed64 100644 --- a/include/asm-m68k/atomic.h +++ b/include/asm-m68k/atomic.h @@ -1,7 +1,6 @@ #ifndef __ARCH_M68K_ATOMIC__ #define __ARCH_M68K_ATOMIC__ -#include #include /* local_irq_XXX() */ diff --git a/include/asm-m68k/bug.h b/include/asm-m68k/bug.h index 072ce27..7b60776 100644 --- a/include/asm-m68k/bug.h +++ b/include/asm-m68k/bug.h @@ -1,7 +1,6 @@ #ifndef _M68K_BUG_H #define _M68K_BUG_H -#include #ifdef CONFIG_BUG #ifdef CONFIG_DEBUG_BUGVERBOSE diff --git a/include/asm-m68k/bvme6000hw.h b/include/asm-m68k/bvme6000hw.h index 28a859b..f40d2f8 100644 --- a/include/asm-m68k/bvme6000hw.h +++ b/include/asm-m68k/bvme6000hw.h @@ -109,23 +109,23 @@ #define BVME_CONFIG_SW4 0x01 #define BVME_IRQ_TYPE_PRIO 0 -#define BVME_IRQ_PRN 0x54 -#define BVME_IRQ_I596 0x1a -#define BVME_IRQ_SCSI 0x1b -#define BVME_IRQ_TIMER 0x59 -#define BVME_IRQ_RTC 0x1e -#define BVME_IRQ_ABORT 0x1f +#define BVME_IRQ_PRN (IRQ_USER+20) +#define BVME_IRQ_TIMER (IRQ_USER+25) +#define BVME_IRQ_I596 IRQ_AUTO_2 +#define BVME_IRQ_SCSI IRQ_AUTO_3 +#define BVME_IRQ_RTC IRQ_AUTO_6 +#define BVME_IRQ_ABORT IRQ_AUTO_7 /* SCC interrupts */ -#define BVME_IRQ_SCC_BASE 0x40 -#define BVME_IRQ_SCCB_TX 0x40 -#define BVME_IRQ_SCCB_STAT 0x42 -#define BVME_IRQ_SCCB_RX 0x44 -#define BVME_IRQ_SCCB_SPCOND 0x46 -#define BVME_IRQ_SCCA_TX 0x48 -#define BVME_IRQ_SCCA_STAT 0x4a -#define BVME_IRQ_SCCA_RX 0x4c -#define BVME_IRQ_SCCA_SPCOND 0x4e +#define BVME_IRQ_SCC_BASE IRQ_USER +#define BVME_IRQ_SCCB_TX IRQ_USER +#define BVME_IRQ_SCCB_STAT (IRQ_USER+2) +#define BVME_IRQ_SCCB_RX (IRQ_USER+4) +#define BVME_IRQ_SCCB_SPCOND (IRQ_USER+6) +#define BVME_IRQ_SCCA_TX (IRQ_USER+8) +#define BVME_IRQ_SCCA_STAT (IRQ_USER+10) +#define BVME_IRQ_SCCA_RX (IRQ_USER+12) +#define BVME_IRQ_SCCA_SPCOND (IRQ_USER+14) /* Address control registers */ diff --git a/include/asm-m68k/cacheflush.h b/include/asm-m68k/cacheflush.h index 8aba971..24d3ff4 100644 --- a/include/asm-m68k/cacheflush.h +++ b/include/asm-m68k/cacheflush.h @@ -3,26 +3,30 @@ #define _M68K_CACHEFLUSH_H #include +/* cache code */ +#define FLUSH_I_AND_D (0x00000808) +#define FLUSH_I (0x00000008) + /* * Cache handling functions */ -#define flush_icache() \ -({ \ - if (CPU_IS_040_OR_060) \ - __asm__ __volatile__("nop\n\t" \ - ".chip 68040\n\t" \ - "cinva %%ic\n\t" \ - ".chip 68k" : ); \ - else { \ - unsigned long _tmp; \ - __asm__ __volatile__("movec %%cacr,%0\n\t" \ - "orw %1,%0\n\t" \ - "movec %0,%%cacr" \ - : "=&d" (_tmp) \ - : "id" (FLUSH_I)); \ - } \ -}) +static inline void flush_icache(void) +{ + if (CPU_IS_040_OR_060) + asm volatile ( "nop\n" + " .chip 68040\n" + " cpusha %bc\n" + " .chip 68k"); + else { + unsigned long tmp; + asm volatile ( "movec %%cacr,%0\n" + " or.w %1,%0\n" + " movec %0,%%cacr" + : "=&d" (tmp) + : "id" (FLUSH_I)); + } +} /* * invalidate the cache for the specified memory range. @@ -43,10 +47,6 @@ extern void cache_push(unsigned long pad */ extern void cache_push_v(unsigned long vaddr, int len); -/* cache code */ -#define FLUSH_I_AND_D (0x00000808) -#define FLUSH_I (0x00000008) - /* This is needed whenever the virtual mapping of the current process changes. */ #define __flush_cache_all() \ diff --git a/include/asm-m68k/dma-mapping.h b/include/asm-m68k/dma-mapping.h index b1920c7..cebbb03 100644 --- a/include/asm-m68k/dma-mapping.h +++ b/include/asm-m68k/dma-mapping.h @@ -1,12 +1,91 @@ #ifndef _M68K_DMA_MAPPING_H #define _M68K_DMA_MAPPING_H -#include +#include -#ifdef CONFIG_PCI -#include -#else -#include -#endif +struct scatterlist; + +static inline int dma_supported(struct device *dev, u64 mask) +{ + return 1; +} + +static inline int dma_set_mask(struct device *dev, u64 mask) +{ + return 0; +} + +static inline int dma_get_cache_alignment(void) +{ + return 1 << L1_CACHE_SHIFT; +} + +static inline int dma_is_consistent(dma_addr_t dma_addr) +{ + return 0; +} + +extern void *dma_alloc_coherent(struct device *, size_t, + dma_addr_t *, int); +extern void dma_free_coherent(struct device *, size_t, + void *, dma_addr_t); + +static inline void *dma_alloc_noncoherent(struct device *dev, size_t size, + dma_addr_t *handle, int flag) +{ + return dma_alloc_coherent(dev, size, handle, flag); +} +static inline void dma_free_noncoherent(struct device *dev, size_t size, + void *addr, dma_addr_t handle) +{ + dma_free_coherent(dev, size, addr, handle); +} +static inline void dma_cache_sync(void *vaddr, size_t size, + enum dma_data_direction dir) +{ + /* we use coherent allocation, so not much to do here. */ +} + +extern dma_addr_t dma_map_single(struct device *, void *, size_t, + enum dma_data_direction); +static inline void dma_unmap_single(struct device *dev, dma_addr_t addr, + size_t size, enum dma_data_direction dir) +{ +} + +extern dma_addr_t dma_map_page(struct device *, struct page *, + unsigned long, size_t size, + enum dma_data_direction); +static inline void dma_unmap_page(struct device *dev, dma_addr_t address, + size_t size, enum dma_data_direction dir) +{ +} + +extern int dma_map_sg(struct device *, struct scatterlist *, int, + enum dma_data_direction); +static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nhwentries, enum dma_data_direction dir) +{ +} + +extern void dma_sync_single_for_device(struct device *, dma_addr_t, size_t, + enum dma_data_direction); +extern void dma_sync_sg_for_device(struct device *, struct scatterlist *, int, + enum dma_data_direction); + +static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, + size_t size, enum dma_data_direction dir) +{ +} + +static inline void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir) +{ +} + +static inline int dma_mapping_error(dma_addr_t handle) +{ + return 0; +} #endif /* _M68K_DMA_MAPPING_H */ diff --git a/include/asm-m68k/dma.h b/include/asm-m68k/dma.h index d5266a8..d0c9e61 100644 --- a/include/asm-m68k/dma.h +++ b/include/asm-m68k/dma.h @@ -1,7 +1,6 @@ #ifndef _M68K_DMA_H #define _M68K_DMA_H 1 -#include /* it's useless on the m68k, but unfortunately needed by the new bootmem allocator (but this should do it for this) */ diff --git a/include/asm-m68k/dvma.h b/include/asm-m68k/dvma.h index 5978f87..e1112de 100644 --- a/include/asm-m68k/dvma.h +++ b/include/asm-m68k/dvma.h @@ -9,7 +9,6 @@ #ifndef __M68K_DVMA_H #define __M68K_DVMA_H -#include #define DVMA_PAGE_SHIFT 13 #define DVMA_PAGE_SIZE (1UL << DVMA_PAGE_SHIFT) diff --git a/include/asm-m68k/elf.h b/include/asm-m68k/elf.h index 38bf834..eb63b85 100644 --- a/include/asm-m68k/elf.h +++ b/include/asm-m68k/elf.h @@ -5,7 +5,6 @@ #define __ASMm68k_ELF_H * ELF register definitions.. */ -#include #include #include diff --git a/include/asm-m68k/entry.h b/include/asm-m68k/entry.h index 0396495..f8f6b18 100644 --- a/include/asm-m68k/entry.h +++ b/include/asm-m68k/entry.h @@ -1,7 +1,6 @@ #ifndef __M68K_ENTRY_H #define __M68K_ENTRY_H -#include #include #include diff --git a/include/asm-m68k/floppy.h b/include/asm-m68k/floppy.h index 63a05ed..57f4fdd 100644 --- a/include/asm-m68k/floppy.h +++ b/include/asm-m68k/floppy.h @@ -88,8 +88,8 @@ static __inline__ void fd_outb(unsigned static int fd_request_irq(void) { if(MACH_IS_Q40) - return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, - "floppy", floppy_hardint); + return request_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy", floppy_hardint); else if(MACH_IS_SUN3X) return sun3xflop_request_irq(); return -ENXIO; diff --git a/include/asm-m68k/fpu.h b/include/asm-m68k/fpu.h index 3bcf850..59701d7 100644 --- a/include/asm-m68k/fpu.h +++ b/include/asm-m68k/fpu.h @@ -1,7 +1,6 @@ #ifndef __M68K_FPU_H #define __M68K_FPU_H -#include /* * MAX floating point unit state size (FSAVE/FRESTORE) diff --git a/include/asm-m68k/hardirq.h b/include/asm-m68k/hardirq.h index 5e1c582..394ee94 100644 --- a/include/asm-m68k/hardirq.h +++ b/include/asm-m68k/hardirq.h @@ -1,7 +1,6 @@ #ifndef __M68K_HARDIRQ_H #define __M68K_HARDIRQ_H -#include #include #include diff --git a/include/asm-m68k/ide.h b/include/asm-m68k/ide.h index 36118fd..365f76f 100644 --- a/include/asm-m68k/ide.h +++ b/include/asm-m68k/ide.h @@ -31,7 +31,6 @@ #define _M68K_IDE_H #ifdef __KERNEL__ -#include #include #include diff --git a/include/asm-m68k/io.h b/include/asm-m68k/io.h index dcfaa35..5e0fcf4 100644 --- a/include/asm-m68k/io.h +++ b/include/asm-m68k/io.h @@ -23,7 +23,6 @@ #define _IO_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-m68k/irq.h b/include/asm-m68k/irq.h index 9ac047c..3257f98 100644 --- a/include/asm-m68k/irq.h +++ b/include/asm-m68k/irq.h @@ -1,14 +1,9 @@ #ifndef _M68K_IRQ_H_ #define _M68K_IRQ_H_ -#include -#include - -/* - * # of m68k interrupts - */ - -#define SYS_IRQS 8 +#include +#include +#include /* * This should be the same as the max(NUM_X_SOURCES) for all the @@ -16,10 +11,20 @@ #define SYS_IRQS 8 * Currently the Atari has 72 and the Amiga 24, but if both are * supported in the kernel it is better to make room for 72. */ -#if defined(CONFIG_ATARI) || defined(CONFIG_MAC) -#define NR_IRQS (72+SYS_IRQS) +#if defined(CONFIG_VME) || defined(CONFIG_SUN3) || defined(CONFIG_SUN3X) +#define NR_IRQS 200 +#elif defined(CONFIG_ATARI) || defined(CONFIG_MAC) +#define NR_IRQS 72 +#elif defined(CONFIG_Q40) +#define NR_IRQS 43 +#elif defined(CONFIG_AMIGA) +#define NR_IRQS 32 +#elif defined(CONFIG_APOLLO) +#define NR_IRQS 24 +#elif defined(CONFIG_HP300) +#define NR_IRQS 8 #else -#define NR_IRQS (24+SYS_IRQS) +#error unknown nr of irqs #endif /* @@ -41,57 +46,29 @@ #endif * that routine requires service. */ -#define IRQ1 (1) /* level 1 interrupt */ -#define IRQ2 (2) /* level 2 interrupt */ -#define IRQ3 (3) /* level 3 interrupt */ -#define IRQ4 (4) /* level 4 interrupt */ -#define IRQ5 (5) /* level 5 interrupt */ -#define IRQ6 (6) /* level 6 interrupt */ -#define IRQ7 (7) /* level 7 interrupt (non-maskable) */ +#define IRQ_SPURIOUS 0 -/* - * "Generic" interrupt sources - */ - -#define IRQ_SCHED_TIMER (8) /* interrupt source for scheduling timer */ +#define IRQ_AUTO_1 1 /* level 1 interrupt */ +#define IRQ_AUTO_2 2 /* level 2 interrupt */ +#define IRQ_AUTO_3 3 /* level 3 interrupt */ +#define IRQ_AUTO_4 4 /* level 4 interrupt */ +#define IRQ_AUTO_5 5 /* level 5 interrupt */ +#define IRQ_AUTO_6 6 /* level 6 interrupt */ +#define IRQ_AUTO_7 7 /* level 7 interrupt (non-maskable) */ -static __inline__ int irq_canonicalize(int irq) -{ - return irq; -} - -/* - * Machine specific interrupt sources. - * - * Adding an interrupt service routine for a source with this bit - * set indicates a special machine specific interrupt source. - * The machine specific files define these sources. - * - * The IRQ_MACHSPEC bit is now gone - the only thing it did was to - * introduce unnecessary overhead. - * - * All interrupt handling is actually machine specific so it is better - * to use function pointers, as used by the Sparc port, and select the - * interrupt handling functions when initializing the kernel. This way - * we save some unnecessary overhead at run-time. - * 01/11/97 - Jes - */ +#define IRQ_USER 8 -extern void (*enable_irq)(unsigned int); -extern void (*disable_irq)(unsigned int); +extern unsigned int irq_canonicalize(unsigned int irq); +extern void enable_irq(unsigned int); +extern void disable_irq(unsigned int); #define disable_irq_nosync disable_irq struct pt_regs; -extern int cpu_request_irq(unsigned int, - irqreturn_t (*)(int, void *, struct pt_regs *), - unsigned long, const char *, void *); -extern void cpu_free_irq(unsigned int, void *); - /* * various flags for request_irq() - the Amiga now uses the standard - * mechanism like all other architectures - SA_INTERRUPT and SA_SHIRQ - * are your friends. + * mechanism like all other architectures - IRQF_DISABLED and + * IRQF_SHARED are your friends. */ #ifndef MACH_AMIGA_ONLY #define IRQ_FLG_LOCK (0x0001) /* handler is not replaceable */ @@ -106,33 +83,45 @@ #endif * interrupt source (if it supports chaining). */ typedef struct irq_node { - irqreturn_t (*handler)(int, void *, struct pt_regs *); - unsigned long flags; + int (*handler)(int, void *, struct pt_regs *); void *dev_id; - const char *devname; struct irq_node *next; + unsigned long flags; + const char *devname; } irq_node_t; /* * This structure has only 4 elements for speed reasons */ typedef struct irq_handler { - irqreturn_t (*handler)(int, void *, struct pt_regs *); + int (*handler)(int, void *, struct pt_regs *); unsigned long flags; void *dev_id; const char *devname; } irq_handler_t; -/* count of spurious interrupts */ -extern volatile unsigned int num_spurious; +struct irq_controller { + const char *name; + spinlock_t lock; + int (*startup)(unsigned int irq); + void (*shutdown)(unsigned int irq); + void (*enable)(unsigned int irq); + void (*disable)(unsigned int irq); +}; + +extern int m68k_irq_startup(unsigned int); +extern void m68k_irq_shutdown(unsigned int); /* * This function returns a new irq_node_t */ extern irq_node_t *new_irq_node(void); -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); +extern void m68k_setup_auto_interrupt(void (*handler)(unsigned int, struct pt_regs *)); +extern void m68k_setup_user_interrupt(unsigned int vec, unsigned int cnt, + void (*handler)(unsigned int, struct pt_regs *)); +extern void m68k_setup_irq_controller(struct irq_controller *, unsigned int, unsigned int); + +asmlinkage void m68k_handle_int(unsigned int, struct pt_regs *); #endif /* _M68K_IRQ_H_ */ diff --git a/include/asm-m68k/mac_oss.h b/include/asm-m68k/mac_oss.h index 7644a63..7221f72 100644 --- a/include/asm-m68k/mac_oss.h +++ b/include/asm-m68k/mac_oss.h @@ -69,12 +69,12 @@ #define OSS_POWEROFF 0x80 #define OSS_IRQLEV_DISABLED 0 #define OSS_IRQLEV_IOPISM 1 /* ADB? */ -#define OSS_IRQLEV_SCSI 2 -#define OSS_IRQLEV_NUBUS 3 /* keep this on its own level */ -#define OSS_IRQLEV_IOPSCC 4 /* matches VIA alternate mapping */ -#define OSS_IRQLEV_SOUND 5 /* matches VIA alternate mapping */ +#define OSS_IRQLEV_SCSI IRQ_AUTO_2 +#define OSS_IRQLEV_NUBUS IRQ_AUTO_3 /* keep this on its own level */ +#define OSS_IRQLEV_IOPSCC IRQ_AUTO_4 /* matches VIA alternate mapping */ +#define OSS_IRQLEV_SOUND IRQ_AUTO_5 /* matches VIA alternate mapping */ #define OSS_IRQLEV_60HZ 6 /* matches VIA alternate mapping */ -#define OSS_IRQLEV_VIA1 6 /* matches VIA alternate mapping */ +#define OSS_IRQLEV_VIA1 IRQ_AUTO_6 /* matches VIA alternate mapping */ #define OSS_IRQLEV_PARITY 7 /* matches VIA alternate mapping */ #ifndef __ASSEMBLY__ diff --git a/include/asm-m68k/machdep.h b/include/asm-m68k/machdep.h index 7d3fee3..df898f2 100644 --- a/include/asm-m68k/machdep.h +++ b/include/asm-m68k/machdep.h @@ -13,14 +13,8 @@ struct buffer_head; extern void (*mach_sched_init) (irqreturn_t (*handler)(int, void *, struct pt_regs *)); /* machine dependent irq functions */ extern void (*mach_init_IRQ) (void); -extern irqreturn_t (*(*mach_default_handler)[]) (int, void *, struct pt_regs *); -extern int (*mach_request_irq) (unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id); -extern void (*mach_free_irq) (unsigned int irq, void *dev_id); extern void (*mach_get_model) (char *model); extern int (*mach_get_hardware_list) (char *buffer); -extern int (*mach_get_irq_list) (struct seq_file *p, void *v); -extern irqreturn_t (*mach_process_int) (int irq, struct pt_regs *fp); /* machine dependent timer functions */ extern unsigned long (*mach_gettimeoffset)(void); extern int (*mach_hwclk)(int, struct rtc_time*); diff --git a/include/asm-m68k/macintosh.h b/include/asm-m68k/macintosh.h index 6fc3d19..27d11da 100644 --- a/include/asm-m68k/macintosh.h +++ b/include/asm-m68k/macintosh.h @@ -11,17 +11,7 @@ #include extern void mac_reset(void); extern void mac_poweroff(void); extern void mac_init_IRQ(void); -extern int mac_request_irq (unsigned int, irqreturn_t (*)(int, void *, - struct pt_regs *), - unsigned long, const char *, void *); -extern void mac_free_irq(unsigned int, void *); -extern void mac_enable_irq(unsigned int); -extern void mac_disable_irq(unsigned int); extern int mac_irq_pending(unsigned int); -extern int show_mac_interrupts(struct seq_file *, void *); -#if 0 -extern void mac_default_handler(int irq); -#endif extern void mac_identify(void); extern void mac_report_hardware(void); extern void mac_debugging_penguin(int); diff --git a/include/asm-m68k/macints.h b/include/asm-m68k/macints.h index fd8c3a9..679c48a 100644 --- a/include/asm-m68k/macints.h +++ b/include/asm-m68k/macints.h @@ -59,17 +59,6 @@ #define NUM_MAC_SOURCES 72 #define IRQ_SRC(irq) (irq >> 3) #define IRQ_IDX(irq) (irq & 7) -#define IRQ_SPURIOUS (0) - -/* auto-vector interrupts */ -#define IRQ_AUTO_1 (1) -#define IRQ_AUTO_2 (2) -#define IRQ_AUTO_3 (3) -#define IRQ_AUTO_4 (4) -#define IRQ_AUTO_5 (5) -#define IRQ_AUTO_6 (6) -#define IRQ_AUTO_7 (7) - /* VIA1 interrupts */ #define IRQ_VIA1_0 (8) /* one second int. */ #define IRQ_VIA1_1 (9) /* VBlank int. */ @@ -163,7 +152,4 @@ #define IRQ2SLOT(x) (x - 47) #define INT_CLK 24576 /* CLK while int_clk =2.456MHz and divide = 100 */ #define INT_TICKS 246 /* to make sched_time = 99.902... HZ */ -extern irq_node_t *mac_irq_list[NUM_MAC_SOURCES]; -extern void mac_do_irq_list(int irq, struct pt_regs *); - #endif /* asm/macints.h */ diff --git a/include/asm-m68k/mc146818rtc.h b/include/asm-m68k/mc146818rtc.h index 1144209..11fe12d 100644 --- a/include/asm-m68k/mc146818rtc.h +++ b/include/asm-m68k/mc146818rtc.h @@ -4,7 +4,6 @@ #ifndef _ASM_MC146818RTC_H #define _ASM_MC146818RTC_H -#include #ifdef CONFIG_ATARI /* RTC in Atari machines */ diff --git a/include/asm-m68k/mmu_context.h b/include/asm-m68k/mmu_context.h index 661191d..231d11b 100644 --- a/include/asm-m68k/mmu_context.h +++ b/include/asm-m68k/mmu_context.h @@ -1,7 +1,6 @@ #ifndef __M68K_MMU_CONTEXT_H #define __M68K_MMU_CONTEXT_H -#include static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) { diff --git a/include/asm-m68k/motorola_pgtable.h b/include/asm-m68k/motorola_pgtable.h index 1628723..1ccc733 100644 --- a/include/asm-m68k/motorola_pgtable.h +++ b/include/asm-m68k/motorola_pgtable.h @@ -1,7 +1,6 @@ #ifndef _MOTOROLA_PGTABLE_H #define _MOTOROLA_PGTABLE_H -#include /* * Definitions for MMU descriptors diff --git a/include/asm-m68k/mvme147hw.h b/include/asm-m68k/mvme147hw.h index f245139..b810431 100644 --- a/include/asm-m68k/mvme147hw.h +++ b/include/asm-m68k/mvme147hw.h @@ -1,6 +1,8 @@ #ifndef _MVME147HW_H_ #define _MVME147HW_H_ +#include + typedef struct { unsigned char ctrl, @@ -72,39 +74,39 @@ #define PCC_LEVEL_TIMER1 0x04 #define PCC_LEVEL_SCSI_PORT 0x04 #define PCC_LEVEL_SCSI_DMA 0x04 -#define PCC_IRQ_AC_FAIL 0x40 -#define PCC_IRQ_BERR 0x41 -#define PCC_IRQ_ABORT 0x42 -/* #define PCC_IRQ_SERIAL 0x43 */ -#define PCC_IRQ_PRINTER 0x47 -#define PCC_IRQ_TIMER1 0x48 -#define PCC_IRQ_TIMER2 0x49 -#define PCC_IRQ_SOFTWARE1 0x4a -#define PCC_IRQ_SOFTWARE2 0x4b +#define PCC_IRQ_AC_FAIL (IRQ_USER+0) +#define PCC_IRQ_BERR (IRQ_USER+1) +#define PCC_IRQ_ABORT (IRQ_USER+2) +/* #define PCC_IRQ_SERIAL (IRQ_USER+3) */ +#define PCC_IRQ_PRINTER (IRQ_USER+7) +#define PCC_IRQ_TIMER1 (IRQ_USER+8) +#define PCC_IRQ_TIMER2 (IRQ_USER+9) +#define PCC_IRQ_SOFTWARE1 (IRQ_USER+10) +#define PCC_IRQ_SOFTWARE2 (IRQ_USER+11) #define M147_SCC_A_ADDR 0xfffe3002 #define M147_SCC_B_ADDR 0xfffe3000 #define M147_SCC_PCLK 5000000 -#define MVME147_IRQ_SCSI_PORT 0x45 -#define MVME147_IRQ_SCSI_DMA 0x46 +#define MVME147_IRQ_SCSI_PORT (IRQ_USER+0x45) +#define MVME147_IRQ_SCSI_DMA (IRQ_USER+0x46) /* SCC interrupts, for MVME147 */ #define MVME147_IRQ_TYPE_PRIO 0 -#define MVME147_IRQ_SCC_BASE 0x60 -#define MVME147_IRQ_SCCB_TX 0x60 -#define MVME147_IRQ_SCCB_STAT 0x62 -#define MVME147_IRQ_SCCB_RX 0x64 -#define MVME147_IRQ_SCCB_SPCOND 0x66 -#define MVME147_IRQ_SCCA_TX 0x68 -#define MVME147_IRQ_SCCA_STAT 0x6a -#define MVME147_IRQ_SCCA_RX 0x6c -#define MVME147_IRQ_SCCA_SPCOND 0x6e +#define MVME147_IRQ_SCC_BASE (IRQ_USER+32) +#define MVME147_IRQ_SCCB_TX (IRQ_USER+32) +#define MVME147_IRQ_SCCB_STAT (IRQ_USER+34) +#define MVME147_IRQ_SCCB_RX (IRQ_USER+36) +#define MVME147_IRQ_SCCB_SPCOND (IRQ_USER+38) +#define MVME147_IRQ_SCCA_TX (IRQ_USER+40) +#define MVME147_IRQ_SCCA_STAT (IRQ_USER+42) +#define MVME147_IRQ_SCCA_RX (IRQ_USER+44) +#define MVME147_IRQ_SCCA_SPCOND (IRQ_USER+46) #define MVME147_LANCE_BASE 0xfffe1800 -#define MVME147_LANCE_IRQ 0x44 +#define MVME147_LANCE_IRQ (IRQ_USER+4) #define ETHERNET_ADDRESS 0xfffe0778 diff --git a/include/asm-m68k/mvme16xhw.h b/include/asm-m68k/mvme16xhw.h index 5d07231..6117f56 100644 --- a/include/asm-m68k/mvme16xhw.h +++ b/include/asm-m68k/mvme16xhw.h @@ -66,28 +66,28 @@ #define MVME_SCC_PCLK 10000000 #define MVME162_IRQ_TYPE_PRIO 0 -#define MVME167_IRQ_PRN 0x54 -#define MVME16x_IRQ_I596 0x57 -#define MVME16x_IRQ_SCSI 0x55 -#define MVME16x_IRQ_FLY 0x7f -#define MVME167_IRQ_SER_ERR 0x5c -#define MVME167_IRQ_SER_MODEM 0x5d -#define MVME167_IRQ_SER_TX 0x5e -#define MVME167_IRQ_SER_RX 0x5f -#define MVME16x_IRQ_TIMER 0x59 -#define MVME167_IRQ_ABORT 0x6e -#define MVME162_IRQ_ABORT 0x5e +#define MVME167_IRQ_PRN (IRQ_USER+20) +#define MVME16x_IRQ_I596 (IRQ_USER+23) +#define MVME16x_IRQ_SCSI (IRQ_USER+21) +#define MVME16x_IRQ_FLY (IRQ_USER+63) +#define MVME167_IRQ_SER_ERR (IRQ_USER+28) +#define MVME167_IRQ_SER_MODEM (IRQ_USER+29) +#define MVME167_IRQ_SER_TX (IRQ_USER+30) +#define MVME167_IRQ_SER_RX (IRQ_USER+31) +#define MVME16x_IRQ_TIMER (IRQ_USER+25) +#define MVME167_IRQ_ABORT (IRQ_USER+46) +#define MVME162_IRQ_ABORT (IRQ_USER+30) /* SCC interrupts, for MVME162 */ -#define MVME162_IRQ_SCC_BASE 0x40 -#define MVME162_IRQ_SCCB_TX 0x40 -#define MVME162_IRQ_SCCB_STAT 0x42 -#define MVME162_IRQ_SCCB_RX 0x44 -#define MVME162_IRQ_SCCB_SPCOND 0x46 -#define MVME162_IRQ_SCCA_TX 0x48 -#define MVME162_IRQ_SCCA_STAT 0x4a -#define MVME162_IRQ_SCCA_RX 0x4c -#define MVME162_IRQ_SCCA_SPCOND 0x4e +#define MVME162_IRQ_SCC_BASE (IRQ_USER+0) +#define MVME162_IRQ_SCCB_TX (IRQ_USER+0) +#define MVME162_IRQ_SCCB_STAT (IRQ_USER+2) +#define MVME162_IRQ_SCCB_RX (IRQ_USER+4) +#define MVME162_IRQ_SCCB_SPCOND (IRQ_USER+6) +#define MVME162_IRQ_SCCA_TX (IRQ_USER+8) +#define MVME162_IRQ_SCCA_STAT (IRQ_USER+10) +#define MVME162_IRQ_SCCA_RX (IRQ_USER+12) +#define MVME162_IRQ_SCCA_SPCOND (IRQ_USER+14) /* MVME162 version register */ diff --git a/include/asm-m68k/openprom.h b/include/asm-m68k/openprom.h index efbfb0b..869ab91 100644 --- a/include/asm-m68k/openprom.h +++ b/include/asm-m68k/openprom.h @@ -8,7 +8,6 @@ #define __SPARC_OPENPROM_H * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) */ -#include /* Empirical constants... */ #ifdef CONFIG_SUN3 diff --git a/include/asm-m68k/page.h b/include/asm-m68k/page.h index f206dfb..db017f8 100644 --- a/include/asm-m68k/page.h +++ b/include/asm-m68k/page.h @@ -1,7 +1,6 @@ #ifndef _M68K_PAGE_H #define _M68K_PAGE_H -#include /* PAGE_SHIFT determines the page size */ #ifndef CONFIG_SUN3 diff --git a/include/asm-m68k/page_offset.h b/include/asm-m68k/page_offset.h index 86d3c28..1cbdb7f 100644 --- a/include/asm-m68k/page_offset.h +++ b/include/asm-m68k/page_offset.h @@ -1,4 +1,3 @@ -#include /* This handles the memory map.. */ #ifndef CONFIG_SUN3 diff --git a/include/asm-m68k/pgalloc.h b/include/asm-m68k/pgalloc.h index b468b79..a9cfb4b 100644 --- a/include/asm-m68k/pgalloc.h +++ b/include/asm-m68k/pgalloc.h @@ -2,7 +2,6 @@ #ifndef M68K_PGALLOC_H #define M68K_PGALLOC_H -#include #include #include #include diff --git a/include/asm-m68k/pgtable.h b/include/asm-m68k/pgtable.h index add129e..f3aa053 100644 --- a/include/asm-m68k/pgtable.h +++ b/include/asm-m68k/pgtable.h @@ -3,7 +3,6 @@ #define _M68K_PGTABLE_H #include -#include #include #ifndef __ASSEMBLY__ diff --git a/include/asm-m68k/processor.h b/include/asm-m68k/processor.h index 7982285..8455f77 100644 --- a/include/asm-m68k/processor.h +++ b/include/asm-m68k/processor.h @@ -13,7 +13,6 @@ #define __ASM_M68K_PROCESSOR_H */ #define current_text_addr() ({ __label__ _l; _l: &&_l;}) -#include #include #include #include @@ -72,10 +71,10 @@ struct thread_struct { }; #define INIT_THREAD { \ - ksp: sizeof(init_stack) + (unsigned long) init_stack, \ - sr: PS_S, \ - fs: __KERNEL_DS, \ - info: INIT_THREAD_INFO(init_task) \ + .ksp = sizeof(init_stack) + (unsigned long) init_stack, \ + .sr = PS_S, \ + .fs = __KERNEL_DS, \ + .info = INIT_THREAD_INFO(init_task), \ } /* diff --git a/include/asm-m68k/scatterlist.h b/include/asm-m68k/scatterlist.h index d7c9b5c..8e61226 100644 --- a/include/asm-m68k/scatterlist.h +++ b/include/asm-m68k/scatterlist.h @@ -2,18 +2,17 @@ #ifndef _M68K_SCATTERLIST_H #define _M68K_SCATTERLIST_H struct scatterlist { - /* These two are only valid if ADDRESS member of this - * struct is NULL. - */ struct page *page; unsigned int offset; - unsigned int length; - __u32 dvma_address; /* A place to hang host-specific addresses at. */ + __u32 dma_address; /* A place to hang host-specific addresses at. */ }; /* This is bogus and should go away. */ #define ISA_DMA_THRESHOLD (0x00ffffff) +#define sg_dma_address(sg) ((sg)->dma_address) +#define sg_dma_len(sg) ((sg)->length) + #endif /* !(_M68K_SCATTERLIST_H) */ diff --git a/include/asm-m68k/semaphore-helper.h b/include/asm-m68k/semaphore-helper.h index 1516a64..eef30ba 100644 --- a/include/asm-m68k/semaphore-helper.h +++ b/include/asm-m68k/semaphore-helper.h @@ -9,7 +9,6 @@ #define _M68K_SEMAPHORE_HELPER_H * m68k version by Andreas Schwab */ -#include #include /* diff --git a/include/asm-m68k/serial.h b/include/asm-m68k/serial.h index 3fe29f8..2b90d6e 100644 --- a/include/asm-m68k/serial.h +++ b/include/asm-m68k/serial.h @@ -6,7 +6,6 @@ * */ -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-m68k/setup.h b/include/asm-m68k/setup.h index a89aa84..7facc9a 100644 --- a/include/asm-m68k/setup.h +++ b/include/asm-m68k/setup.h @@ -23,7 +23,6 @@ #ifndef _M68K_SETUP_H #define _M68K_SETUP_H -#include /* diff --git a/include/asm-m68k/shm.h b/include/asm-m68k/shm.h index 3fa2f36..fa56ec8 100644 --- a/include/asm-m68k/shm.h +++ b/include/asm-m68k/shm.h @@ -1,7 +1,6 @@ #ifndef _M68K_SHM_H #define _M68K_SHM_H -#include /* format of page table entries that correspond to shared memory pages currently out in swap space (see also mm/swap.c): diff --git a/include/asm-m68k/signal.h b/include/asm-m68k/signal.h index b7b7ea2..de1ba6e 100644 --- a/include/asm-m68k/signal.h +++ b/include/asm-m68k/signal.h @@ -74,7 +74,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -94,7 +93,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ /* * sigaltstack controls @@ -156,13 +154,17 @@ #define __HAVE_ARCH_SIG_BITOPS static inline void sigaddset(sigset_t *set, int _sig) { - __asm__("bfset %0{%1,#1}" : "=m" (*set) : "id" ((_sig - 1) ^ 31) + asm ("bfset %0{%1,#1}" + : "+od" (*set) + : "id" ((_sig - 1) ^ 31) : "cc"); } static inline void sigdelset(sigset_t *set, int _sig) { - __asm__("bfclr %0{%1,#1}" : "=m"(*set) : "id"((_sig - 1) ^ 31) + asm ("bfclr %0{%1,#1}" + : "+od" (*set) + : "id" ((_sig - 1) ^ 31) : "cc"); } @@ -175,8 +177,10 @@ static inline int __const_sigismember(si static inline int __gen_sigismember(sigset_t *set, int _sig) { int ret; - __asm__("bfextu %1{%2,#1},%0" - : "=d"(ret) : "m"(*set), "id"((_sig-1) ^ 31)); + asm ("bfextu %1{%2,#1},%0" + : "=d" (ret) + : "od" (*set), "id" ((_sig-1) ^ 31) + : "cc"); return ret; } @@ -187,7 +191,10 @@ #define sigismember(set,sig) \ static inline int sigfindinword(unsigned long word) { - __asm__("bfffo %1{#0,#0},%0" : "=d"(word) : "d"(word & -word) : "cc"); + asm ("bfffo %1{#0,#0},%0" + : "=d" (word) + : "d" (word & -word) + : "cc"); return word ^ 31; } diff --git a/include/asm-m68k/socket.h b/include/asm-m68k/socket.h index f578ca4..a5966ec 100644 --- a/include/asm-m68k/socket.h +++ b/include/asm-m68k/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-m68k/sun3ints.h b/include/asm-m68k/sun3ints.h index bd038fc..de91fa0 100644 --- a/include/asm-m68k/sun3ints.h +++ b/include/asm-m68k/sun3ints.h @@ -12,37 +12,25 @@ #ifndef SUN3INTS_H #define SUN3INTS_H #include -#include -#include -#include #include -#include -#include #include #include +#include #define SUN3_INT_VECS 192 void sun3_enable_irq(unsigned int irq); void sun3_disable_irq(unsigned int irq); -int sun3_request_irq(unsigned int irq, - irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long flags, const char *devname, void *dev_id - ); extern void sun3_init_IRQ (void); -extern irqreturn_t (*sun3_default_handler[]) (int, void *, struct pt_regs *); -extern void sun3_free_irq (unsigned int irq, void *dev_id); extern void sun3_enable_interrupts (void); extern void sun3_disable_interrupts (void); -extern int show_sun3_interrupts(struct seq_file *, void *); -extern irqreturn_t sun3_process_int(int, struct pt_regs *); extern volatile unsigned char* sun3_intreg; /* master list of VME vectors -- don't fuck with this */ -#define SUN3_VEC_FLOPPY 0x40 -#define SUN3_VEC_VMESCSI0 0x40 -#define SUN3_VEC_VMESCSI1 0x41 -#define SUN3_VEC_CG 0xA8 +#define SUN3_VEC_FLOPPY (IRQ_USER+0) +#define SUN3_VEC_VMESCSI0 (IRQ_USER+0) +#define SUN3_VEC_VMESCSI1 (IRQ_USER+1) +#define SUN3_VEC_CG (IRQ_USER+104) #endif /* SUN3INTS_H */ diff --git a/include/asm-m68k/sun3xflop.h b/include/asm-m68k/sun3xflop.h index 98a9f79..ca8cc41 100644 --- a/include/asm-m68k/sun3xflop.h +++ b/include/asm-m68k/sun3xflop.h @@ -208,7 +208,8 @@ static int sun3xflop_request_irq(void) if(!once) { once = 1; - error = request_irq(FLOPPY_IRQ, sun3xflop_hardint, SA_INTERRUPT, "floppy", NULL); + error = request_irq(FLOPPY_IRQ, sun3xflop_hardint, + IRQF_DISABLED, "floppy", NULL); return ((error == 0) ? 0 : -1); } else return 0; } diff --git a/include/asm-m68k/system.h b/include/asm-m68k/system.h index 64d3481..d6dd805 100644 --- a/include/asm-m68k/system.h +++ b/include/asm-m68k/system.h @@ -1,7 +1,6 @@ #ifndef _M68K_SYSTEM_H #define _M68K_SYSTEM_H -#include /* get configuration macros */ #include #include #include diff --git a/include/asm-m68k/tlbflush.h b/include/asm-m68k/tlbflush.h index 8e61ccf..3167883 100644 --- a/include/asm-m68k/tlbflush.h +++ b/include/asm-m68k/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _M68K_TLBFLUSH_H #define _M68K_TLBFLUSH_H -#include #ifndef CONFIG_SUN3 diff --git a/include/asm-m68k/traps.h b/include/asm-m68k/traps.h index 4750561..8caef25 100644 --- a/include/asm-m68k/traps.h +++ b/include/asm-m68k/traps.h @@ -13,8 +13,15 @@ #define _M68K_TRAPS_H #ifndef __ASSEMBLY__ +#include +#include + typedef void (*e_vector)(void); +asmlinkage void auto_inthandler(void); +asmlinkage void user_inthandler(void); +asmlinkage void bad_inthandler(void); + extern e_vector vectors[]; #endif diff --git a/include/asm-m68k/uaccess.h b/include/asm-m68k/uaccess.h index 2ffd87b..88b1f47 100644 --- a/include/asm-m68k/uaccess.h +++ b/include/asm-m68k/uaccess.h @@ -4,8 +4,9 @@ #define __M68K_UACCESS_H /* * User space memory access functions */ +#include #include -#include +#include #include #define VERIFY_READ 0 @@ -32,858 +33,335 @@ struct exception_table_entry unsigned long insn, fixup; }; +extern int __put_user_bad(void); +extern int __get_user_bad(void); + +#define __put_user_asm(res, x, ptr, bwl, reg, err) \ +asm volatile ("\n" \ + "1: moves."#bwl" %2,%1\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "10: moveq.l %3,%0\n" \ + " jra 2b\n" \ + " .previous\n" \ + "\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,10b\n" \ + " .long 2b,10b\n" \ + " .previous" \ + : "+d" (res), "=m" (*(ptr)) \ + : #reg (x), "i" (err)) /* * These are the main single-value transfer routines. They automatically * use the right size if we just have the right pointer type. */ -#define put_user(x, ptr) \ -({ \ - int __pu_err; \ - typeof(*(ptr)) __pu_val = (x); \ - __chk_user_ptr(ptr); \ - switch (sizeof (*(ptr))) { \ - case 1: \ - __put_user_asm(__pu_err, __pu_val, ptr, b); \ - break; \ - case 2: \ - __put_user_asm(__pu_err, __pu_val, ptr, w); \ - break; \ - case 4: \ - __put_user_asm(__pu_err, __pu_val, ptr, l); \ - break; \ - case 8: \ - __pu_err = __constant_copy_to_user(ptr, &__pu_val, 8); \ - break; \ - default: \ - __pu_err = __put_user_bad(); \ - break; \ - } \ - __pu_err; \ +#define __put_user(x, ptr) \ +({ \ + typeof(*(ptr)) __pu_val = (x); \ + int __pu_err = 0; \ + __chk_user_ptr(ptr); \ + switch (sizeof (*(ptr))) { \ + case 1: \ + __put_user_asm(__pu_err, __pu_val, ptr, b, d, -EFAULT); \ + break; \ + case 2: \ + __put_user_asm(__pu_err, __pu_val, ptr, w, d, -EFAULT); \ + break; \ + case 4: \ + __put_user_asm(__pu_err, __pu_val, ptr, l, r, -EFAULT); \ + break; \ + case 8: \ + { \ + const void *__pu_ptr = (ptr); \ + asm volatile ("\n" \ + "1: moves.l %2,(%1)+\n" \ + "2: moves.l %R2,(%1)\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "10: movel %3,%0\n" \ + " jra 3b\n" \ + " .previous\n" \ + "\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,10b\n" \ + " .long 2b,10b\n" \ + " .long 3b,10b\n" \ + " .previous" \ + : "+d" (__pu_err), "+a" (__pu_ptr) \ + : "r" (__pu_val), "i" (-EFAULT) \ + : "memory"); \ + break; \ + } \ + default: \ + __pu_err = __put_user_bad(); \ + break; \ + } \ + __pu_err; \ }) -#define __put_user(x, ptr) put_user(x, ptr) - -extern int __put_user_bad(void); +#define put_user(x, ptr) __put_user(x, ptr) -/* - * Tell gcc we read from memory instead of writing: this is because - * we do not write to any memory gcc knows about, so there are no - * aliasing issues. - */ -#define __put_user_asm(err,x,ptr,bwl) \ -__asm__ __volatile__ \ - ("21:moves" #bwl " %2,%1\n" \ - "1:\n" \ - ".section .fixup,\"ax\"\n" \ - " .even\n" \ - "2: movel %3,%0\n" \ - " jra 1b\n" \ - ".previous\n" \ - ".section __ex_table,\"a\"\n" \ - " .align 4\n" \ - " .long 21b,2b\n" \ - " .long 1b,2b\n" \ - ".previous" \ - : "=d"(err) \ - : "m"(*(ptr)), "r"(x), "i"(-EFAULT), "0"(0)) -#define get_user(x, ptr) \ -({ \ - int __gu_err; \ - typeof(*(ptr)) __gu_val; \ - __chk_user_ptr(ptr); \ - switch (sizeof(*(ptr))) { \ - case 1: \ - __get_user_asm(__gu_err, __gu_val, ptr, b, "=d"); \ - break; \ - case 2: \ - __get_user_asm(__gu_err, __gu_val, ptr, w, "=r"); \ - break; \ - case 4: \ - __get_user_asm(__gu_err, __gu_val, ptr, l, "=r"); \ - break; \ - case 8: \ - __gu_err = __constant_copy_from_user(&__gu_val, ptr, 8); \ - break; \ - default: \ - __gu_val = (typeof(*(ptr)))0; \ - __gu_err = __get_user_bad(); \ - break; \ - } \ - (x) = __gu_val; \ - __gu_err; \ +#define __get_user_asm(res, x, ptr, type, bwl, reg, err) ({ \ + type __gu_val; \ + asm volatile ("\n" \ + "1: moves."#bwl" %2,%1\n" \ + "2:\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "10: move.l %3,%0\n" \ + " sub."#bwl" %1,%1\n" \ + " jra 2b\n" \ + " .previous\n" \ + "\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,10b\n" \ + " .previous" \ + : "+d" (res), "=&" #reg (__gu_val) \ + : "m" (*(ptr)), "i" (err)); \ + (x) = (typeof(*(ptr)))(long)__gu_val; \ }) -#define __get_user(x, ptr) get_user(x, ptr) - -extern int __get_user_bad(void); - -#define __get_user_asm(err,x,ptr,bwl,reg) \ -__asm__ __volatile__ \ - ("1: moves" #bwl " %2,%1\n" \ - "2:\n" \ - ".section .fixup,\"ax\"\n" \ - " .even\n" \ - "3: movel %3,%0\n" \ - " sub" #bwl " %1,%1\n" \ - " jra 2b\n" \ - ".previous\n" \ - ".section __ex_table,\"a\"\n" \ - " .align 4\n" \ - " .long 1b,3b\n" \ - ".previous" \ - : "=d"(err), reg(x) \ - : "m"(*(ptr)), "i" (-EFAULT), "0"(0)) -static inline unsigned long -__generic_copy_from_user(void *to, const void __user *from, unsigned long n) -{ - unsigned long tmp; - __asm__ __volatile__ - (" tstl %2\n" - " jeq 2f\n" - "1: movesl (%1)+,%3\n" - " movel %3,(%0)+\n" - " subql #1,%2\n" - " jne 1b\n" - "2: movel %4,%2\n" - " bclr #1,%2\n" - " jeq 4f\n" - "3: movesw (%1)+,%3\n" - " movew %3,(%0)+\n" - "4: bclr #0,%2\n" - " jeq 6f\n" - "5: movesb (%1)+,%3\n" - " moveb %3,(%0)+\n" - "6:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "7: movel %2,%%d0\n" - "71:clrl (%0)+\n" - " subql #1,%%d0\n" - " jne 71b\n" - " lsll #2,%2\n" - " addl %4,%2\n" - " btst #1,%4\n" - " jne 81f\n" - " btst #0,%4\n" - " jne 91f\n" - " jra 6b\n" - "8: addql #2,%2\n" - "81:clrw (%0)+\n" - " btst #0,%4\n" - " jne 91f\n" - " jra 6b\n" - "9: addql #1,%2\n" - "91:clrb (%0)+\n" - " jra 6b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,7b\n" - " .long 3b,8b\n" - " .long 5b,9b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n), "=&d"(tmp) - : "d"(n & 3), "0"(to), "1"(from), "2"(n/4) - : "d0", "memory"); - return n; -} +#define __get_user(x, ptr) \ +({ \ + int __gu_err = 0; \ + __chk_user_ptr(ptr); \ + switch (sizeof(*(ptr))) { \ + case 1: \ + __get_user_asm(__gu_err, x, ptr, u8, b, d, -EFAULT); \ + break; \ + case 2: \ + __get_user_asm(__gu_err, x, ptr, u16, w, d, -EFAULT); \ + break; \ + case 4: \ + __get_user_asm(__gu_err, x, ptr, u32, l, r, -EFAULT); \ + break; \ +/* case 8: disabled because gcc-4.1 has a broken typeof \ + { \ + const void *__gu_ptr = (ptr); \ + u64 __gu_val; \ + asm volatile ("\n" \ + "1: moves.l (%2)+,%1\n" \ + "2: moves.l (%2),%R1\n" \ + "3:\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "10: move.l %3,%0\n" \ + " sub.l %1,%1\n" \ + " sub.l %R1,%R1\n" \ + " jra 3b\n" \ + " .previous\n" \ + "\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,10b\n" \ + " .long 2b,10b\n" \ + " .previous" \ + : "+d" (__gu_err), "=&r" (__gu_val), \ + "+a" (__gu_ptr) \ + : "i" (-EFAULT) \ + : "memory"); \ + (x) = (typeof(*(ptr)))__gu_val; \ + break; \ + } */ \ + default: \ + __gu_err = __get_user_bad(); \ + break; \ + } \ + __gu_err; \ +}) +#define get_user(x, ptr) __get_user(x, ptr) -static inline unsigned long -__generic_copy_to_user(void __user *to, const void *from, unsigned long n) -{ - unsigned long tmp; - __asm__ __volatile__ - (" tstl %2\n" - " jeq 3f\n" - "1: movel (%1)+,%3\n" - "22:movesl %3,(%0)+\n" - "2: subql #1,%2\n" - " jne 1b\n" - "3: movel %4,%2\n" - " bclr #1,%2\n" - " jeq 4f\n" - " movew (%1)+,%3\n" - "24:movesw %3,(%0)+\n" - "4: bclr #0,%2\n" - " jeq 5f\n" - " moveb (%1)+,%3\n" - "25:movesb %3,(%0)+\n" - "5:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "60:addql #1,%2\n" - "6: lsll #2,%2\n" - " addl %4,%2\n" - " jra 5b\n" - "7: addql #2,%2\n" - " jra 5b\n" - "8: addql #1,%2\n" - " jra 5b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,60b\n" - " .long 22b,6b\n" - " .long 2b,6b\n" - " .long 24b,7b\n" - " .long 3b,60b\n" - " .long 4b,7b\n" - " .long 25b,8b\n" - " .long 5b,8b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n), "=&d"(tmp) - : "r"(n & 3), "0"(to), "1"(from), "2"(n / 4) - : "memory"); - return n; -} +unsigned long __generic_copy_from_user(void *to, const void __user *from, unsigned long n); +unsigned long __generic_copy_to_user(void __user *to, const void *from, unsigned long n); -#define __copy_from_user_big(to, from, n, fixup, copy) \ - __asm__ __volatile__ \ - ("10: movesl (%1)+,%%d0\n" \ - " movel %%d0,(%0)+\n" \ - " subql #1,%2\n" \ - " jne 10b\n" \ - ".section .fixup,\"ax\"\n" \ - " .even\n" \ - "11: movel %2,%%d0\n" \ - "13: clrl (%0)+\n" \ - " subql #1,%%d0\n" \ - " jne 13b\n" \ - " lsll #2,%2\n" \ - fixup "\n" \ - " jra 12f\n" \ - ".previous\n" \ - ".section __ex_table,\"a\"\n" \ - " .align 4\n" \ - " .long 10b,11b\n" \ - ".previous\n" \ - copy "\n" \ - "12:" \ - : "=a"(to), "=a"(from), "=d"(n) \ - : "0"(to), "1"(from), "2"(n/4) \ - : "d0", "memory") +#define __constant_copy_from_user_asm(res, to, from, tmp, n, s1, s2, s3)\ + asm volatile ("\n" \ + "1: moves."#s1" (%2)+,%3\n" \ + " move."#s1" %3,(%1)+\n" \ + "2: moves."#s2" (%2)+,%3\n" \ + " move."#s2" %3,(%1)+\n" \ + " .ifnc \""#s3"\",\"\"\n" \ + "3: moves."#s3" (%2)+,%3\n" \ + " move."#s3" %3,(%1)+\n" \ + " .endif\n" \ + "4:\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 1b,10f\n" \ + " .long 2b,20f\n" \ + " .ifnc \""#s3"\",\"\"\n" \ + " .long 3b,30f\n" \ + " .endif\n" \ + " .previous\n" \ + "\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "10: clr."#s1" (%1)+\n" \ + "20: clr."#s2" (%1)+\n" \ + " .ifnc \""#s3"\",\"\"\n" \ + "30: clr."#s3" (%1)+\n" \ + " .endif\n" \ + " moveq.l #"#n",%0\n" \ + " jra 4b\n" \ + " .previous\n" \ + : "+d" (res), "+&a" (to), "+a" (from), "=&d" (tmp) \ + : : "memory") -static inline unsigned long +static __always_inline unsigned long __constant_copy_from_user(void *to, const void __user *from, unsigned long n) { - switch (n) { - case 0: - break; - case 1: - __asm__ __volatile__ - ("1: movesb (%1)+,%%d0\n" - " moveb %%d0,(%0)+\n" - "2:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "3: addql #1,%2\n" - " clrb (%0)+\n" - " jra 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,3b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 2: - __asm__ __volatile__ - ("1: movesw (%1)+,%%d0\n" - " movew %%d0,(%0)+\n" - "2:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "3: addql #2,%2\n" - " clrw (%0)+\n" - " jra 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,3b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 3: - __asm__ __volatile__ - ("1: movesw (%1)+,%%d0\n" - " movew %%d0,(%0)+\n" - "2: movesb (%1)+,%%d0\n" - " moveb %%d0,(%0)+\n" - "3:" - ".section .fixup,\"ax\"\n" - " .even\n" - "4: addql #2,%2\n" - " clrw (%0)+\n" - "5: addql #1,%2\n" - " clrb (%0)+\n" - " jra 3b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,4b\n" - " .long 2b,5b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 4: - __asm__ __volatile__ - ("1: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "2:" - ".section .fixup,\"ax\"\n" - " .even\n" - "3: addql #4,%2\n" - " clrl (%0)+\n" - " jra 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,3b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 8: - __asm__ __volatile__ - ("1: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "2: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "3:" - ".section .fixup,\"ax\"\n" - " .even\n" - "4: addql #4,%2\n" - " clrl (%0)+\n" - "5: addql #4,%2\n" - " clrl (%0)+\n" - " jra 3b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,4b\n" - " .long 2b,5b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 12: - __asm__ __volatile__ - ("1: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "2: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "3: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "4:" - ".section .fixup,\"ax\"\n" - " .even\n" - "5: addql #4,%2\n" - " clrl (%0)+\n" - "6: addql #4,%2\n" - " clrl (%0)+\n" - "7: addql #4,%2\n" - " clrl (%0)+\n" - " jra 4b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,5b\n" - " .long 2b,6b\n" - " .long 3b,7b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 16: - __asm__ __volatile__ - ("1: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "2: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "3: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "4: movesl (%1)+,%%d0\n" - " movel %%d0,(%0)+\n" - "5:" - ".section .fixup,\"ax\"\n" - " .even\n" - "6: addql #4,%2\n" - " clrl (%0)+\n" - "7: addql #4,%2\n" - " clrl (%0)+\n" - "8: addql #4,%2\n" - " clrl (%0)+\n" - "9: addql #4,%2\n" - " clrl (%0)+\n" - " jra 5b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,6b\n" - " .long 2b,7b\n" - " .long 3b,8b\n" - " .long 4b,9b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - default: - switch (n & 3) { - case 0: - __copy_from_user_big(to, from, n, "", ""); - break; + unsigned long res = 0, tmp; + + switch (n) { case 1: - __copy_from_user_big(to, from, n, - /* fixup */ - "1: addql #1,%2\n" - " clrb (%0)+", - /* copy */ - "2: movesb (%1)+,%%d0\n" - " moveb %%d0,(%0)+\n" - ".section __ex_table,\"a\"\n" - " .long 2b,1b\n" - ".previous"); - break; + __get_user_asm(res, *(u8 *)to, (u8 *)from, u8, b, d, 1); + break; case 2: - __copy_from_user_big(to, from, n, - /* fixup */ - "1: addql #2,%2\n" - " clrw (%0)+", - /* copy */ - "2: movesw (%1)+,%%d0\n" - " movew %%d0,(%0)+\n" - ".section __ex_table,\"a\"\n" - " .long 2b,1b\n" - ".previous"); - break; + __get_user_asm(res, *(u16 *)to, (u16 *)from, u16, w, d, 2); + break; case 3: - __copy_from_user_big(to, from, n, - /* fixup */ - "1: addql #2,%2\n" - " clrw (%0)+\n" - "2: addql #1,%2\n" - " clrb (%0)+", - /* copy */ - "3: movesw (%1)+,%%d0\n" - " movew %%d0,(%0)+\n" - "4: movesb (%1)+,%%d0\n" - " moveb %%d0,(%0)+\n" - ".section __ex_table,\"a\"\n" - " .long 3b,1b\n" - " .long 4b,2b\n" - ".previous"); - break; + __constant_copy_from_user_asm(res, to, from, tmp, 3, w, b,); + break; + case 4: + __get_user_asm(res, *(u32 *)to, (u32 *)from, u32, l, r, 4); + break; + case 5: + __constant_copy_from_user_asm(res, to, from, tmp, 5, l, b,); + break; + case 6: + __constant_copy_from_user_asm(res, to, from, tmp, 6, l, w,); + break; + case 7: + __constant_copy_from_user_asm(res, to, from, tmp, 7, l, w, b); + break; + case 8: + __constant_copy_from_user_asm(res, to, from, tmp, 8, l, l,); + break; + case 9: + __constant_copy_from_user_asm(res, to, from, tmp, 9, l, l, b); + break; + case 10: + __constant_copy_from_user_asm(res, to, from, tmp, 10, l, l, w); + break; + case 12: + __constant_copy_from_user_asm(res, to, from, tmp, 12, l, l, l); + break; + default: + /* we limit the inlined version to 3 moves */ + return __generic_copy_from_user(to, from, n); } - break; - } - return n; -} -#define __copy_to_user_big(to, from, n, fixup, copy) \ - __asm__ __volatile__ \ - ("10: movel (%1)+,%%d0\n" \ - "31: movesl %%d0,(%0)+\n" \ - "11: subql #1,%2\n" \ - " jne 10b\n" \ - "41:\n" \ - ".section .fixup,\"ax\"\n" \ - " .even\n" \ - "22: addql #1,%2\n" \ - "12: lsll #2,%2\n" \ - fixup "\n" \ - " jra 13f\n" \ - ".previous\n" \ - ".section __ex_table,\"a\"\n" \ - " .align 4\n" \ - " .long 10b,22b\n" \ - " .long 31b,12b\n" \ - " .long 11b,12b\n" \ - " .long 41b,22b\n" \ - ".previous\n" \ - copy "\n" \ - "13:" \ - : "=a"(to), "=a"(from), "=d"(n) \ - : "0"(to), "1"(from), "2"(n/4) \ - : "d0", "memory") + return res; +} -#define __copy_to_user_inatomic __copy_to_user -#define __copy_from_user_inatomic __copy_from_user +#define __constant_copy_to_user_asm(res, to, from, tmp, n, s1, s2, s3) \ + asm volatile ("\n" \ + " move."#s1" (%2)+,%3\n" \ + "11: moves."#s1" %3,(%1)+\n" \ + "12: move."#s2" (%2)+,%3\n" \ + "21: moves."#s2" %3,(%1)+\n" \ + "22:\n" \ + " .ifnc \""#s3"\",\"\"\n" \ + " move."#s3" (%2)+,%3\n" \ + "31: moves."#s3" %3,(%1)+\n" \ + "32:\n" \ + " .endif\n" \ + "4:\n" \ + "\n" \ + " .section __ex_table,\"a\"\n" \ + " .align 4\n" \ + " .long 11b,5f\n" \ + " .long 12b,5f\n" \ + " .long 21b,5f\n" \ + " .long 22b,5f\n" \ + " .ifnc \""#s3"\",\"\"\n" \ + " .long 31b,5f\n" \ + " .long 32b,5f\n" \ + " .endif\n" \ + " .previous\n" \ + "\n" \ + " .section .fixup,\"ax\"\n" \ + " .even\n" \ + "5: moveq.l #"#n",%0\n" \ + " jra 4b\n" \ + " .previous\n" \ + : "+d" (res), "+a" (to), "+a" (from), "=&d" (tmp) \ + : : "memory") -static inline unsigned long +static __always_inline unsigned long __constant_copy_to_user(void __user *to, const void *from, unsigned long n) { - switch (n) { - case 0: - break; - case 1: - __asm__ __volatile__ - (" moveb (%1)+,%%d0\n" - "21:movesb %%d0,(%0)+\n" - "1:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "2: addql #1,%2\n" - " jra 1b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n " - " .long 21b,2b\n" - " .long 1b,2b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 2: - __asm__ __volatile__ - (" movew (%1)+,%%d0\n" - "21:movesw %%d0,(%0)+\n" - "1:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "2: addql #2,%2\n" - " jra 1b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 21b,2b\n" - " .long 1b,2b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 3: - __asm__ __volatile__ - (" movew (%1)+,%%d0\n" - "21:movesw %%d0,(%0)+\n" - "1: moveb (%1)+,%%d0\n" - "22:movesb %%d0,(%0)+\n" - "2:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "3: addql #2,%2\n" - "4: addql #1,%2\n" - " jra 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 21b,3b\n" - " .long 1b,3b\n" - " .long 22b,4b\n" - " .long 2b,4b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 4: - __asm__ __volatile__ - (" movel (%1)+,%%d0\n" - "21:movesl %%d0,(%0)+\n" - "1:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "2: addql #4,%2\n" - " jra 1b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 21b,2b\n" - " .long 1b,2b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 8: - __asm__ __volatile__ - (" movel (%1)+,%%d0\n" - "21:movesl %%d0,(%0)+\n" - "1: movel (%1)+,%%d0\n" - "22:movesl %%d0,(%0)+\n" - "2:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "3: addql #4,%2\n" - "4: addql #4,%2\n" - " jra 2b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 21b,3b\n" - " .long 1b,3b\n" - " .long 22b,4b\n" - " .long 2b,4b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 12: - __asm__ __volatile__ - (" movel (%1)+,%%d0\n" - "21:movesl %%d0,(%0)+\n" - "1: movel (%1)+,%%d0\n" - "22:movesl %%d0,(%0)+\n" - "2: movel (%1)+,%%d0\n" - "23:movesl %%d0,(%0)+\n" - "3:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "4: addql #4,%2\n" - "5: addql #4,%2\n" - "6: addql #4,%2\n" - " jra 3b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 21b,4b\n" - " .long 1b,4b\n" - " .long 22b,5b\n" - " .long 2b,5b\n" - " .long 23b,6b\n" - " .long 3b,6b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - case 16: - __asm__ __volatile__ - (" movel (%1)+,%%d0\n" - "21:movesl %%d0,(%0)+\n" - "1: movel (%1)+,%%d0\n" - "22:movesl %%d0,(%0)+\n" - "2: movel (%1)+,%%d0\n" - "23:movesl %%d0,(%0)+\n" - "3: movel (%1)+,%%d0\n" - "24:movesl %%d0,(%0)+\n" - "4:" - ".section .fixup,\"ax\"\n" - " .even\n" - "5: addql #4,%2\n" - "6: addql #4,%2\n" - "7: addql #4,%2\n" - "8: addql #4,%2\n" - " jra 4b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 21b,5b\n" - " .long 1b,5b\n" - " .long 22b,6b\n" - " .long 2b,6b\n" - " .long 23b,7b\n" - " .long 3b,7b\n" - " .long 24b,8b\n" - " .long 4b,8b\n" - ".previous" - : "=a"(to), "=a"(from), "=d"(n) - : "0"(to), "1"(from), "2"(0) - : "d0", "memory"); - break; - default: - switch (n & 3) { - case 0: - __copy_to_user_big(to, from, n, "", ""); - break; + unsigned long res = 0, tmp; + + switch (n) { case 1: - __copy_to_user_big(to, from, n, - /* fixup */ - "1: addql #1,%2", - /* copy */ - " moveb (%1)+,%%d0\n" - "22:movesb %%d0,(%0)+\n" - "2:" - ".section __ex_table,\"a\"\n" - " .long 22b,1b\n" - " .long 2b,1b\n" - ".previous"); - break; + __put_user_asm(res, *(u8 *)from, (u8 *)to, b, d, 1); + break; case 2: - __copy_to_user_big(to, from, n, - /* fixup */ - "1: addql #2,%2", - /* copy */ - " movew (%1)+,%%d0\n" - "22:movesw %%d0,(%0)+\n" - "2:" - ".section __ex_table,\"a\"\n" - " .long 22b,1b\n" - " .long 2b,1b\n" - ".previous"); - break; + __put_user_asm(res, *(u16 *)from, (u16 *)to, w, d, 2); + break; case 3: - __copy_to_user_big(to, from, n, - /* fixup */ - "1: addql #2,%2\n" - "2: addql #1,%2", - /* copy */ - " movew (%1)+,%%d0\n" - "23:movesw %%d0,(%0)+\n" - "3: moveb (%1)+,%%d0\n" - "24:movesb %%d0,(%0)+\n" - "4:" - ".section __ex_table,\"a\"\n" - " .long 23b,1b\n" - " .long 3b,1b\n" - " .long 24b,2b\n" - " .long 4b,2b\n" - ".previous"); - break; + __constant_copy_to_user_asm(res, to, from, tmp, 3, w, b,); + break; + case 4: + __put_user_asm(res, *(u32 *)from, (u32 *)to, l, r, 4); + break; + case 5: + __constant_copy_to_user_asm(res, to, from, tmp, 5, l, b,); + break; + case 6: + __constant_copy_to_user_asm(res, to, from, tmp, 6, l, w,); + break; + case 7: + __constant_copy_to_user_asm(res, to, from, tmp, 7, l, w, b); + break; + case 8: + __constant_copy_to_user_asm(res, to, from, tmp, 8, l, l,); + break; + case 9: + __constant_copy_to_user_asm(res, to, from, tmp, 9, l, l, b); + break; + case 10: + __constant_copy_to_user_asm(res, to, from, tmp, 10, l, l, w); + break; + case 12: + __constant_copy_to_user_asm(res, to, from, tmp, 12, l, l, l); + break; + default: + /* limit the inlined version to 3 moves */ + return __generic_copy_to_user(to, from, n); } - break; - } - return n; + + return res; } -#define copy_from_user(to, from, n) \ +#define __copy_from_user(to, from, n) \ (__builtin_constant_p(n) ? \ __constant_copy_from_user(to, from, n) : \ __generic_copy_from_user(to, from, n)) -#define copy_to_user(to, from, n) \ +#define __copy_to_user(to, from, n) \ (__builtin_constant_p(n) ? \ __constant_copy_to_user(to, from, n) : \ __generic_copy_to_user(to, from, n)) -#define __copy_from_user(to, from, n) copy_from_user(to, from, n) -#define __copy_to_user(to, from, n) copy_to_user(to, from, n) - -/* - * Copy a null terminated string from userspace. - */ - -static inline long -strncpy_from_user(char *dst, const char __user *src, long count) -{ - long res; - if (count == 0) return count; - __asm__ __volatile__ - ("1: movesb (%2)+,%%d0\n" - "12:moveb %%d0,(%1)+\n" - " jeq 2f\n" - " subql #1,%3\n" - " jne 1b\n" - "2: subl %3,%0\n" - "3:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "4: movel %4,%0\n" - " jra 3b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,4b\n" - " .long 12b,4b\n" - ".previous" - : "=d"(res), "=a"(dst), "=a"(src), "=d"(count) - : "i"(-EFAULT), "0"(count), "1"(dst), "2"(src), "3"(count) - : "d0", "memory"); - return res; -} +#define __copy_to_user_inatomic __copy_to_user +#define __copy_from_user_inatomic __copy_from_user -/* - * Return the size of a string (including the ending 0) - * - * Return 0 on exception, a value greater than N if too long - */ -static inline long strnlen_user(const char __user *src, long n) -{ - long res; +#define copy_from_user(to, from, n) __copy_from_user(to, from, n) +#define copy_to_user(to, from, n) __copy_to_user(to, from, n) - res = -(unsigned long)src; - __asm__ __volatile__ - ("1:\n" - " tstl %2\n" - " jeq 3f\n" - "2: movesb (%1)+,%%d0\n" - "22:\n" - " subql #1,%2\n" - " tstb %%d0\n" - " jne 1b\n" - " jra 4f\n" - "3:\n" - " addql #1,%0\n" - "4:\n" - " addl %1,%0\n" - "5:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "6: moveq %3,%0\n" - " jra 5b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 2b,6b\n" - " .long 22b,6b\n" - ".previous" - : "=d"(res), "=a"(src), "=d"(n) - : "i"(0), "0"(res), "1"(src), "2"(n) - : "d0"); - return res; -} +long strncpy_from_user(char *dst, const char __user *src, long count); +long strnlen_user(const char __user *src, long n); +unsigned long clear_user(void __user *to, unsigned long n); #define strlen_user(str) strnlen_user(str, 32767) -/* - * Zero Userspace - */ - -static inline unsigned long -clear_user(void __user *to, unsigned long n) -{ - __asm__ __volatile__ - (" tstl %1\n" - " jeq 3f\n" - "1: movesl %3,(%0)+\n" - "2: subql #1,%1\n" - " jne 1b\n" - "3: movel %2,%1\n" - " bclr #1,%1\n" - " jeq 4f\n" - "24:movesw %3,(%0)+\n" - "4: bclr #0,%1\n" - " jeq 5f\n" - "25:movesb %3,(%0)+\n" - "5:\n" - ".section .fixup,\"ax\"\n" - " .even\n" - "61:addql #1,%1\n" - "6: lsll #2,%1\n" - " addl %2,%1\n" - " jra 5b\n" - "7: addql #2,%1\n" - " jra 5b\n" - "8: addql #1,%1\n" - " jra 5b\n" - ".previous\n" - ".section __ex_table,\"a\"\n" - " .align 4\n" - " .long 1b,61b\n" - " .long 2b,6b\n" - " .long 3b,61b\n" - " .long 24b,7b\n" - " .long 4b,7b\n" - " .long 25b,8b\n" - " .long 5b,8b\n" - ".previous" - : "=a"(to), "=d"(n) - : "r"(n & 3), "r"(0), "0"(to), "1"(n/4)); - return n; -} - #endif /* _M68K_UACCESS_H */ diff --git a/include/asm-m68k/unistd.h b/include/asm-m68k/unistd.h index c2554bc..7c0b629 100644 --- a/include/asm-m68k/unistd.h +++ b/include/asm-m68k/unistd.h @@ -285,6 +285,8 @@ #define __NR_add_key 279 #define __NR_request_key 280 #define __NR_keyctl 281 +#ifdef __KERNEL__ + #define NR_syscalls 282 /* user-visible error numbers are in the range -1 - -124: see @@ -383,7 +385,6 @@ __asm__ __volatile__ ("trap #0" \ __syscall_return(type,__res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -406,52 +407,12 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ -#include -#include -#include - -/* - * we need this inline - forking from kernel space will result - * in NO COPY ON WRITE (!!!), until an execve is executed. This - * is no problem, but for the stack. This is handled by not letting - * main() use the stack at all after fork(). Thus, no function - * calls - which means inline code for fork too, as otherwise we - * would use the stack upon exit from 'fork()'. - * - * Actually only pause and fork are needed inline, so that there - * won't be any messing with the stack from main(), but we define - * some others too. - */ -#define __NR__exit __NR_exit -static inline _syscall0(pid_t,setsid) -static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count) -static inline _syscall3(int,read,int,fd,char *,buf,off_t,count) -static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) -static inline _syscall1(int,dup,int,fd) static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp) -static inline _syscall3(int,open,const char *,file,int,flag,int,mode) -static inline _syscall1(int,close,int,fd) -static inline _syscall1(int,_exit,int,exitcode) -static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options) - -asmlinkage long sys_mmap2( - unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long pgoff); -asmlinkage int sys_execve(char *name, char **argv, char **envp); -asmlinkage int sys_pipe(unsigned long *fildes); -struct pt_regs; -struct sigaction; -asmlinkage long sys_rt_sigaction(int sig, - const struct sigaction __user *act, - struct sigaction __user *oact, - size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -461,4 +422,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ASM_M68K_UNISTD_H_ */ diff --git a/include/asm-m68k/virtconvert.h b/include/asm-m68k/virtconvert.h index 8c4e803..83a87c9 100644 --- a/include/asm-m68k/virtconvert.h +++ b/include/asm-m68k/virtconvert.h @@ -7,7 +7,6 @@ #define __VIRT_CONVERT__ #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-m68knommu/Kbuild b/include/asm-m68knommu/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-m68knommu/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-m68knommu/bitops.h b/include/asm-m68knommu/bitops.h index 0b68ccd..d7fa7d9 100644 --- a/include/asm-m68knommu/bitops.h +++ b/include/asm-m68knommu/bitops.h @@ -5,7 +5,6 @@ #define _M68KNOMMU_BITOPS_H * Copyright 1992, Linus Torvalds. */ -#include #include #include /* swab32 */ #include /* save_flags */ diff --git a/include/asm-m68knommu/bootstd.h b/include/asm-m68knommu/bootstd.h index 3fdc79f..bdc1a4a 100644 --- a/include/asm-m68knommu/bootstd.h +++ b/include/asm-m68knommu/bootstd.h @@ -52,7 +52,7 @@ type name(void) \ __asm__ __volatile__ ("trap #2" \ : "=g" (__res) \ : "0" (__res) \ - : "%d0"); \ + ); \ __bsc_return(type,__res); \ } @@ -64,7 +64,7 @@ type name(atype a) \ __asm__ __volatile__ ("trap #2" \ : "=g" (__res) \ : "0" (__res), "d" (__a) \ - : "%d0"); \ + ); \ __bsc_return(type,__res); \ } @@ -77,7 +77,7 @@ type name(atype a, btype b) \ __asm__ __volatile__ ("trap #2" \ : "=g" (__res) \ : "0" (__res), "d" (__a), "d" (__b) \ - : "%d0"); \ + ); \ __bsc_return(type,__res); \ } @@ -92,7 +92,7 @@ type name(atype a, btype b, ctype c) \ : "=g" (__res) \ : "0" (__res), "d" (__a), "d" (__b), \ "d" (__c) \ - : "%d0"); \ + ); \ __bsc_return(type,__res); \ } @@ -108,7 +108,7 @@ type name(atype a, btype b, ctype c, dty : "=g" (__res) \ : "0" (__res), "d" (__a), "d" (__b), \ "d" (__c), "d" (__d) \ - : "%d0"); \ + ); \ __bsc_return(type,__res); \ } @@ -125,7 +125,7 @@ type name(atype a, btype b, ctype c, dty : "=g" (__res) \ : "0" (__res), "d" (__a), "d" (__b), \ "d" (__c), "d" (__d), "d" (__e) \ - : "%d0"); \ + ); \ __bsc_return(type,__res); \ } diff --git a/include/asm-m68knommu/cacheflush.h b/include/asm-m68knommu/cacheflush.h index 49925e9..c3aadf3 100644 --- a/include/asm-m68knommu/cacheflush.h +++ b/include/asm-m68knommu/cacheflush.h @@ -57,13 +57,13 @@ #if defined(CONFIG_M527x) || defined(CON "nop\n\t" : : : "d0" ); #endif /* CONFIG_M527x || CONFIG_M528x */ -#ifdef CONFIG_M5272 +#if defined(CONFIG_M5206) || defined(CONFIG_M5206e) || defined(CONFIG_M5272) __asm__ __volatile__ ( - "movel #0x01000000, %%d0\n\t" + "movel #0x81000100, %%d0\n\t" "movec %%d0, %%CACR\n\t" "nop\n\t" : : : "d0" ); -#endif /* CONFIG_M5272 */ +#endif /* CONFIG_M5206 || CONFIG_M5206e || CONFIG_M5272 */ #ifdef CONFIG_M5249 __asm__ __volatile__ ( "movel #0xa1000200, %%d0\n\t" @@ -71,6 +71,13 @@ #ifdef CONFIG_M5249 "nop\n\t" : : : "d0" ); #endif /* CONFIG_M5249 */ +#ifdef CONFIG_M532x + __asm__ __volatile__ ( + "movel #0x81000200, %%d0\n\t" + "movec %%d0, %%CACR\n\t" + "nop\n\t" + : : : "d0" ); +#endif /* CONFIG_M532x */ } #endif /* _M68KNOMMU_CACHEFLUSH_H */ diff --git a/include/asm-m68knommu/coldfire.h b/include/asm-m68knommu/coldfire.h index 6190f77..83a9fa4 100644 --- a/include/asm-m68knommu/coldfire.h +++ b/include/asm-m68knommu/coldfire.h @@ -3,7 +3,7 @@ /* * coldfire.h -- Motorola ColdFire CPU sepecific defines * - * (C) Copyright 1999-2002, Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 1999-2006, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2000, Lineo (www.lineo.com) */ @@ -12,7 +12,19 @@ #ifndef coldfire_h #define coldfire_h /****************************************************************************/ -#include + +/* + * Define master clock frequency. This is essentially done at config + * time now. No point enumerating dozens of possible clock options + * here. Also the peripheral clock (bus clock) divide ratio is set + * at config time too. + */ +#ifdef CONFIG_CLOCK_SET +#define MCF_CLK CONFIG_CLOCK_FREQ +#define MCF_BUSCLK (CONFIG_CLOCK_FREQ / CONFIG_CLOCK_DIV) +#else +#error "Don't know what your ColdFire CPU clock frequency is??" +#endif /* * Define the processor support peripherals base address. @@ -30,64 +42,9 @@ #if defined(CONFIG_M523x) || defined(CON defined(CONFIG_M520x) #undef MCF_MBAR #define MCF_MBAR MCF_IPSBAR -#endif - -/* - * Define master clock frequency. - */ -#if defined(CONFIG_CLOCK_11MHz) -#define MCF_CLK 11289600 -#elif defined(CONFIG_CLOCK_16MHz) -#define MCF_CLK 16000000 -#elif defined(CONFIG_CLOCK_20MHz) -#define MCF_CLK 20000000 -#elif defined(CONFIG_CLOCK_24MHz) -#define MCF_CLK 24000000 -#elif defined(CONFIG_CLOCK_25MHz) -#define MCF_CLK 25000000 -#elif defined(CONFIG_CLOCK_33MHz) -#define MCF_CLK 33000000 -#elif defined(CONFIG_CLOCK_40MHz) -#define MCF_CLK 40000000 -#elif defined(CONFIG_CLOCK_45MHz) -#define MCF_CLK 45000000 -#elif defined(CONFIG_CLOCK_48MHz) -#define MCF_CLK 48000000 -#elif defined(CONFIG_CLOCK_50MHz) -#define MCF_CLK 50000000 -#elif defined(CONFIG_CLOCK_54MHz) -#define MCF_CLK 54000000 -#elif defined(CONFIG_CLOCK_60MHz) -#define MCF_CLK 60000000 -#elif defined(CONFIG_CLOCK_62_5MHz) -#define MCF_CLK 62500000 -#elif defined(CONFIG_CLOCK_64MHz) -#define MCF_CLK 64000000 -#elif defined(CONFIG_CLOCK_66MHz) -#define MCF_CLK 66000000 -#elif defined(CONFIG_CLOCK_70MHz) -#define MCF_CLK 70000000 -#elif defined(CONFIG_CLOCK_100MHz) -#define MCF_CLK 100000000 -#elif defined(CONFIG_CLOCK_140MHz) -#define MCF_CLK 140000000 -#elif defined(CONFIG_CLOCK_150MHz) -#define MCF_CLK 150000000 -#elif defined(CONFIG_CLOCK_166MHz) -#define MCF_CLK 166000000 -#else -#error "Don't know what your ColdFire CPU clock frequency is??" -#endif - -/* - * One some ColdFire family members the bus clock (used by internal - * peripherals) is not the same as the CPU clock. - */ -#if defined(CONFIG_M523x) || defined(CONFIG_M5249) || defined(CONFIG_M527x) || \ - defined(CONFIG_M520x) -#define MCF_BUSCLK (MCF_CLK / 2) -#else -#define MCF_BUSCLK MCF_CLK +#elif defined(CONFIG_M532x) +#undef MCF_MBAR +#define MCF_MBAR 0x00000000 #endif /****************************************************************************/ diff --git a/include/asm-m68knommu/commproc.h b/include/asm-m68knommu/commproc.h index e522ca8..0161ebb 100644 --- a/include/asm-m68knommu/commproc.h +++ b/include/asm-m68knommu/commproc.h @@ -17,7 +17,6 @@ #ifndef __CPM_360__ #define __CPM_360__ -#include /* CPM Command register masks: */ #define CPM_CR_RST ((ushort)0x8000) diff --git a/include/asm-m68knommu/dma-mapping.h b/include/asm-m68knommu/dma-mapping.h index a6c42ba..5622b85 100644 --- a/include/asm-m68knommu/dma-mapping.h +++ b/include/asm-m68knommu/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef _M68KNOMMU_DMA_MAPPING_H #define _M68KNOMMU_DMA_MAPPING_H -#include #ifdef CONFIG_PCI #include diff --git a/include/asm-m68knommu/dma.h b/include/asm-m68knommu/dma.h index 43e98c9..3338001 100644 --- a/include/asm-m68knommu/dma.h +++ b/include/asm-m68knommu/dma.h @@ -3,7 +3,6 @@ #define _M68K_DMA_H 1 //#define DMA_DEBUG 1 -#include #ifdef CONFIG_COLDFIRE /* diff --git a/include/asm-m68knommu/elf.h b/include/asm-m68knommu/elf.h index 9919487..40b1ed6 100644 --- a/include/asm-m68knommu/elf.h +++ b/include/asm-m68knommu/elf.h @@ -5,7 +5,6 @@ #define __ASMm68k_ELF_H * ELF register definitions.. */ -#include #include #include diff --git a/include/asm-m68knommu/elia.h b/include/asm-m68knommu/elia.h index f18b8e9..e037d4e 100644 --- a/include/asm-m68knommu/elia.h +++ b/include/asm-m68knommu/elia.h @@ -12,7 +12,6 @@ #ifndef elia_h #define elia_h /****************************************************************************/ -#include #include #ifdef CONFIG_eLIA diff --git a/include/asm-m68knommu/entry.h b/include/asm-m68knommu/entry.h index 06f5aa7..c2553d2 100644 --- a/include/asm-m68knommu/entry.h +++ b/include/asm-m68knommu/entry.h @@ -1,7 +1,6 @@ #ifndef __M68KNOMMU_ENTRY_H #define __M68KNOMMU_ENTRY_H -#include #include #include diff --git a/include/asm-m68knommu/fpu.h b/include/asm-m68knommu/fpu.h index 2250829..b16b2e4 100644 --- a/include/asm-m68knommu/fpu.h +++ b/include/asm-m68knommu/fpu.h @@ -1,7 +1,6 @@ #ifndef __M68KNOMMU_FPU_H #define __M68KNOMMU_FPU_H -#include /* * MAX floating point unit state size (FSAVE/FRESTORE) diff --git a/include/asm-m68knommu/hardirq.h b/include/asm-m68knommu/hardirq.h index 476180f..980075b 100644 --- a/include/asm-m68knommu/hardirq.h +++ b/include/asm-m68knommu/hardirq.h @@ -1,7 +1,6 @@ #ifndef __M68K_HARDIRQ_H #define __M68K_HARDIRQ_H -#include #include #include #include diff --git a/include/asm-m68knommu/io.h b/include/asm-m68knommu/io.h index e08f2ee..8df4cee 100644 --- a/include/asm-m68knommu/io.h +++ b/include/asm-m68knommu/io.h @@ -3,7 +3,6 @@ #define _M68KNOMMU_IO_H #ifdef __KERNEL__ -#include /* * These are for ISA/PCI shared memory _only_ and should never be used diff --git a/include/asm-m68knommu/irq.h b/include/asm-m68knommu/irq.h index 20c48ec..45e7a2f 100644 --- a/include/asm-m68knommu/irq.h +++ b/include/asm-m68knommu/irq.h @@ -1,7 +1,6 @@ #ifndef _M68K_IRQ_H_ #define _M68K_IRQ_H_ -#include #include #ifdef CONFIG_COLDFIRE @@ -63,8 +62,8 @@ extern void (*mach_disable_irq)(unsigned /* * various flags for request_irq() - the Amiga now uses the standard - * mechanism like all other architectures - SA_INTERRUPT and SA_SHIRQ - * are your friends. + * mechanism like all other architectures - IRQF_DISABLED and + * IRQF_SHARED are your friends. */ #define IRQ_FLG_LOCK (0x0001) /* handler is not replaceable */ #define IRQ_FLG_REPLACE (0x0002) /* replace existing handler */ @@ -84,12 +83,8 @@ #endif /* CONFIG_M68360 */ /* * Some drivers want these entry points */ -#define enable_irq(x) 0 +#define enable_irq(x) do { } while (0) #define disable_irq(x) do { } while (0) #define disable_irq_nosync(x) disable_irq(x) -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif /* _M68K_IRQ_H_ */ diff --git a/include/asm-m68knommu/m5206sim.h b/include/asm-m68knommu/m5206sim.h index d1e7509..7e3594d 100644 --- a/include/asm-m68knommu/m5206sim.h +++ b/include/asm-m68knommu/m5206sim.h @@ -12,7 +12,6 @@ #ifndef m5206sim_h #define m5206sim_h /****************************************************************************/ -#include /* * Define the 5206 SIM register set addresses. diff --git a/include/asm-m68knommu/m520xsim.h b/include/asm-m68knommu/m520xsim.h index 6dc6286..1dac22e 100644 --- a/include/asm-m68knommu/m520xsim.h +++ b/include/asm-m68knommu/m520xsim.h @@ -11,7 +11,6 @@ #ifndef m520xsim_h #define m520xsim_h /****************************************************************************/ -#include /* * Define the 5282 SIM register set addresses. diff --git a/include/asm-m68knommu/m523xsim.h b/include/asm-m68knommu/m523xsim.h index 926cfb8..bf39731 100644 --- a/include/asm-m68knommu/m523xsim.h +++ b/include/asm-m68knommu/m523xsim.h @@ -11,7 +11,6 @@ #ifndef m523xsim_h #define m523xsim_h /****************************************************************************/ -#include /* * Define the 523x SIM register set addresses. diff --git a/include/asm-m68knommu/m5249sim.h b/include/asm-m68knommu/m5249sim.h index 9344f52..399814f 100644 --- a/include/asm-m68knommu/m5249sim.h +++ b/include/asm-m68knommu/m5249sim.h @@ -157,7 +157,7 @@ #ifdef __ASSEMBLER__ movel %d0,0x180(%a1) /* set PLL register */ nop -#ifdef CONFIG_CLOCK_140MHz +#if CONFIG_CLOCK_FREQ == 140000000 /* * Set initial clock frequency. This assumes M5249C3 board * is fitted with 11.2896MHz crystal. It will program the diff --git a/include/asm-m68knommu/m5272sim.h b/include/asm-m68knommu/m5272sim.h index b408753..6217edc 100644 --- a/include/asm-m68knommu/m5272sim.h +++ b/include/asm-m68knommu/m5272sim.h @@ -12,7 +12,6 @@ #ifndef m5272sim_h #define m5272sim_h /****************************************************************************/ -#include /* * Define the 5272 SIM register set addresses. diff --git a/include/asm-m68knommu/m527xsim.h b/include/asm-m68knommu/m527xsim.h index e7878d0..1f63ab3 100644 --- a/include/asm-m68knommu/m527xsim.h +++ b/include/asm-m68knommu/m527xsim.h @@ -11,7 +11,6 @@ #ifndef m527xsim_h #define m527xsim_h /****************************************************************************/ -#include /* * Define the 5270/5271 SIM register set addresses. diff --git a/include/asm-m68knommu/m528xsim.h b/include/asm-m68knommu/m528xsim.h index 610774a..1a3b1ae 100644 --- a/include/asm-m68knommu/m528xsim.h +++ b/include/asm-m68knommu/m528xsim.h @@ -11,7 +11,6 @@ #ifndef m528xsim_h #define m528xsim_h /****************************************************************************/ -#include /* * Define the 5280/5282 SIM register set addresses. diff --git a/include/asm-m68knommu/m532xsim.h b/include/asm-m68knommu/m532xsim.h new file mode 100644 index 0000000..1835fd2 --- /dev/null +++ b/include/asm-m68knommu/m532xsim.h @@ -0,0 +1,2238 @@ +/****************************************************************************/ + +/* + * m532xsim.h -- ColdFire 5329 registers + */ + +/****************************************************************************/ +#ifndef m532xsim_h +#define m532xsim_h +/****************************************************************************/ + +#define MCF_REG32(x) (*(volatile unsigned long *)(x)) +#define MCF_REG16(x) (*(volatile unsigned short *)(x)) +#define MCF_REG08(x) (*(volatile unsigned char *)(x)) + +#define MCFINT_VECBASE 64 +#define MCFINT_UART0 26 /* Interrupt number for UART0 */ +#define MCFINT_UART1 27 /* Interrupt number for UART1 */ + +#define MCF_WTM_WCR MCF_REG16(0xFC098000) + +/* + * Define the 532x SIM register set addresses. + */ +#define MCFSIM_IPRL 0xFC048004 +#define MCFSIM_IPRH 0xFC048000 +#define MCFSIM_IPR MCFSIM_IPRL +#define MCFSIM_IMRL 0xFC04800C +#define MCFSIM_IMRH 0xFC048008 +#define MCFSIM_IMR MCFSIM_IMRL +#define MCFSIM_ICR0 0xFC048040 +#define MCFSIM_ICR1 0xFC048041 +#define MCFSIM_ICR2 0xFC048042 +#define MCFSIM_ICR3 0xFC048043 +#define MCFSIM_ICR4 0xFC048044 +#define MCFSIM_ICR5 0xFC048045 +#define MCFSIM_ICR6 0xFC048046 +#define MCFSIM_ICR7 0xFC048047 +#define MCFSIM_ICR8 0xFC048048 +#define MCFSIM_ICR9 0xFC048049 +#define MCFSIM_ICR10 0xFC04804A +#define MCFSIM_ICR11 0xFC04804B + +/* + * Some symbol defines for the above... + */ +#define MCFSIM_SWDICR MCFSIM_ICR0 /* Watchdog timer ICR */ +#define MCFSIM_TIMER1ICR MCFSIM_ICR1 /* Timer 1 ICR */ +#define MCFSIM_TIMER2ICR MCFSIM_ICR2 /* Timer 2 ICR */ +#define MCFSIM_UART1ICR MCFSIM_ICR4 /* UART 1 ICR */ +#define MCFSIM_UART2ICR MCFSIM_ICR5 /* UART 2 ICR */ +#define MCFSIM_DMA0ICR MCFSIM_ICR6 /* DMA 0 ICR */ +#define MCFSIM_DMA1ICR MCFSIM_ICR7 /* DMA 1 ICR */ +#define MCFSIM_DMA2ICR MCFSIM_ICR8 /* DMA 2 ICR */ +#define MCFSIM_DMA3ICR MCFSIM_ICR9 /* DMA 3 ICR */ + + +#define MCFSIM_IMR_MASKALL 0xFFFFFFFF /* All SIM intr sources */ + +#define MCFSIM_IMR_SIMR0 0xFC04801C +#define MCFSIM_IMR_SIMR1 0xFC04C01C +#define MCFSIM_IMR_CIMR0 0xFC04801D +#define MCFSIM_IMR_CIMR1 0xFC04C01D + +#define MCFSIM_ICR_TIMER1 (0xFC048040+32) +#define MCFSIM_ICR_TIMER2 (0xFC048040+33) + + +/* + * Macro to set IMR register. It is 32 bits on the 5307. + */ +#define mcf_getimr() \ + *((volatile unsigned long *) (MCF_MBAR + MCFSIM_IMR)) + +#define mcf_setimr(imr) \ + *((volatile unsigned long *) (MCF_MBAR + MCFSIM_IMR)) = (imr); + +#define mcf_getipr() \ + *((volatile unsigned long *) (MCF_MBAR + MCFSIM_IPR)) + +#define mcf_getiprl() \ + *((volatile unsigned long *) (MCF_MBAR + MCFSIM_IPRL)) + +#define mcf_getiprh() \ + *((volatile unsigned long *) (MCF_MBAR + MCFSIM_IPRH)) + + +#define mcf_enable_irq0(irq) \ + *((volatile unsigned char*) (MCFSIM_IMR_CIMR0)) = (irq); + +#define mcf_enable_irq1(irq) \ + *((volatile unsigned char*) (MCFSIM_IMR_CIMR1)) = (irq); + +#define mcf_disable_irq0(irq) \ + *((volatile unsigned char*) (MCFSIM_IMR_SIMR0)) = (irq); + +#define mcf_disable_irq1(irq) \ + *((volatile unsigned char*) (MCFSIM_IMR_SIMR1)) = (irq); + +/* + * Define the Cache register flags. + */ +#define CACR_EC (1<<31) +#define CACR_ESB (1<<29) +#define CACR_DPI (1<<28) +#define CACR_HLCK (1<<27) +#define CACR_CINVA (1<<24) +#define CACR_DNFB (1<<10) +#define CACR_DCM_WTHRU (0<<8) +#define CACR_DCM_WBACK (1<<8) +#define CACR_DCM_OFF_PRE (2<<8) +#define CACR_DCM_OFF_IMP (3<<8) +#define CACR_DW (1<<5) + +#define ACR_BASE_POS 24 +#define ACR_MASK_POS 16 +#define ACR_ENABLE (1<<15) +#define ACR_USER (0<<13) +#define ACR_SUPER (1<<13) +#define ACR_ANY (2<<13) +#define ACR_CM_WTHRU (0<<5) +#define ACR_CM_WBACK (1<<5) +#define ACR_CM_OFF_PRE (2<<5) +#define ACR_CM_OFF_IMP (3<<5) +#define ACR_WPROTECT (1<<2) + +/********************************************************************* + * + * Inter-IC (I2C) Module + * + *********************************************************************/ + +/* Read/Write access macros for general use */ +#define MCF532x_I2C_I2ADR (volatile u8 *) (0xFC058000) // Address +#define MCF532x_I2C_I2FDR (volatile u8 *) (0xFC058004) // Freq Divider +#define MCF532x_I2C_I2CR (volatile u8 *) (0xFC058008) // Control +#define MCF532x_I2C_I2SR (volatile u8 *) (0xFC05800C) // Status +#define MCF532x_I2C_I2DR (volatile u8 *) (0xFC058010) // Data I/O + +/* Bit level definitions and macros */ +#define MCF532x_I2C_I2ADR_ADDR(x) (((x)&0x7F)<<0x01) + +#define MCF532x_I2C_I2FDR_IC(x) (((x)&0x3F)) + +#define MCF532x_I2C_I2CR_IEN (0x80) // I2C enable +#define MCF532x_I2C_I2CR_IIEN (0x40) // interrupt enable +#define MCF532x_I2C_I2CR_MSTA (0x20) // master/slave mode +#define MCF532x_I2C_I2CR_MTX (0x10) // transmit/receive mode +#define MCF532x_I2C_I2CR_TXAK (0x08) // transmit acknowledge enable +#define MCF532x_I2C_I2CR_RSTA (0x04) // repeat start + +#define MCF532x_I2C_I2SR_ICF (0x80) // data transfer bit +#define MCF532x_I2C_I2SR_IAAS (0x40) // I2C addressed as a slave +#define MCF532x_I2C_I2SR_IBB (0x20) // I2C bus busy +#define MCF532x_I2C_I2SR_IAL (0x10) // aribitration lost +#define MCF532x_I2C_I2SR_SRW (0x04) // slave read/write +#define MCF532x_I2C_I2SR_IIF (0x02) // I2C interrupt +#define MCF532x_I2C_I2SR_RXAK (0x01) // received acknowledge + +#define MCF532x_PAR_FECI2C (volatile u8 *) (0xFC0A4053) + + +/* + * The M5329EVB board needs a help getting its devices initialized + * at kernel start time if dBUG doesn't set it up (for example + * it is not used), so we need to do it manually. + */ +#ifdef __ASSEMBLER__ +.macro m5329EVB_setup + movel #0xFC098000, %a7 + movel #0x0, (%a7) +#define CORE_SRAM 0x80000000 +#define CORE_SRAM_SIZE 0x8000 + movel #CORE_SRAM, %d0 + addl #0x221, %d0 + movec %d0,%RAMBAR1 + movel #CORE_SRAM, %sp + addl #CORE_SRAM_SIZE, %sp + jsr sysinit +.endm +#define PLATFORM_SETUP m5329EVB_setup + +#endif /* __ASSEMBLER__ */ + +/********************************************************************* + * + * Chip Configuration Module (CCM) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_CCM_CCR MCF_REG16(0xFC0A0004) +#define MCF_CCM_RCON MCF_REG16(0xFC0A0008) +#define MCF_CCM_CIR MCF_REG16(0xFC0A000A) +#define MCF_CCM_MISCCR MCF_REG16(0xFC0A0010) +#define MCF_CCM_CDR MCF_REG16(0xFC0A0012) +#define MCF_CCM_UHCSR MCF_REG16(0xFC0A0014) +#define MCF_CCM_UOCSR MCF_REG16(0xFC0A0016) + +/* Bit definitions and macros for MCF_CCM_CCR */ +#define MCF_CCM_CCR_RESERVED (0x0001) +#define MCF_CCM_CCR_PLL_MODE (0x0003) +#define MCF_CCM_CCR_OSC_MODE (0x0005) +#define MCF_CCM_CCR_BOOTPS(x) (((x)&0x0003)<<3|0x0001) +#define MCF_CCM_CCR_LOAD (0x0021) +#define MCF_CCM_CCR_LIMP (0x0041) +#define MCF_CCM_CCR_CSC(x) (((x)&0x0003)<<8|0x0001) + +/* Bit definitions and macros for MCF_CCM_RCON */ +#define MCF_CCM_RCON_RESERVED (0x0001) +#define MCF_CCM_RCON_PLL_MODE (0x0003) +#define MCF_CCM_RCON_OSC_MODE (0x0005) +#define MCF_CCM_RCON_BOOTPS(x) (((x)&0x0003)<<3|0x0001) +#define MCF_CCM_RCON_LOAD (0x0021) +#define MCF_CCM_RCON_LIMP (0x0041) +#define MCF_CCM_RCON_CSC(x) (((x)&0x0003)<<8|0x0001) + +/* Bit definitions and macros for MCF_CCM_CIR */ +#define MCF_CCM_CIR_PRN(x) (((x)&0x003F)<<0) +#define MCF_CCM_CIR_PIN(x) (((x)&0x03FF)<<6) + +/* Bit definitions and macros for MCF_CCM_MISCCR */ +#define MCF_CCM_MISCCR_USBSRC (0x0001) +#define MCF_CCM_MISCCR_USBDIV (0x0002) +#define MCF_CCM_MISCCR_SSI_SRC (0x0010) +#define MCF_CCM_MISCCR_TIM_DMA (0x0020) +#define MCF_CCM_MISCCR_SSI_PUS (0x0040) +#define MCF_CCM_MISCCR_SSI_PUE (0x0080) +#define MCF_CCM_MISCCR_LCD_CHEN (0x0100) +#define MCF_CCM_MISCCR_LIMP (0x1000) +#define MCF_CCM_MISCCR_PLL_LOCK (0x2000) + +/* Bit definitions and macros for MCF_CCM_CDR */ +#define MCF_CCM_CDR_SSIDIV(x) (((x)&0x000F)<<0) +#define MCF_CCM_CDR_LPDIV(x) (((x)&0x000F)<<8) + +/* Bit definitions and macros for MCF_CCM_UHCSR */ +#define MCF_CCM_UHCSR_XPDE (0x0001) +#define MCF_CCM_UHCSR_UHMIE (0x0002) +#define MCF_CCM_UHCSR_WKUP (0x0004) +#define MCF_CCM_UHCSR_PORTIND(x) (((x)&0x0003)<<14) + +/* Bit definitions and macros for MCF_CCM_UOCSR */ +#define MCF_CCM_UOCSR_XPDE (0x0001) +#define MCF_CCM_UOCSR_UOMIE (0x0002) +#define MCF_CCM_UOCSR_WKUP (0x0004) +#define MCF_CCM_UOCSR_PWRFLT (0x0008) +#define MCF_CCM_UOCSR_SEND (0x0010) +#define MCF_CCM_UOCSR_VVLD (0x0020) +#define MCF_CCM_UOCSR_BVLD (0x0040) +#define MCF_CCM_UOCSR_AVLD (0x0080) +#define MCF_CCM_UOCSR_DPPU (0x0100) +#define MCF_CCM_UOCSR_DCR_VBUS (0x0200) +#define MCF_CCM_UOCSR_CRG_VBUS (0x0400) +#define MCF_CCM_UOCSR_DRV_VBUS (0x0800) +#define MCF_CCM_UOCSR_DMPD (0x1000) +#define MCF_CCM_UOCSR_DPPD (0x2000) +#define MCF_CCM_UOCSR_PORTIND(x) (((x)&0x0003)<<14) + +/********************************************************************* + * + * DMA Timers (DTIM) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_DTIM0_DTMR MCF_REG16(0xFC070000) +#define MCF_DTIM0_DTXMR MCF_REG08(0xFC070002) +#define MCF_DTIM0_DTER MCF_REG08(0xFC070003) +#define MCF_DTIM0_DTRR MCF_REG32(0xFC070004) +#define MCF_DTIM0_DTCR MCF_REG32(0xFC070008) +#define MCF_DTIM0_DTCN MCF_REG32(0xFC07000C) +#define MCF_DTIM1_DTMR MCF_REG16(0xFC074000) +#define MCF_DTIM1_DTXMR MCF_REG08(0xFC074002) +#define MCF_DTIM1_DTER MCF_REG08(0xFC074003) +#define MCF_DTIM1_DTRR MCF_REG32(0xFC074004) +#define MCF_DTIM1_DTCR MCF_REG32(0xFC074008) +#define MCF_DTIM1_DTCN MCF_REG32(0xFC07400C) +#define MCF_DTIM2_DTMR MCF_REG16(0xFC078000) +#define MCF_DTIM2_DTXMR MCF_REG08(0xFC078002) +#define MCF_DTIM2_DTER MCF_REG08(0xFC078003) +#define MCF_DTIM2_DTRR MCF_REG32(0xFC078004) +#define MCF_DTIM2_DTCR MCF_REG32(0xFC078008) +#define MCF_DTIM2_DTCN MCF_REG32(0xFC07800C) +#define MCF_DTIM3_DTMR MCF_REG16(0xFC07C000) +#define MCF_DTIM3_DTXMR MCF_REG08(0xFC07C002) +#define MCF_DTIM3_DTER MCF_REG08(0xFC07C003) +#define MCF_DTIM3_DTRR MCF_REG32(0xFC07C004) +#define MCF_DTIM3_DTCR MCF_REG32(0xFC07C008) +#define MCF_DTIM3_DTCN MCF_REG32(0xFC07C00C) +#define MCF_DTIM_DTMR(x) MCF_REG16(0xFC070000+((x)*0x4000)) +#define MCF_DTIM_DTXMR(x) MCF_REG08(0xFC070002+((x)*0x4000)) +#define MCF_DTIM_DTER(x) MCF_REG08(0xFC070003+((x)*0x4000)) +#define MCF_DTIM_DTRR(x) MCF_REG32(0xFC070004+((x)*0x4000)) +#define MCF_DTIM_DTCR(x) MCF_REG32(0xFC070008+((x)*0x4000)) +#define MCF_DTIM_DTCN(x) MCF_REG32(0xFC07000C+((x)*0x4000)) + +/* Bit definitions and macros for MCF_DTIM_DTMR */ +#define MCF_DTIM_DTMR_RST (0x0001) +#define MCF_DTIM_DTMR_CLK(x) (((x)&0x0003)<<1) +#define MCF_DTIM_DTMR_FRR (0x0008) +#define MCF_DTIM_DTMR_ORRI (0x0010) +#define MCF_DTIM_DTMR_OM (0x0020) +#define MCF_DTIM_DTMR_CE(x) (((x)&0x0003)<<6) +#define MCF_DTIM_DTMR_PS(x) (((x)&0x00FF)<<8) +#define MCF_DTIM_DTMR_CE_ANY (0x00C0) +#define MCF_DTIM_DTMR_CE_FALL (0x0080) +#define MCF_DTIM_DTMR_CE_RISE (0x0040) +#define MCF_DTIM_DTMR_CE_NONE (0x0000) +#define MCF_DTIM_DTMR_CLK_DTIN (0x0006) +#define MCF_DTIM_DTMR_CLK_DIV16 (0x0004) +#define MCF_DTIM_DTMR_CLK_DIV1 (0x0002) +#define MCF_DTIM_DTMR_CLK_STOP (0x0000) + +/* Bit definitions and macros for MCF_DTIM_DTXMR */ +#define MCF_DTIM_DTXMR_MODE16 (0x01) +#define MCF_DTIM_DTXMR_DMAEN (0x80) + +/* Bit definitions and macros for MCF_DTIM_DTER */ +#define MCF_DTIM_DTER_CAP (0x01) +#define MCF_DTIM_DTER_REF (0x02) + +/* Bit definitions and macros for MCF_DTIM_DTRR */ +#define MCF_DTIM_DTRR_REF(x) (((x)&0xFFFFFFFF)<<0) + +/* Bit definitions and macros for MCF_DTIM_DTCR */ +#define MCF_DTIM_DTCR_CAP(x) (((x)&0xFFFFFFFF)<<0) + +/* Bit definitions and macros for MCF_DTIM_DTCN */ +#define MCF_DTIM_DTCN_CNT(x) (((x)&0xFFFFFFFF)<<0) + +/********************************************************************* + * + * FlexBus Chip Selects (FBCS) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_FBCS0_CSAR MCF_REG32(0xFC008000) +#define MCF_FBCS0_CSMR MCF_REG32(0xFC008004) +#define MCF_FBCS0_CSCR MCF_REG32(0xFC008008) +#define MCF_FBCS1_CSAR MCF_REG32(0xFC00800C) +#define MCF_FBCS1_CSMR MCF_REG32(0xFC008010) +#define MCF_FBCS1_CSCR MCF_REG32(0xFC008014) +#define MCF_FBCS2_CSAR MCF_REG32(0xFC008018) +#define MCF_FBCS2_CSMR MCF_REG32(0xFC00801C) +#define MCF_FBCS2_CSCR MCF_REG32(0xFC008020) +#define MCF_FBCS3_CSAR MCF_REG32(0xFC008024) +#define MCF_FBCS3_CSMR MCF_REG32(0xFC008028) +#define MCF_FBCS3_CSCR MCF_REG32(0xFC00802C) +#define MCF_FBCS4_CSAR MCF_REG32(0xFC008030) +#define MCF_FBCS4_CSMR MCF_REG32(0xFC008034) +#define MCF_FBCS4_CSCR MCF_REG32(0xFC008038) +#define MCF_FBCS5_CSAR MCF_REG32(0xFC00803C) +#define MCF_FBCS5_CSMR MCF_REG32(0xFC008040) +#define MCF_FBCS5_CSCR MCF_REG32(0xFC008044) +#define MCF_FBCS_CSAR(x) MCF_REG32(0xFC008000+((x)*0x00C)) +#define MCF_FBCS_CSMR(x) MCF_REG32(0xFC008004+((x)*0x00C)) +#define MCF_FBCS_CSCR(x) MCF_REG32(0xFC008008+((x)*0x00C)) + +/* Bit definitions and macros for MCF_FBCS_CSAR */ +#define MCF_FBCS_CSAR_BA(x) ((x)&0xFFFF0000) + +/* Bit definitions and macros for MCF_FBCS_CSMR */ +#define MCF_FBCS_CSMR_V (0x00000001) +#define MCF_FBCS_CSMR_WP (0x00000100) +#define MCF_FBCS_CSMR_BAM(x) (((x)&0x0000FFFF)<<16) +#define MCF_FBCS_CSMR_BAM_4G (0xFFFF0000) +#define MCF_FBCS_CSMR_BAM_2G (0x7FFF0000) +#define MCF_FBCS_CSMR_BAM_1G (0x3FFF0000) +#define MCF_FBCS_CSMR_BAM_1024M (0x3FFF0000) +#define MCF_FBCS_CSMR_BAM_512M (0x1FFF0000) +#define MCF_FBCS_CSMR_BAM_256M (0x0FFF0000) +#define MCF_FBCS_CSMR_BAM_128M (0x07FF0000) +#define MCF_FBCS_CSMR_BAM_64M (0x03FF0000) +#define MCF_FBCS_CSMR_BAM_32M (0x01FF0000) +#define MCF_FBCS_CSMR_BAM_16M (0x00FF0000) +#define MCF_FBCS_CSMR_BAM_8M (0x007F0000) +#define MCF_FBCS_CSMR_BAM_4M (0x003F0000) +#define MCF_FBCS_CSMR_BAM_2M (0x001F0000) +#define MCF_FBCS_CSMR_BAM_1M (0x000F0000) +#define MCF_FBCS_CSMR_BAM_1024K (0x000F0000) +#define MCF_FBCS_CSMR_BAM_512K (0x00070000) +#define MCF_FBCS_CSMR_BAM_256K (0x00030000) +#define MCF_FBCS_CSMR_BAM_128K (0x00010000) +#define MCF_FBCS_CSMR_BAM_64K (0x00000000) + +/* Bit definitions and macros for MCF_FBCS_CSCR */ +#define MCF_FBCS_CSCR_BSTW (0x00000008) +#define MCF_FBCS_CSCR_BSTR (0x00000010) +#define MCF_FBCS_CSCR_BEM (0x00000020) +#define MCF_FBCS_CSCR_PS(x) (((x)&0x00000003)<<6) +#define MCF_FBCS_CSCR_AA (0x00000100) +#define MCF_FBCS_CSCR_SBM (0x00000200) +#define MCF_FBCS_CSCR_WS(x) (((x)&0x0000003F)<<10) +#define MCF_FBCS_CSCR_WRAH(x) (((x)&0x00000003)<<16) +#define MCF_FBCS_CSCR_RDAH(x) (((x)&0x00000003)<<18) +#define MCF_FBCS_CSCR_ASET(x) (((x)&0x00000003)<<20) +#define MCF_FBCS_CSCR_SWSEN (0x00800000) +#define MCF_FBCS_CSCR_SWS(x) (((x)&0x0000003F)<<26) +#define MCF_FBCS_CSCR_PS_8 (0x0040) +#define MCF_FBCS_CSCR_PS_16 (0x0080) +#define MCF_FBCS_CSCR_PS_32 (0x0000) + +/********************************************************************* + * + * General Purpose I/O (GPIO) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_GPIO_PODR_FECH MCF_REG08(0xFC0A4000) +#define MCF_GPIO_PODR_FECL MCF_REG08(0xFC0A4001) +#define MCF_GPIO_PODR_SSI MCF_REG08(0xFC0A4002) +#define MCF_GPIO_PODR_BUSCTL MCF_REG08(0xFC0A4003) +#define MCF_GPIO_PODR_BE MCF_REG08(0xFC0A4004) +#define MCF_GPIO_PODR_CS MCF_REG08(0xFC0A4005) +#define MCF_GPIO_PODR_PWM MCF_REG08(0xFC0A4006) +#define MCF_GPIO_PODR_FECI2C MCF_REG08(0xFC0A4007) +#define MCF_GPIO_PODR_UART MCF_REG08(0xFC0A4009) +#define MCF_GPIO_PODR_QSPI MCF_REG08(0xFC0A400A) +#define MCF_GPIO_PODR_TIMER MCF_REG08(0xFC0A400B) +#define MCF_GPIO_PODR_LCDDATAH MCF_REG08(0xFC0A400D) +#define MCF_GPIO_PODR_LCDDATAM MCF_REG08(0xFC0A400E) +#define MCF_GPIO_PODR_LCDDATAL MCF_REG08(0xFC0A400F) +#define MCF_GPIO_PODR_LCDCTLH MCF_REG08(0xFC0A4010) +#define MCF_GPIO_PODR_LCDCTLL MCF_REG08(0xFC0A4011) +#define MCF_GPIO_PDDR_FECH MCF_REG08(0xFC0A4014) +#define MCF_GPIO_PDDR_FECL MCF_REG08(0xFC0A4015) +#define MCF_GPIO_PDDR_SSI MCF_REG08(0xFC0A4016) +#define MCF_GPIO_PDDR_BUSCTL MCF_REG08(0xFC0A4017) +#define MCF_GPIO_PDDR_BE MCF_REG08(0xFC0A4018) +#define MCF_GPIO_PDDR_CS MCF_REG08(0xFC0A4019) +#define MCF_GPIO_PDDR_PWM MCF_REG08(0xFC0A401A) +#define MCF_GPIO_PDDR_FECI2C MCF_REG08(0xFC0A401B) +#define MCF_GPIO_PDDR_UART MCF_REG08(0xFC0A401C) +#define MCF_GPIO_PDDR_QSPI MCF_REG08(0xFC0A401E) +#define MCF_GPIO_PDDR_TIMER MCF_REG08(0xFC0A401F) +#define MCF_GPIO_PDDR_LCDDATAH MCF_REG08(0xFC0A4021) +#define MCF_GPIO_PDDR_LCDDATAM MCF_REG08(0xFC0A4022) +#define MCF_GPIO_PDDR_LCDDATAL MCF_REG08(0xFC0A4023) +#define MCF_GPIO_PDDR_LCDCTLH MCF_REG08(0xFC0A4024) +#define MCF_GPIO_PDDR_LCDCTLL MCF_REG08(0xFC0A4025) +#define MCF_GPIO_PPDSDR_FECH MCF_REG08(0xFC0A4028) +#define MCF_GPIO_PPDSDR_FECL MCF_REG08(0xFC0A4029) +#define MCF_GPIO_PPDSDR_SSI MCF_REG08(0xFC0A402A) +#define MCF_GPIO_PPDSDR_BUSCTL MCF_REG08(0xFC0A402B) +#define MCF_GPIO_PPDSDR_BE MCF_REG08(0xFC0A402C) +#define MCF_GPIO_PPDSDR_CS MCF_REG08(0xFC0A402D) +#define MCF_GPIO_PPDSDR_PWM MCF_REG08(0xFC0A402E) +#define MCF_GPIO_PPDSDR_FECI2C MCF_REG08(0xFC0A402F) +#define MCF_GPIO_PPDSDR_UART MCF_REG08(0xFC0A4031) +#define MCF_GPIO_PPDSDR_QSPI MCF_REG08(0xFC0A4032) +#define MCF_GPIO_PPDSDR_TIMER MCF_REG08(0xFC0A4033) +#define MCF_GPIO_PPDSDR_LCDDATAH MCF_REG08(0xFC0A4035) +#define MCF_GPIO_PPDSDR_LCDDATAM MCF_REG08(0xFC0A4036) +#define MCF_GPIO_PPDSDR_LCDDATAL MCF_REG08(0xFC0A4037) +#define MCF_GPIO_PPDSDR_LCDCTLH MCF_REG08(0xFC0A4038) +#define MCF_GPIO_PPDSDR_LCDCTLL MCF_REG08(0xFC0A4039) +#define MCF_GPIO_PCLRR_FECH MCF_REG08(0xFC0A403C) +#define MCF_GPIO_PCLRR_FECL MCF_REG08(0xFC0A403D) +#define MCF_GPIO_PCLRR_SSI MCF_REG08(0xFC0A403E) +#define MCF_GPIO_PCLRR_BUSCTL MCF_REG08(0xFC0A403F) +#define MCF_GPIO_PCLRR_BE MCF_REG08(0xFC0A4040) +#define MCF_GPIO_PCLRR_CS MCF_REG08(0xFC0A4041) +#define MCF_GPIO_PCLRR_PWM MCF_REG08(0xFC0A4042) +#define MCF_GPIO_PCLRR_FECI2C MCF_REG08(0xFC0A4043) +#define MCF_GPIO_PCLRR_UART MCF_REG08(0xFC0A4045) +#define MCF_GPIO_PCLRR_QSPI MCF_REG08(0xFC0A4046) +#define MCF_GPIO_PCLRR_TIMER MCF_REG08(0xFC0A4047) +#define MCF_GPIO_PCLRR_LCDDATAH MCF_REG08(0xFC0A4049) +#define MCF_GPIO_PCLRR_LCDDATAM MCF_REG08(0xFC0A404A) +#define MCF_GPIO_PCLRR_LCDDATAL MCF_REG08(0xFC0A404B) +#define MCF_GPIO_PCLRR_LCDCTLH MCF_REG08(0xFC0A404C) +#define MCF_GPIO_PCLRR_LCDCTLL MCF_REG08(0xFC0A404D) +#define MCF_GPIO_PAR_FEC MCF_REG08(0xFC0A4050) +#define MCF_GPIO_PAR_PWM MCF_REG08(0xFC0A4051) +#define MCF_GPIO_PAR_BUSCTL MCF_REG08(0xFC0A4052) +#define MCF_GPIO_PAR_FECI2C MCF_REG08(0xFC0A4053) +#define MCF_GPIO_PAR_BE MCF_REG08(0xFC0A4054) +#define MCF_GPIO_PAR_CS MCF_REG08(0xFC0A4055) +#define MCF_GPIO_PAR_SSI MCF_REG16(0xFC0A4056) +#define MCF_GPIO_PAR_UART MCF_REG16(0xFC0A4058) +#define MCF_GPIO_PAR_QSPI MCF_REG16(0xFC0A405A) +#define MCF_GPIO_PAR_TIMER MCF_REG08(0xFC0A405C) +#define MCF_GPIO_PAR_LCDDATA MCF_REG08(0xFC0A405D) +#define MCF_GPIO_PAR_LCDCTL MCF_REG16(0xFC0A405E) +#define MCF_GPIO_PAR_IRQ MCF_REG16(0xFC0A4060) +#define MCF_GPIO_MSCR_FLEXBUS MCF_REG08(0xFC0A4064) +#define MCF_GPIO_MSCR_SDRAM MCF_REG08(0xFC0A4065) +#define MCF_GPIO_DSCR_I2C MCF_REG08(0xFC0A4068) +#define MCF_GPIO_DSCR_PWM MCF_REG08(0xFC0A4069) +#define MCF_GPIO_DSCR_FEC MCF_REG08(0xFC0A406A) +#define MCF_GPIO_DSCR_UART MCF_REG08(0xFC0A406B) +#define MCF_GPIO_DSCR_QSPI MCF_REG08(0xFC0A406C) +#define MCF_GPIO_DSCR_TIMER MCF_REG08(0xFC0A406D) +#define MCF_GPIO_DSCR_SSI MCF_REG08(0xFC0A406E) +#define MCF_GPIO_DSCR_LCD MCF_REG08(0xFC0A406F) +#define MCF_GPIO_DSCR_DEBUG MCF_REG08(0xFC0A4070) +#define MCF_GPIO_DSCR_CLKRST MCF_REG08(0xFC0A4071) +#define MCF_GPIO_DSCR_IRQ MCF_REG08(0xFC0A4072) + +/* Bit definitions and macros for MCF_GPIO_PODR_FECH */ +#define MCF_GPIO_PODR_FECH_PODR_FECH0 (0x01) +#define MCF_GPIO_PODR_FECH_PODR_FECH1 (0x02) +#define MCF_GPIO_PODR_FECH_PODR_FECH2 (0x04) +#define MCF_GPIO_PODR_FECH_PODR_FECH3 (0x08) +#define MCF_GPIO_PODR_FECH_PODR_FECH4 (0x10) +#define MCF_GPIO_PODR_FECH_PODR_FECH5 (0x20) +#define MCF_GPIO_PODR_FECH_PODR_FECH6 (0x40) +#define MCF_GPIO_PODR_FECH_PODR_FECH7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PODR_FECL */ +#define MCF_GPIO_PODR_FECL_PODR_FECL0 (0x01) +#define MCF_GPIO_PODR_FECL_PODR_FECL1 (0x02) +#define MCF_GPIO_PODR_FECL_PODR_FECL2 (0x04) +#define MCF_GPIO_PODR_FECL_PODR_FECL3 (0x08) +#define MCF_GPIO_PODR_FECL_PODR_FECL4 (0x10) +#define MCF_GPIO_PODR_FECL_PODR_FECL5 (0x20) +#define MCF_GPIO_PODR_FECL_PODR_FECL6 (0x40) +#define MCF_GPIO_PODR_FECL_PODR_FECL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PODR_SSI */ +#define MCF_GPIO_PODR_SSI_PODR_SSI0 (0x01) +#define MCF_GPIO_PODR_SSI_PODR_SSI1 (0x02) +#define MCF_GPIO_PODR_SSI_PODR_SSI2 (0x04) +#define MCF_GPIO_PODR_SSI_PODR_SSI3 (0x08) +#define MCF_GPIO_PODR_SSI_PODR_SSI4 (0x10) + +/* Bit definitions and macros for MCF_GPIO_PODR_BUSCTL */ +#define MCF_GPIO_PODR_BUSCTL_POSDR_BUSCTL0 (0x01) +#define MCF_GPIO_PODR_BUSCTL_PODR_BUSCTL1 (0x02) +#define MCF_GPIO_PODR_BUSCTL_PODR_BUSCTL2 (0x04) +#define MCF_GPIO_PODR_BUSCTL_PODR_BUSCTL3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PODR_BE */ +#define MCF_GPIO_PODR_BE_PODR_BE0 (0x01) +#define MCF_GPIO_PODR_BE_PODR_BE1 (0x02) +#define MCF_GPIO_PODR_BE_PODR_BE2 (0x04) +#define MCF_GPIO_PODR_BE_PODR_BE3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PODR_CS */ +#define MCF_GPIO_PODR_CS_PODR_CS1 (0x02) +#define MCF_GPIO_PODR_CS_PODR_CS2 (0x04) +#define MCF_GPIO_PODR_CS_PODR_CS3 (0x08) +#define MCF_GPIO_PODR_CS_PODR_CS4 (0x10) +#define MCF_GPIO_PODR_CS_PODR_CS5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PODR_PWM */ +#define MCF_GPIO_PODR_PWM_PODR_PWM2 (0x04) +#define MCF_GPIO_PODR_PWM_PODR_PWM3 (0x08) +#define MCF_GPIO_PODR_PWM_PODR_PWM4 (0x10) +#define MCF_GPIO_PODR_PWM_PODR_PWM5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PODR_FECI2C */ +#define MCF_GPIO_PODR_FECI2C_PODR_FECI2C0 (0x01) +#define MCF_GPIO_PODR_FECI2C_PODR_FECI2C1 (0x02) +#define MCF_GPIO_PODR_FECI2C_PODR_FECI2C2 (0x04) +#define MCF_GPIO_PODR_FECI2C_PODR_FECI2C3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PODR_UART */ +#define MCF_GPIO_PODR_UART_PODR_UART0 (0x01) +#define MCF_GPIO_PODR_UART_PODR_UART1 (0x02) +#define MCF_GPIO_PODR_UART_PODR_UART2 (0x04) +#define MCF_GPIO_PODR_UART_PODR_UART3 (0x08) +#define MCF_GPIO_PODR_UART_PODR_UART4 (0x10) +#define MCF_GPIO_PODR_UART_PODR_UART5 (0x20) +#define MCF_GPIO_PODR_UART_PODR_UART6 (0x40) +#define MCF_GPIO_PODR_UART_PODR_UART7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PODR_QSPI */ +#define MCF_GPIO_PODR_QSPI_PODR_QSPI0 (0x01) +#define MCF_GPIO_PODR_QSPI_PODR_QSPI1 (0x02) +#define MCF_GPIO_PODR_QSPI_PODR_QSPI2 (0x04) +#define MCF_GPIO_PODR_QSPI_PODR_QSPI3 (0x08) +#define MCF_GPIO_PODR_QSPI_PODR_QSPI4 (0x10) +#define MCF_GPIO_PODR_QSPI_PODR_QSPI5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PODR_TIMER */ +#define MCF_GPIO_PODR_TIMER_PODR_TIMER0 (0x01) +#define MCF_GPIO_PODR_TIMER_PODR_TIMER1 (0x02) +#define MCF_GPIO_PODR_TIMER_PODR_TIMER2 (0x04) +#define MCF_GPIO_PODR_TIMER_PODR_TIMER3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PODR_LCDDATAH */ +#define MCF_GPIO_PODR_LCDDATAH_PODR_LCDDATAH0 (0x01) +#define MCF_GPIO_PODR_LCDDATAH_PODR_LCDDATAH1 (0x02) + +/* Bit definitions and macros for MCF_GPIO_PODR_LCDDATAM */ +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM0 (0x01) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM1 (0x02) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM2 (0x04) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM3 (0x08) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM4 (0x10) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM5 (0x20) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM6 (0x40) +#define MCF_GPIO_PODR_LCDDATAM_PODR_LCDDATAM7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PODR_LCDDATAL */ +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL0 (0x01) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL1 (0x02) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL2 (0x04) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL3 (0x08) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL4 (0x10) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL5 (0x20) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL6 (0x40) +#define MCF_GPIO_PODR_LCDDATAL_PODR_LCDDATAL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PODR_LCDCTLH */ +#define MCF_GPIO_PODR_LCDCTLH_PODR_LCDCTLH0 (0x01) + +/* Bit definitions and macros for MCF_GPIO_PODR_LCDCTLL */ +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL0 (0x01) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL1 (0x02) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL2 (0x04) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL3 (0x08) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL4 (0x10) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL5 (0x20) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL6 (0x40) +#define MCF_GPIO_PODR_LCDCTLL_PODR_LCDCTLL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PDDR_FECH */ +#define MCF_GPIO_PDDR_FECH_PDDR_FECH0 (0x01) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH1 (0x02) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH2 (0x04) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH3 (0x08) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH4 (0x10) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH5 (0x20) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH6 (0x40) +#define MCF_GPIO_PDDR_FECH_PDDR_FECH7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PDDR_FECL */ +#define MCF_GPIO_PDDR_FECL_PDDR_FECL0 (0x01) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL1 (0x02) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL2 (0x04) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL3 (0x08) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL4 (0x10) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL5 (0x20) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL6 (0x40) +#define MCF_GPIO_PDDR_FECL_PDDR_FECL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PDDR_SSI */ +#define MCF_GPIO_PDDR_SSI_PDDR_SSI0 (0x01) +#define MCF_GPIO_PDDR_SSI_PDDR_SSI1 (0x02) +#define MCF_GPIO_PDDR_SSI_PDDR_SSI2 (0x04) +#define MCF_GPIO_PDDR_SSI_PDDR_SSI3 (0x08) +#define MCF_GPIO_PDDR_SSI_PDDR_SSI4 (0x10) + +/* Bit definitions and macros for MCF_GPIO_PDDR_BUSCTL */ +#define MCF_GPIO_PDDR_BUSCTL_POSDR_BUSCTL0 (0x01) +#define MCF_GPIO_PDDR_BUSCTL_PDDR_BUSCTL1 (0x02) +#define MCF_GPIO_PDDR_BUSCTL_PDDR_BUSCTL2 (0x04) +#define MCF_GPIO_PDDR_BUSCTL_PDDR_BUSCTL3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PDDR_BE */ +#define MCF_GPIO_PDDR_BE_PDDR_BE0 (0x01) +#define MCF_GPIO_PDDR_BE_PDDR_BE1 (0x02) +#define MCF_GPIO_PDDR_BE_PDDR_BE2 (0x04) +#define MCF_GPIO_PDDR_BE_PDDR_BE3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PDDR_CS */ +#define MCF_GPIO_PDDR_CS_PDDR_CS1 (0x02) +#define MCF_GPIO_PDDR_CS_PDDR_CS2 (0x04) +#define MCF_GPIO_PDDR_CS_PDDR_CS3 (0x08) +#define MCF_GPIO_PDDR_CS_PDDR_CS4 (0x10) +#define MCF_GPIO_PDDR_CS_PDDR_CS5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PDDR_PWM */ +#define MCF_GPIO_PDDR_PWM_PDDR_PWM2 (0x04) +#define MCF_GPIO_PDDR_PWM_PDDR_PWM3 (0x08) +#define MCF_GPIO_PDDR_PWM_PDDR_PWM4 (0x10) +#define MCF_GPIO_PDDR_PWM_PDDR_PWM5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PDDR_FECI2C */ +#define MCF_GPIO_PDDR_FECI2C_PDDR_FECI2C0 (0x01) +#define MCF_GPIO_PDDR_FECI2C_PDDR_FECI2C1 (0x02) +#define MCF_GPIO_PDDR_FECI2C_PDDR_FECI2C2 (0x04) +#define MCF_GPIO_PDDR_FECI2C_PDDR_FECI2C3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PDDR_UART */ +#define MCF_GPIO_PDDR_UART_PDDR_UART0 (0x01) +#define MCF_GPIO_PDDR_UART_PDDR_UART1 (0x02) +#define MCF_GPIO_PDDR_UART_PDDR_UART2 (0x04) +#define MCF_GPIO_PDDR_UART_PDDR_UART3 (0x08) +#define MCF_GPIO_PDDR_UART_PDDR_UART4 (0x10) +#define MCF_GPIO_PDDR_UART_PDDR_UART5 (0x20) +#define MCF_GPIO_PDDR_UART_PDDR_UART6 (0x40) +#define MCF_GPIO_PDDR_UART_PDDR_UART7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PDDR_QSPI */ +#define MCF_GPIO_PDDR_QSPI_PDDR_QSPI0 (0x01) +#define MCF_GPIO_PDDR_QSPI_PDDR_QSPI1 (0x02) +#define MCF_GPIO_PDDR_QSPI_PDDR_QSPI2 (0x04) +#define MCF_GPIO_PDDR_QSPI_PDDR_QSPI3 (0x08) +#define MCF_GPIO_PDDR_QSPI_PDDR_QSPI4 (0x10) +#define MCF_GPIO_PDDR_QSPI_PDDR_QSPI5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PDDR_TIMER */ +#define MCF_GPIO_PDDR_TIMER_PDDR_TIMER0 (0x01) +#define MCF_GPIO_PDDR_TIMER_PDDR_TIMER1 (0x02) +#define MCF_GPIO_PDDR_TIMER_PDDR_TIMER2 (0x04) +#define MCF_GPIO_PDDR_TIMER_PDDR_TIMER3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PDDR_LCDDATAH */ +#define MCF_GPIO_PDDR_LCDDATAH_PDDR_LCDDATAH0 (0x01) +#define MCF_GPIO_PDDR_LCDDATAH_PDDR_LCDDATAH1 (0x02) + +/* Bit definitions and macros for MCF_GPIO_PDDR_LCDDATAM */ +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM0 (0x01) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM1 (0x02) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM2 (0x04) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM3 (0x08) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM4 (0x10) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM5 (0x20) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM6 (0x40) +#define MCF_GPIO_PDDR_LCDDATAM_PDDR_LCDDATAM7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PDDR_LCDDATAL */ +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL0 (0x01) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL1 (0x02) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL2 (0x04) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL3 (0x08) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL4 (0x10) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL5 (0x20) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL6 (0x40) +#define MCF_GPIO_PDDR_LCDDATAL_PDDR_LCDDATAL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PDDR_LCDCTLH */ +#define MCF_GPIO_PDDR_LCDCTLH_PDDR_LCDCTLH0 (0x01) + +/* Bit definitions and macros for MCF_GPIO_PDDR_LCDCTLL */ +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL0 (0x01) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL1 (0x02) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL2 (0x04) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL3 (0x08) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL4 (0x10) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL5 (0x20) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL6 (0x40) +#define MCF_GPIO_PDDR_LCDCTLL_PDDR_LCDCTLL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_FECH */ +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH0 (0x01) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH1 (0x02) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH2 (0x04) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH3 (0x08) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH4 (0x10) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH5 (0x20) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH6 (0x40) +#define MCF_GPIO_PPDSDR_FECH_PPDSDR_FECH7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_FECL */ +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL0 (0x01) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL1 (0x02) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL2 (0x04) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL3 (0x08) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL4 (0x10) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL5 (0x20) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL6 (0x40) +#define MCF_GPIO_PPDSDR_FECL_PPDSDR_FECL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_SSI */ +#define MCF_GPIO_PPDSDR_SSI_PPDSDR_SSI0 (0x01) +#define MCF_GPIO_PPDSDR_SSI_PPDSDR_SSI1 (0x02) +#define MCF_GPIO_PPDSDR_SSI_PPDSDR_SSI2 (0x04) +#define MCF_GPIO_PPDSDR_SSI_PPDSDR_SSI3 (0x08) +#define MCF_GPIO_PPDSDR_SSI_PPDSDR_SSI4 (0x10) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_BUSCTL */ +#define MCF_GPIO_PPDSDR_BUSCTL_POSDR_BUSCTL0 (0x01) +#define MCF_GPIO_PPDSDR_BUSCTL_PPDSDR_BUSCTL1 (0x02) +#define MCF_GPIO_PPDSDR_BUSCTL_PPDSDR_BUSCTL2 (0x04) +#define MCF_GPIO_PPDSDR_BUSCTL_PPDSDR_BUSCTL3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_BE */ +#define MCF_GPIO_PPDSDR_BE_PPDSDR_BE0 (0x01) +#define MCF_GPIO_PPDSDR_BE_PPDSDR_BE1 (0x02) +#define MCF_GPIO_PPDSDR_BE_PPDSDR_BE2 (0x04) +#define MCF_GPIO_PPDSDR_BE_PPDSDR_BE3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_CS */ +#define MCF_GPIO_PPDSDR_CS_PPDSDR_CS1 (0x02) +#define MCF_GPIO_PPDSDR_CS_PPDSDR_CS2 (0x04) +#define MCF_GPIO_PPDSDR_CS_PPDSDR_CS3 (0x08) +#define MCF_GPIO_PPDSDR_CS_PPDSDR_CS4 (0x10) +#define MCF_GPIO_PPDSDR_CS_PPDSDR_CS5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_PWM */ +#define MCF_GPIO_PPDSDR_PWM_PPDSDR_PWM2 (0x04) +#define MCF_GPIO_PPDSDR_PWM_PPDSDR_PWM3 (0x08) +#define MCF_GPIO_PPDSDR_PWM_PPDSDR_PWM4 (0x10) +#define MCF_GPIO_PPDSDR_PWM_PPDSDR_PWM5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_FECI2C */ +#define MCF_GPIO_PPDSDR_FECI2C_PPDSDR_FECI2C0 (0x01) +#define MCF_GPIO_PPDSDR_FECI2C_PPDSDR_FECI2C1 (0x02) +#define MCF_GPIO_PPDSDR_FECI2C_PPDSDR_FECI2C2 (0x04) +#define MCF_GPIO_PPDSDR_FECI2C_PPDSDR_FECI2C3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_UART */ +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART0 (0x01) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART1 (0x02) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART2 (0x04) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART3 (0x08) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART4 (0x10) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART5 (0x20) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART6 (0x40) +#define MCF_GPIO_PPDSDR_UART_PPDSDR_UART7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_QSPI */ +#define MCF_GPIO_PPDSDR_QSPI_PPDSDR_QSPI0 (0x01) +#define MCF_GPIO_PPDSDR_QSPI_PPDSDR_QSPI1 (0x02) +#define MCF_GPIO_PPDSDR_QSPI_PPDSDR_QSPI2 (0x04) +#define MCF_GPIO_PPDSDR_QSPI_PPDSDR_QSPI3 (0x08) +#define MCF_GPIO_PPDSDR_QSPI_PPDSDR_QSPI4 (0x10) +#define MCF_GPIO_PPDSDR_QSPI_PPDSDR_QSPI5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_TIMER */ +#define MCF_GPIO_PPDSDR_TIMER_PPDSDR_TIMER0 (0x01) +#define MCF_GPIO_PPDSDR_TIMER_PPDSDR_TIMER1 (0x02) +#define MCF_GPIO_PPDSDR_TIMER_PPDSDR_TIMER2 (0x04) +#define MCF_GPIO_PPDSDR_TIMER_PPDSDR_TIMER3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_LCDDATAH */ +#define MCF_GPIO_PPDSDR_LCDDATAH_PPDSDR_LCDDATAH0 (0x01) +#define MCF_GPIO_PPDSDR_LCDDATAH_PPDSDR_LCDDATAH1 (0x02) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_LCDDATAM */ +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM0 (0x01) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM1 (0x02) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM2 (0x04) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM3 (0x08) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM4 (0x10) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM5 (0x20) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM6 (0x40) +#define MCF_GPIO_PPDSDR_LCDDATAM_PPDSDR_LCDDATAM7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_LCDDATAL */ +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL0 (0x01) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL1 (0x02) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL2 (0x04) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL3 (0x08) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL4 (0x10) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL5 (0x20) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL6 (0x40) +#define MCF_GPIO_PPDSDR_LCDDATAL_PPDSDR_LCDDATAL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_LCDCTLH */ +#define MCF_GPIO_PPDSDR_LCDCTLH_PPDSDR_LCDCTLH0 (0x01) + +/* Bit definitions and macros for MCF_GPIO_PPDSDR_LCDCTLL */ +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL0 (0x01) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL1 (0x02) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL2 (0x04) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL3 (0x08) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL4 (0x10) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL5 (0x20) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL6 (0x40) +#define MCF_GPIO_PPDSDR_LCDCTLL_PPDSDR_LCDCTLL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_FECH */ +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH0 (0x01) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH1 (0x02) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH2 (0x04) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH3 (0x08) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH4 (0x10) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH5 (0x20) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH6 (0x40) +#define MCF_GPIO_PCLRR_FECH_PCLRR_FECH7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_FECL */ +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL0 (0x01) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL1 (0x02) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL2 (0x04) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL3 (0x08) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL4 (0x10) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL5 (0x20) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL6 (0x40) +#define MCF_GPIO_PCLRR_FECL_PCLRR_FECL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_SSI */ +#define MCF_GPIO_PCLRR_SSI_PCLRR_SSI0 (0x01) +#define MCF_GPIO_PCLRR_SSI_PCLRR_SSI1 (0x02) +#define MCF_GPIO_PCLRR_SSI_PCLRR_SSI2 (0x04) +#define MCF_GPIO_PCLRR_SSI_PCLRR_SSI3 (0x08) +#define MCF_GPIO_PCLRR_SSI_PCLRR_SSI4 (0x10) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_BUSCTL */ +#define MCF_GPIO_PCLRR_BUSCTL_POSDR_BUSCTL0 (0x01) +#define MCF_GPIO_PCLRR_BUSCTL_PCLRR_BUSCTL1 (0x02) +#define MCF_GPIO_PCLRR_BUSCTL_PCLRR_BUSCTL2 (0x04) +#define MCF_GPIO_PCLRR_BUSCTL_PCLRR_BUSCTL3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_BE */ +#define MCF_GPIO_PCLRR_BE_PCLRR_BE0 (0x01) +#define MCF_GPIO_PCLRR_BE_PCLRR_BE1 (0x02) +#define MCF_GPIO_PCLRR_BE_PCLRR_BE2 (0x04) +#define MCF_GPIO_PCLRR_BE_PCLRR_BE3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_CS */ +#define MCF_GPIO_PCLRR_CS_PCLRR_CS1 (0x02) +#define MCF_GPIO_PCLRR_CS_PCLRR_CS2 (0x04) +#define MCF_GPIO_PCLRR_CS_PCLRR_CS3 (0x08) +#define MCF_GPIO_PCLRR_CS_PCLRR_CS4 (0x10) +#define MCF_GPIO_PCLRR_CS_PCLRR_CS5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_PWM */ +#define MCF_GPIO_PCLRR_PWM_PCLRR_PWM2 (0x04) +#define MCF_GPIO_PCLRR_PWM_PCLRR_PWM3 (0x08) +#define MCF_GPIO_PCLRR_PWM_PCLRR_PWM4 (0x10) +#define MCF_GPIO_PCLRR_PWM_PCLRR_PWM5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_FECI2C */ +#define MCF_GPIO_PCLRR_FECI2C_PCLRR_FECI2C0 (0x01) +#define MCF_GPIO_PCLRR_FECI2C_PCLRR_FECI2C1 (0x02) +#define MCF_GPIO_PCLRR_FECI2C_PCLRR_FECI2C2 (0x04) +#define MCF_GPIO_PCLRR_FECI2C_PCLRR_FECI2C3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_UART */ +#define MCF_GPIO_PCLRR_UART_PCLRR_UART0 (0x01) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART1 (0x02) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART2 (0x04) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART3 (0x08) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART4 (0x10) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART5 (0x20) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART6 (0x40) +#define MCF_GPIO_PCLRR_UART_PCLRR_UART7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_QSPI */ +#define MCF_GPIO_PCLRR_QSPI_PCLRR_QSPI0 (0x01) +#define MCF_GPIO_PCLRR_QSPI_PCLRR_QSPI1 (0x02) +#define MCF_GPIO_PCLRR_QSPI_PCLRR_QSPI2 (0x04) +#define MCF_GPIO_PCLRR_QSPI_PCLRR_QSPI3 (0x08) +#define MCF_GPIO_PCLRR_QSPI_PCLRR_QSPI4 (0x10) +#define MCF_GPIO_PCLRR_QSPI_PCLRR_QSPI5 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_TIMER */ +#define MCF_GPIO_PCLRR_TIMER_PCLRR_TIMER0 (0x01) +#define MCF_GPIO_PCLRR_TIMER_PCLRR_TIMER1 (0x02) +#define MCF_GPIO_PCLRR_TIMER_PCLRR_TIMER2 (0x04) +#define MCF_GPIO_PCLRR_TIMER_PCLRR_TIMER3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_LCDDATAH */ +#define MCF_GPIO_PCLRR_LCDDATAH_PCLRR_LCDDATAH0 (0x01) +#define MCF_GPIO_PCLRR_LCDDATAH_PCLRR_LCDDATAH1 (0x02) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_LCDDATAM */ +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM0 (0x01) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM1 (0x02) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM2 (0x04) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM3 (0x08) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM4 (0x10) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM5 (0x20) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM6 (0x40) +#define MCF_GPIO_PCLRR_LCDDATAM_PCLRR_LCDDATAM7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_LCDDATAL */ +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL0 (0x01) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL1 (0x02) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL2 (0x04) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL3 (0x08) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL4 (0x10) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL5 (0x20) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL6 (0x40) +#define MCF_GPIO_PCLRR_LCDDATAL_PCLRR_LCDDATAL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_LCDCTLH */ +#define MCF_GPIO_PCLRR_LCDCTLH_PCLRR_LCDCTLH0 (0x01) + +/* Bit definitions and macros for MCF_GPIO_PCLRR_LCDCTLL */ +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL0 (0x01) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL1 (0x02) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL2 (0x04) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL3 (0x08) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL4 (0x10) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL5 (0x20) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL6 (0x40) +#define MCF_GPIO_PCLRR_LCDCTLL_PCLRR_LCDCTLL7 (0x80) + +/* Bit definitions and macros for MCF_GPIO_PAR_FEC */ +#define MCF_GPIO_PAR_FEC_PAR_FEC_MII(x) (((x)&0x03)<<0) +#define MCF_GPIO_PAR_FEC_PAR_FEC_7W(x) (((x)&0x03)<<2) +#define MCF_GPIO_PAR_FEC_PAR_FEC_7W_GPIO (0x00) +#define MCF_GPIO_PAR_FEC_PAR_FEC_7W_URTS1 (0x04) +#define MCF_GPIO_PAR_FEC_PAR_FEC_7W_FEC (0x0C) +#define MCF_GPIO_PAR_FEC_PAR_FEC_MII_GPIO (0x00) +#define MCF_GPIO_PAR_FEC_PAR_FEC_MII_UART (0x01) +#define MCF_GPIO_PAR_FEC_PAR_FEC_MII_FEC (0x03) + +/* Bit definitions and macros for MCF_GPIO_PAR_PWM */ +#define MCF_GPIO_PAR_PWM_PAR_PWM1(x) (((x)&0x03)<<0) +#define MCF_GPIO_PAR_PWM_PAR_PWM3(x) (((x)&0x03)<<2) +#define MCF_GPIO_PAR_PWM_PAR_PWM5 (0x10) +#define MCF_GPIO_PAR_PWM_PAR_PWM7 (0x20) + +/* Bit definitions and macros for MCF_GPIO_PAR_BUSCTL */ +#define MCF_GPIO_PAR_BUSCTL_PAR_TS(x) (((x)&0x03)<<3) +#define MCF_GPIO_PAR_BUSCTL_PAR_RWB (0x20) +#define MCF_GPIO_PAR_BUSCTL_PAR_TA (0x40) +#define MCF_GPIO_PAR_BUSCTL_PAR_OE (0x80) +#define MCF_GPIO_PAR_BUSCTL_PAR_OE_GPIO (0x00) +#define MCF_GPIO_PAR_BUSCTL_PAR_OE_OE (0x80) +#define MCF_GPIO_PAR_BUSCTL_PAR_TA_GPIO (0x00) +#define MCF_GPIO_PAR_BUSCTL_PAR_TA_TA (0x40) +#define MCF_GPIO_PAR_BUSCTL_PAR_RWB_GPIO (0x00) +#define MCF_GPIO_PAR_BUSCTL_PAR_RWB_RWB (0x20) +#define MCF_GPIO_PAR_BUSCTL_PAR_TS_GPIO (0x00) +#define MCF_GPIO_PAR_BUSCTL_PAR_TS_DACK0 (0x10) +#define MCF_GPIO_PAR_BUSCTL_PAR_TS_TS (0x18) + +/* Bit definitions and macros for MCF_GPIO_PAR_FECI2C */ +#define MCF_GPIO_PAR_FECI2C_PAR_SDA(x) (((x)&0x03)<<0) +#define MCF_GPIO_PAR_FECI2C_PAR_SCL(x) (((x)&0x03)<<2) +#define MCF_GPIO_PAR_FECI2C_PAR_MDIO(x) (((x)&0x03)<<4) +#define MCF_GPIO_PAR_FECI2C_PAR_MDC(x) (((x)&0x03)<<6) +#define MCF_GPIO_PAR_FECI2C_PAR_MDC_GPIO (0x00) +#define MCF_GPIO_PAR_FECI2C_PAR_MDC_UTXD2 (0x40) +#define MCF_GPIO_PAR_FECI2C_PAR_MDC_SCL (0x80) +#define MCF_GPIO_PAR_FECI2C_PAR_MDC_EMDC (0xC0) +#define MCF_GPIO_PAR_FECI2C_PAR_MDIO_GPIO (0x00) +#define MCF_GPIO_PAR_FECI2C_PAR_MDIO_URXD2 (0x10) +#define MCF_GPIO_PAR_FECI2C_PAR_MDIO_SDA (0x20) +#define MCF_GPIO_PAR_FECI2C_PAR_MDIO_EMDIO (0x30) +#define MCF_GPIO_PAR_FECI2C_PAR_SCL_GPIO (0x00) +#define MCF_GPIO_PAR_FECI2C_PAR_SCL_UTXD2 (0x04) +#define MCF_GPIO_PAR_FECI2C_PAR_SCL_SCL (0x0C) +#define MCF_GPIO_PAR_FECI2C_PAR_SDA_GPIO (0x00) +#define MCF_GPIO_PAR_FECI2C_PAR_SDA_URXD2 (0x02) +#define MCF_GPIO_PAR_FECI2C_PAR_SDA_SDA (0x03) + +/* Bit definitions and macros for MCF_GPIO_PAR_BE */ +#define MCF_GPIO_PAR_BE_PAR_BE0 (0x01) +#define MCF_GPIO_PAR_BE_PAR_BE1 (0x02) +#define MCF_GPIO_PAR_BE_PAR_BE2 (0x04) +#define MCF_GPIO_PAR_BE_PAR_BE3 (0x08) + +/* Bit definitions and macros for MCF_GPIO_PAR_CS */ +#define MCF_GPIO_PAR_CS_PAR_CS1 (0x02) +#define MCF_GPIO_PAR_CS_PAR_CS2 (0x04) +#define MCF_GPIO_PAR_CS_PAR_CS3 (0x08) +#define MCF_GPIO_PAR_CS_PAR_CS4 (0x10) +#define MCF_GPIO_PAR_CS_PAR_CS5 (0x20) +#define MCF_GPIO_PAR_CS_PAR_CS_CS1_GPIO (0x00) +#define MCF_GPIO_PAR_CS_PAR_CS_CS1_SDCS1 (0x01) +#define MCF_GPIO_PAR_CS_PAR_CS_CS1_CS1 (0x03) + +/* Bit definitions and macros for MCF_GPIO_PAR_SSI */ +#define MCF_GPIO_PAR_SSI_PAR_MCLK (0x0080) +#define MCF_GPIO_PAR_SSI_PAR_TXD(x) (((x)&0x0003)<<8) +#define MCF_GPIO_PAR_SSI_PAR_RXD(x) (((x)&0x0003)<<10) +#define MCF_GPIO_PAR_SSI_PAR_FS(x) (((x)&0x0003)<<12) +#define MCF_GPIO_PAR_SSI_PAR_BCLK(x) (((x)&0x0003)<<14) + +/* Bit definitions and macros for MCF_GPIO_PAR_UART */ +#define MCF_GPIO_PAR_UART_PAR_UTXD0 (0x0001) +#define MCF_GPIO_PAR_UART_PAR_URXD0 (0x0002) +#define MCF_GPIO_PAR_UART_PAR_URTS0 (0x0004) +#define MCF_GPIO_PAR_UART_PAR_UCTS0 (0x0008) +#define MCF_GPIO_PAR_UART_PAR_UTXD1(x) (((x)&0x0003)<<4) +#define MCF_GPIO_PAR_UART_PAR_URXD1(x) (((x)&0x0003)<<6) +#define MCF_GPIO_PAR_UART_PAR_URTS1(x) (((x)&0x0003)<<8) +#define MCF_GPIO_PAR_UART_PAR_UCTS1(x) (((x)&0x0003)<<10) +#define MCF_GPIO_PAR_UART_PAR_UCTS1_GPIO (0x0000) +#define MCF_GPIO_PAR_UART_PAR_UCTS1_SSI_BCLK (0x0800) +#define MCF_GPIO_PAR_UART_PAR_UCTS1_ULPI_D7 (0x0400) +#define MCF_GPIO_PAR_UART_PAR_UCTS1_UCTS1 (0x0C00) +#define MCF_GPIO_PAR_UART_PAR_URTS1_GPIO (0x0000) +#define MCF_GPIO_PAR_UART_PAR_URTS1_SSI_FS (0x0200) +#define MCF_GPIO_PAR_UART_PAR_URTS1_ULPI_D6 (0x0100) +#define MCF_GPIO_PAR_UART_PAR_URTS1_URTS1 (0x0300) +#define MCF_GPIO_PAR_UART_PAR_URXD1_GPIO (0x0000) +#define MCF_GPIO_PAR_UART_PAR_URXD1_SSI_RXD (0x0080) +#define MCF_GPIO_PAR_UART_PAR_URXD1_ULPI_D5 (0x0040) +#define MCF_GPIO_PAR_UART_PAR_URXD1_URXD1 (0x00C0) +#define MCF_GPIO_PAR_UART_PAR_UTXD1_GPIO (0x0000) +#define MCF_GPIO_PAR_UART_PAR_UTXD1_SSI_TXD (0x0020) +#define MCF_GPIO_PAR_UART_PAR_UTXD1_ULPI_D4 (0x0010) +#define MCF_GPIO_PAR_UART_PAR_UTXD1_UTXD1 (0x0030) + +/* Bit definitions and macros for MCF_GPIO_PAR_QSPI */ +#define MCF_GPIO_PAR_QSPI_PAR_SCK(x) (((x)&0x0003)<<4) +#define MCF_GPIO_PAR_QSPI_PAR_DOUT(x) (((x)&0x0003)<<6) +#define MCF_GPIO_PAR_QSPI_PAR_DIN(x) (((x)&0x0003)<<8) +#define MCF_GPIO_PAR_QSPI_PAR_PCS0(x) (((x)&0x0003)<<10) +#define MCF_GPIO_PAR_QSPI_PAR_PCS1(x) (((x)&0x0003)<<12) +#define MCF_GPIO_PAR_QSPI_PAR_PCS2(x) (((x)&0x0003)<<14) + +/* Bit definitions and macros for MCF_GPIO_PAR_TIMER */ +#define MCF_GPIO_PAR_TIMER_PAR_TIN0(x) (((x)&0x03)<<0) +#define MCF_GPIO_PAR_TIMER_PAR_TIN1(x) (((x)&0x03)<<2) +#define MCF_GPIO_PAR_TIMER_PAR_TIN2(x) (((x)&0x03)<<4) +#define MCF_GPIO_PAR_TIMER_PAR_TIN3(x) (((x)&0x03)<<6) +#define MCF_GPIO_PAR_TIMER_PAR_TIN3_GPIO (0x00) +#define MCF_GPIO_PAR_TIMER_PAR_TIN3_TOUT3 (0x80) +#define MCF_GPIO_PAR_TIMER_PAR_TIN3_URXD2 (0x40) +#define MCF_GPIO_PAR_TIMER_PAR_TIN3_TIN3 (0xC0) +#define MCF_GPIO_PAR_TIMER_PAR_TIN2_GPIO (0x00) +#define MCF_GPIO_PAR_TIMER_PAR_TIN2_TOUT2 (0x20) +#define MCF_GPIO_PAR_TIMER_PAR_TIN2_UTXD2 (0x10) +#define MCF_GPIO_PAR_TIMER_PAR_TIN2_TIN2 (0x30) +#define MCF_GPIO_PAR_TIMER_PAR_TIN1_GPIO (0x00) +#define MCF_GPIO_PAR_TIMER_PAR_TIN1_TOUT1 (0x08) +#define MCF_GPIO_PAR_TIMER_PAR_TIN1_DACK1 (0x04) +#define MCF_GPIO_PAR_TIMER_PAR_TIN1_TIN1 (0x0C) +#define MCF_GPIO_PAR_TIMER_PAR_TIN0_GPIO (0x00) +#define MCF_GPIO_PAR_TIMER_PAR_TIN0_TOUT0 (0x02) +#define MCF_GPIO_PAR_TIMER_PAR_TIN0_DREQ0 (0x01) +#define MCF_GPIO_PAR_TIMER_PAR_TIN0_TIN0 (0x03) + +/* Bit definitions and macros for MCF_GPIO_PAR_LCDDATA */ +#define MCF_GPIO_PAR_LCDDATA_PAR_LD7_0(x) (((x)&0x03)<<0) +#define MCF_GPIO_PAR_LCDDATA_PAR_LD15_8(x) (((x)&0x03)<<2) +#define MCF_GPIO_PAR_LCDDATA_PAR_LD16(x) (((x)&0x03)<<4) +#define MCF_GPIO_PAR_LCDDATA_PAR_LD17(x) (((x)&0x03)<<6) + +/* Bit definitions and macros for MCF_GPIO_PAR_LCDCTL */ +#define MCF_GPIO_PAR_LCDCTL_PAR_CLS (0x0001) +#define MCF_GPIO_PAR_LCDCTL_PAR_PS (0x0002) +#define MCF_GPIO_PAR_LCDCTL_PAR_REV (0x0004) +#define MCF_GPIO_PAR_LCDCTL_PAR_SPL_SPR (0x0008) +#define MCF_GPIO_PAR_LCDCTL_PAR_CONTRAST (0x0010) +#define MCF_GPIO_PAR_LCDCTL_PAR_LSCLK (0x0020) +#define MCF_GPIO_PAR_LCDCTL_PAR_LP_HSYNC (0x0040) +#define MCF_GPIO_PAR_LCDCTL_PAR_FLM_VSYNC (0x0080) +#define MCF_GPIO_PAR_LCDCTL_PAR_ACD_OE (0x0100) + +/* Bit definitions and macros for MCF_GPIO_PAR_IRQ */ +#define MCF_GPIO_PAR_IRQ_PAR_IRQ1(x) (((x)&0x0003)<<4) +#define MCF_GPIO_PAR_IRQ_PAR_IRQ2(x) (((x)&0x0003)<<6) +#define MCF_GPIO_PAR_IRQ_PAR_IRQ4(x) (((x)&0x0003)<<8) +#define MCF_GPIO_PAR_IRQ_PAR_IRQ5(x) (((x)&0x0003)<<10) +#define MCF_GPIO_PAR_IRQ_PAR_IRQ6(x) (((x)&0x0003)<<12) + +/* Bit definitions and macros for MCF_GPIO_MSCR_FLEXBUS */ +#define MCF_GPIO_MSCR_FLEXBUS_MSCR_ADDRCTL(x) (((x)&0x03)<<0) +#define MCF_GPIO_MSCR_FLEXBUS_MSCR_DLOWER(x) (((x)&0x03)<<2) +#define MCF_GPIO_MSCR_FLEXBUS_MSCR_DUPPER(x) (((x)&0x03)<<4) + +/* Bit definitions and macros for MCF_GPIO_MSCR_SDRAM */ +#define MCF_GPIO_MSCR_SDRAM_MSCR_SDRAM(x) (((x)&0x03)<<0) +#define MCF_GPIO_MSCR_SDRAM_MSCR_SDCLK(x) (((x)&0x03)<<2) +#define MCF_GPIO_MSCR_SDRAM_MSCR_SDCLKB(x) (((x)&0x03)<<4) + +/* Bit definitions and macros for MCF_GPIO_DSCR_I2C */ +#define MCF_GPIO_DSCR_I2C_I2C_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_PWM */ +#define MCF_GPIO_DSCR_PWM_PWM_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_FEC */ +#define MCF_GPIO_DSCR_FEC_FEC_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_UART */ +#define MCF_GPIO_DSCR_UART_UART0_DSE(x) (((x)&0x03)<<0) +#define MCF_GPIO_DSCR_UART_UART1_DSE(x) (((x)&0x03)<<2) + +/* Bit definitions and macros for MCF_GPIO_DSCR_QSPI */ +#define MCF_GPIO_DSCR_QSPI_QSPI_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_TIMER */ +#define MCF_GPIO_DSCR_TIMER_TIMER_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_SSI */ +#define MCF_GPIO_DSCR_SSI_SSI_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_LCD */ +#define MCF_GPIO_DSCR_LCD_LCD_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_DEBUG */ +#define MCF_GPIO_DSCR_DEBUG_DEBUG_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_CLKRST */ +#define MCF_GPIO_DSCR_CLKRST_CLKRST_DSE(x) (((x)&0x03)<<0) + +/* Bit definitions and macros for MCF_GPIO_DSCR_IRQ */ +#define MCF_GPIO_DSCR_IRQ_IRQ_DSE(x) (((x)&0x03)<<0) + +/********************************************************************* + * + * Interrupt Controller (INTC) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_INTC0_IPRH MCF_REG32(0xFC048000) +#define MCF_INTC0_IPRL MCF_REG32(0xFC048004) +#define MCF_INTC0_IMRH MCF_REG32(0xFC048008) +#define MCF_INTC0_IMRL MCF_REG32(0xFC04800C) +#define MCF_INTC0_INTFRCH MCF_REG32(0xFC048010) +#define MCF_INTC0_INTFRCL MCF_REG32(0xFC048014) +#define MCF_INTC0_ICONFIG MCF_REG16(0xFC04801A) +#define MCF_INTC0_SIMR MCF_REG08(0xFC04801C) +#define MCF_INTC0_CIMR MCF_REG08(0xFC04801D) +#define MCF_INTC0_CLMASK MCF_REG08(0xFC04801E) +#define MCF_INTC0_SLMASK MCF_REG08(0xFC04801F) +#define MCF_INTC0_ICR0 MCF_REG08(0xFC048040) +#define MCF_INTC0_ICR1 MCF_REG08(0xFC048041) +#define MCF_INTC0_ICR2 MCF_REG08(0xFC048042) +#define MCF_INTC0_ICR3 MCF_REG08(0xFC048043) +#define MCF_INTC0_ICR4 MCF_REG08(0xFC048044) +#define MCF_INTC0_ICR5 MCF_REG08(0xFC048045) +#define MCF_INTC0_ICR6 MCF_REG08(0xFC048046) +#define MCF_INTC0_ICR7 MCF_REG08(0xFC048047) +#define MCF_INTC0_ICR8 MCF_REG08(0xFC048048) +#define MCF_INTC0_ICR9 MCF_REG08(0xFC048049) +#define MCF_INTC0_ICR10 MCF_REG08(0xFC04804A) +#define MCF_INTC0_ICR11 MCF_REG08(0xFC04804B) +#define MCF_INTC0_ICR12 MCF_REG08(0xFC04804C) +#define MCF_INTC0_ICR13 MCF_REG08(0xFC04804D) +#define MCF_INTC0_ICR14 MCF_REG08(0xFC04804E) +#define MCF_INTC0_ICR15 MCF_REG08(0xFC04804F) +#define MCF_INTC0_ICR16 MCF_REG08(0xFC048050) +#define MCF_INTC0_ICR17 MCF_REG08(0xFC048051) +#define MCF_INTC0_ICR18 MCF_REG08(0xFC048052) +#define MCF_INTC0_ICR19 MCF_REG08(0xFC048053) +#define MCF_INTC0_ICR20 MCF_REG08(0xFC048054) +#define MCF_INTC0_ICR21 MCF_REG08(0xFC048055) +#define MCF_INTC0_ICR22 MCF_REG08(0xFC048056) +#define MCF_INTC0_ICR23 MCF_REG08(0xFC048057) +#define MCF_INTC0_ICR24 MCF_REG08(0xFC048058) +#define MCF_INTC0_ICR25 MCF_REG08(0xFC048059) +#define MCF_INTC0_ICR26 MCF_REG08(0xFC04805A) +#define MCF_INTC0_ICR27 MCF_REG08(0xFC04805B) +#define MCF_INTC0_ICR28 MCF_REG08(0xFC04805C) +#define MCF_INTC0_ICR29 MCF_REG08(0xFC04805D) +#define MCF_INTC0_ICR30 MCF_REG08(0xFC04805E) +#define MCF_INTC0_ICR31 MCF_REG08(0xFC04805F) +#define MCF_INTC0_ICR32 MCF_REG08(0xFC048060) +#define MCF_INTC0_ICR33 MCF_REG08(0xFC048061) +#define MCF_INTC0_ICR34 MCF_REG08(0xFC048062) +#define MCF_INTC0_ICR35 MCF_REG08(0xFC048063) +#define MCF_INTC0_ICR36 MCF_REG08(0xFC048064) +#define MCF_INTC0_ICR37 MCF_REG08(0xFC048065) +#define MCF_INTC0_ICR38 MCF_REG08(0xFC048066) +#define MCF_INTC0_ICR39 MCF_REG08(0xFC048067) +#define MCF_INTC0_ICR40 MCF_REG08(0xFC048068) +#define MCF_INTC0_ICR41 MCF_REG08(0xFC048069) +#define MCF_INTC0_ICR42 MCF_REG08(0xFC04806A) +#define MCF_INTC0_ICR43 MCF_REG08(0xFC04806B) +#define MCF_INTC0_ICR44 MCF_REG08(0xFC04806C) +#define MCF_INTC0_ICR45 MCF_REG08(0xFC04806D) +#define MCF_INTC0_ICR46 MCF_REG08(0xFC04806E) +#define MCF_INTC0_ICR47 MCF_REG08(0xFC04806F) +#define MCF_INTC0_ICR48 MCF_REG08(0xFC048070) +#define MCF_INTC0_ICR49 MCF_REG08(0xFC048071) +#define MCF_INTC0_ICR50 MCF_REG08(0xFC048072) +#define MCF_INTC0_ICR51 MCF_REG08(0xFC048073) +#define MCF_INTC0_ICR52 MCF_REG08(0xFC048074) +#define MCF_INTC0_ICR53 MCF_REG08(0xFC048075) +#define MCF_INTC0_ICR54 MCF_REG08(0xFC048076) +#define MCF_INTC0_ICR55 MCF_REG08(0xFC048077) +#define MCF_INTC0_ICR56 MCF_REG08(0xFC048078) +#define MCF_INTC0_ICR57 MCF_REG08(0xFC048079) +#define MCF_INTC0_ICR58 MCF_REG08(0xFC04807A) +#define MCF_INTC0_ICR59 MCF_REG08(0xFC04807B) +#define MCF_INTC0_ICR60 MCF_REG08(0xFC04807C) +#define MCF_INTC0_ICR61 MCF_REG08(0xFC04807D) +#define MCF_INTC0_ICR62 MCF_REG08(0xFC04807E) +#define MCF_INTC0_ICR63 MCF_REG08(0xFC04807F) +#define MCF_INTC0_ICR(x) MCF_REG08(0xFC048040+((x)*0x001)) +#define MCF_INTC0_SWIACK MCF_REG08(0xFC0480E0) +#define MCF_INTC0_L1IACK MCF_REG08(0xFC0480E4) +#define MCF_INTC0_L2IACK MCF_REG08(0xFC0480E8) +#define MCF_INTC0_L3IACK MCF_REG08(0xFC0480EC) +#define MCF_INTC0_L4IACK MCF_REG08(0xFC0480F0) +#define MCF_INTC0_L5IACK MCF_REG08(0xFC0480F4) +#define MCF_INTC0_L6IACK MCF_REG08(0xFC0480F8) +#define MCF_INTC0_L7IACK MCF_REG08(0xFC0480FC) +#define MCF_INTC0_LIACK(x) MCF_REG08(0xFC0480E4+((x)*0x004)) +#define MCF_INTC1_IPRH MCF_REG32(0xFC04C000) +#define MCF_INTC1_IPRL MCF_REG32(0xFC04C004) +#define MCF_INTC1_IMRH MCF_REG32(0xFC04C008) +#define MCF_INTC1_IMRL MCF_REG32(0xFC04C00C) +#define MCF_INTC1_INTFRCH MCF_REG32(0xFC04C010) +#define MCF_INTC1_INTFRCL MCF_REG32(0xFC04C014) +#define MCF_INTC1_ICONFIG MCF_REG16(0xFC04C01A) +#define MCF_INTC1_SIMR MCF_REG08(0xFC04C01C) +#define MCF_INTC1_CIMR MCF_REG08(0xFC04C01D) +#define MCF_INTC1_CLMASK MCF_REG08(0xFC04C01E) +#define MCF_INTC1_SLMASK MCF_REG08(0xFC04C01F) +#define MCF_INTC1_ICR0 MCF_REG08(0xFC04C040) +#define MCF_INTC1_ICR1 MCF_REG08(0xFC04C041) +#define MCF_INTC1_ICR2 MCF_REG08(0xFC04C042) +#define MCF_INTC1_ICR3 MCF_REG08(0xFC04C043) +#define MCF_INTC1_ICR4 MCF_REG08(0xFC04C044) +#define MCF_INTC1_ICR5 MCF_REG08(0xFC04C045) +#define MCF_INTC1_ICR6 MCF_REG08(0xFC04C046) +#define MCF_INTC1_ICR7 MCF_REG08(0xFC04C047) +#define MCF_INTC1_ICR8 MCF_REG08(0xFC04C048) +#define MCF_INTC1_ICR9 MCF_REG08(0xFC04C049) +#define MCF_INTC1_ICR10 MCF_REG08(0xFC04C04A) +#define MCF_INTC1_ICR11 MCF_REG08(0xFC04C04B) +#define MCF_INTC1_ICR12 MCF_REG08(0xFC04C04C) +#define MCF_INTC1_ICR13 MCF_REG08(0xFC04C04D) +#define MCF_INTC1_ICR14 MCF_REG08(0xFC04C04E) +#define MCF_INTC1_ICR15 MCF_REG08(0xFC04C04F) +#define MCF_INTC1_ICR16 MCF_REG08(0xFC04C050) +#define MCF_INTC1_ICR17 MCF_REG08(0xFC04C051) +#define MCF_INTC1_ICR18 MCF_REG08(0xFC04C052) +#define MCF_INTC1_ICR19 MCF_REG08(0xFC04C053) +#define MCF_INTC1_ICR20 MCF_REG08(0xFC04C054) +#define MCF_INTC1_ICR21 MCF_REG08(0xFC04C055) +#define MCF_INTC1_ICR22 MCF_REG08(0xFC04C056) +#define MCF_INTC1_ICR23 MCF_REG08(0xFC04C057) +#define MCF_INTC1_ICR24 MCF_REG08(0xFC04C058) +#define MCF_INTC1_ICR25 MCF_REG08(0xFC04C059) +#define MCF_INTC1_ICR26 MCF_REG08(0xFC04C05A) +#define MCF_INTC1_ICR27 MCF_REG08(0xFC04C05B) +#define MCF_INTC1_ICR28 MCF_REG08(0xFC04C05C) +#define MCF_INTC1_ICR29 MCF_REG08(0xFC04C05D) +#define MCF_INTC1_ICR30 MCF_REG08(0xFC04C05E) +#define MCF_INTC1_ICR31 MCF_REG08(0xFC04C05F) +#define MCF_INTC1_ICR32 MCF_REG08(0xFC04C060) +#define MCF_INTC1_ICR33 MCF_REG08(0xFC04C061) +#define MCF_INTC1_ICR34 MCF_REG08(0xFC04C062) +#define MCF_INTC1_ICR35 MCF_REG08(0xFC04C063) +#define MCF_INTC1_ICR36 MCF_REG08(0xFC04C064) +#define MCF_INTC1_ICR37 MCF_REG08(0xFC04C065) +#define MCF_INTC1_ICR38 MCF_REG08(0xFC04C066) +#define MCF_INTC1_ICR39 MCF_REG08(0xFC04C067) +#define MCF_INTC1_ICR40 MCF_REG08(0xFC04C068) +#define MCF_INTC1_ICR41 MCF_REG08(0xFC04C069) +#define MCF_INTC1_ICR42 MCF_REG08(0xFC04C06A) +#define MCF_INTC1_ICR43 MCF_REG08(0xFC04C06B) +#define MCF_INTC1_ICR44 MCF_REG08(0xFC04C06C) +#define MCF_INTC1_ICR45 MCF_REG08(0xFC04C06D) +#define MCF_INTC1_ICR46 MCF_REG08(0xFC04C06E) +#define MCF_INTC1_ICR47 MCF_REG08(0xFC04C06F) +#define MCF_INTC1_ICR48 MCF_REG08(0xFC04C070) +#define MCF_INTC1_ICR49 MCF_REG08(0xFC04C071) +#define MCF_INTC1_ICR50 MCF_REG08(0xFC04C072) +#define MCF_INTC1_ICR51 MCF_REG08(0xFC04C073) +#define MCF_INTC1_ICR52 MCF_REG08(0xFC04C074) +#define MCF_INTC1_ICR53 MCF_REG08(0xFC04C075) +#define MCF_INTC1_ICR54 MCF_REG08(0xFC04C076) +#define MCF_INTC1_ICR55 MCF_REG08(0xFC04C077) +#define MCF_INTC1_ICR56 MCF_REG08(0xFC04C078) +#define MCF_INTC1_ICR57 MCF_REG08(0xFC04C079) +#define MCF_INTC1_ICR58 MCF_REG08(0xFC04C07A) +#define MCF_INTC1_ICR59 MCF_REG08(0xFC04C07B) +#define MCF_INTC1_ICR60 MCF_REG08(0xFC04C07C) +#define MCF_INTC1_ICR61 MCF_REG08(0xFC04C07D) +#define MCF_INTC1_ICR62 MCF_REG08(0xFC04C07E) +#define MCF_INTC1_ICR63 MCF_REG08(0xFC04C07F) +#define MCF_INTC1_ICR(x) MCF_REG08(0xFC04C040+((x)*0x001)) +#define MCF_INTC1_SWIACK MCF_REG08(0xFC04C0E0) +#define MCF_INTC1_L1IACK MCF_REG08(0xFC04C0E4) +#define MCF_INTC1_L2IACK MCF_REG08(0xFC04C0E8) +#define MCF_INTC1_L3IACK MCF_REG08(0xFC04C0EC) +#define MCF_INTC1_L4IACK MCF_REG08(0xFC04C0F0) +#define MCF_INTC1_L5IACK MCF_REG08(0xFC04C0F4) +#define MCF_INTC1_L6IACK MCF_REG08(0xFC04C0F8) +#define MCF_INTC1_L7IACK MCF_REG08(0xFC04C0FC) +#define MCF_INTC1_LIACK(x) MCF_REG08(0xFC04C0E4+((x)*0x004)) +#define MCF_INTC_IPRH(x) MCF_REG32(0xFC048000+((x)*0x4000)) +#define MCF_INTC_IPRL(x) MCF_REG32(0xFC048004+((x)*0x4000)) +#define MCF_INTC_IMRH(x) MCF_REG32(0xFC048008+((x)*0x4000)) +#define MCF_INTC_IMRL(x) MCF_REG32(0xFC04800C+((x)*0x4000)) +#define MCF_INTC_INTFRCH(x) MCF_REG32(0xFC048010+((x)*0x4000)) +#define MCF_INTC_INTFRCL(x) MCF_REG32(0xFC048014+((x)*0x4000)) +#define MCF_INTC_ICONFIG(x) MCF_REG16(0xFC04801A+((x)*0x4000)) +#define MCF_INTC_SIMR(x) MCF_REG08(0xFC04801C+((x)*0x4000)) +#define MCF_INTC_CIMR(x) MCF_REG08(0xFC04801D+((x)*0x4000)) +#define MCF_INTC_CLMASK(x) MCF_REG08(0xFC04801E+((x)*0x4000)) +#define MCF_INTC_SLMASK(x) MCF_REG08(0xFC04801F+((x)*0x4000)) +#define MCF_INTC_ICR0(x) MCF_REG08(0xFC048040+((x)*0x4000)) +#define MCF_INTC_ICR1(x) MCF_REG08(0xFC048041+((x)*0x4000)) +#define MCF_INTC_ICR2(x) MCF_REG08(0xFC048042+((x)*0x4000)) +#define MCF_INTC_ICR3(x) MCF_REG08(0xFC048043+((x)*0x4000)) +#define MCF_INTC_ICR4(x) MCF_REG08(0xFC048044+((x)*0x4000)) +#define MCF_INTC_ICR5(x) MCF_REG08(0xFC048045+((x)*0x4000)) +#define MCF_INTC_ICR6(x) MCF_REG08(0xFC048046+((x)*0x4000)) +#define MCF_INTC_ICR7(x) MCF_REG08(0xFC048047+((x)*0x4000)) +#define MCF_INTC_ICR8(x) MCF_REG08(0xFC048048+((x)*0x4000)) +#define MCF_INTC_ICR9(x) MCF_REG08(0xFC048049+((x)*0x4000)) +#define MCF_INTC_ICR10(x) MCF_REG08(0xFC04804A+((x)*0x4000)) +#define MCF_INTC_ICR11(x) MCF_REG08(0xFC04804B+((x)*0x4000)) +#define MCF_INTC_ICR12(x) MCF_REG08(0xFC04804C+((x)*0x4000)) +#define MCF_INTC_ICR13(x) MCF_REG08(0xFC04804D+((x)*0x4000)) +#define MCF_INTC_ICR14(x) MCF_REG08(0xFC04804E+((x)*0x4000)) +#define MCF_INTC_ICR15(x) MCF_REG08(0xFC04804F+((x)*0x4000)) +#define MCF_INTC_ICR16(x) MCF_REG08(0xFC048050+((x)*0x4000)) +#define MCF_INTC_ICR17(x) MCF_REG08(0xFC048051+((x)*0x4000)) +#define MCF_INTC_ICR18(x) MCF_REG08(0xFC048052+((x)*0x4000)) +#define MCF_INTC_ICR19(x) MCF_REG08(0xFC048053+((x)*0x4000)) +#define MCF_INTC_ICR20(x) MCF_REG08(0xFC048054+((x)*0x4000)) +#define MCF_INTC_ICR21(x) MCF_REG08(0xFC048055+((x)*0x4000)) +#define MCF_INTC_ICR22(x) MCF_REG08(0xFC048056+((x)*0x4000)) +#define MCF_INTC_ICR23(x) MCF_REG08(0xFC048057+((x)*0x4000)) +#define MCF_INTC_ICR24(x) MCF_REG08(0xFC048058+((x)*0x4000)) +#define MCF_INTC_ICR25(x) MCF_REG08(0xFC048059+((x)*0x4000)) +#define MCF_INTC_ICR26(x) MCF_REG08(0xFC04805A+((x)*0x4000)) +#define MCF_INTC_ICR27(x) MCF_REG08(0xFC04805B+((x)*0x4000)) +#define MCF_INTC_ICR28(x) MCF_REG08(0xFC04805C+((x)*0x4000)) +#define MCF_INTC_ICR29(x) MCF_REG08(0xFC04805D+((x)*0x4000)) +#define MCF_INTC_ICR30(x) MCF_REG08(0xFC04805E+((x)*0x4000)) +#define MCF_INTC_ICR31(x) MCF_REG08(0xFC04805F+((x)*0x4000)) +#define MCF_INTC_ICR32(x) MCF_REG08(0xFC048060+((x)*0x4000)) +#define MCF_INTC_ICR33(x) MCF_REG08(0xFC048061+((x)*0x4000)) +#define MCF_INTC_ICR34(x) MCF_REG08(0xFC048062+((x)*0x4000)) +#define MCF_INTC_ICR35(x) MCF_REG08(0xFC048063+((x)*0x4000)) +#define MCF_INTC_ICR36(x) MCF_REG08(0xFC048064+((x)*0x4000)) +#define MCF_INTC_ICR37(x) MCF_REG08(0xFC048065+((x)*0x4000)) +#define MCF_INTC_ICR38(x) MCF_REG08(0xFC048066+((x)*0x4000)) +#define MCF_INTC_ICR39(x) MCF_REG08(0xFC048067+((x)*0x4000)) +#define MCF_INTC_ICR40(x) MCF_REG08(0xFC048068+((x)*0x4000)) +#define MCF_INTC_ICR41(x) MCF_REG08(0xFC048069+((x)*0x4000)) +#define MCF_INTC_ICR42(x) MCF_REG08(0xFC04806A+((x)*0x4000)) +#define MCF_INTC_ICR43(x) MCF_REG08(0xFC04806B+((x)*0x4000)) +#define MCF_INTC_ICR44(x) MCF_REG08(0xFC04806C+((x)*0x4000)) +#define MCF_INTC_ICR45(x) MCF_REG08(0xFC04806D+((x)*0x4000)) +#define MCF_INTC_ICR46(x) MCF_REG08(0xFC04806E+((x)*0x4000)) +#define MCF_INTC_ICR47(x) MCF_REG08(0xFC04806F+((x)*0x4000)) +#define MCF_INTC_ICR48(x) MCF_REG08(0xFC048070+((x)*0x4000)) +#define MCF_INTC_ICR49(x) MCF_REG08(0xFC048071+((x)*0x4000)) +#define MCF_INTC_ICR50(x) MCF_REG08(0xFC048072+((x)*0x4000)) +#define MCF_INTC_ICR51(x) MCF_REG08(0xFC048073+((x)*0x4000)) +#define MCF_INTC_ICR52(x) MCF_REG08(0xFC048074+((x)*0x4000)) +#define MCF_INTC_ICR53(x) MCF_REG08(0xFC048075+((x)*0x4000)) +#define MCF_INTC_ICR54(x) MCF_REG08(0xFC048076+((x)*0x4000)) +#define MCF_INTC_ICR55(x) MCF_REG08(0xFC048077+((x)*0x4000)) +#define MCF_INTC_ICR56(x) MCF_REG08(0xFC048078+((x)*0x4000)) +#define MCF_INTC_ICR57(x) MCF_REG08(0xFC048079+((x)*0x4000)) +#define MCF_INTC_ICR58(x) MCF_REG08(0xFC04807A+((x)*0x4000)) +#define MCF_INTC_ICR59(x) MCF_REG08(0xFC04807B+((x)*0x4000)) +#define MCF_INTC_ICR60(x) MCF_REG08(0xFC04807C+((x)*0x4000)) +#define MCF_INTC_ICR61(x) MCF_REG08(0xFC04807D+((x)*0x4000)) +#define MCF_INTC_ICR62(x) MCF_REG08(0xFC04807E+((x)*0x4000)) +#define MCF_INTC_ICR63(x) MCF_REG08(0xFC04807F+((x)*0x4000)) +#define MCF_INTC_SWIACK(x) MCF_REG08(0xFC0480E0+((x)*0x4000)) +#define MCF_INTC_L1IACK(x) MCF_REG08(0xFC0480E4+((x)*0x4000)) +#define MCF_INTC_L2IACK(x) MCF_REG08(0xFC0480E8+((x)*0x4000)) +#define MCF_INTC_L3IACK(x) MCF_REG08(0xFC0480EC+((x)*0x4000)) +#define MCF_INTC_L4IACK(x) MCF_REG08(0xFC0480F0+((x)*0x4000)) +#define MCF_INTC_L5IACK(x) MCF_REG08(0xFC0480F4+((x)*0x4000)) +#define MCF_INTC_L6IACK(x) MCF_REG08(0xFC0480F8+((x)*0x4000)) +#define MCF_INTC_L7IACK(x) MCF_REG08(0xFC0480FC+((x)*0x4000)) + +/* Bit definitions and macros for MCF_INTC_IPRH */ +#define MCF_INTC_IPRH_INT32 (0x00000001) +#define MCF_INTC_IPRH_INT33 (0x00000002) +#define MCF_INTC_IPRH_INT34 (0x00000004) +#define MCF_INTC_IPRH_INT35 (0x00000008) +#define MCF_INTC_IPRH_INT36 (0x00000010) +#define MCF_INTC_IPRH_INT37 (0x00000020) +#define MCF_INTC_IPRH_INT38 (0x00000040) +#define MCF_INTC_IPRH_INT39 (0x00000080) +#define MCF_INTC_IPRH_INT40 (0x00000100) +#define MCF_INTC_IPRH_INT41 (0x00000200) +#define MCF_INTC_IPRH_INT42 (0x00000400) +#define MCF_INTC_IPRH_INT43 (0x00000800) +#define MCF_INTC_IPRH_INT44 (0x00001000) +#define MCF_INTC_IPRH_INT45 (0x00002000) +#define MCF_INTC_IPRH_INT46 (0x00004000) +#define MCF_INTC_IPRH_INT47 (0x00008000) +#define MCF_INTC_IPRH_INT48 (0x00010000) +#define MCF_INTC_IPRH_INT49 (0x00020000) +#define MCF_INTC_IPRH_INT50 (0x00040000) +#define MCF_INTC_IPRH_INT51 (0x00080000) +#define MCF_INTC_IPRH_INT52 (0x00100000) +#define MCF_INTC_IPRH_INT53 (0x00200000) +#define MCF_INTC_IPRH_INT54 (0x00400000) +#define MCF_INTC_IPRH_INT55 (0x00800000) +#define MCF_INTC_IPRH_INT56 (0x01000000) +#define MCF_INTC_IPRH_INT57 (0x02000000) +#define MCF_INTC_IPRH_INT58 (0x04000000) +#define MCF_INTC_IPRH_INT59 (0x08000000) +#define MCF_INTC_IPRH_INT60 (0x10000000) +#define MCF_INTC_IPRH_INT61 (0x20000000) +#define MCF_INTC_IPRH_INT62 (0x40000000) +#define MCF_INTC_IPRH_INT63 (0x80000000) + +/* Bit definitions and macros for MCF_INTC_IPRL */ +#define MCF_INTC_IPRL_INT0 (0x00000001) +#define MCF_INTC_IPRL_INT1 (0x00000002) +#define MCF_INTC_IPRL_INT2 (0x00000004) +#define MCF_INTC_IPRL_INT3 (0x00000008) +#define MCF_INTC_IPRL_INT4 (0x00000010) +#define MCF_INTC_IPRL_INT5 (0x00000020) +#define MCF_INTC_IPRL_INT6 (0x00000040) +#define MCF_INTC_IPRL_INT7 (0x00000080) +#define MCF_INTC_IPRL_INT8 (0x00000100) +#define MCF_INTC_IPRL_INT9 (0x00000200) +#define MCF_INTC_IPRL_INT10 (0x00000400) +#define MCF_INTC_IPRL_INT11 (0x00000800) +#define MCF_INTC_IPRL_INT12 (0x00001000) +#define MCF_INTC_IPRL_INT13 (0x00002000) +#define MCF_INTC_IPRL_INT14 (0x00004000) +#define MCF_INTC_IPRL_INT15 (0x00008000) +#define MCF_INTC_IPRL_INT16 (0x00010000) +#define MCF_INTC_IPRL_INT17 (0x00020000) +#define MCF_INTC_IPRL_INT18 (0x00040000) +#define MCF_INTC_IPRL_INT19 (0x00080000) +#define MCF_INTC_IPRL_INT20 (0x00100000) +#define MCF_INTC_IPRL_INT21 (0x00200000) +#define MCF_INTC_IPRL_INT22 (0x00400000) +#define MCF_INTC_IPRL_INT23 (0x00800000) +#define MCF_INTC_IPRL_INT24 (0x01000000) +#define MCF_INTC_IPRL_INT25 (0x02000000) +#define MCF_INTC_IPRL_INT26 (0x04000000) +#define MCF_INTC_IPRL_INT27 (0x08000000) +#define MCF_INTC_IPRL_INT28 (0x10000000) +#define MCF_INTC_IPRL_INT29 (0x20000000) +#define MCF_INTC_IPRL_INT30 (0x40000000) +#define MCF_INTC_IPRL_INT31 (0x80000000) + +/* Bit definitions and macros for MCF_INTC_IMRH */ +#define MCF_INTC_IMRH_INT_MASK32 (0x00000001) +#define MCF_INTC_IMRH_INT_MASK33 (0x00000002) +#define MCF_INTC_IMRH_INT_MASK34 (0x00000004) +#define MCF_INTC_IMRH_INT_MASK35 (0x00000008) +#define MCF_INTC_IMRH_INT_MASK36 (0x00000010) +#define MCF_INTC_IMRH_INT_MASK37 (0x00000020) +#define MCF_INTC_IMRH_INT_MASK38 (0x00000040) +#define MCF_INTC_IMRH_INT_MASK39 (0x00000080) +#define MCF_INTC_IMRH_INT_MASK40 (0x00000100) +#define MCF_INTC_IMRH_INT_MASK41 (0x00000200) +#define MCF_INTC_IMRH_INT_MASK42 (0x00000400) +#define MCF_INTC_IMRH_INT_MASK43 (0x00000800) +#define MCF_INTC_IMRH_INT_MASK44 (0x00001000) +#define MCF_INTC_IMRH_INT_MASK45 (0x00002000) +#define MCF_INTC_IMRH_INT_MASK46 (0x00004000) +#define MCF_INTC_IMRH_INT_MASK47 (0x00008000) +#define MCF_INTC_IMRH_INT_MASK48 (0x00010000) +#define MCF_INTC_IMRH_INT_MASK49 (0x00020000) +#define MCF_INTC_IMRH_INT_MASK50 (0x00040000) +#define MCF_INTC_IMRH_INT_MASK51 (0x00080000) +#define MCF_INTC_IMRH_INT_MASK52 (0x00100000) +#define MCF_INTC_IMRH_INT_MASK53 (0x00200000) +#define MCF_INTC_IMRH_INT_MASK54 (0x00400000) +#define MCF_INTC_IMRH_INT_MASK55 (0x00800000) +#define MCF_INTC_IMRH_INT_MASK56 (0x01000000) +#define MCF_INTC_IMRH_INT_MASK57 (0x02000000) +#define MCF_INTC_IMRH_INT_MASK58 (0x04000000) +#define MCF_INTC_IMRH_INT_MASK59 (0x08000000) +#define MCF_INTC_IMRH_INT_MASK60 (0x10000000) +#define MCF_INTC_IMRH_INT_MASK61 (0x20000000) +#define MCF_INTC_IMRH_INT_MASK62 (0x40000000) +#define MCF_INTC_IMRH_INT_MASK63 (0x80000000) + +/* Bit definitions and macros for MCF_INTC_IMRL */ +#define MCF_INTC_IMRL_INT_MASK0 (0x00000001) +#define MCF_INTC_IMRL_INT_MASK1 (0x00000002) +#define MCF_INTC_IMRL_INT_MASK2 (0x00000004) +#define MCF_INTC_IMRL_INT_MASK3 (0x00000008) +#define MCF_INTC_IMRL_INT_MASK4 (0x00000010) +#define MCF_INTC_IMRL_INT_MASK5 (0x00000020) +#define MCF_INTC_IMRL_INT_MASK6 (0x00000040) +#define MCF_INTC_IMRL_INT_MASK7 (0x00000080) +#define MCF_INTC_IMRL_INT_MASK8 (0x00000100) +#define MCF_INTC_IMRL_INT_MASK9 (0x00000200) +#define MCF_INTC_IMRL_INT_MASK10 (0x00000400) +#define MCF_INTC_IMRL_INT_MASK11 (0x00000800) +#define MCF_INTC_IMRL_INT_MASK12 (0x00001000) +#define MCF_INTC_IMRL_INT_MASK13 (0x00002000) +#define MCF_INTC_IMRL_INT_MASK14 (0x00004000) +#define MCF_INTC_IMRL_INT_MASK15 (0x00008000) +#define MCF_INTC_IMRL_INT_MASK16 (0x00010000) +#define MCF_INTC_IMRL_INT_MASK17 (0x00020000) +#define MCF_INTC_IMRL_INT_MASK18 (0x00040000) +#define MCF_INTC_IMRL_INT_MASK19 (0x00080000) +#define MCF_INTC_IMRL_INT_MASK20 (0x00100000) +#define MCF_INTC_IMRL_INT_MASK21 (0x00200000) +#define MCF_INTC_IMRL_INT_MASK22 (0x00400000) +#define MCF_INTC_IMRL_INT_MASK23 (0x00800000) +#define MCF_INTC_IMRL_INT_MASK24 (0x01000000) +#define MCF_INTC_IMRL_INT_MASK25 (0x02000000) +#define MCF_INTC_IMRL_INT_MASK26 (0x04000000) +#define MCF_INTC_IMRL_INT_MASK27 (0x08000000) +#define MCF_INTC_IMRL_INT_MASK28 (0x10000000) +#define MCF_INTC_IMRL_INT_MASK29 (0x20000000) +#define MCF_INTC_IMRL_INT_MASK30 (0x40000000) +#define MCF_INTC_IMRL_INT_MASK31 (0x80000000) + +/* Bit definitions and macros for MCF_INTC_INTFRCH */ +#define MCF_INTC_INTFRCH_INTFRC32 (0x00000001) +#define MCF_INTC_INTFRCH_INTFRC33 (0x00000002) +#define MCF_INTC_INTFRCH_INTFRC34 (0x00000004) +#define MCF_INTC_INTFRCH_INTFRC35 (0x00000008) +#define MCF_INTC_INTFRCH_INTFRC36 (0x00000010) +#define MCF_INTC_INTFRCH_INTFRC37 (0x00000020) +#define MCF_INTC_INTFRCH_INTFRC38 (0x00000040) +#define MCF_INTC_INTFRCH_INTFRC39 (0x00000080) +#define MCF_INTC_INTFRCH_INTFRC40 (0x00000100) +#define MCF_INTC_INTFRCH_INTFRC41 (0x00000200) +#define MCF_INTC_INTFRCH_INTFRC42 (0x00000400) +#define MCF_INTC_INTFRCH_INTFRC43 (0x00000800) +#define MCF_INTC_INTFRCH_INTFRC44 (0x00001000) +#define MCF_INTC_INTFRCH_INTFRC45 (0x00002000) +#define MCF_INTC_INTFRCH_INTFRC46 (0x00004000) +#define MCF_INTC_INTFRCH_INTFRC47 (0x00008000) +#define MCF_INTC_INTFRCH_INTFRC48 (0x00010000) +#define MCF_INTC_INTFRCH_INTFRC49 (0x00020000) +#define MCF_INTC_INTFRCH_INTFRC50 (0x00040000) +#define MCF_INTC_INTFRCH_INTFRC51 (0x00080000) +#define MCF_INTC_INTFRCH_INTFRC52 (0x00100000) +#define MCF_INTC_INTFRCH_INTFRC53 (0x00200000) +#define MCF_INTC_INTFRCH_INTFRC54 (0x00400000) +#define MCF_INTC_INTFRCH_INTFRC55 (0x00800000) +#define MCF_INTC_INTFRCH_INTFRC56 (0x01000000) +#define MCF_INTC_INTFRCH_INTFRC57 (0x02000000) +#define MCF_INTC_INTFRCH_INTFRC58 (0x04000000) +#define MCF_INTC_INTFRCH_INTFRC59 (0x08000000) +#define MCF_INTC_INTFRCH_INTFRC60 (0x10000000) +#define MCF_INTC_INTFRCH_INTFRC61 (0x20000000) +#define MCF_INTC_INTFRCH_INTFRC62 (0x40000000) +#define MCF_INTC_INTFRCH_INTFRC63 (0x80000000) + +/* Bit definitions and macros for MCF_INTC_INTFRCL */ +#define MCF_INTC_INTFRCL_INTFRC0 (0x00000001) +#define MCF_INTC_INTFRCL_INTFRC1 (0x00000002) +#define MCF_INTC_INTFRCL_INTFRC2 (0x00000004) +#define MCF_INTC_INTFRCL_INTFRC3 (0x00000008) +#define MCF_INTC_INTFRCL_INTFRC4 (0x00000010) +#define MCF_INTC_INTFRCL_INTFRC5 (0x00000020) +#define MCF_INTC_INTFRCL_INTFRC6 (0x00000040) +#define MCF_INTC_INTFRCL_INTFRC7 (0x00000080) +#define MCF_INTC_INTFRCL_INTFRC8 (0x00000100) +#define MCF_INTC_INTFRCL_INTFRC9 (0x00000200) +#define MCF_INTC_INTFRCL_INTFRC10 (0x00000400) +#define MCF_INTC_INTFRCL_INTFRC11 (0x00000800) +#define MCF_INTC_INTFRCL_INTFRC12 (0x00001000) +#define MCF_INTC_INTFRCL_INTFRC13 (0x00002000) +#define MCF_INTC_INTFRCL_INTFRC14 (0x00004000) +#define MCF_INTC_INTFRCL_INTFRC15 (0x00008000) +#define MCF_INTC_INTFRCL_INTFRC16 (0x00010000) +#define MCF_INTC_INTFRCL_INTFRC17 (0x00020000) +#define MCF_INTC_INTFRCL_INTFRC18 (0x00040000) +#define MCF_INTC_INTFRCL_INTFRC19 (0x00080000) +#define MCF_INTC_INTFRCL_INTFRC20 (0x00100000) +#define MCF_INTC_INTFRCL_INTFRC21 (0x00200000) +#define MCF_INTC_INTFRCL_INTFRC22 (0x00400000) +#define MCF_INTC_INTFRCL_INTFRC23 (0x00800000) +#define MCF_INTC_INTFRCL_INTFRC24 (0x01000000) +#define MCF_INTC_INTFRCL_INTFRC25 (0x02000000) +#define MCF_INTC_INTFRCL_INTFRC26 (0x04000000) +#define MCF_INTC_INTFRCL_INTFRC27 (0x08000000) +#define MCF_INTC_INTFRCL_INTFRC28 (0x10000000) +#define MCF_INTC_INTFRCL_INTFRC29 (0x20000000) +#define MCF_INTC_INTFRCL_INTFRC30 (0x40000000) +#define MCF_INTC_INTFRCL_INTFRC31 (0x80000000) + +/* Bit definitions and macros for MCF_INTC_ICONFIG */ +#define MCF_INTC_ICONFIG_EMASK (0x0020) +#define MCF_INTC_ICONFIG_ELVLPRI1 (0x0200) +#define MCF_INTC_ICONFIG_ELVLPRI2 (0x0400) +#define MCF_INTC_ICONFIG_ELVLPRI3 (0x0800) +#define MCF_INTC_ICONFIG_ELVLPRI4 (0x1000) +#define MCF_INTC_ICONFIG_ELVLPRI5 (0x2000) +#define MCF_INTC_ICONFIG_ELVLPRI6 (0x4000) +#define MCF_INTC_ICONFIG_ELVLPRI7 (0x8000) + +/* Bit definitions and macros for MCF_INTC_SIMR */ +#define MCF_INTC_SIMR_SIMR(x) (((x)&0x7F)<<0) + +/* Bit definitions and macros for MCF_INTC_CIMR */ +#define MCF_INTC_CIMR_CIMR(x) (((x)&0x7F)<<0) + +/* Bit definitions and macros for MCF_INTC_CLMASK */ +#define MCF_INTC_CLMASK_CLMASK(x) (((x)&0x0F)<<0) + +/* Bit definitions and macros for MCF_INTC_SLMASK */ +#define MCF_INTC_SLMASK_SLMASK(x) (((x)&0x0F)<<0) + +/* Bit definitions and macros for MCF_INTC_ICR */ +#define MCF_INTC_ICR_IL(x) (((x)&0x07)<<0) + +/* Bit definitions and macros for MCF_INTC_SWIACK */ +#define MCF_INTC_SWIACK_VECTOR(x) (((x)&0xFF)<<0) + +/* Bit definitions and macros for MCF_INTC_LIACK */ +#define MCF_INTC_LIACK_VECTOR(x) (((x)&0xFF)<<0) + +/********************************************************************/ +/********************************************************************* +* +* LCD Controller (LCDC) +* +*********************************************************************/ + +/* Register read/write macros */ +#define MCF_LCDC_LSSAR MCF_REG32(0xFC0AC000) +#define MCF_LCDC_LSR MCF_REG32(0xFC0AC004) +#define MCF_LCDC_LVPWR MCF_REG32(0xFC0AC008) +#define MCF_LCDC_LCPR MCF_REG32(0xFC0AC00C) +#define MCF_LCDC_LCWHBR MCF_REG32(0xFC0AC010) +#define MCF_LCDC_LCCMR MCF_REG32(0xFC0AC014) +#define MCF_LCDC_LPCR MCF_REG32(0xFC0AC018) +#define MCF_LCDC_LHCR MCF_REG32(0xFC0AC01C) +#define MCF_LCDC_LVCR MCF_REG32(0xFC0AC020) +#define MCF_LCDC_LPOR MCF_REG32(0xFC0AC024) +#define MCF_LCDC_LSCR MCF_REG32(0xFC0AC028) +#define MCF_LCDC_LPCCR MCF_REG32(0xFC0AC02C) +#define MCF_LCDC_LDCR MCF_REG32(0xFC0AC030) +#define MCF_LCDC_LRMCR MCF_REG32(0xFC0AC034) +#define MCF_LCDC_LICR MCF_REG32(0xFC0AC038) +#define MCF_LCDC_LIER MCF_REG32(0xFC0AC03C) +#define MCF_LCDC_LISR MCF_REG32(0xFC0AC040) +#define MCF_LCDC_LGWSAR MCF_REG32(0xFC0AC050) +#define MCF_LCDC_LGWSR MCF_REG32(0xFC0AC054) +#define MCF_LCDC_LGWVPWR MCF_REG32(0xFC0AC058) +#define MCF_LCDC_LGWPOR MCF_REG32(0xFC0AC05C) +#define MCF_LCDC_LGWPR MCF_REG32(0xFC0AC060) +#define MCF_LCDC_LGWCR MCF_REG32(0xFC0AC064) +#define MCF_LCDC_LGWDCR MCF_REG32(0xFC0AC068) +#define MCF_LCDC_BPLUT_BASE MCF_REG32(0xFC0AC800) +#define MCF_LCDC_GWLUT_BASE MCF_REG32(0xFC0ACC00) + +/* Bit definitions and macros for MCF_LCDC_LSSAR */ +#define MCF_LCDC_LSSAR_SSA(x) (((x)&0x3FFFFFFF)<<2) + +/* Bit definitions and macros for MCF_LCDC_LSR */ +#define MCF_LCDC_LSR_YMAX(x) (((x)&0x000003FF)<<0) +#define MCF_LCDC_LSR_XMAX(x) (((x)&0x0000003F)<<20) + +/* Bit definitions and macros for MCF_LCDC_LVPWR */ +#define MCF_LCDC_LVPWR_VPW(x) (((x)&0x000003FF)<<0) + +/* Bit definitions and macros for MCF_LCDC_LCPR */ +#define MCF_LCDC_LCPR_CYP(x) (((x)&0x000003FF)<<0) +#define MCF_LCDC_LCPR_CXP(x) (((x)&0x000003FF)<<16) +#define MCF_LCDC_LCPR_OP (0x10000000) +#define MCF_LCDC_LCPR_CC(x) (((x)&0x00000003)<<30) +#define MCF_LCDC_LCPR_CC_TRANSPARENT (0x00000000) +#define MCF_LCDC_LCPR_CC_OR (0x40000000) +#define MCF_LCDC_LCPR_CC_XOR (0x80000000) +#define MCF_LCDC_LCPR_CC_AND (0xC0000000) +#define MCF_LCDC_LCPR_OP_ON (0x10000000) +#define MCF_LCDC_LCPR_OP_OFF (0x00000000) + +/* Bit definitions and macros for MCF_LCDC_LCWHBR */ +#define MCF_LCDC_LCWHBR_BD(x) (((x)&0x000000FF)<<0) +#define MCF_LCDC_LCWHBR_CH(x) (((x)&0x0000001F)<<16) +#define MCF_LCDC_LCWHBR_CW(x) (((x)&0x0000001F)<<24) +#define MCF_LCDC_LCWHBR_BK_EN (0x80000000) +#define MCF_LCDC_LCWHBR_BK_EN_ON (0x80000000) +#define MCF_LCDC_LCWHBR_BK_EN_OFF (0x00000000) + +/* Bit definitions and macros for MCF_LCDC_LCCMR */ +#define MCF_LCDC_LCCMR_CUR_COL_B(x) (((x)&0x0000003F)<<0) +#define MCF_LCDC_LCCMR_CUR_COL_G(x) (((x)&0x0000003F)<<6) +#define MCF_LCDC_LCCMR_CUR_COL_R(x) (((x)&0x0000003F)<<12) + +/* Bit definitions and macros for MCF_LCDC_LPCR */ +#define MCF_LCDC_LPCR_PCD(x) (((x)&0x0000003F)<<0) +#define MCF_LCDC_LPCR_SHARP (0x00000040) +#define MCF_LCDC_LPCR_SCLKSEL (0x00000080) +#define MCF_LCDC_LPCR_ACD(x) (((x)&0x0000007F)<<8) +#define MCF_LCDC_LPCR_ACDSEL (0x00008000) +#define MCF_LCDC_LPCR_REV_VS (0x00010000) +#define MCF_LCDC_LPCR_SWAP_SEL (0x00020000) +#define MCF_LCDC_LPCR_ENDSEL (0x00040000) +#define MCF_LCDC_LPCR_SCLKIDLE (0x00080000) +#define MCF_LCDC_LPCR_OEPOL (0x00100000) +#define MCF_LCDC_LPCR_CLKPOL (0x00200000) +#define MCF_LCDC_LPCR_LPPOL (0x00400000) +#define MCF_LCDC_LPCR_FLM (0x00800000) +#define MCF_LCDC_LPCR_PIXPOL (0x01000000) +#define MCF_LCDC_LPCR_BPIX(x) (((x)&0x00000007)<<25) +#define MCF_LCDC_LPCR_PBSIZ(x) (((x)&0x00000003)<<28) +#define MCF_LCDC_LPCR_COLOR (0x40000000) +#define MCF_LCDC_LPCR_TFT (0x80000000) +#define MCF_LCDC_LPCR_MODE_MONOCGROME (0x00000000) +#define MCF_LCDC_LPCR_MODE_CSTN (0x40000000) +#define MCF_LCDC_LPCR_MODE_TFT (0xC0000000) +#define MCF_LCDC_LPCR_PBSIZ_1 (0x00000000) +#define MCF_LCDC_LPCR_PBSIZ_2 (0x10000000) +#define MCF_LCDC_LPCR_PBSIZ_4 (0x20000000) +#define MCF_LCDC_LPCR_PBSIZ_8 (0x30000000) +#define MCF_LCDC_LPCR_BPIX_1bpp (0x00000000) +#define MCF_LCDC_LPCR_BPIX_2bpp (0x02000000) +#define MCF_LCDC_LPCR_BPIX_4bpp (0x04000000) +#define MCF_LCDC_LPCR_BPIX_8bpp (0x06000000) +#define MCF_LCDC_LPCR_BPIX_12bpp (0x08000000) +#define MCF_LCDC_LPCR_BPIX_16bpp (0x0A000000) +#define MCF_LCDC_LPCR_BPIX_18bpp (0x0C000000) + +#define MCF_LCDC_LPCR_PANEL_TYPE(x) (((x)&0x00000003)<<30) + +/* Bit definitions and macros for MCF_LCDC_LHCR */ +#define MCF_LCDC_LHCR_H_WAIT_2(x) (((x)&0x000000FF)<<0) +#define MCF_LCDC_LHCR_H_WAIT_1(x) (((x)&0x000000FF)<<8) +#define MCF_LCDC_LHCR_H_WIDTH(x) (((x)&0x0000003F)<<26) + +/* Bit definitions and macros for MCF_LCDC_LVCR */ +#define MCF_LCDC_LVCR_V_WAIT_2(x) (((x)&0x000000FF)<<0) +#define MCF_LCDC_LVCR_V_WAIT_1(x) (((x)&0x000000FF)<<8) +#define MCF_LCDC_LVCR_V_WIDTH(x) (((x)&0x0000003F)<<26) + +/* Bit definitions and macros for MCF_LCDC_LPOR */ +#define MCF_LCDC_LPOR_POS(x) (((x)&0x0000001F)<<0) + +/* Bit definitions and macros for MCF_LCDC_LPCCR */ +#define MCF_LCDC_LPCCR_PW(x) (((x)&0x000000FF)<<0) +#define MCF_LCDC_LPCCR_CC_EN (0x00000100) +#define MCF_LCDC_LPCCR_SCR(x) (((x)&0x00000003)<<9) +#define MCF_LCDC_LPCCR_LDMSK (0x00008000) +#define MCF_LCDC_LPCCR_CLS_HI_WIDTH(x) (((x)&0x000001FF)<<16) +#define MCF_LCDC_LPCCR_SCR_LINEPULSE (0x00000000) +#define MCF_LCDC_LPCCR_SCR_PIXELCLK (0x00002000) +#define MCF_LCDC_LPCCR_SCR_LCDCLOCK (0x00004000) + +/* Bit definitions and macros for MCF_LCDC_LDCR */ +#define MCF_LCDC_LDCR_TM(x) (((x)&0x0000001F)<<0) +#define MCF_LCDC_LDCR_HM(x) (((x)&0x0000001F)<<16) +#define MCF_LCDC_LDCR_BURST (0x80000000) + +/* Bit definitions and macros for MCF_LCDC_LRMCR */ +#define MCF_LCDC_LRMCR_SEL_REF (0x00000001) + +/* Bit definitions and macros for MCF_LCDC_LICR */ +#define MCF_LCDC_LICR_INTCON (0x00000001) +#define MCF_LCDC_LICR_INTSYN (0x00000004) +#define MCF_LCDC_LICR_GW_INT_CON (0x00000010) + +/* Bit definitions and macros for MCF_LCDC_LIER */ +#define MCF_LCDC_LIER_BOF_EN (0x00000001) +#define MCF_LCDC_LIER_EOF_EN (0x00000002) +#define MCF_LCDC_LIER_ERR_RES_EN (0x00000004) +#define MCF_LCDC_LIER_UDR_ERR_EN (0x00000008) +#define MCF_LCDC_LIER_GW_BOF_EN (0x00000010) +#define MCF_LCDC_LIER_GW_EOF_EN (0x00000020) +#define MCF_LCDC_LIER_GW_ERR_RES_EN (0x00000040) +#define MCF_LCDC_LIER_GW_UDR_ERR_EN (0x00000080) + +/* Bit definitions and macros for MCF_LCDC_LISR */ +#define MCF_LCDC_LISR_BOF (0x00000001) +#define MCF_LCDC_LISR_EOF (0x00000002) +#define MCF_LCDC_LISR_ERR_RES (0x00000004) +#define MCF_LCDC_LISR_UDR_ERR (0x00000008) +#define MCF_LCDC_LISR_GW_BOF (0x00000010) +#define MCF_LCDC_LISR_GW_EOF (0x00000020) +#define MCF_LCDC_LISR_GW_ERR_RES (0x00000040) +#define MCF_LCDC_LISR_GW_UDR_ERR (0x00000080) + +/* Bit definitions and macros for MCF_LCDC_LGWSAR */ +#define MCF_LCDC_LGWSAR_GWSA(x) (((x)&0x3FFFFFFF)<<2) + +/* Bit definitions and macros for MCF_LCDC_LGWSR */ +#define MCF_LCDC_LGWSR_GWH(x) (((x)&0x000003FF)<<0) +#define MCF_LCDC_LGWSR_GWW(x) (((x)&0x0000003F)<<20) + +/* Bit definitions and macros for MCF_LCDC_LGWVPWR */ +#define MCF_LCDC_LGWVPWR_GWVPW(x) (((x)&0x000003FF)<<0) + +/* Bit definitions and macros for MCF_LCDC_LGWPOR */ +#define MCF_LCDC_LGWPOR_GWPO(x) (((x)&0x0000001F)<<0) + +/* Bit definitions and macros for MCF_LCDC_LGWPR */ +#define MCF_LCDC_LGWPR_GWYP(x) (((x)&0x000003FF)<<0) +#define MCF_LCDC_LGWPR_GWXP(x) (((x)&0x000003FF)<<16) + +/* Bit definitions and macros for MCF_LCDC_LGWCR */ +#define MCF_LCDC_LGWCR_GWCKB(x) (((x)&0x0000003F)<<0) +#define MCF_LCDC_LGWCR_GWCKG(x) (((x)&0x0000003F)<<6) +#define MCF_LCDC_LGWCR_GWCKR(x) (((x)&0x0000003F)<<12) +#define MCF_LCDC_LGWCR_GW_RVS (0x00200000) +#define MCF_LCDC_LGWCR_GWE (0x00400000) +#define MCF_LCDC_LGWCR_GWCKE (0x00800000) +#define MCF_LCDC_LGWCR_GWAV(x) (((x)&0x000000FF)<<24) + +/* Bit definitions and macros for MCF_LCDC_LGWDCR */ +#define MCF_LCDC_LGWDCR_GWTM(x) (((x)&0x0000001F)<<0) +#define MCF_LCDC_LGWDCR_GWHM(x) (((x)&0x0000001F)<<16) +#define MCF_LCDC_LGWDCR_GWBT (0x80000000) + +/* Bit definitions and macros for MCF_LCDC_LSCR */ +#define MCF_LCDC_LSCR_PS_RISE_DELAY(x) (((x)&0x0000003F)<<26) +#define MCF_LCDC_LSCR_CLS_RISE_DELAY(x) (((x)&0x000000FF)<<16) +#define MCF_LCDC_LSCR_REV_TOGGLE_DELAY(x) (((x)&0x0000000F)<<8) +#define MCF_LCDC_LSCR_GRAY_2(x) (((x)&0x0000000F)<<4) +#define MCF_LCDC_LSCR_GRAY_1(x) (((x)&0x0000000F)<<0) + +/* Bit definitions and macros for MCF_LCDC_BPLUT_BASE */ +#define MCF_LCDC_BPLUT_BASE_BASE(x) (((x)&0xFFFFFFFF)<<0) + +/* Bit definitions and macros for MCF_LCDC_GWLUT_BASE */ +#define MCF_LCDC_GWLUT_BASE_BASE(x) (((x)&0xFFFFFFFF)<<0) + +/********************************************************************* + * + * Phase Locked Loop (PLL) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_PLL_PODR MCF_REG08(0xFC0C0000) +#define MCF_PLL_PLLCR MCF_REG08(0xFC0C0004) +#define MCF_PLL_PMDR MCF_REG08(0xFC0C0008) +#define MCF_PLL_PFDR MCF_REG08(0xFC0C000C) + +/* Bit definitions and macros for MCF_PLL_PODR */ +#define MCF_PLL_PODR_BUSDIV(x) (((x)&0x0F)<<0) +#define MCF_PLL_PODR_CPUDIV(x) (((x)&0x0F)<<4) + +/* Bit definitions and macros for MCF_PLL_PLLCR */ +#define MCF_PLL_PLLCR_DITHDEV(x) (((x)&0x07)<<0) +#define MCF_PLL_PLLCR_DITHEN (0x80) + +/* Bit definitions and macros for MCF_PLL_PMDR */ +#define MCF_PLL_PMDR_MODDIV(x) (((x)&0xFF)<<0) + +/* Bit definitions and macros for MCF_PLL_PFDR */ +#define MCF_PLL_PFDR_MFD(x) (((x)&0xFF)<<0) + +/********************************************************************* + * + * System Control Module Registers (SCM) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_SCM_MPR MCF_REG32(0xFC000000) +#define MCF_SCM_PACRA MCF_REG32(0xFC000020) +#define MCF_SCM_PACRB MCF_REG32(0xFC000024) +#define MCF_SCM_PACRC MCF_REG32(0xFC000028) +#define MCF_SCM_PACRD MCF_REG32(0xFC00002C) +#define MCF_SCM_PACRE MCF_REG32(0xFC000040) +#define MCF_SCM_PACRF MCF_REG32(0xFC000044) + +#define MCF_SCM_BCR MCF_REG32(0xFC040024) + +/********************************************************************* + * + * SDRAM Controller (SDRAMC) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_SDRAMC_SDMR MCF_REG32(0xFC0B8000) +#define MCF_SDRAMC_SDCR MCF_REG32(0xFC0B8004) +#define MCF_SDRAMC_SDCFG1 MCF_REG32(0xFC0B8008) +#define MCF_SDRAMC_SDCFG2 MCF_REG32(0xFC0B800C) +#define MCF_SDRAMC_LIMP_FIX MCF_REG32(0xFC0B8080) +#define MCF_SDRAMC_SDDS MCF_REG32(0xFC0B8100) +#define MCF_SDRAMC_SDCS0 MCF_REG32(0xFC0B8110) +#define MCF_SDRAMC_SDCS1 MCF_REG32(0xFC0B8114) +#define MCF_SDRAMC_SDCS2 MCF_REG32(0xFC0B8118) +#define MCF_SDRAMC_SDCS3 MCF_REG32(0xFC0B811C) +#define MCF_SDRAMC_SDCS(x) MCF_REG32(0xFC0B8110+((x)*0x004)) + +/* Bit definitions and macros for MCF_SDRAMC_SDMR */ +#define MCF_SDRAMC_SDMR_CMD (0x00010000) +#define MCF_SDRAMC_SDMR_AD(x) (((x)&0x00000FFF)<<18) +#define MCF_SDRAMC_SDMR_BNKAD(x) (((x)&0x00000003)<<30) +#define MCF_SDRAMC_SDMR_BNKAD_LMR (0x00000000) +#define MCF_SDRAMC_SDMR_BNKAD_LEMR (0x40000000) + +/* Bit definitions and macros for MCF_SDRAMC_SDCR */ +#define MCF_SDRAMC_SDCR_IPALL (0x00000002) +#define MCF_SDRAMC_SDCR_IREF (0x00000004) +#define MCF_SDRAMC_SDCR_DQS_OE(x) (((x)&0x0000000F)<<8) +#define MCF_SDRAMC_SDCR_PS(x) (((x)&0x00000003)<<12) +#define MCF_SDRAMC_SDCR_RCNT(x) (((x)&0x0000003F)<<16) +#define MCF_SDRAMC_SDCR_OE_RULE (0x00400000) +#define MCF_SDRAMC_SDCR_MUX(x) (((x)&0x00000003)<<24) +#define MCF_SDRAMC_SDCR_REF (0x10000000) +#define MCF_SDRAMC_SDCR_DDR (0x20000000) +#define MCF_SDRAMC_SDCR_CKE (0x40000000) +#define MCF_SDRAMC_SDCR_MODE_EN (0x80000000) +#define MCF_SDRAMC_SDCR_PS_16 (0x00002000) +#define MCF_SDRAMC_SDCR_PS_32 (0x00000000) + +/* Bit definitions and macros for MCF_SDRAMC_SDCFG1 */ +#define MCF_SDRAMC_SDCFG1_WTLAT(x) (((x)&0x00000007)<<4) +#define MCF_SDRAMC_SDCFG1_REF2ACT(x) (((x)&0x0000000F)<<8) +#define MCF_SDRAMC_SDCFG1_PRE2ACT(x) (((x)&0x00000007)<<12) +#define MCF_SDRAMC_SDCFG1_ACT2RW(x) (((x)&0x00000007)<<16) +#define MCF_SDRAMC_SDCFG1_RDLAT(x) (((x)&0x0000000F)<<20) +#define MCF_SDRAMC_SDCFG1_SWT2RD(x) (((x)&0x00000007)<<24) +#define MCF_SDRAMC_SDCFG1_SRD2RW(x) (((x)&0x0000000F)<<28) + +/* Bit definitions and macros for MCF_SDRAMC_SDCFG2 */ +#define MCF_SDRAMC_SDCFG2_BL(x) (((x)&0x0000000F)<<16) +#define MCF_SDRAMC_SDCFG2_BRD2WT(x) (((x)&0x0000000F)<<20) +#define MCF_SDRAMC_SDCFG2_BWT2RW(x) (((x)&0x0000000F)<<24) +#define MCF_SDRAMC_SDCFG2_BRD2PRE(x) (((x)&0x0000000F)<<28) + +/* Device Errata - LIMP mode work around */ +#define MCF_SDRAMC_REFRESH (0x40000000) + +/* Bit definitions and macros for MCF_SDRAMC_SDDS */ +#define MCF_SDRAMC_SDDS_SB_D(x) (((x)&0x00000003)<<0) +#define MCF_SDRAMC_SDDS_SB_S(x) (((x)&0x00000003)<<2) +#define MCF_SDRAMC_SDDS_SB_A(x) (((x)&0x00000003)<<4) +#define MCF_SDRAMC_SDDS_SB_C(x) (((x)&0x00000003)<<6) +#define MCF_SDRAMC_SDDS_SB_E(x) (((x)&0x00000003)<<8) + +/* Bit definitions and macros for MCF_SDRAMC_SDCS */ +#define MCF_SDRAMC_SDCS_CSSZ(x) (((x)&0x0000001F)<<0) +#define MCF_SDRAMC_SDCS_BASE(x) (((x)&0x00000FFF)<<20) +#define MCF_SDRAMC_SDCS_BA(x) ((x)&0xFFF00000) +#define MCF_SDRAMC_SDCS_CSSZ_DIABLE (0x00000000) +#define MCF_SDRAMC_SDCS_CSSZ_1MBYTE (0x00000013) +#define MCF_SDRAMC_SDCS_CSSZ_2MBYTE (0x00000014) +#define MCF_SDRAMC_SDCS_CSSZ_4MBYTE (0x00000015) +#define MCF_SDRAMC_SDCS_CSSZ_8MBYTE (0x00000016) +#define MCF_SDRAMC_SDCS_CSSZ_16MBYTE (0x00000017) +#define MCF_SDRAMC_SDCS_CSSZ_32MBYTE (0x00000018) +#define MCF_SDRAMC_SDCS_CSSZ_64MBYTE (0x00000019) +#define MCF_SDRAMC_SDCS_CSSZ_128MBYTE (0x0000001A) +#define MCF_SDRAMC_SDCS_CSSZ_256MBYTE (0x0000001B) +#define MCF_SDRAMC_SDCS_CSSZ_512MBYTE (0x0000001C) +#define MCF_SDRAMC_SDCS_CSSZ_1GBYTE (0x0000001D) +#define MCF_SDRAMC_SDCS_CSSZ_2GBYTE (0x0000001E) +#define MCF_SDRAMC_SDCS_CSSZ_4GBYTE (0x0000001F) + +/********************************************************************* + * + * FlexCAN module registers + * + *********************************************************************/ +#define MCF_FLEXCAN_BASEADDR(x) (0xFC020000+(x)*0x0800) +#define MCF_FLEXCAN_CANMCR(x) MCF_REG32(0xFC020000+(x)*0x0800+0x00) +#define MCF_FLEXCAN_CANCTRL(x) MCF_REG32(0xFC020000+(x)*0x0800+0x04) +#define MCF_FLEXCAN_TIMER(x) MCF_REG32(0xFC020000+(x)*0x0800+0x08) +#define MCF_FLEXCAN_RXGMASK(x) MCF_REG32(0xFC020000+(x)*0x0800+0x10) +#define MCF_FLEXCAN_RX14MASK(x) MCF_REG32(0xFC020000+(x)*0x0800+0x14) +#define MCF_FLEXCAN_RX15MASK(x) MCF_REG32(0xFC020000+(x)*0x0800+0x18) +#define MCF_FLEXCAN_ERRCNT(x) MCF_REG32(0xFC020000+(x)*0x0800+0x1C) +#define MCF_FLEXCAN_ERRSTAT(x) MCF_REG32(0xFC020000+(x)*0x0800+0x20) +#define MCF_FLEXCAN_IMASK(x) MCF_REG32(0xFC020000+(x)*0x0800+0x28) +#define MCF_FLEXCAN_IFLAG(x) MCF_REG32(0xFC020000+(x)*0x0800+0x30) + +#define MCF_FLEXCAN_MB_CNT(x,y) MCF_REG32(0xFC020080+(x)*0x0800+(y)*0x10+0x0) +#define MCF_FLEXCAN_MB_ID(x,y) MCF_REG32(0xFC020080+(x)*0x0800+(y)*0x10+0x4) +#define MCF_FLEXCAN_MB_DB(x,y,z) MCF_REG08(0xFC020080+(x)*0x0800+(y)*0x10+0x8+(z)*0x1) + +/* + * FlexCAN Module Configuration Register + */ +#define CANMCR_MDIS (0x80000000) +#define CANMCR_FRZ (0x40000000) +#define CANMCR_HALT (0x10000000) +#define CANMCR_SOFTRST (0x02000000) +#define CANMCR_FRZACK (0x01000000) +#define CANMCR_SUPV (0x00800000) +#define CANMCR_MAXMB(x) ((x)&0x0F) + +/* + * FlexCAN Control Register + */ +#define CANCTRL_PRESDIV(x) (((x)&0xFF)<<24) +#define CANCTRL_RJW(x) (((x)&0x03)<<22) +#define CANCTRL_PSEG1(x) (((x)&0x07)<<19) +#define CANCTRL_PSEG2(x) (((x)&0x07)<<16) +#define CANCTRL_BOFFMSK (0x00008000) +#define CANCTRL_ERRMSK (0x00004000) +#define CANCTRL_CLKSRC (0x00002000) +#define CANCTRL_LPB (0x00001000) +#define CANCTRL_SAMP (0x00000080) +#define CANCTRL_BOFFREC (0x00000040) +#define CANCTRL_TSYNC (0x00000020) +#define CANCTRL_LBUF (0x00000010) +#define CANCTRL_LOM (0x00000008) +#define CANCTRL_PROPSEG(x) ((x)&0x07) + +/* + * FlexCAN Error Counter Register + */ +#define ERRCNT_RXECTR(x) (((x)&0xFF)<<8) +#define ERRCNT_TXECTR(x) ((x)&0xFF) + +/* + * FlexCAN Error and Status Register + */ +#define ERRSTAT_BITERR(x) (((x)&0x03)<<14) +#define ERRSTAT_ACKERR (0x00002000) +#define ERRSTAT_CRCERR (0x00001000) +#define ERRSTAT_FRMERR (0x00000800) +#define ERRSTAT_STFERR (0x00000400) +#define ERRSTAT_TXWRN (0x00000200) +#define ERRSTAT_RXWRN (0x00000100) +#define ERRSTAT_IDLE (0x00000080) +#define ERRSTAT_TXRX (0x00000040) +#define ERRSTAT_FLTCONF(x) (((x)&0x03)<<4) +#define ERRSTAT_BOFFINT (0x00000004) +#define ERRSTAT_ERRINT (0x00000002) + +/* + * Interrupt Mask Register + */ +#define IMASK_BUF15M (0x8000) +#define IMASK_BUF14M (0x4000) +#define IMASK_BUF13M (0x2000) +#define IMASK_BUF12M (0x1000) +#define IMASK_BUF11M (0x0800) +#define IMASK_BUF10M (0x0400) +#define IMASK_BUF9M (0x0200) +#define IMASK_BUF8M (0x0100) +#define IMASK_BUF7M (0x0080) +#define IMASK_BUF6M (0x0040) +#define IMASK_BUF5M (0x0020) +#define IMASK_BUF4M (0x0010) +#define IMASK_BUF3M (0x0008) +#define IMASK_BUF2M (0x0004) +#define IMASK_BUF1M (0x0002) +#define IMASK_BUF0M (0x0001) +#define IMASK_BUFnM(x) (0x1<<(x)) +#define IMASK_BUFF_ENABLE_ALL (0x1111) +#define IMASK_BUFF_DISABLE_ALL (0x0000) + +/* + * Interrupt Flag Register + */ +#define IFLAG_BUF15M (0x8000) +#define IFLAG_BUF14M (0x4000) +#define IFLAG_BUF13M (0x2000) +#define IFLAG_BUF12M (0x1000) +#define IFLAG_BUF11M (0x0800) +#define IFLAG_BUF10M (0x0400) +#define IFLAG_BUF9M (0x0200) +#define IFLAG_BUF8M (0x0100) +#define IFLAG_BUF7M (0x0080) +#define IFLAG_BUF6M (0x0040) +#define IFLAG_BUF5M (0x0020) +#define IFLAG_BUF4M (0x0010) +#define IFLAG_BUF3M (0x0008) +#define IFLAG_BUF2M (0x0004) +#define IFLAG_BUF1M (0x0002) +#define IFLAG_BUF0M (0x0001) +#define IFLAG_BUFF_SET_ALL (0xFFFF) +#define IFLAG_BUFF_CLEAR_ALL (0x0000) +#define IFLAG_BUFnM(x) (0x1<<(x)) + +/* + * Message Buffers + */ +#define MB_CNT_CODE(x) (((x)&0x0F)<<24) +#define MB_CNT_SRR (0x00400000) +#define MB_CNT_IDE (0x00200000) +#define MB_CNT_RTR (0x00100000) +#define MB_CNT_LENGTH(x) (((x)&0x0F)<<16) +#define MB_CNT_TIMESTAMP(x) ((x)&0xFFFF) +#define MB_ID_STD(x) (((x)&0x07FF)<<18) +#define MB_ID_EXT(x) ((x)&0x3FFFF) + +/********************************************************************* + * + * Edge Port Module (EPORT) + * + *********************************************************************/ + +/* Register read/write macros */ +#define MCF_EPORT_EPPAR MCF_REG16(0xFC094000) +#define MCF_EPORT_EPDDR MCF_REG08(0xFC094002) +#define MCF_EPORT_EPIER MCF_REG08(0xFC094003) +#define MCF_EPORT_EPDR MCF_REG08(0xFC094004) +#define MCF_EPORT_EPPDR MCF_REG08(0xFC094005) +#define MCF_EPORT_EPFR MCF_REG08(0xFC094006) + +/* Bit definitions and macros for MCF_EPORT_EPPAR */ +#define MCF_EPORT_EPPAR_EPPA1(x) (((x)&0x0003)<<2) +#define MCF_EPORT_EPPAR_EPPA2(x) (((x)&0x0003)<<4) +#define MCF_EPORT_EPPAR_EPPA3(x) (((x)&0x0003)<<6) +#define MCF_EPORT_EPPAR_EPPA4(x) (((x)&0x0003)<<8) +#define MCF_EPORT_EPPAR_EPPA5(x) (((x)&0x0003)<<10) +#define MCF_EPORT_EPPAR_EPPA6(x) (((x)&0x0003)<<12) +#define MCF_EPORT_EPPAR_EPPA7(x) (((x)&0x0003)<<14) +#define MCF_EPORT_EPPAR_LEVEL (0) +#define MCF_EPORT_EPPAR_RISING (1) +#define MCF_EPORT_EPPAR_FALLING (2) +#define MCF_EPORT_EPPAR_BOTH (3) +#define MCF_EPORT_EPPAR_EPPA7_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA7_RISING (0x4000) +#define MCF_EPORT_EPPAR_EPPA7_FALLING (0x8000) +#define MCF_EPORT_EPPAR_EPPA7_BOTH (0xC000) +#define MCF_EPORT_EPPAR_EPPA6_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA6_RISING (0x1000) +#define MCF_EPORT_EPPAR_EPPA6_FALLING (0x2000) +#define MCF_EPORT_EPPAR_EPPA6_BOTH (0x3000) +#define MCF_EPORT_EPPAR_EPPA5_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA5_RISING (0x0400) +#define MCF_EPORT_EPPAR_EPPA5_FALLING (0x0800) +#define MCF_EPORT_EPPAR_EPPA5_BOTH (0x0C00) +#define MCF_EPORT_EPPAR_EPPA4_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA4_RISING (0x0100) +#define MCF_EPORT_EPPAR_EPPA4_FALLING (0x0200) +#define MCF_EPORT_EPPAR_EPPA4_BOTH (0x0300) +#define MCF_EPORT_EPPAR_EPPA3_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA3_RISING (0x0040) +#define MCF_EPORT_EPPAR_EPPA3_FALLING (0x0080) +#define MCF_EPORT_EPPAR_EPPA3_BOTH (0x00C0) +#define MCF_EPORT_EPPAR_EPPA2_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA2_RISING (0x0010) +#define MCF_EPORT_EPPAR_EPPA2_FALLING (0x0020) +#define MCF_EPORT_EPPAR_EPPA2_BOTH (0x0030) +#define MCF_EPORT_EPPAR_EPPA1_LEVEL (0x0000) +#define MCF_EPORT_EPPAR_EPPA1_RISING (0x0004) +#define MCF_EPORT_EPPAR_EPPA1_FALLING (0x0008) +#define MCF_EPORT_EPPAR_EPPA1_BOTH (0x000C) + +/* Bit definitions and macros for MCF_EPORT_EPDDR */ +#define MCF_EPORT_EPDDR_EPDD1 (0x02) +#define MCF_EPORT_EPDDR_EPDD2 (0x04) +#define MCF_EPORT_EPDDR_EPDD3 (0x08) +#define MCF_EPORT_EPDDR_EPDD4 (0x10) +#define MCF_EPORT_EPDDR_EPDD5 (0x20) +#define MCF_EPORT_EPDDR_EPDD6 (0x40) +#define MCF_EPORT_EPDDR_EPDD7 (0x80) + +/* Bit definitions and macros for MCF_EPORT_EPIER */ +#define MCF_EPORT_EPIER_EPIE1 (0x02) +#define MCF_EPORT_EPIER_EPIE2 (0x04) +#define MCF_EPORT_EPIER_EPIE3 (0x08) +#define MCF_EPORT_EPIER_EPIE4 (0x10) +#define MCF_EPORT_EPIER_EPIE5 (0x20) +#define MCF_EPORT_EPIER_EPIE6 (0x40) +#define MCF_EPORT_EPIER_EPIE7 (0x80) + +/* Bit definitions and macros for MCF_EPORT_EPDR */ +#define MCF_EPORT_EPDR_EPD1 (0x02) +#define MCF_EPORT_EPDR_EPD2 (0x04) +#define MCF_EPORT_EPDR_EPD3 (0x08) +#define MCF_EPORT_EPDR_EPD4 (0x10) +#define MCF_EPORT_EPDR_EPD5 (0x20) +#define MCF_EPORT_EPDR_EPD6 (0x40) +#define MCF_EPORT_EPDR_EPD7 (0x80) + +/* Bit definitions and macros for MCF_EPORT_EPPDR */ +#define MCF_EPORT_EPPDR_EPPD1 (0x02) +#define MCF_EPORT_EPPDR_EPPD2 (0x04) +#define MCF_EPORT_EPPDR_EPPD3 (0x08) +#define MCF_EPORT_EPPDR_EPPD4 (0x10) +#define MCF_EPORT_EPPDR_EPPD5 (0x20) +#define MCF_EPORT_EPPDR_EPPD6 (0x40) +#define MCF_EPORT_EPPDR_EPPD7 (0x80) + +/* Bit definitions and macros for MCF_EPORT_EPFR */ +#define MCF_EPORT_EPFR_EPF1 (0x02) +#define MCF_EPORT_EPFR_EPF2 (0x04) +#define MCF_EPORT_EPFR_EPF3 (0x08) +#define MCF_EPORT_EPFR_EPF4 (0x10) +#define MCF_EPORT_EPFR_EPF5 (0x20) +#define MCF_EPORT_EPFR_EPF6 (0x40) +#define MCF_EPORT_EPFR_EPF7 (0x80) + +/********************************************************************/ +#endif /* m532xsim_h */ diff --git a/include/asm-m68knommu/mcfcache.h b/include/asm-m68knommu/mcfcache.h index 9cb4014..7b61a8a 100644 --- a/include/asm-m68knommu/mcfcache.h +++ b/include/asm-m68knommu/mcfcache.h @@ -11,7 +11,6 @@ #ifndef __M68KNOMMU_MCFCACHE_H #define __M68KNOMMU_MCFCACHE_H /****************************************************************************/ -#include /* * The different ColdFire families have different cache arrangments. @@ -93,6 +92,21 @@ #endif .endm #endif /* CONFIG_M5249 || CONFIG_M5307 */ +#if defined(CONFIG_M532x) +.macro CACHE_ENABLE + movel #0x01000000,%d0 /* invalidate cache cmd */ + movec %d0,%CACR /* do invalidate cache */ + nop + movel #0x4001C000,%d0 /* set SDRAM cached (write-thru) */ + movec %d0,%ACR0 + movel #0x00000000,%d0 /* no other regions cached */ + movec %d0,%ACR1 + movel #0x80000200,%d0 /* setup cache mask */ + movec %d0,%CACR /* enable cache */ + nop +.endm +#endif /* CONFIG_M532x */ + #if defined(CONFIG_M5407) /* * Version 4 cores have a true harvard style separate instruction diff --git a/include/asm-m68knommu/mcfdma.h b/include/asm-m68knommu/mcfdma.h index b93f8ba..ea729e8 100644 --- a/include/asm-m68knommu/mcfdma.h +++ b/include/asm-m68knommu/mcfdma.h @@ -11,7 +11,6 @@ #ifndef mcfdma_h #define mcfdma_h /****************************************************************************/ -#include /* * Get address specific defines for this Coldfire member. diff --git a/include/asm-m68knommu/mcfmbus.h b/include/asm-m68knommu/mcfmbus.h index 4762589..13df9d4 100644 --- a/include/asm-m68knommu/mcfmbus.h +++ b/include/asm-m68knommu/mcfmbus.h @@ -11,7 +11,6 @@ #ifndef mcfmbus_h #define mcfmbus_h -#include #define MCFMBUS_BASE 0x280 diff --git a/include/asm-m68knommu/mcfne.h b/include/asm-m68knommu/mcfne.h index a71b1c8..c920ccd 100644 --- a/include/asm-m68knommu/mcfne.h +++ b/include/asm-m68knommu/mcfne.h @@ -18,7 +18,6 @@ #ifndef mcfne_h #define mcfne_h /****************************************************************************/ -#include /* * Support for NE2000 clones devices in ColdFire based boards. diff --git a/include/asm-m68knommu/mcfpci.h b/include/asm-m68knommu/mcfpci.h index d622904..f1507dd 100644 --- a/include/asm-m68knommu/mcfpci.h +++ b/include/asm-m68knommu/mcfpci.h @@ -12,7 +12,6 @@ #ifndef mcfpci_h #define mcfpci_h /****************************************************************************/ -#include #ifdef CONFIG_PCI diff --git a/include/asm-m68knommu/mcfpit.h b/include/asm-m68knommu/mcfpit.h index a685f1b..f570cf6 100644 --- a/include/asm-m68knommu/mcfpit.h +++ b/include/asm-m68knommu/mcfpit.h @@ -11,7 +11,6 @@ #ifndef mcfpit_h #define mcfpit_h /****************************************************************************/ -#include /* * Get address specific defines for the 5270/5271, 5280/5282, and 5208. @@ -29,11 +28,9 @@ #endif /* * Define the PIT timer register set addresses. */ -struct mcfpit { - unsigned short pcsr; /* PIT control and status */ - unsigned short pmr; /* PIT modulus register */ - unsigned short pcntr; /* PIT count register */ -} __attribute__((packed)); +#define MCFPIT_PCSR 0x0 /* PIT control register */ +#define MCFPIT_PMR 0x2 /* PIT modulus register */ +#define MCFPIT_PCNTR 0x4 /* PIT count register */ /* * Bit definitions for the PIT Control and Status register. diff --git a/include/asm-m68knommu/mcfsim.h b/include/asm-m68knommu/mcfsim.h index 81d74a3..1074ae7 100644 --- a/include/asm-m68knommu/mcfsim.h +++ b/include/asm-m68knommu/mcfsim.h @@ -12,7 +12,6 @@ #ifndef mcfsim_h #define mcfsim_h /****************************************************************************/ -#include /* * Include 5204, 5206/e, 5235, 5249, 5270/5271, 5272, 5280/5282, @@ -36,6 +35,8 @@ #elif defined(CONFIG_M528x) #include #elif defined(CONFIG_M5307) #include +#elif defined(CONFIG_M532x) +#include #elif defined(CONFIG_M5407) #include #endif @@ -101,6 +102,7 @@ #ifndef MCFSIM_IMR_MASKALL #define MCFSIM_IMR_MASKALL 0x3ffe /* All intr sources */ #endif + /* * PIT interrupt settings, if not found in mXXXXsim.h file. */ diff --git a/include/asm-m68knommu/mcfsmc.h b/include/asm-m68knommu/mcfsmc.h index 2583900..2d7a4db 100644 --- a/include/asm-m68knommu/mcfsmc.h +++ b/include/asm-m68knommu/mcfsmc.h @@ -17,7 +17,6 @@ #define mcfsmc_h * allow 8 bit accesses. So this code is 16bit access only. */ -#include #undef outb #undef inb diff --git a/include/asm-m68knommu/mcftimer.h b/include/asm-m68knommu/mcftimer.h index 0f47164..6f4d796 100644 --- a/include/asm-m68knommu/mcftimer.h +++ b/include/asm-m68knommu/mcftimer.h @@ -3,7 +3,7 @@ /* * mcftimer.h -- ColdFire internal TIMER support defines. * - * (C) Copyright 1999-2002, Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 1999-2006, Greg Ungerer * (C) Copyright 2000, Lineo Inc. (www.lineo.com) */ @@ -12,7 +12,6 @@ #ifndef mcftimer_h #define mcftimer_h /****************************************************************************/ -#include /* * Get address specific defines for this ColdFire member. @@ -28,6 +27,11 @@ #define MCFTIMER_BASE4 0x260 #elif defined(CONFIG_M5249) || defined(CONFIG_M5307) || defined(CONFIG_M5407) #define MCFTIMER_BASE1 0x140 /* Base address of TIMER1 */ #define MCFTIMER_BASE2 0x180 /* Base address of TIMER2 */ +#elif defined(CONFIG_M532x) +#define MCFTIMER_BASE1 0xfc070000 /* Base address of TIMER1 */ +#define MCFTIMER_BASE2 0xfc074000 /* Base address of TIMER2 */ +#define MCFTIMER_BASE3 0xfc078000 /* Base address of TIMER3 */ +#define MCFTIMER_BASE4 0xfc07c000 /* Base address of TIMER4 */ #endif @@ -35,23 +39,14 @@ #endif * Define the TIMER register set addresses. */ #define MCFTIMER_TMR 0x00 /* Timer Mode reg (r/w) */ -#define MCFTIMER_TRR 0x02 /* Timer Reference (r/w) */ -#define MCFTIMER_TCR 0x04 /* Timer Capture reg (r/w) */ -#define MCFTIMER_TCN 0x06 /* Timer Counter reg (r/w) */ +#define MCFTIMER_TRR 0x04 /* Timer Reference (r/w) */ +#define MCFTIMER_TCR 0x08 /* Timer Capture reg (r/w) */ +#define MCFTIMER_TCN 0x0C /* Timer Counter reg (r/w) */ +#if defined(CONFIG_M532x) +#define MCFTIMER_TER 0x03 /* Timer Event reg (r/w) */ +#else #define MCFTIMER_TER 0x11 /* Timer Event reg (r/w) */ - -struct mcftimer { - unsigned short tmr; /* Timer Mode reg (r/w) */ - unsigned short reserved1; - unsigned short trr; /* Timer Reference (r/w) */ - unsigned short reserved2; - unsigned short tcr; /* Timer Capture reg (r/w) */ - unsigned short reserved3; - unsigned short tcn; /* Timer Counter reg (r/w) */ - unsigned short reserved4; - unsigned char reserved5; - unsigned char ter; /* Timer Event reg (r/w) */ -} __attribute__((packed)); +#endif /* * Bit definitions for the Timer Mode Register (TMR). diff --git a/include/asm-m68knommu/mcfuart.h b/include/asm-m68knommu/mcfuart.h index b016fad..dc0146c 100644 --- a/include/asm-m68knommu/mcfuart.h +++ b/include/asm-m68knommu/mcfuart.h @@ -12,7 +12,6 @@ #ifndef mcfuart_h #define mcfuart_h /****************************************************************************/ -#include /* * Define the base address of the UARTS within the MBAR address @@ -45,6 +44,10 @@ #elif defined(CONFIG_M520x) #define MCFUART_BASE1 0x60000 /* Base address of UART1 */ #define MCFUART_BASE2 0x64000 /* Base address of UART2 */ #define MCFUART_BASE3 0x68000 /* Base address of UART2 */ +#elif defined(CONFIG_M532x) +#define MCFUART_BASE1 0xfc060000 /* Base address of UART1 */ +#define MCFUART_BASE2 0xfc064000 /* Base address of UART2 */ +#define MCFUART_BASE3 0xfc068000 /* Base address of UART3 */ #endif diff --git a/include/asm-m68knommu/mcfwdebug.h b/include/asm-m68knommu/mcfwdebug.h index 6ceae10..27f70e4 100644 --- a/include/asm-m68knommu/mcfwdebug.h +++ b/include/asm-m68knommu/mcfwdebug.h @@ -10,7 +10,6 @@ #ifndef mcfdebug_h #define mcfdebug_h /****************************************************************************/ -#include /* Define the debug module registers */ #define MCFDEBUG_CSR 0x0 /* Configuration status */ diff --git a/include/asm-m68knommu/mmu_context.h b/include/asm-m68knommu/mmu_context.h index 1e080ec..6c077d3 100644 --- a/include/asm-m68knommu/mmu_context.h +++ b/include/asm-m68knommu/mmu_context.h @@ -1,7 +1,6 @@ #ifndef __M68KNOMMU_MMU_CONTEXT_H #define __M68KNOMMU_MMU_CONTEXT_H -#include #include #include #include diff --git a/include/asm-m68knommu/nettel.h b/include/asm-m68knommu/nettel.h index 9bda307..0299f6a 100644 --- a/include/asm-m68knommu/nettel.h +++ b/include/asm-m68knommu/nettel.h @@ -13,7 +13,6 @@ #ifndef nettel_h #define nettel_h /****************************************************************************/ -#include /****************************************************************************/ #ifdef CONFIG_NETtel diff --git a/include/asm-m68knommu/page.h b/include/asm-m68knommu/page.h index 942dfbe..a22bf5a 100644 --- a/include/asm-m68knommu/page.h +++ b/include/asm-m68knommu/page.h @@ -1,7 +1,6 @@ #ifndef _M68KNOMMU_PAGE_H #define _M68KNOMMU_PAGE_H -#include /* PAGE_SHIFT determines the page size */ diff --git a/include/asm-m68knommu/page_offset.h b/include/asm-m68knommu/page_offset.h index 2b45645..d4e73e0 100644 --- a/include/asm-m68knommu/page_offset.h +++ b/include/asm-m68knommu/page_offset.h @@ -1,47 +1,5 @@ -#include /* This handles the memory map.. */ - -#ifdef CONFIG_COLDFIRE -#if defined(CONFIG_SMALL) -#define PAGE_OFFSET_RAW 0x30020000 -#elif defined(CONFIG_CFV240) -#define PAGE_OFFSET_RAW 0x02000000 -#else -#define PAGE_OFFSET_RAW 0x00000000 -#endif -#endif - -#ifdef CONFIG_M68360 -#define PAGE_OFFSET_RAW 0x00000000 -#endif - -#ifdef CONFIG_PILOT -#ifdef CONFIG_M68328 -#define PAGE_OFFSET_RAW 0x10000000 -#endif -#ifdef CONFIG_M68EZ328 -#define PAGE_OFFSET_RAW 0x00000000 -#endif -#endif -#ifdef CONFIG_UCSIMM -#define PAGE_OFFSET_RAW 0x00000000 -#endif - -#if defined(CONFIG_UCDIMM) || defined(CONFIG_DRAGEN2) -#ifdef CONFIG_M68VZ328 -#define PAGE_OFFSET_RAW 0x00000000 -#endif /* CONFIG_M68VZ328 */ -#endif /* CONFIG_UCDIMM */ - -#ifdef CONFIG_M68EZ328ADS -#define PAGE_OFFSET_RAW 0x00000000 -#endif -#ifdef CONFIG_ALMA_ANS -#define PAGE_OFFSET_RAW 0x00000000 -#endif -#ifdef CONFIG_M68EN302 -#define PAGE_OFFSET_RAW 0x00000000 -#endif +#define PAGE_OFFSET_RAW CONFIG_RAMBASE diff --git a/include/asm-m68knommu/param.h b/include/asm-m68knommu/param.h index 3f57d5d..4c9904d 100644 --- a/include/asm-m68knommu/param.h +++ b/include/asm-m68knommu/param.h @@ -1,7 +1,6 @@ #ifndef _M68KNOMMU_PARAM_H #define _M68KNOMMU_PARAM_H -#include #if defined(CONFIG_CLEOPATRA) #define HZ 1000 diff --git a/include/asm-m68knommu/pgtable.h b/include/asm-m68knommu/pgtable.h index 0089305..549ad23 100644 --- a/include/asm-m68knommu/pgtable.h +++ b/include/asm-m68knommu/pgtable.h @@ -7,7 +7,6 @@ #include * (C) Copyright 2000-2002, Greg Ungerer */ -#include #include #include #include diff --git a/include/asm-m68knommu/processor.h b/include/asm-m68knommu/processor.h index ba393b1..0ee158e 100644 --- a/include/asm-m68knommu/processor.h +++ b/include/asm-m68knommu/processor.h @@ -13,7 +13,6 @@ #define __ASM_M68K_PROCESSOR_H */ #define current_text_addr() ({ __label__ _l; _l: &&_l;}) -#include #include #include #include @@ -79,19 +78,31 @@ #define INIT_THREAD { \ } /* + * Coldfire stacks need to be re-aligned on trap exit, conventional + * 68k can handle this case cleanly. + */ +#if defined(CONFIG_COLDFIRE) +#define reformat(_regs) do { (_regs)->format = 0x4; } while(0) +#else +#define reformat(_regs) do { } while (0) +#endif + +/* * Do necessary setup to start up a newly executed thread. * * pass the data segment into user programs if it exists, * it can't hurt anything as far as I can tell */ -#define start_thread(_regs, _pc, _usp) \ -do { \ - set_fs(USER_DS); /* reads from user space */ \ - (_regs)->pc = (_pc); \ - if (current->mm) \ - (_regs)->d5 = current->mm->start_data; \ - (_regs)->sr &= ~0x2000; \ - wrusp(_usp); \ +#define start_thread(_regs, _pc, _usp) \ +do { \ + set_fs(USER_DS); /* reads from user space */ \ + (_regs)->pc = (_pc); \ + ((struct switch_stack *)(_regs))[-1].a6 = 0; \ + reformat(_regs); \ + if (current->mm) \ + (_regs)->d5 = current->mm->start_data; \ + (_regs)->sr &= ~0x2000; \ + wrusp(_usp); \ } while(0) /* Forward declaration, a strange C thing */ diff --git a/include/asm-m68knommu/ptrace.h b/include/asm-m68knommu/ptrace.h index f65bd90..47258e8 100644 --- a/include/asm-m68knommu/ptrace.h +++ b/include/asm-m68knommu/ptrace.h @@ -46,11 +46,9 @@ #ifdef CONFIG_COLDFIRE #else unsigned short sr; unsigned long pc; -#ifndef NO_FORMAT_VEC unsigned format : 4; /* frame format specifier */ unsigned vector : 12; /* vector offset */ #endif -#endif }; /* @@ -70,7 +68,7 @@ struct switch_stack { /* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */ #define PTRACE_GETREGS 12 #define PTRACE_SETREGS 13 -#ifdef COFNIG_FPU +#ifdef CONFIG_FPU #define PTRACE_GETFPREGS 14 #define PTRACE_SETFPREGS 15 #endif diff --git a/include/asm-m68knommu/semaphore-helper.h b/include/asm-m68knommu/semaphore-helper.h index a658641..43da7bc 100644 --- a/include/asm-m68knommu/semaphore-helper.h +++ b/include/asm-m68knommu/semaphore-helper.h @@ -9,7 +9,6 @@ #define _M68K_SEMAPHORE_HELPER_H * m68k version by Andreas Schwab */ -#include /* * These two _must_ execute atomically wrt each other. diff --git a/include/asm-m68knommu/signal.h b/include/asm-m68knommu/signal.h index 1d13187..216c08b 100644 --- a/include/asm-m68knommu/signal.h +++ b/include/asm-m68knommu/signal.h @@ -74,7 +74,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -94,7 +93,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ /* * sigaltstack controls diff --git a/include/asm-m68knommu/system.h b/include/asm-m68knommu/system.h index 6338afc..2bbe2db 100644 --- a/include/asm-m68knommu/system.h +++ b/include/asm-m68knommu/system.h @@ -1,7 +1,6 @@ #ifndef _M68KNOMMU_SYSTEM_H #define _M68KNOMMU_SYSTEM_H -#include /* get configuration macros */ #include #include #include diff --git a/include/asm-m68knommu/unaligned.h b/include/asm-m68knommu/unaligned.h index 8876f03..869e9dd 100644 --- a/include/asm-m68knommu/unaligned.h +++ b/include/asm-m68knommu/unaligned.h @@ -1,7 +1,6 @@ #ifndef __M68K_UNALIGNED_H #define __M68K_UNALIGNED_H -#include #ifdef CONFIG_COLDFIRE diff --git a/include/asm-m68knommu/unistd.h b/include/asm-m68knommu/unistd.h index 5373988..1b2abdf 100644 --- a/include/asm-m68knommu/unistd.h +++ b/include/asm-m68knommu/unistd.h @@ -286,6 +286,8 @@ #define __NR_add_key 279 #define __NR_request_key 280 #define __NR_keyctl 281 +#ifdef __KERNEL__ + #define NR_syscalls 282 /* user-visible error numbers are in the range -1 - -122: see @@ -437,7 +439,6 @@ type name(atype a, btype b, ctype c, dty return (type)__res; \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -460,7 +461,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -515,7 +515,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -525,4 +525,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ASM_M68K_UNISTD_H_ */ diff --git a/include/asm-mips/Kbuild b/include/asm-mips/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-mips/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-mips/a.out.h b/include/asm-mips/a.out.h index 2b3dc3b..ef33c3f 100644 --- a/include/asm-mips/a.out.h +++ b/include/asm-mips/a.out.h @@ -10,7 +10,6 @@ #define _ASM_A_OUT_H #ifdef __KERNEL__ -#include #endif diff --git a/include/asm-mips/addrspace.h b/include/asm-mips/addrspace.h index 1386af1..45c706e 100644 --- a/include/asm-mips/addrspace.h +++ b/include/asm-mips/addrspace.h @@ -10,7 +10,6 @@ #ifndef _ASM_ADDRSPACE_H #define _ASM_ADDRSPACE_H -#include #include /* @@ -133,57 +132,22 @@ #if defined (CONFIG_CPU_R4300) \ || defined (CONFIG_CPU_NEVADA) \ || defined (CONFIG_CPU_TX49XX) \ || defined (CONFIG_CPU_MIPS64) -#define KUSIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define KUSIZE_64 _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K0SIZE _LLCONST_(0x0000001000000000) /* 2^^36 */ -#define K1SIZE _LLCONST_(0x0000001000000000) /* 2^^36 */ -#define K2SIZE _LLCONST_(0x000000ff80000000) -#define KSEGSIZE _LLCONST_(0x000000ff80000000) /* max syssegsz */ #define TO_PHYS_MASK _LLCONST_(0x0000000fffffffff) /* 2^^36 - 1 */ #endif #if defined (CONFIG_CPU_R8000) /* We keep KUSIZE consistent with R4000 for now (2^^40) instead of (2^^48) */ -#define KUSIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define KUSIZE_64 _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K0SIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K1SIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K2SIZE _LLCONST_(0x0001000000000000) -#define KSEGSIZE _LLCONST_(0x0000010000000000) /* max syssegsz */ #define TO_PHYS_MASK _LLCONST_(0x000000ffffffffff) /* 2^^40 - 1 */ #endif #if defined (CONFIG_CPU_R10000) -#define KUSIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define KUSIZE_64 _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K0SIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K1SIZE _LLCONST_(0x0000010000000000) /* 2^^40 */ -#define K2SIZE _LLCONST_(0x00000fff80000000) -#define KSEGSIZE _LLCONST_(0x00000fff80000000) /* max syssegsz */ #define TO_PHYS_MASK _LLCONST_(0x000000ffffffffff) /* 2^^40 - 1 */ #endif #if defined(CONFIG_CPU_SB1) || defined(CONFIG_CPU_SB1A) -#define KUSIZE _LLCONST_(0x0000100000000000) /* 2^^44 */ -#define KUSIZE_64 _LLCONST_(0x0000100000000000) /* 2^^44 */ -#define K0SIZE _LLCONST_(0x0000100000000000) /* 2^^44 */ -#define K1SIZE _LLCONST_(0x0000100000000000) /* 2^^44 */ -#define K2SIZE _LLCONST_(0x0000ffff80000000) -#define KSEGSIZE _LLCONST_(0x0000ffff80000000) /* max syssegsz */ #define TO_PHYS_MASK _LLCONST_(0x00000fffffffffff) /* 2^^44 - 1 */ #endif -/* - * Further names for SGI source compatibility. These are stolen from - * IRIX's . - */ -#define KUBASE _LLCONST_(0) -#define KUSIZE_32 _LLCONST_(0x0000000080000000) /* KUSIZE - for a 32 bit proc */ -#define K0BASE_EXL_WR _LLCONST_(0xa800000000000000) /* exclusive on write */ -#define K0BASE_NONCOH _LLCONST_(0x9800000000000000) /* noncoherent */ -#define K0BASE_EXL _LLCONST_(0xa000000000000000) /* exclusive */ - #ifndef CONFIG_CPU_R8000 /* diff --git a/include/asm-mips/apm.h b/include/asm-mips/apm.h new file mode 100644 index 0000000..e8c6920 --- /dev/null +++ b/include/asm-mips/apm.h @@ -0,0 +1,65 @@ +/* -*- linux-c -*- + * + * (C) 2003 zecke@handhelds.org + * + * GPL version 2 + * + * based on arch/arm/kernel/apm.c + * factor out the information needed by architectures to provide + * apm status + * + * + */ +#ifndef MIPS_ASM_SA1100_APM_H +#define MIPS_ASM_SA1100_APM_H + +#include +#include + +/* + * This structure gets filled in by the machine specific 'get_power_status' + * implementation. Any fields which are not set default to a safe value. + */ +struct apm_power_info { + unsigned char ac_line_status; +#define APM_AC_OFFLINE 0 +#define APM_AC_ONLINE 1 +#define APM_AC_BACKUP 2 +#define APM_AC_UNKNOWN 0xff + + unsigned char battery_status; +#define APM_BATTERY_STATUS_HIGH 0 +#define APM_BATTERY_STATUS_LOW 1 +#define APM_BATTERY_STATUS_CRITICAL 2 +#define APM_BATTERY_STATUS_CHARGING 3 +#define APM_BATTERY_STATUS_NOT_PRESENT 4 +#define APM_BATTERY_STATUS_UNKNOWN 0xff + + unsigned char battery_flag; +#define APM_BATTERY_FLAG_HIGH (1 << 0) +#define APM_BATTERY_FLAG_LOW (1 << 1) +#define APM_BATTERY_FLAG_CRITICAL (1 << 2) +#define APM_BATTERY_FLAG_CHARGING (1 << 3) +#define APM_BATTERY_FLAG_NOT_PRESENT (1 << 7) +#define APM_BATTERY_FLAG_UNKNOWN 0xff + + int battery_life; + int time; + int units; +#define APM_UNITS_MINS 0 +#define APM_UNITS_SECS 1 +#define APM_UNITS_UNKNOWN -1 + +}; + +/* + * This allows machines to provide their own "apm get power status" function. + */ +extern void (*apm_get_power_status)(struct apm_power_info *); + +/* + * Queue an event (APM_SYS_SUSPEND or APM_CRITICAL_SUSPEND) + */ +void apm_queue_event(apm_event_t event); + +#endif diff --git a/include/asm-mips/arc/types.h b/include/asm-mips/arc/types.h index bbb725c..b9adcd6 100644 --- a/include/asm-mips/arc/types.h +++ b/include/asm-mips/arc/types.h @@ -9,7 +9,6 @@ #ifndef _ASM_ARC_TYPES_H #define _ASM_ARC_TYPES_H -#include #ifdef CONFIG_ARC32 diff --git a/include/asm-mips/asm.h b/include/asm-mips/asm.h index 4b090f3..e3038a4 100644 --- a/include/asm-mips/asm.h +++ b/include/asm-mips/asm.h @@ -17,7 +17,6 @@ #ifndef __ASM_ASM_H #define __ASM_ASM_H -#include #include #ifndef CAT diff --git a/include/asm-mips/asmmacro-32.h b/include/asm-mips/asmmacro-32.h index 11daf5c..5de3963 100644 --- a/include/asm-mips/asmmacro-32.h +++ b/include/asm-mips/asmmacro-32.h @@ -12,7 +12,7 @@ #include #include #include - .macro fpu_save_double thread status tmp1=t0 tmp2 + .macro fpu_save_double thread status tmp1=t0 cfc1 \tmp1, fcr31 sdc1 $f0, THREAD_FPR0(\thread) sdc1 $f2, THREAD_FPR2(\thread) @@ -70,7 +70,7 @@ #include sw \tmp, THREAD_FCR31(\thread) .endm - .macro fpu_restore_double thread tmp=t0 + .macro fpu_restore_double thread status tmp=t0 lw \tmp, THREAD_FCR31(\thread) ldc1 $f0, THREAD_FPR0(\thread) ldc1 $f2, THREAD_FPR2(\thread) diff --git a/include/asm-mips/asmmacro-64.h b/include/asm-mips/asmmacro-64.h index 559c355..225feef 100644 --- a/include/asm-mips/asmmacro-64.h +++ b/include/asm-mips/asmmacro-64.h @@ -53,12 +53,12 @@ #include sdc1 $f31, THREAD_FPR31(\thread) .endm - .macro fpu_save_double thread status tmp1 tmp2 - sll \tmp2, \tmp1, 5 - bgez \tmp2, 2f + .macro fpu_save_double thread status tmp + sll \tmp, \status, 5 + bgez \tmp, 2f fpu_save_16odd \thread 2: - fpu_save_16even \thread \tmp1 # clobbers t1 + fpu_save_16even \thread \tmp .endm .macro fpu_restore_16even thread tmp=t0 @@ -101,13 +101,12 @@ #include ldc1 $f31, THREAD_FPR31(\thread) .endm - .macro fpu_restore_double thread tmp - mfc0 t0, CP0_STATUS - sll t1, t0, 5 - bgez t1, 1f # 16 register mode? + .macro fpu_restore_double thread status tmp + sll \tmp, \status, 5 + bgez \tmp, 1f # 16 register mode? - fpu_restore_16odd a0 -1: fpu_restore_16even a0, t0 # clobbers t0 + fpu_restore_16odd \thread +1: fpu_restore_16even \thread \tmp .endm .macro cpu_save_nonscratch thread diff --git a/include/asm-mips/asmmacro.h b/include/asm-mips/asmmacro.h index f54aa14..92e62ef 100644 --- a/include/asm-mips/asmmacro.h +++ b/include/asm-mips/asmmacro.h @@ -8,7 +8,6 @@ #ifndef _ASM_ASMMACRO_H #define _ASM_ASMMACRO_H -#include #include #ifdef CONFIG_32BIT @@ -27,14 +26,14 @@ #ifdef CONFIG_MIPS_MT_SMTC ori \reg, \reg, TCSTATUS_IXMT xori \reg, \reg, TCSTATUS_IXMT mtc0 \reg, CP0_TCSTATUS - ehb + _ehb .endm .macro local_irq_disable reg=t0 mfc0 \reg, CP0_TCSTATUS ori \reg, \reg, TCSTATUS_IXMT mtc0 \reg, CP0_TCSTATUS - ehb + _ehb .endm #else .macro local_irq_enable reg=t0 diff --git a/include/asm-mips/atomic.h b/include/asm-mips/atomic.h index 2c8b853..13d44e1 100644 --- a/include/asm-mips/atomic.h +++ b/include/asm-mips/atomic.h @@ -17,7 +17,6 @@ * we have to include outside the * main big wrapper ... */ -#include #include #ifndef _ASM_ATOMIC_H diff --git a/include/asm-mips/bcache.h b/include/asm-mips/bcache.h index 446102b..3646a3f 100644 --- a/include/asm-mips/bcache.h +++ b/include/asm-mips/bcache.h @@ -9,7 +9,6 @@ #ifndef _ASM_BCACHE_H #define _ASM_BCACHE_H -#include /* Some R4000 / R4400 / R4600 / R5000 machines may have a non-dma-coherent, chipset implemented caches. On machines with other CPUs the CPU does the diff --git a/include/asm-mips/bitops.h b/include/asm-mips/bitops.h index d2f4445..098cec2 100644 --- a/include/asm-mips/bitops.h +++ b/include/asm-mips/bitops.h @@ -9,7 +9,6 @@ #ifndef _ASM_BITOPS_H #define _ASM_BITOPS_H -#include #include #include #include diff --git a/include/asm-mips/bootinfo.h b/include/asm-mips/bootinfo.h index 14fc88f..3b745e7 100644 --- a/include/asm-mips/bootinfo.h +++ b/include/asm-mips/bootinfo.h @@ -217,6 +217,13 @@ #define MACH_LASAT_200 1 /* Masquerade */ #define MACH_GROUP_TITAN 22 /* PMC-Sierra Titan */ #define MACH_TITAN_YOSEMITE 1 /* PMC-Sierra Yosemite */ +#define MACH_TITAN_EXCITE 2 /* Basler eXcite */ + +/* + * Valid machtype for group NEC EMMA2RH + */ +#define MACH_GROUP_NEC_EMMA2RH 25 /* NEC EMMA2RH (was 23) */ +#define MACH_NEC_MARKEINS 0 /* NEC EMMA2RH Mark-eins */ #define CL_SIZE COMMAND_LINE_SIZE @@ -258,4 +265,10 @@ extern char arcs_cmdline[CL_SIZE]; * Registers a0, a1, a3 and a4 as passed to the kenrel entry by firmware */ extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3; + +/* + * Platform memory detection hook called by setup_arch + */ +extern void plat_mem_setup(void); + #endif /* _ASM_BOOTINFO_H */ diff --git a/include/asm-mips/bug.h b/include/asm-mips/bug.h index 87d49a5..7b4739d 100644 --- a/include/asm-mips/bug.h +++ b/include/asm-mips/bug.h @@ -1,7 +1,6 @@ #ifndef __ASM_BUG_H #define __ASM_BUG_H -#include #ifdef CONFIG_BUG diff --git a/include/asm-mips/bugs.h b/include/asm-mips/bugs.h index cb2ea7c..0d7f9c1 100644 --- a/include/asm-mips/bugs.h +++ b/include/asm-mips/bugs.h @@ -7,7 +7,6 @@ #ifndef _ASM_BUGS_H #define _ASM_BUGS_H -#include #include #include #include diff --git a/include/asm-mips/byteorder.h b/include/asm-mips/byteorder.h index aefc02f..eee83cb 100644 --- a/include/asm-mips/byteorder.h +++ b/include/asm-mips/byteorder.h @@ -8,7 +8,6 @@ #ifndef _ASM_BYTEORDER_H #define _ASM_BYTEORDER_H -#include #include #include diff --git a/include/asm-mips/cache.h b/include/asm-mips/cache.h index 55e19f2..37f175c 100644 --- a/include/asm-mips/cache.h +++ b/include/asm-mips/cache.h @@ -9,7 +9,6 @@ #ifndef _ASM_CACHE_H #define _ASM_CACHE_H -#include #include #define L1_CACHE_SHIFT CONFIG_MIPS_L1_CACHE_SHIFT diff --git a/include/asm-mips/checksum.h b/include/asm-mips/checksum.h index b09f897..a5e6050 100644 --- a/include/asm-mips/checksum.h +++ b/include/asm-mips/checksum.h @@ -11,7 +11,6 @@ #ifndef _ASM_CHECKSUM_H #define _ASM_CHECKSUM_H -#include #include #include diff --git a/include/asm-mips/compat.h b/include/asm-mips/compat.h index 986511d..900f472 100644 --- a/include/asm-mips/compat.h +++ b/include/asm-mips/compat.h @@ -145,8 +145,5 @@ static inline void __user *compat_alloc_ return (void __user *) (regs->regs[29] - len); } -#if defined (__MIPSEL__) -#define __COMPAT_ENDIAN_SWAP__ 1 -#endif #endif /* _ASM_COMPAT_H */ diff --git a/include/asm-mips/cpu-features.h b/include/asm-mips/cpu-features.h index 254e11e..44285a9 100644 --- a/include/asm-mips/cpu-features.h +++ b/include/asm-mips/cpu-features.h @@ -9,7 +9,6 @@ #ifndef __ASM_CPU_FEATURES_H #define __ASM_CPU_FEATURES_H -#include #include #include @@ -188,19 +187,15 @@ # define cpu_has_64bit_addresses 1 # endif #endif -#ifdef CONFIG_CPU_MIPSR2 -# if defined(CONFIG_CPU_MIPSR2_IRQ_VI) && !defined(cpu_has_vint) -# define cpu_has_vint (cpu_data[0].options & MIPS_CPU_VINT) -# else -# define cpu_has_vint 0 -# endif -# if defined(CONFIG_CPU_MIPSR2_IRQ_EI) && !defined(cpu_has_veic) -# define cpu_has_veic (cpu_data[0].options & MIPS_CPU_VEIC) -# else -# define cpu_has_veic 0 -# endif -#else +#if defined(CONFIG_CPU_MIPSR2_IRQ_VI) && !defined(cpu_has_vint) +# define cpu_has_vint (cpu_data[0].options & MIPS_CPU_VINT) +#elif !defined(cpu_has_vint) # define cpu_has_vint 0 +#endif + +#if defined(CONFIG_CPU_MIPSR2_IRQ_EI) && !defined(cpu_has_veic) +# define cpu_has_veic (cpu_data[0].options & MIPS_CPU_VEIC) +#elif !defined(cpu_has_veic) # define cpu_has_veic 0 #endif diff --git a/include/asm-mips/cpu-info.h b/include/asm-mips/cpu-info.h index 6572ac7..a2f0c8e 100644 --- a/include/asm-mips/cpu-info.h +++ b/include/asm-mips/cpu-info.h @@ -12,7 +12,6 @@ #ifndef __ASM_CPU_INFO_H #define __ASM_CPU_INFO_H -#include #include #ifdef CONFIG_SGI_IP27 diff --git a/include/asm-mips/ddb5074.h b/include/asm-mips/ddb5074.h deleted file mode 100644 index 0d09ac2..0000000 --- a/include/asm-mips/ddb5074.h +++ /dev/null @@ -1,11 +0,0 @@ -/* - * include/asm-mips/ddb5074.h -- NEC DDB Vrc-5074 definitions - * - * Copyright (C) 2000 Geert Uytterhoeven - * Sony Software Development Center Europe (SDCE), Brussels - */ - -extern void ddb5074_led_hex(int hex); -extern void ddb5074_led_d2(int on); -extern void ddb5074_led_d3(int on); - diff --git a/include/asm-mips/ddb5xxx/ddb5074.h b/include/asm-mips/ddb5xxx/ddb5074.h deleted file mode 100644 index 58d8830..0000000 --- a/include/asm-mips/ddb5xxx/ddb5074.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * include/asm-mips/ddb5074.h -- NEC DDB Vrc-5074 definitions - * - * Copyright (C) 2000 Geert Uytterhoeven - * Sony Software Development Center Europe (SDCE), Brussels - */ - -#ifndef _ASM_DDB5XXX_DDB5074_H -#define _ASM_DDB5XXX_DDB5074_H - -#include - -#define DDB_SDRAM_SIZE 0x04000000 /* 64MB */ - -#define DDB_PCI_IO_BASE 0x06000000 -#define DDB_PCI_IO_SIZE 0x02000000 /* 32 MB */ - -#define DDB_PCI_MEM_BASE 0x08000000 -#define DDB_PCI_MEM_SIZE 0x08000000 /* 128 MB */ - -#define DDB_PCI_CONFIG_BASE DDB_PCI_MEM_BASE -#define DDB_PCI_CONFIG_SIZE DDB_PCI_MEM_SIZE - -#define NILE4_PCI_IO_BASE 0xa6000000 -#define NILE4_PCI_MEM_BASE 0xa8000000 -#define NILE4_PCI_CFG_BASE NILE4_PCI_MEM_BASE -#define DDB_PCI_IACK_BASE NILE4_PCI_IO_BASE - -#define NILE4_IRQ_BASE NUM_I8259_INTERRUPTS -#define CPU_IRQ_BASE (NUM_NILE4_INTERRUPTS + NILE4_IRQ_BASE) -#define CPU_NILE4_CASCADE 2 - -extern void ddb5074_led_hex(int hex); -extern void ddb5074_led_d2(int on); -extern void ddb5074_led_d3(int on); - -extern void nile4_irq_setup(u32 base); -#endif diff --git a/include/asm-mips/ddb5xxx/ddb5476.h b/include/asm-mips/ddb5xxx/ddb5476.h deleted file mode 100644 index 4c23390..0000000 --- a/include/asm-mips/ddb5xxx/ddb5476.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * header file specific for ddb5476 - * - * Copyright (C) 2001 MontaVista Software Inc. - * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -/* - * Memory map (physical address) - * - * Note most of the following address must be properly aligned by the - * corresponding size. For example, if PCI_IO_SIZE is 16MB, then - * PCI_IO_BASE must be aligned along 16MB boundary. - */ -#define DDB_SDRAM_BASE 0x00000000 -#define DDB_SDRAM_SIZE 0x04000000 /* 64MB */ - -#define DDB_DCS3_BASE 0x04000000 /* flash 1 */ -#define DDB_DCS3_SIZE 0x01000000 /* 16MB */ - -#define DDB_DCS2_BASE 0x05000000 /* flash 2 */ -#define DDB_DCS2_SIZE 0x01000000 /* 16MB */ - -#define DDB_PCI_IO_BASE 0x06000000 -#define DDB_PCI_IO_SIZE 0x02000000 /* 32 MB */ - -#define DDB_PCI_MEM_BASE 0x08000000 -#define DDB_PCI_MEM_SIZE 0x08000000 /* 128 MB */ - -#define DDB_DCS5_BASE 0x13000000 /* DDB status regs */ -#define DDB_DCS5_SIZE 0x00200000 /* 2MB, 8-bit */ - -#define DDB_DCS4_BASE 0x14000000 /* DDB control regs */ -#define DDB_DCS4_SIZE 0x00200000 /* 2MB, 8-bit */ - -#define DDB_INTCS_BASE 0x1fa00000 /* VRC5476 control regs */ -#define DDB_INTCS_SIZE 0x00200000 /* 2MB */ - -#define DDB_BOOTCS_BASE 0x1fc00000 /* Boot ROM / EPROM /Flash */ -#define DDB_BOOTCS_SIZE 0x00200000 /* 2 MB - doc says 4MB */ - - -/* aliases */ -#define DDB_PCI_CONFIG_BASE DDB_PCI_MEM_BASE -#define DDB_PCI_CONFIG_SIZE DDB_PCI_MEM_SIZE - -/* PCI intr ack share PCIW0 with PCI IO */ -#define DDB_PCI_IACK_BASE DDB_PCI_IO_BASE - -/* - * Interrupt mapping - * - * We have three interrupt controllers: - * - * . CPU itself - 8 sources - * . i8259 - 16 sources - * . vrc5476 - 16 sources - * - * They connected as follows: - * all vrc5476 interrupts are routed to cpu IP2 (by software setting) - * all i2869 are routed to INTC in vrc5476 (by hardware connection) - * - * All VRC5476 PCI interrupts are level-triggered (no ack needed). - * All PCI irq but INTC are active low. - */ - -/* - * irq number block assignment - */ - -#define NUM_CPU_IRQ 8 -#define NUM_I8259_IRQ 16 -#define NUM_VRC5476_IRQ 16 - -#define DDB_IRQ_BASE 0 - -#define I8259_IRQ_BASE DDB_IRQ_BASE -#define VRC5476_IRQ_BASE (I8259_IRQ_BASE + NUM_I8259_IRQ) -#define CPU_IRQ_BASE (VRC5476_IRQ_BASE + NUM_VRC5476_IRQ) - -/* - * vrc5476 irq defs, see page 52-64 of Vrc5074 system controller manual - */ - -#define VRC5476_IRQ_CPCE 0 /* cpu parity error */ -#define VRC5476_IRQ_CNTD 1 /* cpu no target */ -#define VRC5476_IRQ_MCE 2 /* memory check error */ -#define VRC5476_IRQ_DMA 3 /* DMA */ -#define VRC5476_IRQ_UART 4 /* vrc5476 builtin UART, not used */ -#define VRC5476_IRQ_WDOG 5 /* watchdog timer */ -#define VRC5476_IRQ_GPT 6 /* general purpose timer */ -#define VRC5476_IRQ_LBRT 7 /* local bus read timeout */ -#define VRC5476_IRQ_INTA 8 /* PCI INT #A */ -#define VRC5476_IRQ_INTB 9 /* PCI INT #B */ -#define VRC5476_IRQ_INTC 10 /* PCI INT #C */ -#define VRC5476_IRQ_INTD 11 /* PCI INT #D */ -#define VRC5476_IRQ_INTE 12 /* PCI INT #E */ -#define VRC5476_IRQ_RESERVED_13 13 /* reserved */ -#define VRC5476_IRQ_PCIS 14 /* PCI SERR # */ -#define VRC5476_IRQ_PCI 15 /* PCI internal error */ - -/* - * i2859 irq assignment - */ -#define I8259_IRQ_RESERVED_0 0 -#define I8259_IRQ_KEYBOARD 1 /* M1543 default */ -#define I8259_IRQ_CASCADE 2 -#define I8259_IRQ_UART_B 3 /* M1543 default, may conflict with RTC according to schematic diagram */ -#define I8259_IRQ_UART_A 4 /* M1543 default */ -#define I8259_IRQ_PARALLEL 5 /* M1543 default */ -#define I8259_IRQ_RESERVED_6 6 -#define I8259_IRQ_RESERVED_7 7 -#define I8259_IRQ_RTC 8 /* who set this? */ -#define I8259_IRQ_USB 9 /* ddb_setup */ -#define I8259_IRQ_PMU 10 /* ddb_setup */ -#define I8259_IRQ_RESERVED_11 11 -#define I8259_IRQ_RESERVED_12 12 /* m1543_irq_setup */ -#define I8259_IRQ_RESERVED_13 13 -#define I8259_IRQ_HDC1 14 /* default and ddb_setup */ -#define I8259_IRQ_HDC2 15 /* default */ - - -/* - * misc - */ -#define VRC5476_I8259_CASCADE VRC5476_IRQ_INTC -#define CPU_VRC5476_CASCADE 2 - -#define is_i8259_irq(irq) ((irq) < NUM_I8259_IRQ) -#define nile4_to_irq(n) ((n)+NUM_I8259_IRQ) -#define irq_to_nile4(n) ((n)-NUM_I8259_IRQ) - -/* - * low-level irq functions - */ -#ifndef __ASSEMBLY__ -extern void nile4_map_irq(int nile4_irq, int cpu_irq); -extern void nile4_map_irq_all(int cpu_irq); -extern void nile4_enable_irq(int nile4_irq); -extern void nile4_disable_irq(int nile4_irq); -extern void nile4_disable_irq_all(void); -extern u16 nile4_get_irq_stat(int cpu_irq); -extern void nile4_enable_irq_output(int cpu_irq); -extern void nile4_disable_irq_output(int cpu_irq); -extern void nile4_set_pci_irq_polarity(int pci_irq, int high); -extern void nile4_set_pci_irq_level_or_edge(int pci_irq, int level); -extern void nile4_clear_irq(int nile4_irq); -extern void nile4_clear_irq_mask(u32 mask); -extern u8 nile4_i8259_iack(void); -extern void nile4_dump_irq_status(void); /* Debug */ -#endif /* !__ASSEMBLY__ */ diff --git a/include/asm-mips/ddb5xxx/ddb5477.h b/include/asm-mips/ddb5xxx/ddb5477.h index a438548..c5af4b7 100644 --- a/include/asm-mips/ddb5xxx/ddb5477.h +++ b/include/asm-mips/ddb5xxx/ddb5477.h @@ -17,7 +17,6 @@ #ifndef __ASM_DDB5XXX_DDB5477_H #define __ASM_DDB5XXX_DDB5477_H -#include /* * This contains macros that are specific to DDB5477 or renamed from diff --git a/include/asm-mips/ddb5xxx/ddb5xxx.h b/include/asm-mips/ddb5xxx/ddb5xxx.h index 873c03f..e97fcc8 100644 --- a/include/asm-mips/ddb5xxx/ddb5xxx.h +++ b/include/asm-mips/ddb5xxx/ddb5xxx.h @@ -18,7 +18,6 @@ #ifndef __ASM_DDB5XXX_DDB5XXX_H #define __ASM_DDB5XXX_DDB5XXX_H -#include #include /* @@ -174,13 +173,8 @@ #define DDB_BARB 0x0278 /* PCI Base Addr static inline void ddb_sync(void) { -/* The DDB5074 doesn't seem to like these accesses. They kill the board on - * interrupt load - */ -#ifndef CONFIG_DDB5074 volatile u32 *p = (volatile u32 *)0xbfc00000; (void)(*p); -#endif } static inline void ddb_out32(u32 offset, u32 val) @@ -260,11 +254,7 @@ extern void ddb_pci_reset_bus(void); /* * include the board dependent part */ -#if defined(CONFIG_DDB5074) -#include -#elif defined(CONFIG_DDB5476) -#include -#elif defined(CONFIG_DDB5477) +#if defined(CONFIG_DDB5477) #include #else #error "Unknown DDB board!" diff --git a/include/asm-mips/debug.h b/include/asm-mips/debug.h index 930f2b7..1fd5a2b 100644 --- a/include/asm-mips/debug.h +++ b/include/asm-mips/debug.h @@ -15,7 +15,6 @@ #ifndef _ASM_DEBUG_H #define _ASM_DEBUG_H -#include /* * run-time macros for catching spurious errors. Eable CONFIG_RUNTIME_DEBUG in diff --git a/include/asm-mips/dec/prom.h b/include/asm-mips/dec/prom.h index 1384dd0..b9c8203 100644 --- a/include/asm-mips/dec/prom.h +++ b/include/asm-mips/dec/prom.h @@ -15,7 +15,6 @@ #ifndef _ASM_DEC_PROM_H #define _ASM_DEC_PROM_H -#include #include #include diff --git a/include/asm-mips/delay.h b/include/asm-mips/delay.h index 928f30f..ea77050 100644 --- a/include/asm-mips/delay.h +++ b/include/asm-mips/delay.h @@ -10,7 +10,6 @@ #ifndef _ASM_DELAY_H #define _ASM_DELAY_H -#include #include #include #include diff --git a/include/asm-mips/dma.h b/include/asm-mips/dma.h index 6aaf993..e85849a 100644 --- a/include/asm-mips/dma.h +++ b/include/asm-mips/dma.h @@ -12,7 +12,6 @@ #ifndef _ASM_DMA_H #define _ASM_DMA_H -#include #include /* need byte IO */ #include /* And spinlocks */ #include diff --git a/include/asm-mips/elf.h b/include/asm-mips/elf.h index bdc9de2..ebd6bfb 100644 --- a/include/asm-mips/elf.h +++ b/include/asm-mips/elf.h @@ -8,7 +8,6 @@ #ifndef _ASM_ELF_H #define _ASM_ELF_H -#include /* ELF header e_flags defines. */ /* MIPS architecture level. */ diff --git a/include/asm-mips/emma2rh/emma2rh.h b/include/asm-mips/emma2rh/emma2rh.h new file mode 100644 index 0000000..4fb8df7 --- /dev/null +++ b/include/asm-mips/emma2rh/emma2rh.h @@ -0,0 +1,330 @@ +/* + * include/asm-mips/emma2rh/emma2rh.h + * This file is EMMA2RH common header. + * + * Copyright (C) NEC Electronics Corporation 2005-2006 + * + * This file based on include/asm-mips/ddb5xxx/ddb5xxx.h + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ASM_EMMA2RH_EMMA2RH_H +#define __ASM_EMMA2RH_EMMA2RH_H + +/* + * EMMA2RH registers + */ +#define REGBASE 0x10000000 + +#define EMMA2RH_BHIF_STRAP_0 (0x000010+REGBASE) +#define EMMA2RH_BHIF_INT_ST_0 (0x000030+REGBASE) +#define EMMA2RH_BHIF_INT_ST_1 (0x000034+REGBASE) +#define EMMA2RH_BHIF_INT_ST_2 (0x000038+REGBASE) +#define EMMA2RH_BHIF_INT_EN_0 (0x000040+REGBASE) +#define EMMA2RH_BHIF_INT_EN_1 (0x000044+REGBASE) +#define EMMA2RH_BHIF_INT_EN_2 (0x000048+REGBASE) +#define EMMA2RH_BHIF_INT1_EN_0 (0x000050+REGBASE) +#define EMMA2RH_BHIF_INT1_EN_1 (0x000054+REGBASE) +#define EMMA2RH_BHIF_INT1_EN_2 (0x000058+REGBASE) +#define EMMA2RH_BHIF_SW_INT (0x000070+REGBASE) +#define EMMA2RH_BHIF_SW_INT_EN (0x000080+REGBASE) +#define EMMA2RH_BHIF_SW_INT_CLR (0x000090+REGBASE) +#define EMMA2RH_BHIF_MAIN_CTRL (0x0000b4+REGBASE) +#define EMMA2RH_BHIF_EXCEPT_VECT_BASE_ADDRESS (0x0000c0+REGBASE) +#define EMMA2RH_GPIO_DIR (0x110d20+REGBASE) +#define EMMA2RH_GPIO_INT_ST (0x110d30+REGBASE) +#define EMMA2RH_GPIO_INT_MASK (0x110d3c+REGBASE) +#define EMMA2RH_GPIO_INT_MODE (0x110d48+REGBASE) +#define EMMA2RH_GPIO_INT_CND_A (0x110d54+REGBASE) +#define EMMA2RH_GPIO_INT_CND_B (0x110d60+REGBASE) +#define EMMA2RH_PBRD_INT_EN (0x100010+REGBASE) +#define EMMA2RH_PBRD_CLKSEL (0x100028+REGBASE) +#define EMMA2RH_PFUR0_BASE (0x101000+REGBASE) +#define EMMA2RH_PFUR1_BASE (0x102000+REGBASE) +#define EMMA2RH_PFUR2_BASE (0x103000+REGBASE) +#define EMMA2RH_PIIC0_BASE (0x107000+REGBASE) +#define EMMA2RH_PIIC1_BASE (0x108000+REGBASE) +#define EMMA2RH_PIIC2_BASE (0x109000+REGBASE) +#define EMMA2RH_PCI_CONTROL (0x200000+REGBASE) +#define EMMA2RH_PCI_ARBIT_CTR (0x200004+REGBASE) +#define EMMA2RH_PCI_IWIN0_CTR (0x200010+REGBASE) +#define EMMA2RH_PCI_IWIN1_CTR (0x200014+REGBASE) +#define EMMA2RH_PCI_INIT_ESWP (0x200018+REGBASE) +#define EMMA2RH_PCI_INT (0x200020+REGBASE) +#define EMMA2RH_PCI_INT_EN (0x200024+REGBASE) +#define EMMA2RH_PCI_TWIN_CTR (0x200030+REGBASE) +#define EMMA2RH_PCI_TWIN_BADR (0x200034+REGBASE) +#define EMMA2RH_PCI_TWIN0_DADR (0x200038+REGBASE) +#define EMMA2RH_PCI_TWIN1_DADR (0x20003c+REGBASE) + +/* + * Memory map (physical address) + * + * Note most of the following address must be properly aligned by the + * corresponding size. For example, if PCI_IO_SIZE is 16MB, then + * PCI_IO_BASE must be aligned along 16MB boundary. + */ + +/* the actual ram size is detected at run-time */ +#define EMMA2RH_RAM_BASE 0x00000000 +#define EMMA2RH_RAM_SIZE 0x10000000 /* less than 256MB */ + +#define EMMA2RH_IO_BASE 0x10000000 +#define EMMA2RH_IO_SIZE 0x01000000 /* 16 MB */ + +#define EMMA2RH_GENERALIO_BASE 0x11000000 +#define EMMA2RH_GENERALIO_SIZE 0x01000000 /* 16 MB */ + +#define EMMA2RH_PCI_IO_BASE 0x12000000 +#define EMMA2RH_PCI_IO_SIZE 0x02000000 /* 32 MB */ + +#define EMMA2RH_PCI_MEM_BASE 0x14000000 +#define EMMA2RH_PCI_MEM_SIZE 0x08000000 /* 128 MB */ + +#define EMMA2RH_ROM_BASE 0x1c000000 +#define EMMA2RH_ROM_SIZE 0x04000000 /* 64 MB */ + +#define EMMA2RH_PCI_CONFIG_BASE EMMA2RH_PCI_IO_BASE +#define EMMA2RH_PCI_CONFIG_SIZE EMMA2RH_PCI_IO_SIZE + +#define NUM_CPU_IRQ 8 +#define NUM_EMMA2RH_IRQ 96 + +#define CPU_EMMA2RH_CASCADE 2 +#define EMMA2RH_IRQ_BASE 0 + +/* + * emma2rh irq defs + */ + +#define EMMA2RH_IRQ_INT0 (0 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT1 (1 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT2 (2 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT3 (3 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT4 (4 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT5 (5 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT6 (6 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT7 (7 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT8 (8 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT9 (9 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT10 (10 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT11 (11 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT12 (12 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT13 (13 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT14 (14 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT15 (15 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT16 (16 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT17 (17 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT18 (18 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT19 (19 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT20 (20 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT21 (21 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT22 (22 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT23 (23 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT24 (24 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT25 (25 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT26 (26 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT27 (27 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT28 (28 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT29 (29 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT30 (30 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT31 (31 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT32 (32 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT33 (33 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT34 (34 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT35 (35 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT36 (36 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT37 (37 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT38 (38 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT39 (39 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT40 (40 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT41 (41 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT42 (42 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT43 (43 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT44 (44 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT45 (45 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT46 (46 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT47 (47 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT48 (48 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT49 (49 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT50 (50 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT51 (51 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT52 (52 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT53 (53 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT54 (54 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT55 (55 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT56 (56 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT57 (57 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT58 (58 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT59 (59 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT60 (60 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT61 (61 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT62 (62 + EMMA2RH_IRQ_BASE) +#define EMMA2RH_IRQ_INT63 (63 + EMMA2RH_IRQ_BASE) + +#define EMMA2RH_IRQ_PFUR0 EMMA2RH_IRQ_INT49 +#define EMMA2RH_IRQ_PFUR1 EMMA2RH_IRQ_INT50 +#define EMMA2RH_IRQ_PFUR2 EMMA2RH_IRQ_INT51 +#define EMMA2RH_IRQ_PIIC0 EMMA2RH_IRQ_INT56 +#define EMMA2RH_IRQ_PIIC1 EMMA2RH_IRQ_INT57 +#define EMMA2RH_IRQ_PIIC2 EMMA2RH_IRQ_INT58 + +/* + * EMMA2RH Register Access + */ + +#define EMMA2RH_BASE (0xa0000000) + +static inline void emma2rh_sync(void) +{ + volatile u32 *p = (volatile u32 *)0xbfc00000; + (void)(*p); +} + +static inline void emma2rh_out32(u32 offset, u32 val) +{ + *(volatile u32 *)(EMMA2RH_BASE | offset) = val; + emma2rh_sync(); +} + +static inline u32 emma2rh_in32(u32 offset) +{ + u32 val = *(volatile u32 *)(EMMA2RH_BASE | offset); + emma2rh_sync(); + return val; +} + +static inline void emma2rh_out16(u32 offset, u16 val) +{ + *(volatile u16 *)(EMMA2RH_BASE | offset) = val; + emma2rh_sync(); +} + +static inline u16 emma2rh_in16(u32 offset) +{ + u16 val = *(volatile u16 *)(EMMA2RH_BASE | offset); + emma2rh_sync(); + return val; +} + +static inline void emma2rh_out8(u32 offset, u8 val) +{ + *(volatile u8 *)(EMMA2RH_BASE | offset) = val; + emma2rh_sync(); +} + +static inline u8 emma2rh_in8(u32 offset) +{ + u8 val = *(volatile u8 *)(EMMA2RH_BASE | offset); + emma2rh_sync(); + return val; +} + +/** + * IIC registers map + **/ + +/*---------------------------------------------------------------------------*/ +/* CNT - Control register (00H R/W) */ +/*---------------------------------------------------------------------------*/ +#define SPT 0x00000001 +#define STT 0x00000002 +#define ACKE 0x00000004 +#define WTIM 0x00000008 +#define SPIE 0x00000010 +#define WREL 0x00000020 +#define LREL 0x00000040 +#define IICE 0x00000080 +#define CNT_RESERVED 0x000000ff /* reserved bit 0 */ + +#define I2C_EMMA_START (IICE | STT) +#define I2C_EMMA_STOP (IICE | SPT) +#define I2C_EMMA_REPSTART I2C_EMMA_START + +/*---------------------------------------------------------------------------*/ +/* STA - Status register (10H Read) */ +/*---------------------------------------------------------------------------*/ +#define MSTS 0x00000080 +#define ALD 0x00000040 +#define EXC 0x00000020 +#define COI 0x00000010 +#define TRC 0x00000008 +#define ACKD 0x00000004 +#define STD 0x00000002 +#define SPD 0x00000001 + +/*---------------------------------------------------------------------------*/ +/* CSEL - Clock select register (20H R/W) */ +/*---------------------------------------------------------------------------*/ +#define FCL 0x00000080 +#define ND50 0x00000040 +#define CLD 0x00000020 +#define DAD 0x00000010 +#define SMC 0x00000008 +#define DFC 0x00000004 +#define CL 0x00000003 +#define CSEL_RESERVED 0x000000ff /* reserved bit 0 */ + +#define FAST397 0x0000008b +#define FAST297 0x0000008a +#define FAST347 0x0000000b +#define FAST260 0x0000000a +#define FAST130 0x00000008 +#define STANDARD108 0x00000083 +#define STANDARD83 0x00000082 +#define STANDARD95 0x00000003 +#define STANDARD73 0x00000002 +#define STANDARD36 0x00000001 +#define STANDARD71 0x00000000 + +/*---------------------------------------------------------------------------*/ +/* SVA - Slave address register (30H R/W) */ +/*---------------------------------------------------------------------------*/ +#define SVA 0x000000fe + +/*---------------------------------------------------------------------------*/ +/* SHR - Shift register (40H R/W) */ +/*---------------------------------------------------------------------------*/ +#define SR 0x000000ff + +/*---------------------------------------------------------------------------*/ +/* INT - Interrupt register (50H R/W) */ +/* INTM - Interrupt mask register (60H R/W) */ +/*---------------------------------------------------------------------------*/ +#define INTE0 0x00000001 + +/*********************************************************************** + * I2C registers + *********************************************************************** + */ +#define I2C_EMMA_CNT 0x00 +#define I2C_EMMA_STA 0x10 +#define I2C_EMMA_CSEL 0x20 +#define I2C_EMMA_SVA 0x30 +#define I2C_EMMA_SHR 0x40 +#define I2C_EMMA_INT 0x50 +#define I2C_EMMA_INTM 0x60 + +/* + * include the board dependent part + */ +#if defined(CONFIG_MARKEINS) +#include +#else +#error "Unknown EMMA2RH board!" +#endif + +#endif /* __ASM_EMMA2RH_EMMA2RH_H */ diff --git a/include/asm-mips/emma2rh/markeins.h b/include/asm-mips/emma2rh/markeins.h new file mode 100644 index 0000000..8fa7667 --- /dev/null +++ b/include/asm-mips/emma2rh/markeins.h @@ -0,0 +1,76 @@ +/* + * include/asm-mips/emma2rh/markeins.h + * This file is EMMA2RH board depended header. + * + * Copyright (C) NEC Electronics Corporation 2005-2006 + * + * This file based on include/asm-mips/ddb5xxx/ddb5xxx.h + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MARKEINS_H +#define MARKEINS_H + +#define NUM_EMMA2RH_IRQ_SW 32 +#define NUM_EMMA2RH_IRQ_GPIO 32 + +#define EMMA2RH_SW_CASCADE (EMMA2RH_IRQ_INT7 - EMMA2RH_IRQ_INT0) +#define EMMA2RH_GPIO_CASCADE (EMMA2RH_IRQ_INT46 - EMMA2RH_IRQ_INT0) + +#define EMMA2RH_SW_IRQ_BASE (EMMA2RH_IRQ_BASE + NUM_EMMA2RH_IRQ) +#define EMMA2RH_GPIO_IRQ_BASE (EMMA2RH_SW_IRQ_BASE + NUM_EMMA2RH_IRQ_SW) +#define CPU_IRQ_BASE (EMMA2RH_GPIO_IRQ_BASE + NUM_EMMA2RH_IRQ_GPIO) + +#define EMMA2RH_SW_IRQ_INT0 (0+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT1 (1+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT2 (2+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT3 (3+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT4 (4+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT5 (5+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT6 (6+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT7 (7+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT8 (8+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT9 (9+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT10 (10+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT11 (11+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT12 (12+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT13 (13+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT14 (14+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT15 (15+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT16 (16+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT17 (17+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT18 (18+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT19 (19+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT20 (20+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT21 (21+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT22 (22+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT23 (23+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT24 (24+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT25 (25+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT26 (26+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT27 (27+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT28 (28+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT29 (29+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT30 (30+EMMA2RH_SW_IRQ_BASE) +#define EMMA2RH_SW_IRQ_INT31 (31+EMMA2RH_SW_IRQ_BASE) + +#define MARKEINS_PCI_IRQ_INTA EMMA2RH_GPIO_IRQ_BASE+15 +#define MARKEINS_PCI_IRQ_INTB EMMA2RH_GPIO_IRQ_BASE+16 +#define MARKEINS_PCI_IRQ_INTC EMMA2RH_GPIO_IRQ_BASE+17 +#define MARKEINS_PCI_IRQ_INTD EMMA2RH_GPIO_IRQ_BASE+18 + +#endif /* CONFIG_MARKEINS */ diff --git a/include/asm-mips/fcntl.h b/include/asm-mips/fcntl.h index 43d047a..787220e 100644 --- a/include/asm-mips/fcntl.h +++ b/include/asm-mips/fcntl.h @@ -8,7 +8,6 @@ #ifndef _ASM_FCNTL_H #define _ASM_FCNTL_H -#include #define O_APPEND 0x0008 #define O_SYNC 0x0010 diff --git a/include/asm-mips/fixmap.h b/include/asm-mips/fixmap.h index 73a3028..6959bdb 100644 --- a/include/asm-mips/fixmap.h +++ b/include/asm-mips/fixmap.h @@ -13,7 +13,6 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H -#include #include #ifdef CONFIG_HIGHMEM #include @@ -70,7 +69,11 @@ #define set_fixmap_nocache(idx, phys) \ * the start of the fixmap, and leave one page empty * at the top of mem.. */ +#if defined(CONFIG_CPU_TX39XX) || defined(CONFIG_CPU_TX49XX) +#define FIXADDR_TOP (0xff000000UL - 0x2000) +#else #define FIXADDR_TOP (0xffffe000UL) +#endif #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) diff --git a/include/asm-mips/fpu.h b/include/asm-mips/fpu.h index b0f5001..58c561a 100644 --- a/include/asm-mips/fpu.h +++ b/include/asm-mips/fpu.h @@ -10,7 +10,6 @@ #ifndef _ASM_FPU_H #define _ASM_FPU_H -#include #include #include @@ -138,10 +137,9 @@ static inline fpureg_t *get_fpu_regs(str if (cpu_has_fpu) { if ((tsk == current) && __is_fpu_owner()) _save_fp(current); - return tsk->thread.fpu.hard.fpr; } - return tsk->thread.fpu.soft.fpr; + return tsk->thread.fpu.fpr; } #endif /* _ASM_FPU_H */ diff --git a/include/asm-mips/fpu_emulator.h b/include/asm-mips/fpu_emulator.h index 16cb4d1..2731c38 100644 --- a/include/asm-mips/fpu_emulator.h +++ b/include/asm-mips/fpu_emulator.h @@ -12,8 +12,8 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. * - * Further private data for which no space exists in mips_fpu_soft_struct. - * This should be subsumed into the mips_fpu_soft_struct structure as + * Further private data for which no space exists in mips_fpu_struct. + * This should be subsumed into the mips_fpu_struct structure as * defined in processor.h as soon as the absurd wired absolute assembler * offsets become dynamic at compile time. * diff --git a/include/asm-mips/futex.h b/include/asm-mips/futex.h index 12d118f..ed023ea 100644 --- a/include/asm-mips/futex.h +++ b/include/asm-mips/futex.h @@ -3,7 +3,6 @@ #define _ASM_FUTEX_H #ifdef __KERNEL__ -#include #include #include #include @@ -22,51 +21,53 @@ #define __futex_atomic_op(insn, ret, old " .set push \n" \ " .set noat \n" \ " .set mips3 \n" \ - "1: ll %1, (%3) # __futex_atomic_op \n" \ + "1: ll %1, %4 # __futex_atomic_op \n" \ " .set mips0 \n" \ " " insn " \n" \ " .set mips3 \n" \ - "2: sc $1, (%3) \n" \ + "2: sc $1, %2 \n" \ " beqzl $1, 1b \n" \ __FUTEX_SMP_SYNC \ "3: \n" \ " .set pop \n" \ " .set mips0 \n" \ " .section .fixup,\"ax\" \n" \ - "4: li %0, %5 \n" \ + "4: li %0, %6 \n" \ " j 2b \n" \ " .previous \n" \ " .section __ex_table,\"a\" \n" \ " "__UA_ADDR "\t1b, 4b \n" \ " "__UA_ADDR "\t2b, 4b \n" \ " .previous \n" \ - : "=r" (ret), "=r" (oldval) \ - : "0" (0), "r" (uaddr), "Jr" (oparg), "i" (-EFAULT)); \ + : "=r" (ret), "=&r" (oldval), "=R" (*uaddr) \ + : "0" (0), "R" (*uaddr), "Jr" (oparg), "i" (-EFAULT) \ + : "memory"); \ } else if (cpu_has_llsc) { \ __asm__ __volatile__( \ " .set push \n" \ " .set noat \n" \ " .set mips3 \n" \ - "1: ll %1, (%3) # __futex_atomic_op \n" \ + "1: ll %1, %4 # __futex_atomic_op \n" \ " .set mips0 \n" \ " " insn " \n" \ " .set mips3 \n" \ - "2: sc $1, (%3) \n" \ + "2: sc $1, %2 \n" \ " beqz $1, 1b \n" \ __FUTEX_SMP_SYNC \ "3: \n" \ " .set pop \n" \ " .set mips0 \n" \ " .section .fixup,\"ax\" \n" \ - "4: li %0, %5 \n" \ + "4: li %0, %6 \n" \ " j 2b \n" \ " .previous \n" \ " .section __ex_table,\"a\" \n" \ " "__UA_ADDR "\t1b, 4b \n" \ " "__UA_ADDR "\t2b, 4b \n" \ " .previous \n" \ - : "=r" (ret), "=r" (oldval) \ - : "0" (0), "r" (uaddr), "Jr" (oparg), "i" (-EFAULT)); \ + : "=r" (ret), "=&r" (oldval), "=R" (*uaddr) \ + : "0" (0), "R" (*uaddr), "Jr" (oparg), "i" (-EFAULT) \ + : "memory"); \ } else \ ret = -ENOSYS; \ } @@ -89,23 +90,23 @@ futex_atomic_op_inuser (int encoded_op, switch (op) { case FUTEX_OP_SET: - __futex_atomic_op("move $1, %z4", ret, oldval, uaddr, oparg); + __futex_atomic_op("move $1, %z5", ret, oldval, uaddr, oparg); break; case FUTEX_OP_ADD: - __futex_atomic_op("addu $1, %1, %z4", + __futex_atomic_op("addu $1, %1, %z5", ret, oldval, uaddr, oparg); break; case FUTEX_OP_OR: - __futex_atomic_op("or $1, %1, %z4", + __futex_atomic_op("or $1, %1, %z5", ret, oldval, uaddr, oparg); break; case FUTEX_OP_ANDN: - __futex_atomic_op("and $1, %1, %z4", + __futex_atomic_op("and $1, %1, %z5", ret, oldval, uaddr, ~oparg); break; case FUTEX_OP_XOR: - __futex_atomic_op("xor $1, %1, %z4", + __futex_atomic_op("xor $1, %1, %z5", ret, oldval, uaddr, oparg); break; default: diff --git a/include/asm-mips/hazards.h b/include/asm-mips/hazards.h index dadc051..25f5e8a 100644 --- a/include/asm-mips/hazards.h +++ b/include/asm-mips/hazards.h @@ -10,7 +10,6 @@ #ifndef _ASM_HAZARDS_H #define _ASM_HAZARDS_H -#include #ifdef __ASSEMBLY__ @@ -70,10 +69,10 @@ #ifdef CONFIG_CPU_MIPSR2 * Use a macro for ehb unless explicit support for MIPSR2 is enabled */ -#define irq_enable_hazard +#define irq_enable_hazard \ _ehb -#define irq_disable_hazard +#define irq_disable_hazard \ _ehb #elif defined(CONFIG_CPU_R10000) || defined(CONFIG_CPU_RM9000) diff --git a/include/asm-mips/highmem.h b/include/asm-mips/highmem.h index 8cf5984..c976bfa 100644 --- a/include/asm-mips/highmem.h +++ b/include/asm-mips/highmem.h @@ -19,7 +19,6 @@ #define _ASM_HIGHMEM_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-mips/hw_irq.h b/include/asm-mips/hw_irq.h index c854d01..458d9fd 100644 --- a/include/asm-mips/hw_irq.h +++ b/include/asm-mips/hw_irq.h @@ -19,9 +19,9 @@ extern void init_8259A(int aeoi); extern atomic_t irq_err_count; -/* This may not be apropriate for all machines, we'll see ... */ -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) -{ -} +/* + * interrupt-retrigger: NOP for now. This may not be apropriate for all + * machines, we'll see ... + */ #endif /* __ASM_HW_IRQ_H */ diff --git a/include/asm-mips/interrupt.h b/include/asm-mips/interrupt.h index 4bb9c06..a99d686 100644 --- a/include/asm-mips/interrupt.h +++ b/include/asm-mips/interrupt.h @@ -11,7 +11,6 @@ #ifndef _ASM_INTERRUPT_H #define _ASM_INTERRUPT_H -#include #include __asm__ ( diff --git a/include/asm-mips/io.h b/include/asm-mips/io.h index 6b17eb9..df624e1 100644 --- a/include/asm-mips/io.h +++ b/include/asm-mips/io.h @@ -12,7 +12,6 @@ #ifndef _ASM_IO_H #define _ASM_IO_H -#include #include #include #include diff --git a/include/asm-mips/ip32/machine.h b/include/asm-mips/ip32/machine.h index e440fdf..1b631b8 100644 --- a/include/asm-mips/ip32/machine.h +++ b/include/asm-mips/ip32/machine.h @@ -10,7 +10,6 @@ #ifndef _ASM_IP32_MACHINE_H #define _ASM_IP32_MACHINE_H -#include #ifdef CONFIG_SGI_IP32 diff --git a/include/asm-mips/irq.h b/include/asm-mips/irq.h index dde677f..896550b 100644 --- a/include/asm-mips/irq.h +++ b/include/asm-mips/irq.h @@ -9,7 +9,6 @@ #ifndef _ASM_IRQ_H #define _ASM_IRQ_H -#include #include #include @@ -77,4 +76,8 @@ extern int setup_irq_smtc(unsigned int i unsigned long hwmask); #endif /* CONFIG_MIPS_MT_SMTC */ +#ifdef CONFIG_SMP +#define ARCH_HAS_IRQ_PER_CPU +#endif + #endif /* _ASM_IRQ_H */ diff --git a/include/asm-mips/isadep.h b/include/asm-mips/isadep.h index 7bb0035..24c6cda 100644 --- a/include/asm-mips/isadep.h +++ b/include/asm-mips/isadep.h @@ -5,7 +5,6 @@ * * Copyright (c) 1998 Harald Koerfgen */ -#include #ifndef __ASM_ISADEP_H #define __ASM_ISADEP_H diff --git a/include/asm-mips/jmr3927/irq.h b/include/asm-mips/jmr3927/irq.h index b0c325a..fe551f3 100644 --- a/include/asm-mips/jmr3927/irq.h +++ b/include/asm-mips/jmr3927/irq.h @@ -12,7 +12,6 @@ #define __ASM_TX3927_IRQ_H #ifndef __ASSEMBLY__ -#include #include struct tb_irq_space { diff --git a/include/asm-mips/kmap_types.h b/include/asm-mips/kmap_types.h index 6886a0c..806aae3 100644 --- a/include/asm-mips/kmap_types.h +++ b/include/asm-mips/kmap_types.h @@ -1,7 +1,6 @@ #ifndef _ASM_KMAP_TYPES_H #define _ASM_KMAP_TYPES_H -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-mips/local.h b/include/asm-mips/local.h index c38844f..9e2d43b 100644 --- a/include/asm-mips/local.h +++ b/include/asm-mips/local.h @@ -1,7 +1,6 @@ #ifndef _ASM_LOCAL_H #define _ASM_LOCAL_H -#include #include #include diff --git a/include/asm-mips/mach-au1x00/au1000.h b/include/asm-mips/mach-au1x00/au1000.h index 4686e17..582acd8 100644 --- a/include/asm-mips/mach-au1x00/au1000.h +++ b/include/asm-mips/mach-au1x00/au1000.h @@ -35,7 +35,6 @@ #ifndef _AU1000_H_ #define _AU1000_H_ -#include #ifndef _LANGUAGE_ASSEMBLY diff --git a/include/asm-mips/mach-au1x00/au1xxx.h b/include/asm-mips/mach-au1x00/au1xxx.h index b7b46dd..9471359 100644 --- a/include/asm-mips/mach-au1x00/au1xxx.h +++ b/include/asm-mips/mach-au1x00/au1xxx.h @@ -23,7 +23,6 @@ #ifndef _AU1XXX_H_ #define _AU1XXX_H_ -#include #include diff --git a/include/asm-mips/mach-au1x00/au1xxx_dbdma.h b/include/asm-mips/mach-au1x00/au1xxx_dbdma.h index b327bcd..d5b38a2 100644 --- a/include/asm-mips/mach-au1x00/au1xxx_dbdma.h +++ b/include/asm-mips/mach-au1x00/au1xxx_dbdma.h @@ -34,7 +34,6 @@ #ifndef _AU1000_DBDMA_H_ #define _AU1000_DBDMA_H_ -#include #ifndef _LANGUAGE_ASSEMBLY diff --git a/include/asm-mips/mach-au1x00/au1xxx_ide.h b/include/asm-mips/mach-au1x00/au1xxx_ide.h index e867b4e..301e713 100644 --- a/include/asm-mips/mach-au1x00/au1xxx_ide.h +++ b/include/asm-mips/mach-au1x00/au1xxx_ide.h @@ -29,7 +29,6 @@ * Note: for more information, please refer "AMD Alchemy Au1200/Au1550 IDE * Interface and Linux Device Driver" Application Note. */ -#include #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA #define DMA_WAIT_TIMEOUT 100 diff --git a/include/asm-mips/mach-au1x00/au1xxx_psc.h b/include/asm-mips/mach-au1x00/au1xxx_psc.h index 8e5fb3c..1bd4e27 100644 --- a/include/asm-mips/mach-au1x00/au1xxx_psc.h +++ b/include/asm-mips/mach-au1x00/au1xxx_psc.h @@ -33,14 +33,18 @@ #ifndef _AU1000_PSC_H_ #define _AU1000_PSC_H_ -#include /* The PSC base addresses. */ #ifdef CONFIG_SOC_AU1550 #define PSC0_BASE_ADDR 0xb1a00000 #define PSC1_BASE_ADDR 0xb1b00000 #define PSC2_BASE_ADDR 0xb0a00000 -#define PSC3_BASE_ADDR 0xb0d00000 +#define PSC3_BASE_ADDR 0xb0b00000 +#endif + +#ifdef CONFIG_SOC_AU1200 +#define PSC0_BASE_ADDR 0xb1a00000 +#define PSC1_BASE_ADDR 0xb1b00000 #endif /* The PSC select and control registers are common to @@ -228,6 +232,8 @@ #define PSC_I2SCFG_TT_FIFO8 (3 << 28) #define PSC_I2SCFG_DD_DISABLE (1 << 27) #define PSC_I2SCFG_DE_ENABLE (1 << 26) #define PSC_I2SCFG_SET_WS(x) (((((x) / 2) - 1) & 0x7f) << 16) +#define PSC_I2SCFG_WS(n) ((n & 0xFF) << 16) +#define PSC_I2SCFG_WS_MASK (PSC_I2SCFG_WS(0x3F)) #define PSC_I2SCFG_WI (1 << 15) #define PSC_I2SCFG_DIV_MASK (3 << 13) @@ -506,7 +512,7 @@ #define PSC_SMBEVNT_ALLCLR (PSC_SMBEVNT_ /* Transmit register control. */ -#define PSC_SMBTXRX_RSR (1 << 30) +#define PSC_SMBTXRX_RSR (1 << 28) #define PSC_SMBTXRX_STP (1 << 29) #define PSC_SMBTXRX_DATAMASK (0xff) diff --git a/include/asm-mips/mach-au1x00/ioremap.h b/include/asm-mips/mach-au1x00/ioremap.h index d3ec627..098fca4 100644 --- a/include/asm-mips/mach-au1x00/ioremap.h +++ b/include/asm-mips/mach-au1x00/ioremap.h @@ -9,7 +9,6 @@ #ifndef __ASM_MACH_AU1X00_IOREMAP_H #define __ASM_MACH_AU1X00_IOREMAP_H -#include #include #ifdef CONFIG_64BIT_PHYS_ADDR diff --git a/include/asm-mips/mach-cobalt/cpu-feature-overrides.h b/include/asm-mips/mach-cobalt/cpu-feature-overrides.h index ace8c5e..e0e08fc 100644 --- a/include/asm-mips/mach-cobalt/cpu-feature-overrides.h +++ b/include/asm-mips/mach-cobalt/cpu-feature-overrides.h @@ -8,7 +8,6 @@ #ifndef __ASM_COBALT_CPU_FEATURE_OVERRIDES_H #define __ASM_COBALT_CPU_FEATURE_OVERRIDES_H -#include #define cpu_has_tlb 1 #define cpu_has_4kex 1 diff --git a/include/asm-mips/mach-db1x00/db1x00.h b/include/asm-mips/mach-db1x00/db1x00.h index 7b28b23..0f5f4c2 100644 --- a/include/asm-mips/mach-db1x00/db1x00.h +++ b/include/asm-mips/mach-db1x00/db1x00.h @@ -28,11 +28,22 @@ #ifndef __ASM_DB1X00_H #define __ASM_DB1X00_H -#include #ifdef CONFIG_MIPS_DB1550 + +#define DBDMA_AC97_TX_CHAN DSCR_CMD0_PSC1_TX +#define DBDMA_AC97_RX_CHAN DSCR_CMD0_PSC1_RX +#define DBDMA_I2S_TX_CHAN DSCR_CMD0_PSC3_TX +#define DBDMA_I2S_RX_CHAN DSCR_CMD0_PSC3_RX + +#define SPI_PSC_BASE PSC0_BASE_ADDR +#define AC97_PSC_BASE PSC1_BASE_ADDR +#define SMBUS_PSC_BASE PSC2_BASE_ADDR +#define I2S_PSC_BASE PSC3_BASE_ADDR + #define BCSR_KSEG1_ADDR 0xAF000000 #define NAND_PHYS_ADDR 0x20000000 + #else #define BCSR_KSEG1_ADDR 0xAE000000 #endif diff --git a/include/asm-mips/mach-ddb5074/mc146818rtc.h b/include/asm-mips/mach-ddb5074/mc146818rtc.h deleted file mode 100644 index 2eb9acb..0000000 --- a/include/asm-mips/mach-ddb5074/mc146818rtc.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 1998, 2001, 03 by Ralf Baechle - * - * RTC routines for PC style attached Dallas chip. - */ -#ifndef __ASM_MACH_DDB5074_MC146818RTC_H -#define __ASM_MACH_DDB5074_MC146818RTC_H - -#include -#include - -#define RTC_PORT(x) (0x70 + (x)) -#define RTC_IRQ 8 - -static inline unsigned char CMOS_READ(unsigned long addr) -{ - return *(volatile unsigned char *)(KSEG1ADDR(DDB_PCI_MEM_BASE)+addr); -} - -static inline void CMOS_WRITE(unsigned char data, unsigned long addr) -{ - *(volatile unsigned char *)(KSEG1ADDR(DDB_PCI_MEM_BASE)+addr) = data; -} - -#define RTC_ALWAYS_BCD 1 - -#endif /* __ASM_MACH_DDB5074_MC146818RTC_H */ diff --git a/include/asm-mips/mach-dec/param.h b/include/asm-mips/mach-dec/param.h deleted file mode 100644 index 3e4f0e3..0000000 --- a/include/asm-mips/mach-dec/param.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003 by Ralf Baechle - */ -#ifndef __ASM_MACH_DEC_PARAM_H -#define __ASM_MACH_DEC_PARAM_H - -/* - * log2(HZ), change this here if you want another HZ value. This is also - * used in dec_time_init. Minimum is 1, Maximum is 15. - */ -#define LOG_2_HZ 7 -#define HZ (1 << LOG_2_HZ) - -#endif /* __ASM_MACH_DEC_PARAM_H */ diff --git a/include/asm-mips/mach-emma2rh/irq.h b/include/asm-mips/mach-emma2rh/irq.h new file mode 100644 index 0000000..bce6424 --- /dev/null +++ b/include/asm-mips/mach-emma2rh/irq.h @@ -0,0 +1,13 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2003 by Ralf Baechle + */ +#ifndef __ASM_MACH_EMMA2RH_IRQ_H +#define __ASM_MACH_EMMA2RH_IRQ_H + +#define NR_IRQS 256 + +#endif /* __ASM_MACH_EMMA2RH_IRQ_H */ diff --git a/include/asm-mips/mach-excite/cpu-feature-overrides.h b/include/asm-mips/mach-excite/cpu-feature-overrides.h new file mode 100644 index 0000000..abb76b2 --- /dev/null +++ b/include/asm-mips/mach-excite/cpu-feature-overrides.h @@ -0,0 +1,40 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2004 Thomas Koeller + */ +#ifndef __ASM_MACH_EXCITE_CPU_FEATURE_OVERRIDES_H +#define __ASM_MACH_EXCITE_CPU_FEATURE_OVERRIDES_H + +/* + * Basler eXcite has an RM9122 processor. + */ +#define cpu_has_watch 1 +#define cpu_has_mips16 0 +#define cpu_has_divec 0 +#define cpu_has_vce 0 +#define cpu_has_cache_cdex_p 0 +#define cpu_has_cache_cdex_s 0 +#define cpu_has_prefetch 1 +#define cpu_has_mcheck 0 +#define cpu_has_ejtag 0 + +#define cpu_has_llsc 1 +#define cpu_has_vtag_icache 0 +#define cpu_has_dc_aliases 0 +#define cpu_has_ic_fills_f_dc 0 +#define cpu_has_dsp 0 +#define cpu_icache_snoops_remote_store 0 + +#define cpu_has_nofpuex 0 +#define cpu_has_64bits 1 + +#define cpu_has_subset_pcaches 0 + +#define cpu_dcache_line_size() 32 +#define cpu_icache_line_size() 32 +#define cpu_scache_line_size() 32 + +#endif /* __ASM_MACH_EXCITE_CPU_FEATURE_OVERRIDES_H */ diff --git a/include/asm-mips/mach-excite/excite.h b/include/asm-mips/mach-excite/excite.h new file mode 100644 index 0000000..c52610d --- /dev/null +++ b/include/asm-mips/mach-excite/excite.h @@ -0,0 +1,155 @@ +#ifndef __EXCITE_H__ +#define __EXCITE_H__ + +#include +#include +#include +#include + +#define EXCITE_CPU_EXT_CLOCK 100000000 + +#if !defined(__ASSEMBLER__) +void __init excite_kgdb_init(void); +void excite_procfs_init(void); +extern unsigned long memsize; +extern char modetty[]; +extern u32 unit_id; +#endif + +/* Base name for XICAP devices */ +#define XICAP_NAME "xicap_gpi" + +/* OCD register offsets */ +#define LKB0 0x0038 +#define LKB5 0x0128 +#define LKM5 0x012C +#define LKB7 0x0138 +#define LKM7 0x013c +#define LKB8 0x0140 +#define LKM8 0x0144 +#define LKB9 0x0148 +#define LKM9 0x014c +#define LKB10 0x0150 +#define LKM10 0x0154 +#define LKB11 0x0158 +#define LKM11 0x015c +#define LKB12 0x0160 +#define LKM12 0x0164 +#define LKB13 0x0168 +#define LKM13 0x016c +#define LDP0 0x0200 +#define LDP1 0x0210 +#define LDP2 0x0220 +#define LDP3 0x0230 +#define INTPIN0 0x0A40 +#define INTPIN1 0x0A44 +#define INTPIN2 0x0A48 +#define INTPIN3 0x0A4C +#define INTPIN4 0x0A50 +#define INTPIN5 0x0A54 +#define INTPIN6 0x0A58 +#define INTPIN7 0x0A5C + + + + +/* TITAN register offsets */ +#define CPRR 0x0004 +#define CPDSR 0x0008 +#define CPTC0R 0x000c +#define CPTC1R 0x0010 +#define CPCFG0 0x0020 +#define CPCFG1 0x0024 +#define CPDST0A 0x0028 +#define CPDST0B 0x002c +#define CPDST1A 0x0030 +#define CPDST1B 0x0034 +#define CPXDSTA 0x0038 +#define CPXDSTB 0x003c +#define CPXCISRA 0x0048 +#define CPXCISRB 0x004c +#define CPGIG0ER 0x0050 +#define CPGIG1ER 0x0054 +#define CPGRWL 0x0068 +#define CPURSLMT 0x00f8 +#define UACFG 0x0200 +#define UAINTS 0x0204 +#define SDRXFCIE 0x4828 +#define SDTXFCIE 0x4928 +#define INTP0Status0 0x1B00 +#define INTP0Mask0 0x1B04 +#define INTP0Set0 0x1B08 +#define INTP0Clear0 0x1B0C +#define GXCFG 0x5000 +#define GXDMADRPFX 0x5018 +#define GXDMA_DESCADR 0x501c +#define GXCH0TDESSTRT 0x5054 + +/* IRQ definitions */ +#define NMICONFIG 0xac0 +#define TITAN_MSGINT 0xc4 +#define TITAN_IRQ ((TITAN_MSGINT / 0x20) + 2) +#define FPGA0_MSGINT 0x5a +#define FPGA0_IRQ ((FPGA0_MSGINT / 0x20) + 2) +#define FPGA1_MSGINT 0x7b +#define FPGA1_IRQ ((FPGA1_MSGINT / 0x20) + 2) +#define PHY_MSGINT 0x9c +#define PHY_IRQ ((PHY_MSGINT / 0x20) + 2) + +#if defined(CONFIG_BASLER_EXCITE_PROTOTYPE) +/* Pre-release units used interrupt pin #9 */ +#define USB_IRQ 11 +#else +/* Re-designed units use interrupt pin #1 */ +#define USB_MSGINT 0x39 +#define USB_IRQ ((USB_MSGINT / 0x20) + 2) +#endif +#define TIMER_IRQ 12 + + +/* Device address ranges */ +#define EXCITE_OFFS_OCD 0x1fffc000 +#define EXCITE_SIZE_OCD (16 * 1024) +#define EXCITE_PHYS_OCD CPHYSADDR(EXCITE_OFFS_OCD) +#define EXCITE_ADDR_OCD CKSEG1ADDR(EXCITE_OFFS_OCD) + +#define EXCITE_OFFS_SCRAM 0x1fffa000 +#define EXCITE_SIZE_SCRAM (8 << 10) +#define EXCITE_PHYS_SCRAM CPHYSADDR(EXCITE_OFFS_SCRAM) +#define EXCITE_ADDR_SCRAM CKSEG1ADDR(EXCITE_OFFS_SCRAM) + +#define EXCITE_OFFS_PCI_IO 0x1fff8000 +#define EXCITE_SIZE_PCI_IO (8 << 10) +#define EXCITE_PHYS_PCI_IO CPHYSADDR(EXCITE_OFFS_PCI_IO) +#define EXCITE_ADDR_PCI_IO CKSEG1ADDR(EXCITE_OFFS_PCI_IO) + +#define EXCITE_OFFS_TITAN 0x1fff0000 +#define EXCITE_SIZE_TITAN (32 << 10) +#define EXCITE_PHYS_TITAN CPHYSADDR(EXCITE_OFFS_TITAN) +#define EXCITE_ADDR_TITAN CKSEG1ADDR(EXCITE_OFFS_TITAN) + +#define EXCITE_OFFS_PCI_MEM 0x1ffe0000 +#define EXCITE_SIZE_PCI_MEM (64 << 10) +#define EXCITE_PHYS_PCI_MEM CPHYSADDR(EXCITE_OFFS_PCI_MEM) +#define EXCITE_ADDR_PCI_MEM CKSEG1ADDR(EXCITE_OFFS_PCI_MEM) + +#define EXCITE_OFFS_FPGA 0x1ffdc000 +#define EXCITE_SIZE_FPGA (16 << 10) +#define EXCITE_PHYS_FPGA CPHYSADDR(EXCITE_OFFS_FPGA) +#define EXCITE_ADDR_FPGA CKSEG1ADDR(EXCITE_OFFS_FPGA) + +#define EXCITE_OFFS_NAND 0x1ffd8000 +#define EXCITE_SIZE_NAND (16 << 10) +#define EXCITE_PHYS_NAND CPHYSADDR(EXCITE_OFFS_NAND) +#define EXCITE_ADDR_NAND CKSEG1ADDR(EXCITE_OFFS_NAND) + +#define EXCITE_OFFS_BOOTROM 0x1f000000 +#define EXCITE_SIZE_BOOTROM (8 << 20) +#define EXCITE_PHYS_BOOTROM CPHYSADDR(EXCITE_OFFS_BOOTROM) +#define EXCITE_ADDR_BOOTROM CKSEG1ADDR(EXCITE_OFFS_BOOTROM) + +/* FPGA address offsets */ +#define EXCITE_FPGA_DPR 0x0104 /* dual-ported ram */ +#define EXCITE_FPGA_SYSCTL 0x0200 /* system control register block */ + +#endif /* __EXCITE_H__ */ diff --git a/include/asm-mips/mach-excite/excite_nandflash.h b/include/asm-mips/mach-excite/excite_nandflash.h new file mode 100644 index 0000000..c4cf614 --- /dev/null +++ b/include/asm-mips/mach-excite/excite_nandflash.h @@ -0,0 +1,7 @@ +#ifndef __EXCITE_NANDFLASH_H__ +#define __EXCITE_NANDFLASH_H__ + +/* Resource names */ +#define EXCITE_NANDFLASH_RESOURCE_REGS "excite_nandflash_regs" + +#endif /* __EXCITE_NANDFLASH_H__ */ diff --git a/include/asm-mips/mach-excite/rm9k_eth.h b/include/asm-mips/mach-excite/rm9k_eth.h new file mode 100644 index 0000000..94705a4 --- /dev/null +++ b/include/asm-mips/mach-excite/rm9k_eth.h @@ -0,0 +1,23 @@ +#if !defined(__RM9K_ETH_H__) +#define __RM9K_ETH_H__ + +#define RM9K_GE_NAME "rm9k_ge" + +/* Resource names */ +#define RM9K_GE_RESOURCE_MAC "rm9k_ge_mac" +#define RM9K_GE_RESOURCE_MSTAT "rm9k_ge_mstat" +#define RM9K_GE_RESOURCE_PKTPROC "rm9k_ge_pktproc" +#define RM9K_GE_RESOURCE_XDMA "rm9k_ge_xdma" +#define RM9K_GE_RESOURCE_FIFO_RX "rm9k_ge_fifo_rx" +#define RM9K_GE_RESOURCE_FIFO_TX "rm9k_ge_fifo_tx" +#define RM9K_GE_RESOURCE_FIFOMEM_RX "rm9k_ge_fifo_memory_rx" +#define RM9K_GE_RESOURCE_FIFOMEM_TX "rm9k_ge_fifo_memory_tx" +#define RM9K_GE_RESOURCE_PHY "rm9k_ge_phy" +#define RM9K_GE_RESOURCE_DMADESC_RX "rm9k_ge_dmadesc_rx" +#define RM9K_GE_RESOURCE_DMADESC_TX "rm9k_ge_dmadesc_tx" +#define RM9K_GE_RESOURCE_IRQ_MAIN "rm9k_ge_irq_main" +#define RM9K_GE_RESOURCE_IRQ_PHY "rm9k_ge_irq_phy" +#define RM9K_GE_RESOURCE_GPI_SLICE "rm9k_ge_gpi_slice" +#define RM9K_GE_RESOURCE_MDIO_CHANNEL "rm9k_ge_mdio_channel" + +#endif /* !defined(__RM9K_ETH_H__) */ diff --git a/include/asm-mips/mach-excite/rm9k_wdt.h b/include/asm-mips/mach-excite/rm9k_wdt.h new file mode 100644 index 0000000..3fa3c08 --- /dev/null +++ b/include/asm-mips/mach-excite/rm9k_wdt.h @@ -0,0 +1,12 @@ +#ifndef __RM9K_WDT_H__ +#define __RM9K_WDT_H__ + +/* Device name */ +#define WDT_NAME "wdt_gpi" + +/* Resource names */ +#define WDT_RESOURCE_REGS "excite_watchdog_regs" +#define WDT_RESOURCE_IRQ "excite_watchdog_irq" +#define WDT_RESOURCE_COUNTER "excite_watchdog_counter" + +#endif /* __RM9K_WDT_H__ */ diff --git a/include/asm-mips/mach-excite/rm9k_xicap.h b/include/asm-mips/mach-excite/rm9k_xicap.h new file mode 100644 index 0000000..0095777 --- /dev/null +++ b/include/asm-mips/mach-excite/rm9k_xicap.h @@ -0,0 +1,16 @@ +#ifndef __EXCITE_XICAP_H__ +#define __EXCITE_XICAP_H__ + + +/* Resource names */ +#define XICAP_RESOURCE_FIFO_RX "xicap_fifo_rx" +#define XICAP_RESOURCE_FIFO_TX "xicap_fifo_tx" +#define XICAP_RESOURCE_XDMA "xicap_xdma" +#define XICAP_RESOURCE_DMADESC "xicap_dmadesc" +#define XICAP_RESOURCE_PKTPROC "xicap_pktproc" +#define XICAP_RESOURCE_IRQ "xicap_irq" +#define XICAP_RESOURCE_GPI_SLICE "xicap_gpi_slice" +#define XICAP_RESOURCE_FIFO_BLK "xicap_fifo_blocks" +#define XICAP_RESOURCE_PKT_STREAM "xicap_pkt_stream" + +#endif /* __EXCITE_XICAP_H__ */ diff --git a/include/asm-mips/mach-generic/floppy.h b/include/asm-mips/mach-generic/floppy.h index 682a585..001a8ce 100644 --- a/include/asm-mips/mach-generic/floppy.h +++ b/include/asm-mips/mach-generic/floppy.h @@ -98,7 +98,7 @@ static inline void fd_disable_irq(void) static inline int fd_request_irq(void) { return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT | SA_SAMPLE_RANDOM, "floppy", NULL); + IRQF_DISABLED, "floppy", NULL); } static inline void fd_free_irq(void) diff --git a/include/asm-mips/mach-generic/ide.h b/include/asm-mips/mach-generic/ide.h index e331535..6eba2e5 100644 --- a/include/asm-mips/mach-generic/ide.h +++ b/include/asm-mips/mach-generic/ide.h @@ -15,7 +15,6 @@ #define __ASM_MACH_GENERIC_IDE_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-mips/mach-generic/kmalloc.h b/include/asm-mips/mach-generic/kmalloc.h index 373d66d..410ab5f 100644 --- a/include/asm-mips/mach-generic/kmalloc.h +++ b/include/asm-mips/mach-generic/kmalloc.h @@ -1,7 +1,6 @@ #ifndef __ASM_MACH_GENERIC_KMALLOC_H #define __ASM_MACH_GENERIC_KMALLOC_H -#include #ifndef CONFIG_DMA_COHERENT /* diff --git a/include/asm-mips/mach-generic/param.h b/include/asm-mips/mach-generic/param.h deleted file mode 100644 index a0d12f9..0000000 --- a/include/asm-mips/mach-generic/param.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003 by Ralf Baechle - */ -#ifndef __ASM_MACH_GENERIC_PARAM_H -#define __ASM_MACH_GENERIC_PARAM_H - -#define HZ 1000 /* Internal kernel timer frequency */ - -#endif /* __ASM_MACH_GENERIC_PARAM_H */ diff --git a/include/asm-mips/mach-generic/spaces.h b/include/asm-mips/mach-generic/spaces.h index b849d8d..0ae9997 100644 --- a/include/asm-mips/mach-generic/spaces.h +++ b/include/asm-mips/mach-generic/spaces.h @@ -10,7 +10,6 @@ #ifndef _ASM_MACH_GENERIC_SPACES_H #define _ASM_MACH_GENERIC_SPACES_H -#include #ifdef CONFIG_32BIT diff --git a/include/asm-mips/mach-ip22/cpu-feature-overrides.h b/include/asm-mips/mach-ip22/cpu-feature-overrides.h index 2a37bed..f7c5dc8 100644 --- a/include/asm-mips/mach-ip22/cpu-feature-overrides.h +++ b/include/asm-mips/mach-ip22/cpu-feature-overrides.h @@ -13,7 +13,7 @@ #define __ASM_MACH_IP22_CPU_FEATURE_OVER */ #define cpu_has_tlb 1 #define cpu_has_4kex 1 -#define cpu_has_4kcache 1 +#define cpu_has_4k_cache 1 #define cpu_has_fpu 1 #define cpu_has_32fpr 1 #define cpu_has_counter 1 diff --git a/include/asm-mips/mach-ip22/spaces.h b/include/asm-mips/mach-ip22/spaces.h index 8385f71..ab20c02 100644 --- a/include/asm-mips/mach-ip22/spaces.h +++ b/include/asm-mips/mach-ip22/spaces.h @@ -10,7 +10,6 @@ #ifndef _ASM_MACH_IP22_SPACES_H #define _ASM_MACH_IP22_SPACES_H -#include #ifdef CONFIG_32BIT diff --git a/include/asm-mips/mach-ip27/cpu-feature-overrides.h b/include/asm-mips/mach-ip27/cpu-feature-overrides.h index 2d2f5b9..19c2d13 100644 --- a/include/asm-mips/mach-ip27/cpu-feature-overrides.h +++ b/include/asm-mips/mach-ip27/cpu-feature-overrides.h @@ -31,6 +31,9 @@ #define cpu_icache_snoops_remote_store 1 #define cpu_has_nofpuex 0 #define cpu_has_64bits 1 +#define cpu_has_4kex 1 +#define cpu_has_4k_cache 1 + #define cpu_has_subset_pcaches 1 #define cpu_dcache_line_size() 32 diff --git a/include/asm-mips/mach-ip32/cpu-feature-overrides.h b/include/asm-mips/mach-ip32/cpu-feature-overrides.h index 36070b5..2a3de09 100644 --- a/include/asm-mips/mach-ip32/cpu-feature-overrides.h +++ b/include/asm-mips/mach-ip32/cpu-feature-overrides.h @@ -9,7 +9,6 @@ #ifndef __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H #define __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H -#include /* * R5000 has an interesting "restriction": ll(d)/sc(d) @@ -38,6 +37,8 @@ #define cpu_has_ejtag 0 #define cpu_has_vtag_icache 0 #define cpu_has_ic_fills_f_dc 0 #define cpu_has_dsp 0 +#define cpu_has_4k_cache 1 + #define cpu_has_mips32r1 0 #define cpu_has_mips32r2 0 diff --git a/include/asm-mips/mach-ip32/kmalloc.h b/include/asm-mips/mach-ip32/kmalloc.h index 9d2d4d9..f6198a2 100644 --- a/include/asm-mips/mach-ip32/kmalloc.h +++ b/include/asm-mips/mach-ip32/kmalloc.h @@ -1,7 +1,6 @@ #ifndef __ASM_MACH_IP32_KMALLOC_H #define __ASM_MACH_IP32_KMALLOC_H -#include #if defined(CONFIG_CPU_R5000) || defined (CONFIG_CPU_RM7000) #define ARCH_KMALLOC_MINALIGN 32 diff --git a/include/asm-mips/mach-jazz/floppy.h b/include/asm-mips/mach-jazz/floppy.h index c9dad99..56e9ca6 100644 --- a/include/asm-mips/mach-jazz/floppy.h +++ b/include/asm-mips/mach-jazz/floppy.h @@ -90,7 +90,7 @@ static inline void fd_disable_irq(void) static inline int fd_request_irq(void) { return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT | SA_SAMPLE_RANDOM, "floppy", NULL); + IRQF_DISABLED, "floppy", NULL); } static inline void fd_free_irq(void) diff --git a/include/asm-mips/mach-jazz/param.h b/include/asm-mips/mach-jazz/param.h deleted file mode 100644 index 639763a..0000000 --- a/include/asm-mips/mach-jazz/param.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003 by Ralf Baechle - */ -#ifndef __ASM_MACH_JAZZ_PARAM_H -#define __ASM_MACH_JAZZ_PARAM_H - -/* - * Jazz is currently using the internal 100Hz timer of the R4030 - */ -#define HZ 100 /* Internal kernel timer frequency */ - -#endif /* __ASM_MACH_JAZZ_PARAM_H */ diff --git a/include/asm-mips/mach-mips/cpu-feature-overrides.h b/include/asm-mips/mach-mips/cpu-feature-overrides.h index e06af6c..e960679 100644 --- a/include/asm-mips/mach-mips/cpu-feature-overrides.h +++ b/include/asm-mips/mach-mips/cpu-feature-overrides.h @@ -9,7 +9,6 @@ #ifndef __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H #define __ASM_MACH_MIPS_CPU_FEATURE_OVERRIDES_H -#include /* * CPU feature overrides for MIPS boards @@ -17,7 +16,7 @@ #include #ifdef CONFIG_CPU_MIPS32 #define cpu_has_tlb 1 #define cpu_has_4kex 1 -#define cpu_has_4kcache 1 +#define cpu_has_4k_cache 1 /* #define cpu_has_fpu ? */ /* #define cpu_has_32fpr ? */ #define cpu_has_counter 1 @@ -47,7 +46,7 @@ #endif #ifdef CONFIG_CPU_MIPS64 #define cpu_has_tlb 1 #define cpu_has_4kex 1 -#define cpu_has_4kcache 1 +#define cpu_has_4k_cache 1 /* #define cpu_has_fpu ? */ /* #define cpu_has_32fpr ? */ #define cpu_has_counter 1 diff --git a/include/asm-mips/mach-mips/irq.h b/include/asm-mips/mach-mips/irq.h index f857969..e994b0c 100644 --- a/include/asm-mips/mach-mips/irq.h +++ b/include/asm-mips/mach-mips/irq.h @@ -1,14 +1,7 @@ #ifndef __ASM_MACH_MIPS_IRQ_H #define __ASM_MACH_MIPS_IRQ_H -#include #define NR_IRQS 256 -#ifdef CONFIG_SMP - -#define ARCH_HAS_IRQ_PER_CPU - -#endif - #endif /* __ASM_MACH_MIPS_IRQ_H */ diff --git a/include/asm-mips/mach-mips/param.h b/include/asm-mips/mach-mips/param.h deleted file mode 100644 index 805ef6d..0000000 --- a/include/asm-mips/mach-mips/param.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2003 by Ralf Baechle - */ -#ifndef __ASM_MACH_MIPS_PARAM_H -#define __ASM_MACH_MIPS_PARAM_H - -#define HZ 100 /* Internal kernel timer frequency */ - -#endif /* __ASM_MACH_MIPS_PARAM_H */ diff --git a/include/asm-mips/mach-pb1x00/pb1550.h b/include/asm-mips/mach-pb1x00/pb1550.h index 9578ead..9a4955c 100644 --- a/include/asm-mips/mach-pb1x00/pb1550.h +++ b/include/asm-mips/mach-pb1x00/pb1550.h @@ -27,7 +27,6 @@ #ifndef __ASM_PB1550_H #define __ASM_PB1550_H -#include #include #define DBDMA_AC97_TX_CHAN DSCR_CMD0_PSC1_TX diff --git a/include/asm-mips/mach-qemu/param.h b/include/asm-mips/mach-qemu/param.h deleted file mode 100644 index cb30ee4..0000000 --- a/include/asm-mips/mach-qemu/param.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2005 by Ralf Baechle - */ -#ifndef __ASM_MACH_QEMU_PARAM_H -#define __ASM_MACH_QEMU_PARAM_H - -#define HZ 100 /* Internal kernel timer frequency */ - -#endif /* __ASM_MACH_QEMU_PARAM_H */ diff --git a/include/asm-mips/mach-rm200/cpu-feature-overrides.h b/include/asm-mips/mach-rm200/cpu-feature-overrides.h index 91e7cf5..11410ae 100644 --- a/include/asm-mips/mach-rm200/cpu-feature-overrides.h +++ b/include/asm-mips/mach-rm200/cpu-feature-overrides.h @@ -14,7 +14,7 @@ #include #define cpu_has_tlb 1 #define cpu_has_4kex 1 -#define cpu_has_4kcache 1 +#define cpu_has_4k_cache 1 #define cpu_has_fpu 1 #define cpu_has_32fpr 1 #define cpu_has_counter 1 @@ -35,10 +35,8 @@ #define cpu_has_dsp 0 #define cpu_has_nofpuex 0 #define cpu_has_64bits 1 -#define cpu_has_subset_pcaches 0 /* No S-cache on R5000 I think ... */ #define cpu_dcache_line_size() 32 #define cpu_icache_line_size() 32 -#define cpu_scache_line_size() 0 /* No S-cache on R5000 I think ... */ #define cpu_has_mips32r1 0 #define cpu_has_mips32r2 0 diff --git a/include/asm-mips/mach-sim/cpu-feature-overrides.h b/include/asm-mips/mach-sim/cpu-feature-overrides.h index cadbe8e..d736bda 100644 --- a/include/asm-mips/mach-sim/cpu-feature-overrides.h +++ b/include/asm-mips/mach-sim/cpu-feature-overrides.h @@ -8,7 +8,6 @@ #ifndef __ASM_MACH_SIM_CPU_FEATURE_OVERRIDES_H #define __ASM_MACH_SIM_CPU_FEATURE_OVERRIDES_H -#include /* * CPU feature overrides for MIPS boards @@ -16,7 +15,7 @@ #include #ifdef CONFIG_CPU_MIPS32 #define cpu_has_tlb 1 #define cpu_has_4kex 1 -#define cpu_has_4kcache 1 +#define cpu_has_4k_cache 1 #define cpu_has_fpu 0 /* #define cpu_has_32fpr ? */ #define cpu_has_counter 1 @@ -41,7 +40,7 @@ #endif #ifdef CONFIG_CPU_MIPS64 #define cpu_has_tlb 1 #define cpu_has_4kex 1 -#define cpu_has_4kcache 1 +#define cpu_has_4k_cache 1 /* #define cpu_has_fpu ? */ /* #define cpu_has_32fpr ? */ #define cpu_has_counter 1 diff --git a/include/asm-mips/mach-wrppmc/mach-gt64120.h b/include/asm-mips/mach-wrppmc/mach-gt64120.h new file mode 100644 index 0000000..ba9205a --- /dev/null +++ b/include/asm-mips/mach-wrppmc/mach-gt64120.h @@ -0,0 +1,84 @@ +/* + * This is a direct copy of the ev96100.h file, with a global + * search and replace. The numbers are the same. + * + * The reason I'm duplicating this is so that the 64120/96100 + * defines won't be confusing in the source code. + */ +#ifndef __ASM_MIPS_GT64120_H +#define __ASM_MIPS_GT64120_H + +/* + * This is the CPU physical memory map of PPMC Board: + * + * 0x00000000-0x03FFFFFF - 64MB SDRAM (SCS[0]#) + * 0x1C000000-0x1C000000 - LED (CS0) + * 0x1C800000-0x1C800007 - UART 16550 port (CS1) + * 0x1F000000-0x1F000000 - MailBox (CS3) + * 0x1FC00000-0x20000000 - 4MB Flash (BOOT CS) + */ + +#define WRPPMC_SDRAM_SCS0_BASE 0x00000000 +#define WRPPMC_SDRAM_SCS0_SIZE 0x04000000 + +#define WRPPMC_UART16550_BASE 0x1C800000 +#define WRPPMC_UART16550_CLOCK 3686400 /* 3.68MHZ */ + +#define WRPPMC_LED_BASE 0x1C000000 +#define WRPPMC_MBOX_BASE 0x1F000000 + +#define WRPPMC_BOOTROM_BASE 0x1FC00000 +#define WRPPMC_BOOTROM_SIZE 0x00400000 /* 4M Flash */ + +#define WRPPMC_MIPS_TIMER_IRQ 7 /* MIPS compare/count timer interrupt */ +#define WRPPMC_UART16550_IRQ 6 +#define WRPPMC_PCI_INTA_IRQ 3 + +/* + * PCI Bus I/O and Memory resources allocation + * + * NOTE: We only have PCI_0 hose interface + */ +#define GT_PCI_MEM_BASE 0x13000000UL +#define GT_PCI_MEM_SIZE 0x02000000UL +#define GT_PCI_IO_BASE 0x11000000UL +#define GT_PCI_IO_SIZE 0x02000000UL +#define GT_ISA_IO_BASE PCI_IO_BASE + +/* + * PCI interrupts will come in on either the INTA or INTD interrups lines, + * which are mapped to the #2 and #5 interrupt pins of the MIPS. On our + * boards, they all either come in on IntD or they all come in on IntA, they + * aren't mixed. There can be numerous PCI interrupts, so we keep a list of the + * "requested" interrupt numbers and go through the list whenever we get an + * IntA/D. + * + * Interrupts < 8 are directly wired to the processor; PCI INTA is 8 and + * INTD is 11. + */ +#define GT_TIMER 4 +#define GT_INTA 2 +#define GT_INTD 5 + +#ifndef __ASSEMBLY__ + +/* + * GT64120 internal register space base address + */ +extern unsigned long gt64120_base; + +#define GT64120_BASE (gt64120_base) + +/* define WRPPMC_EARLY_DEBUG to enable early output something to UART */ +#undef WRPPMC_EARLY_DEBUG + +#ifdef WRPPMC_EARLY_DEBUG +extern void wrppmc_led_on(int mask); +extern void wrppmc_led_off(int mask); +extern void wrppmc_early_printk(const char *fmt, ...); +#else +#define wrppmc_early_printk(fmt, ...) do {} while (0) +#endif /* WRPPMC_EARLY_DEBUG */ + +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_MIPS_GT64120_H */ diff --git a/include/asm-mips/mips-boards/generic.h b/include/asm-mips/mips-boards/generic.h index fa8b913..b98f165 100644 --- a/include/asm-mips/mips-boards/generic.h +++ b/include/asm-mips/mips-boards/generic.h @@ -20,7 +20,6 @@ #ifndef __ASM_MIPS_BOARDS_GENERIC_H #define __ASM_MIPS_BOARDS_GENERIC_H -#include #include #include #include diff --git a/include/asm-mips/mipsregs.h b/include/asm-mips/mipsregs.h index 5af7517..9192d76 100644 --- a/include/asm-mips/mipsregs.h +++ b/include/asm-mips/mipsregs.h @@ -13,7 +13,6 @@ #ifndef _ASM_MIPSREGS_H #define _ASM_MIPSREGS_H -#include #include #include @@ -1451,18 +1450,17 @@ static inline void __emt(unsigned int pr { if ((previous & __EMT_ENABLE)) __asm__ __volatile__( - " .set noreorder \n" " .set mips32r2 \n" " .word 0x41600be1 # emt \n" " ehb \n" - " .set mips0 \n" - " .set reorder \n"); + " .set mips0 \n"); } static inline void __ehb(void) { __asm__ __volatile__( - " ehb \n"); + " .set mips32r2 \n" + " ehb \n" " .set mips0 \n"); } /* diff --git a/include/asm-mips/mmu_context.h b/include/asm-mips/mmu_context.h index 6e09f4c..18b69de 100644 --- a/include/asm-mips/mmu_context.h +++ b/include/asm-mips/mmu_context.h @@ -11,7 +11,6 @@ #ifndef _ASM_MMU_CONTEXT_H #define _ASM_MMU_CONTEXT_H -#include #include #include #include diff --git a/include/asm-mips/mmzone.h b/include/asm-mips/mmzone.h index 7bde443..f53ec54 100644 --- a/include/asm-mips/mmzone.h +++ b/include/asm-mips/mmzone.h @@ -5,26 +5,13 @@ #ifndef _ASM_MMZONE_H_ #define _ASM_MMZONE_H_ -#include #include #include #ifdef CONFIG_DISCONTIGMEM -#define kvaddr_to_nid(kvaddr) pa_to_nid(__pa(kvaddr)) #define pfn_to_nid(pfn) pa_to_nid((pfn) << PAGE_SHIFT) -#define pfn_valid(pfn) \ -({ \ - unsigned long __pfn = (pfn); \ - int __n = pfn_to_nid(__pfn); \ - ((__n >= 0) ? (__pfn < NODE_DATA(__n)->node_start_pfn + \ - NODE_DATA(__n)->node_spanned_pages) : 0);\ -}) - -/* XXX: FIXME -- wli */ -#define kern_addr_valid(addr) (0) - #endif /* CONFIG_DISCONTIGMEM */ #endif /* _ASM_MMZONE_H_ */ diff --git a/include/asm-mips/module.h b/include/asm-mips/module.h index 2af496c..399d03f 100644 --- a/include/asm-mips/module.h +++ b/include/asm-mips/module.h @@ -1,7 +1,6 @@ #ifndef _ASM_MODULE_H #define _ASM_MODULE_H -#include #include #include diff --git a/include/asm-mips/msgbuf.h b/include/asm-mips/msgbuf.h index a153395..0d6c7f1 100644 --- a/include/asm-mips/msgbuf.h +++ b/include/asm-mips/msgbuf.h @@ -1,7 +1,6 @@ #ifndef _ASM_MSGBUF_H #define _ASM_MSGBUF_H -#include /* * The msqid64_ds structure for the MIPS architecture. diff --git a/include/asm-mips/paccess.h b/include/asm-mips/paccess.h index 46f2d23..147844e 100644 --- a/include/asm-mips/paccess.h +++ b/include/asm-mips/paccess.h @@ -13,7 +13,6 @@ #ifndef _ASM_PACCESS_H #define _ASM_PACCESS_H -#include #include #ifdef CONFIG_32BIT diff --git a/include/asm-mips/page.h b/include/asm-mips/page.h index 4035ec7..6b97744 100644 --- a/include/asm-mips/page.h +++ b/include/asm-mips/page.h @@ -9,7 +9,6 @@ #ifndef _ASM_PAGE_H #define _ASM_PAGE_H -#include #ifdef __KERNEL__ @@ -145,6 +144,25 @@ #define pfn_valid(pfn) ((pfn) < max_map #endif #endif +#ifdef CONFIG_FLATMEM + +#define pfn_valid(pfn) ((pfn) < max_mapnr) + +#elif defined(CONFIG_NEED_MULTIPLE_NODES) + +#define pfn_valid(pfn) \ +({ \ + unsigned long __pfn = (pfn); \ + int __n = pfn_to_nid(__pfn); \ + ((__n >= 0) ? (__pfn < NODE_DATA(__n)->node_start_pfn + \ + NODE_DATA(__n)->node_spanned_pages) \ + : 0); \ +}) + +#else +#error Provide a definition of pfn_valid +#endif + #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) diff --git a/include/asm-mips/param.h b/include/asm-mips/param.h index 2bead82..1d9bb8c 100644 --- a/include/asm-mips/param.h +++ b/include/asm-mips/param.h @@ -11,7 +11,7 @@ #define _ASM_PARAM_H #ifdef __KERNEL__ -# include /* Internal kernel timer frequency */ +# define HZ CONFIG_HZ /* Internal kernel timer frequency */ # define USER_HZ 100 /* .. some user interfaces are in "ticks" */ # define CLOCKS_PER_SEC (USER_HZ) /* like times() */ #endif diff --git a/include/asm-mips/pci.h b/include/asm-mips/pci.h index 6c9ad81..c4d68be 100644 --- a/include/asm-mips/pci.h +++ b/include/asm-mips/pci.h @@ -6,7 +6,6 @@ #ifndef _ASM_PCI_H #define _ASM_PCI_H -#include #include #ifdef __KERNEL__ diff --git a/include/asm-mips/pci/bridge.h b/include/asm-mips/pci/bridge.h index b4ee995..0c45e75 100644 --- a/include/asm-mips/pci/bridge.h +++ b/include/asm-mips/pci/bridge.h @@ -15,6 +15,7 @@ #define _ASM_PCI_BRIDGE_H #include #include #include /* generic widget header */ +#include /* I/O page size */ @@ -848,4 +849,6 @@ #define BRIDGE_CONTROLLER(bus) \ extern void register_bridge_irq(unsigned int irq); extern int request_bridge_irq(struct bridge_controller *bc); +extern struct pci_ops bridge_pci_ops; + #endif /* _ASM_PCI_BRIDGE_H */ diff --git a/include/asm-mips/pgalloc.h b/include/asm-mips/pgalloc.h index fe1df57..582c1fe 100644 --- a/include/asm-mips/pgalloc.h +++ b/include/asm-mips/pgalloc.h @@ -9,7 +9,6 @@ #ifndef _ASM_PGALLOC_H #define _ASM_PGALLOC_H -#include #include #include diff --git a/include/asm-mips/pgtable-32.h b/include/asm-mips/pgtable-32.h index 087c207..4b26d85 100644 --- a/include/asm-mips/pgtable-32.h +++ b/include/asm-mips/pgtable-32.h @@ -9,7 +9,6 @@ #ifndef _ASM_PGTABLE_32_H #define _ASM_PGTABLE_32_H -#include #include #include diff --git a/include/asm-mips/pgtable-64.h b/include/asm-mips/pgtable-64.h index 2faf5c9..e3db932 100644 --- a/include/asm-mips/pgtable-64.h +++ b/include/asm-mips/pgtable-64.h @@ -9,7 +9,6 @@ #ifndef _ASM_PGTABLE_64_H #define _ASM_PGTABLE_64_H -#include #include #include diff --git a/include/asm-mips/pgtable-bits.h b/include/asm-mips/pgtable-bits.h index 01e76e9..7494ba9 100644 --- a/include/asm-mips/pgtable-bits.h +++ b/include/asm-mips/pgtable-bits.h @@ -10,7 +10,6 @@ #ifndef _ASM_PGTABLE_BITS_H #define _ASM_PGTABLE_BITS_H -#include /* * Note that we shift the lower 32bits of each EntryLo[01] entry diff --git a/include/asm-mips/pgtable.h b/include/asm-mips/pgtable.h index d0af2a3..a36ca1b 100644 --- a/include/asm-mips/pgtable.h +++ b/include/asm-mips/pgtable.h @@ -8,7 +8,6 @@ #ifndef _ASM_PGTABLE_H #define _ASM_PGTABLE_H -#include #ifdef CONFIG_32BIT #include #endif @@ -379,9 +378,7 @@ static inline void update_mmu_cache(stru __update_cache(vma, address, pte); } -#ifndef CONFIG_NEED_MULTIPLE_NODES #define kern_addr_valid(addr) (1) -#endif #ifdef CONFIG_64BIT_PHYS_ADDR extern int remap_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot); diff --git a/include/asm-mips/prefetch.h b/include/asm-mips/prefetch.h index 71293ec..1785083 100644 --- a/include/asm-mips/prefetch.h +++ b/include/asm-mips/prefetch.h @@ -8,7 +8,6 @@ #ifndef __ASM_PREFETCH_H #define __ASM_PREFETCH_H -#include /* * R5000 and RM5200 implements pref and prefx instructions but they're nops, so diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h index 0fb75f0..5f80ba7 100644 --- a/include/asm-mips/processor.h +++ b/include/asm-mips/processor.h @@ -11,7 +11,6 @@ #ifndef _ASM_PROCESSOR_H #define _ASM_PROCESSOR_H -#include #include #include @@ -71,11 +70,6 @@ #define NUM_FPU_REGS 32 typedef __u64 fpureg_t; -struct mips_fpu_hard_struct { - fpureg_t fpr[NUM_FPU_REGS]; - unsigned int fcr31; -}; - /* * It would be nice to add some more fields for emulator statistics, but there * are a number of fixed offsets in offset.h and elsewhere that would have to @@ -83,18 +77,13 @@ struct mips_fpu_hard_struct { * the FPU emulator for now. See asm-mips/fpu_emulator.h. */ -struct mips_fpu_soft_struct { +struct mips_fpu_struct { fpureg_t fpr[NUM_FPU_REGS]; unsigned int fcr31; }; -union mips_fpu_union { - struct mips_fpu_hard_struct hard; - struct mips_fpu_soft_struct soft; -}; - #define INIT_FPU { \ - {{0,},} \ + {0,} \ } #define NUM_DSP_REGS 6 @@ -133,7 +122,7 @@ struct thread_struct { unsigned long cp0_status; /* Saved fpu/fpu emulator stuff. */ - union mips_fpu_union fpu; + struct mips_fpu_struct fpu; #ifdef CONFIG_MIPS_MT_FPAFF /* Emulated instruction count */ unsigned long emulated_fp; diff --git a/include/asm-mips/ptrace.h b/include/asm-mips/ptrace.h index fa9d871..4113316 100644 --- a/include/asm-mips/ptrace.h +++ b/include/asm-mips/ptrace.h @@ -9,7 +9,6 @@ #ifndef _ASM_PTRACE_H #define _ASM_PTRACE_H -#include #include diff --git a/include/asm-mips/qemu.h b/include/asm-mips/qemu.h index 905c395..531caf4 100644 --- a/include/asm-mips/qemu.h +++ b/include/asm-mips/qemu.h @@ -21,4 +21,10 @@ #define Q_COUNT_COMPARE_IRQ 16 */ #define QEMU_C0_COUNTER_CLOCK 100000000 +/* + * Magic qemu system control location. + */ +#define QEMU_RESTART_REG 0xBFBF0000 +#define QEMU_HALT_REG 0xBFBF0004 + #endif /* __ASM_QEMU_H */ diff --git a/include/asm-mips/reg.h b/include/asm-mips/reg.h index 6173004..634b55d 100644 --- a/include/asm-mips/reg.h +++ b/include/asm-mips/reg.h @@ -12,7 +12,6 @@ #ifndef __ASM_MIPS_REG_H #define __ASM_MIPS_REG_H -#include #if defined(CONFIG_32BIT) || defined(WANT_COMPAT_REG_H) diff --git a/include/asm-mips/resource.h b/include/asm-mips/resource.h index 1fba00c..87cb308 100644 --- a/include/asm-mips/resource.h +++ b/include/asm-mips/resource.h @@ -9,7 +9,6 @@ #ifndef _ASM_RESOURCE_H #define _ASM_RESOURCE_H -#include /* * These five resource limit IDs have a MIPS/Linux-specific ordering, diff --git a/include/asm-mips/rm9k-ocd.h b/include/asm-mips/rm9k-ocd.h new file mode 100644 index 0000000..b0b80d9 --- /dev/null +++ b/include/asm-mips/rm9k-ocd.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2004 by Basler Vision Technologies AG + * Author: Thomas Koeller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(_ASM_RM9K_OCD_H) +#define _ASM_RM9K_OCD_H + +#include +#include +#include + +extern volatile void __iomem * const ocd_base; +extern volatile void __iomem * const titan_base; + +#define ocd_addr(__x__) (ocd_base + (__x__)) +#define titan_addr(__x__) (titan_base + (__x__)) +#define scram_addr(__x__) (scram_base + (__x__)) + +/* OCD register access */ +#define ocd_readl(__offs__) __raw_readl(ocd_addr(__offs__)) +#define ocd_readw(__offs__) __raw_readw(ocd_addr(__offs__)) +#define ocd_readb(__offs__) __raw_readb(ocd_addr(__offs__)) +#define ocd_writel(__val__, __offs__) \ + __raw_writel((__val__), ocd_addr(__offs__)) +#define ocd_writew(__val__, __offs__) \ + __raw_writew((__val__), ocd_addr(__offs__)) +#define ocd_writeb(__val__, __offs__) \ + __raw_writeb((__val__), ocd_addr(__offs__)) + +/* TITAN register access - 32 bit-wide only */ +#define titan_readl(__offs__) __raw_readl(titan_addr(__offs__)) +#define titan_writel(__val__, __offs__) \ + __raw_writel((__val__), titan_addr(__offs__)) + +/* Protect access to shared TITAN registers */ +extern spinlock_t titan_lock; +extern int titan_irqflags; +#define lock_titan_regs() spin_lock_irqsave(&titan_lock, titan_irqflags) +#define unlock_titan_regs() spin_unlock_irqrestore(&titan_lock, titan_irqflags) + +#endif /* !defined(_ASM_RM9K_OCD_H) */ diff --git a/include/asm-mips/serial.h b/include/asm-mips/serial.h index 7196ceb..584bd9c 100644 --- a/include/asm-mips/serial.h +++ b/include/asm-mips/serial.h @@ -9,7 +9,6 @@ #ifndef _ASM_SERIAL_H #define _ASM_SERIAL_H -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-mips/sgiarcs.h b/include/asm-mips/sgiarcs.h index 722b77a..ddb859d 100644 --- a/include/asm-mips/sgiarcs.h +++ b/include/asm-mips/sgiarcs.h @@ -12,7 +12,6 @@ #ifndef _ASM_SGIARCS_H #define _ASM_SGIARCS_H -#include #include #include diff --git a/include/asm-mips/sibyte/board.h b/include/asm-mips/sibyte/board.h index 900edcb..3dfe29e 100644 --- a/include/asm-mips/sibyte/board.h +++ b/include/asm-mips/sibyte/board.h @@ -19,7 +19,6 @@ #ifndef _SIBYTE_BOARD_H #define _SIBYTE_BOARD_H -#include #if defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_PTSWARM) || \ defined(CONFIG_SIBYTE_CRHONE) || defined(CONFIG_SIBYTE_CRHINE) || \ diff --git a/include/asm-mips/sibyte/carmel.h b/include/asm-mips/sibyte/carmel.h index b5e7dae..57c53e6 100644 --- a/include/asm-mips/sibyte/carmel.h +++ b/include/asm-mips/sibyte/carmel.h @@ -18,7 +18,6 @@ #ifndef __ASM_SIBYTE_CARMEL_H #define __ASM_SIBYTE_CARMEL_H -#include #include #include diff --git a/include/asm-mips/sibyte/sentosa.h b/include/asm-mips/sibyte/sentosa.h index 8246058..64c4787 100644 --- a/include/asm-mips/sibyte/sentosa.h +++ b/include/asm-mips/sibyte/sentosa.h @@ -18,7 +18,6 @@ #ifndef __ASM_SIBYTE_SENTOSA_H #define __ASM_SIBYTE_SENTOSA_H -#include #include #include diff --git a/include/asm-mips/sibyte/swarm.h b/include/asm-mips/sibyte/swarm.h index 06e1d52..86db37e 100644 --- a/include/asm-mips/sibyte/swarm.h +++ b/include/asm-mips/sibyte/swarm.h @@ -18,7 +18,6 @@ #ifndef __ASM_SIBYTE_SWARM_H #define __ASM_SIBYTE_SWARM_H -#include #include #include diff --git a/include/asm-mips/siginfo.h b/include/asm-mips/siginfo.h index 2ba313d..2e32949 100644 --- a/include/asm-mips/siginfo.h +++ b/include/asm-mips/siginfo.h @@ -9,7 +9,6 @@ #ifndef _ASM_SIGINFO_H #define _ASM_SIGINFO_H -#include #define __ARCH_SIGEV_PREAMBLE_SIZE (sizeof(long) + 2*sizeof(int)) #undef __ARCH_SI_TRAPNO /* exception code needs to fill this ... */ diff --git a/include/asm-mips/signal.h b/include/asm-mips/signal.h index d8349e4..87a1dff 100644 --- a/include/asm-mips/signal.h +++ b/include/asm-mips/signal.h @@ -9,7 +9,6 @@ #ifndef _ASM_SIGNAL_H #define _ASM_SIGNAL_H -#include #include #define _NSIG 128 @@ -65,7 +64,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -85,7 +83,6 @@ #define SA_NOCLDSTOP 0x00000001 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 /* Only for o32 */ @@ -100,15 +97,6 @@ #define SIGSTKSZ 8192 #ifdef __KERNEL__ -/* - * These values of sa_flags are used only by the kernel as part of the - * irq handling routines. - * - * SA_INTERRUPT is also used by the irq handling routines. - * SA_SHIRQ flag is for shared interrupt support on PCI and EISA. - */ -#define SA_SAMPLE_RANDOM SA_RESTART - #ifdef CONFIG_TRAD_SIGNALS #define sig_uses_siginfo(ka) ((ka)->sa.sa_flags & SA_SIGINFO) #else diff --git a/include/asm-mips/sim.h b/include/asm-mips/sim.h index 9c2af1b..67c4fe5 100644 --- a/include/asm-mips/sim.h +++ b/include/asm-mips/sim.h @@ -9,7 +9,6 @@ #ifndef _ASM_SIM_H #define _ASM_SIM_H -#include #include diff --git a/include/asm-mips/smp.h b/include/asm-mips/smp.h index e14e4b6..1608fd7 100644 --- a/include/asm-mips/smp.h +++ b/include/asm-mips/smp.h @@ -11,7 +11,6 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H -#include #ifdef CONFIG_SMP diff --git a/include/asm-mips/sn/addrs.h b/include/asm-mips/sn/addrs.h index 2b5cef1..8fa0af6 100644 --- a/include/asm-mips/sn/addrs.h +++ b/include/asm-mips/sn/addrs.h @@ -9,7 +9,6 @@ #ifndef _ASM_SN_ADDRS_H #define _ASM_SN_ADDRS_H -#include #ifndef __ASSEMBLY__ #include @@ -27,13 +26,8 @@ #endif #ifndef __ASSEMBLY__ -#if defined(CONFIG_SGI_IO) /* FIXME */ -#define PS_UINT_CAST (__psunsigned_t) -#define UINT64_CAST (__uint64_t) -#else /* CONFIG_SGI_IO */ #define PS_UINT_CAST (unsigned long) #define UINT64_CAST (unsigned long) -#endif /* CONFIG_SGI_IO */ #define HUBREG_CAST (volatile hubreg_t *) @@ -253,14 +247,6 @@ #endif /* CONFIG_SGI_IP27 */ * for _x. */ -#ifdef _STANDALONE - -/* DO NOT USE THESE DIRECTLY IN THE KERNEL. SEE BELOW. */ -#define LOCAL_HUB(_x) (HUBREG_CAST (IALIAS_BASE + (_x))) -#define REMOTE_HUB(_n, _x) (HUBREG_CAST (NODE_SWIN_BASE(_n, 1) + \ - 0x800000 + (_x))) -#endif /* _STANDALONE */ - /* * WARNING: * When certain Hub chip workaround are defined, it's not sufficient @@ -327,20 +313,6 @@ #define ARCS_SPB_ADDR(nasid) \ PHYS_TO_K0(NODE_OFFSET(nasid) | ARCS_SPB_OFFSET) #define ARCS_SPB_SIZE 0x0400 -#ifdef _STANDALONE - -#define ARCS_TVECTOR_OFFSET 0x2800 -#define ARCS_PVECTOR_OFFSET 0x2c00 - -/* - * These addresses are used by the master CPU to install the transfer - * and private vectors. All others use the SPB to find them. - */ -#define TVADDR (NODE_CAC_BASE(get_nasid()) + ARCS_TVECTOR_OFFSET) -#define PVADDR (NODE_CAC_BASE(get_nasid()) + ARCS_PVECTOR_OFFSET) - -#endif /* _STANDALONE */ - #define KLDIR_OFFSET 0x2000 #define KLDIR_ADDR(nasid) \ TO_NODE_UNCAC((nasid), KLDIR_OFFSET) diff --git a/include/asm-mips/sn/agent.h b/include/asm-mips/sn/agent.h index d6df13a..ac4ea85 100644 --- a/include/asm-mips/sn/agent.h +++ b/include/asm-mips/sn/agent.h @@ -11,7 +11,6 @@ #ifndef _ASM_SGI_SN_AGENT_H #define _ASM_SGI_SN_AGENT_H -#include #include #include #include diff --git a/include/asm-mips/sn/arch.h b/include/asm-mips/sn/arch.h index d247a81..51174af 100644 --- a/include/asm-mips/sn/arch.h +++ b/include/asm-mips/sn/arch.h @@ -11,7 +11,6 @@ #ifndef _ASM_SN_ARCH_H #define _ASM_SN_ARCH_H -#include #include #include #ifdef CONFIG_SGI_IP27 diff --git a/include/asm-mips/sn/fru.h b/include/asm-mips/sn/fru.h new file mode 100644 index 0000000..b3e3606 --- /dev/null +++ b/include/asm-mips/sn/fru.h @@ -0,0 +1,44 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Derived from IRIX + * + * Copyright (C) 1992 - 1997, 1999 Silcon Graphics, Inc. + * Copyright (C) 1999, 2006 Ralf Baechle (ralf@linux-mips) + */ +#ifndef __ASM_SN_FRU_H +#define __ASM_SN_FRU_H + +#define MAX_DIMMS 8 /* max # of dimm banks */ +#define MAX_PCIDEV 8 /* max # of pci devices on a pci bus */ + +typedef unsigned char confidence_t; + +typedef struct kf_mem_s { + confidence_t km_confidence; /* confidence level that the memory is bad + * is this necessary ? + */ + confidence_t km_dimm[MAX_DIMMS]; + /* confidence level that dimm[i] is bad + *I think this is the right number + */ + +} kf_mem_t; + +typedef struct kf_cpu_s { + confidence_t kc_confidence; /* confidence level that cpu is bad */ + confidence_t kc_icache; /* confidence level that instr. cache is bad */ + confidence_t kc_dcache; /* confidence level that data cache is bad */ + confidence_t kc_scache; /* confidence level that sec. cache is bad */ + confidence_t kc_sysbus; /* confidence level that sysad/cmd/state bus is bad */ +} kf_cpu_t; + +typedef struct kf_pci_bus_s { + confidence_t kpb_belief; /* confidence level that the pci bus is bad */ + confidence_t kpb_pcidev_belief[MAX_PCIDEV]; + /* confidence level that the pci dev is bad */ +} kf_pci_bus_t; + +#endif /* __ASM_SN_FRU_H */ diff --git a/include/asm-mips/sn/io.h b/include/asm-mips/sn/io.h index 1332645..ab2fa8c 100644 --- a/include/asm-mips/sn/io.h +++ b/include/asm-mips/sn/io.h @@ -9,7 +9,6 @@ #ifndef _ASM_SN_IO_H #define _ASM_SN_IO_H -#include #if defined (CONFIG_SGI_IP27) #include #endif diff --git a/include/asm-mips/sn/ioc3.h b/include/asm-mips/sn/ioc3.h index f7d530f..0996777 100644 --- a/include/asm-mips/sn/ioc3.h +++ b/include/asm-mips/sn/ioc3.h @@ -5,6 +5,8 @@ #ifndef _IOC3_H #define _IOC3_H +#include + /* SUPERIO uart register map */ typedef volatile struct ioc3_uartregs { union { diff --git a/include/asm-mips/sn/klconfig.h b/include/asm-mips/sn/klconfig.h index 9709ff7..b63cd06 100644 --- a/include/asm-mips/sn/klconfig.h +++ b/include/asm-mips/sn/klconfig.h @@ -27,7 +27,6 @@ #define _ASM_SN_KLCONFIG_H * that offsets of existing fields do not change. */ -#include #include #include @@ -37,7 +36,7 @@ #include //#include // XXX Stolen from : #define MAX_ROUTER_PORTS (6) /* Max. number of ports on a router */ -#include +#include //#include //#include @@ -54,32 +53,21 @@ #if defined(CONFIG_SGI_IP27) || defined( #include #include #include -#if defined(CONFIG_SGI_IO) || defined(CONFIG_SGI_IP35) +#if defined(CONFIG_SGI_IP35) // The hack file has to be before vector and after sn0_fru.... #include #include #include -#endif /* CONFIG_SGI_IO || CONFIG_SGI_IP35 */ +#endif /* CONFIG_SGI_IP35 */ #endif /* CONFIG_SGI_IP27 || CONFIG_SGI_IP35 */ #define KLCFGINFO_MAGIC 0xbeedbabe -#ifdef FRUTEST -typedef u64 klconf_off_t; -#else typedef s32 klconf_off_t; -#endif /* * Some IMPORTANT OFFSETS. These are the offsets on all NODES. */ -#if 0 -#define RAMBASE 0 -#define ARCSSPB_OFF 0x1000 /* shift it to sys/arcs/spb.h */ - -#define OFF_HWGRAPH 0 -#endif - #define MAX_MODULE_ID 255 #define SIZE_PAD 4096 /* 4k padding for structures */ /* @@ -134,15 +122,9 @@ #define DEVICE_STRUCT 3 typedef struct console_s { -#if defined(CONFIG_SGI_IO) /* FIXME */ - __psunsigned_t uart_base; - __psunsigned_t config_base; - __psunsigned_t memory_base; -#else unsigned long uart_base; unsigned long config_base; unsigned long memory_base; -#endif short baud; short flag; int type; @@ -174,10 +156,6 @@ typedef struct kl_config_hdr { #define KL_CONFIG_HDR(_nasid) ((kl_config_hdr_t *)(KLCONFIG_ADDR(_nasid))) -#if 0 -#define KL_CONFIG_MALLOC_HDR(_nasid) \ - (KL_CONFIG_HDR(_nasid)->ch_malloc_hdr) -#endif #define KL_CONFIG_INFO_OFFSET(_nasid) \ (KL_CONFIG_HDR(_nasid)->ch_board_info) #define KL_CONFIG_INFO_SET_OFFSET(_nasid, _off) \ @@ -197,23 +175,13 @@ #define KL_CONFIG_HDR_INIT_MAGIC(_nasid) /* --- New Macros for the changed kl_config_hdr_t structure --- */ -#if defined(CONFIG_SGI_IO) -#define PTR_CH_MALLOC_HDR(_k) ((klc_malloc_hdr_t *)\ - ((__psunsigned_t)_k + (_k->ch_malloc_hdr_off))) -#else #define PTR_CH_MALLOC_HDR(_k) ((klc_malloc_hdr_t *)\ (unsigned long)_k + (_k->ch_malloc_hdr_off))) -#endif #define KL_CONFIG_CH_MALLOC_HDR(_n) PTR_CH_MALLOC_HDR(KL_CONFIG_HDR(_n)) -#if defined(CONFIG_SGI_IO) -#define PTR_CH_CONS_INFO(_k) ((console_t *)\ - ((__psunsigned_t)_k + (_k->ch_cons_off))) -#else #define PTR_CH_CONS_INFO(_k) ((console_t *)\ ((unsigned long)_k + (_k->ch_cons_off))) -#endif #define KL_CONFIG_CH_CONS_INFO(_n) PTR_CH_CONS_INFO(KL_CONFIG_HDR(_n)) @@ -490,14 +458,6 @@ #define KLCF_REMOTE(_brd) (((_brd)->st #define KLCF_NUM_COMPS(_brd) ((_brd)->brd_numcompts) #define KLCF_MODULE_ID(_brd) ((_brd)->brd_module) -#ifdef FRUTEST - -#define KLCF_NEXT(_brd) ((_brd)->brd_next ? (lboard_t *)((_brd)->brd_next): NULL) -#define KLCF_COMP(_brd, _ndx) (klinfo_t *)((_brd)->brd_compts[(_ndx)]) -#define KLCF_COMP_ERROR(_brd, _comp) (_brd = _brd , (_comp)->errinfo) - -#else - #define KLCF_NEXT(_brd) \ ((_brd)->brd_next ? \ (lboard_t *)(NODE_OFFSET_TO_K1(NASID_GET(_brd), (_brd)->brd_next)):\ @@ -509,8 +469,6 @@ #define KLCF_COMP(_brd, _ndx) \ #define KLCF_COMP_ERROR(_brd, _comp) \ (NODE_OFFSET_TO_K1(NASID_GET(_brd), (_comp)->errinfo)) -#endif - #define KLCF_COMP_TYPE(_comp) ((_comp)->struct_type) #define KLCF_BRIDGE_W_ID(_comp) ((_comp)->physid) /* Widget ID */ @@ -631,18 +589,6 @@ typedef struct klport_s { klconf_off_t port_offset; } klport_t; -#if 0 -/* - * This is very similar to the klport_s but instead of having a componant - * offset it has a board offset. - */ -typedef struct klxbow_port_s { - nasid_t port_nasid; - unsigned char port_flag; - klconf_off_t board_offset; -} klxbow_port_t; -#endif - typedef struct klcpu_s { /* CPU */ klinfo_t cpu_info; unsigned short cpu_prid; /* Processor PRID value */ @@ -656,7 +602,7 @@ #define CPU_STRUCT_VERSION 2 typedef struct klhub_s { /* HUB */ klinfo_t hub_info; - uint hub_flags; /* PCFG_HUB_xxx flags */ + unsigned int hub_flags; /* PCFG_HUB_xxx flags */ klport_t hub_port; /* hub is connected to this */ nic_t hub_box_nic; /* nic of containing box */ klconf_off_t hub_mfg_nic; /* MFG NIC string */ @@ -665,7 +611,7 @@ typedef struct klhub_s { /* HUB */ typedef struct klhub_uart_s { /* HUB */ klinfo_t hubuart_info; - uint hubuart_flags; /* PCFG_HUB_xxx flags */ + unsigned int hubuart_flags; /* PCFG_HUB_xxx flags */ nic_t hubuart_box_nic; /* nic of containing box */ } klhub_uart_t ; @@ -764,7 +710,7 @@ #define ROUTER_VECTOR_VERS 2 /* XXX - Don't we need the number of ports here?!? */ typedef struct klrou_s { /* ROUTER */ klinfo_t rou_info ; - uint rou_flags ; /* PCFG_ROUTER_xxx flags */ + unsigned int rou_flags ; /* PCFG_ROUTER_xxx flags */ nic_t rou_box_nic ; /* nic of the containing module */ klport_t rou_port[MAX_ROUTER_PORTS + 1] ; /* array index 1 to 6 */ klconf_off_t rou_mfg_nic ; /* MFG NIC string */ @@ -787,8 +733,8 @@ typedef struct klgfx_s { /* GRAPHICS De klinfo_t gfx_info; klconf_off_t old_gndevs; /* for compatibility with older proms */ klconf_off_t old_gdoff0; /* for compatibility with older proms */ - uint cookie; /* for compatibility with older proms */ - uint moduleslot; + unsigned int cookie; /* for compatibility with older proms */ + unsigned int moduleslot; struct klgfx_s *gfx_next_pipe; graphics_t gfx_specific; klconf_off_t pad0; /* for compatibility with older proms */ @@ -945,36 +891,6 @@ extern klcpu_t *nasid_slice_to_cpuinfo(n extern lboard_t *find_lboard_class(lboard_t *start, unsigned char brd_class); -#if defined(CONFIG_SGI_IO) -extern xwidgetnum_t nodevertex_widgetnum_get(vertex_hdl_t node_vtx); -extern vertex_hdl_t nodevertex_xbow_peer_get(vertex_hdl_t node_vtx); -extern lboard_t *find_gfxpipe(int pipenum); -extern void setup_gfxpipe_link(vertex_hdl_t vhdl,int pipenum); -extern lboard_t *find_lboard_module_class(lboard_t *start, moduleid_t mod, - unsigned char brd_class); -extern lboard_t *find_nic_lboard(lboard_t *, nic_t); -extern lboard_t *find_nic_type_lboard(nasid_t, unsigned char, nic_t); -extern lboard_t *find_lboard_modslot(lboard_t *start, moduleid_t mod, slotid_t slot); -extern lboard_t *find_lboard_module(lboard_t *start, moduleid_t mod); -extern lboard_t *get_board_name(nasid_t nasid, moduleid_t mod, slotid_t slot, char *name); -extern int config_find_nic_router(nasid_t, nic_t, lboard_t **, klrou_t**); -extern int config_find_nic_hub(nasid_t, nic_t, lboard_t **, klhub_t**); -extern int config_find_xbow(nasid_t, lboard_t **, klxbow_t**); -extern klcpu_t *get_cpuinfo(cpuid_t cpu); -extern int update_klcfg_cpuinfo(nasid_t, int); -extern void board_to_path(lboard_t *brd, char *path); -extern moduleid_t get_module_id(nasid_t nasid); -extern void nic_name_convert(char *old_name, char *new_name); -extern int module_brds(nasid_t nasid, lboard_t **module_brds, int n); -extern lboard_t *brd_from_key(ulong_t key); -extern void device_component_canonical_name_get(lboard_t *,klinfo_t *, - char *); -extern int board_serial_number_get(lboard_t *,char *); -extern int is_master_baseio(nasid_t,moduleid_t,slotid_t); -extern nasid_t get_actual_nasid(lboard_t *brd) ; -extern net_vec_t klcfg_discover_route(lboard_t *, lboard_t *, int); -#else /* CONFIG_SGI_IO */ extern klcpu_t *sn_get_cpuinfo(cpuid_t cpu); -#endif /* CONFIG_SGI_IO */ #endif /* _ASM_SN_KLCONFIG_H */ diff --git a/include/asm-mips/sn/kldir.h b/include/asm-mips/sn/kldir.h index f0efab1..0573cbf 100644 --- a/include/asm-mips/sn/kldir.h +++ b/include/asm-mips/sn/kldir.h @@ -11,11 +11,6 @@ #ifndef _ASM_SN_KLDIR_H #define _ASM_SN_KLDIR_H -#include - -#if defined(CONFIG_SGI_IO) -#include -#endif /* * The kldir memory area resides at a fixed place in each node's memory and @@ -136,8 +131,6 @@ #define KLDIR_OFF_COUNT 0x20 #define KLDIR_OFF_STRIDE 0x28 #endif /* __ASSEMBLY__ */ -#if !defined(CONFIG_SGI_IO) - /* * This is defined here because IP27_SYMMON_STK_SIZE must be at least what * we define here. Since it's set up in the prom. We can't redefine it later @@ -147,7 +140,7 @@ #if !defined(CONFIG_SGI_IO) */ #define SYMMON_STACK_SIZE 0x8000 -#if defined (PROM) || defined (SABLE) +#if defined (PROM) /* * These defines are prom version dependent. No code other than the IP27 @@ -184,7 +177,7 @@ #define IP27_FREEMEM_SIZE -1 #define IP27_FREEMEM_COUNT 1 #define IP27_FREEMEM_STRIDE 0 -#endif /* PROM || SABLE*/ +#endif /* PROM */ /* * There will be only one of these in a partition so the IO6 must set it up. */ @@ -207,17 +200,11 @@ #define IP27_NMI_EFRAME_SIZE 0x200 #define KLDIR_ENT_SIZE 0x40 #define KLDIR_MAX_ENTRIES (0x400 / 0x40) -#endif /* !CONFIG_SGI_IO */ - #ifndef __ASSEMBLY__ typedef struct kldir_ent_s { u64 magic; /* Indicates validity of entry */ off_t offset; /* Offset from start of node space */ -#if defined(CONFIG_SGI_IO) /* FIXME */ - __psunsigned_t pointer; /* Pointer to area in some cases */ -#else unsigned long pointer; /* Pointer to area in some cases */ -#endif size_t size; /* Size in bytes */ u64 count; /* Repeat count if array, 1 if not */ size_t stride; /* Stride if array, 0 if not */ @@ -227,22 +214,4 @@ #endif } kldir_ent_t; #endif /* !__ASSEMBLY__ */ -#if defined(CONFIG_SGI_IO) - -#define KLDIR_ENT_SIZE 0x40 -#define KLDIR_MAX_ENTRIES (0x400 / 0x40) - -/* - * The actual offsets of each memory area are machine-dependent - */ -#ifdef CONFIG_SGI_IP27 -// Not yet #include -#elif defined(CONFIG_SGI_IP35) -#include -#else -#error "kldir.h is currently defined for IP27 and IP35 platforms only" -#endif - -#endif /* CONFIG_SGI_IO */ - #endif /* _ASM_SN_KLDIR_H */ diff --git a/include/asm-mips/sn/launch.h b/include/asm-mips/sn/launch.h index b67699c..b7c2226 100644 --- a/include/asm-mips/sn/launch.h +++ b/include/asm-mips/sn/launch.h @@ -9,7 +9,6 @@ #ifndef _ASM_SN_LAUNCH_H #define _ASM_SN_LAUNCH_H -#include #include #include diff --git a/include/asm-mips/sn/mapped_kernel.h b/include/asm-mips/sn/mapped_kernel.h index 59edb20..c3dd5d0 100644 --- a/include/asm-mips/sn/mapped_kernel.h +++ b/include/asm-mips/sn/mapped_kernel.h @@ -20,7 +20,6 @@ #define __ASM_SN_MAPPED_KERNEL_H * code. So no jumps can be done before we have switched to using * cksseg addresses. */ -#include #include #define REP_BASE CAC_BASE diff --git a/include/asm-mips/sn/sn0/addrs.h b/include/asm-mips/sn/sn0/addrs.h index 3988156..9e8cc52 100644 --- a/include/asm-mips/sn/sn0/addrs.h +++ b/include/asm-mips/sn/sn0/addrs.h @@ -11,7 +11,6 @@ #ifndef _ASM_SN_SN0_ADDRS_H #define _ASM_SN_SN0_ADDRS_H -#include /* * SN0 (on a T5) Address map @@ -49,7 +48,7 @@ #include * so for now we just use defines bracketed by an ifdef. */ -#ifdef CONFIG_SGI_SN0_N_MODE +#ifdef CONFIG_SGI_SN_N_MODE #define NODE_SIZE_BITS 31 #define BWIN_SIZE_BITS 28 @@ -63,7 +62,7 @@ #define NASID_LOCAL_BITS 4 #define BDDIR_UPPER_MASK (UINT64_CAST 0x7ffff << 10) #define BDECC_UPPER_MASK (UINT64_CAST 0x3ffffff << 3) -#else /* !defined(CONFIG_SGI_SN0_N_MODE), assume that M-mode is desired */ +#else /* !defined(CONFIG_SGI_SN_N_MODE), assume that M-mode is desired */ #define NODE_SIZE_BITS 32 #define BWIN_SIZE_BITS 29 @@ -77,7 +76,7 @@ #define NASID_LOCAL_BITS 4 #define BDDIR_UPPER_MASK (UINT64_CAST 0xfffff << 10) #define BDECC_UPPER_MASK (UINT64_CAST 0x7ffffff << 3) -#endif /* !defined(CONFIG_SGI_SN0_N_MODE) */ +#endif /* !defined(CONFIG_SGI_SN_N_MODE) */ #define NODE_ADDRSPACE_SIZE (UINT64_CAST 1 << NODE_SIZE_BITS) @@ -85,15 +84,15 @@ #define NASID_MASK (UINT64_CAST NASID_B #define NASID_GET(_pa) (int) ((UINT64_CAST (_pa) >> \ NASID_SHFT) & NASID_BITMASK) -#if !defined(__ASSEMBLY__) && !defined(_STANDALONE) +#if !defined(__ASSEMBLY__) #define NODE_SWIN_BASE(nasid, widget) \ ((widget == 0) ? NODE_BWIN_BASE((nasid), SWIN0_BIGWIN) \ : RAW_NODE_SWIN_BASE(nasid, widget)) -#else /* __ASSEMBLY__ || _STANDALONE */ +#else /* __ASSEMBLY__ */ #define NODE_SWIN_BASE(nasid, widget) \ (NODE_IO_BASE(nasid) + (UINT64_CAST (widget) << SWIN_SIZE_BITS)) -#endif /* __ASSEMBLY__ || _STANDALONE */ +#endif /* __ASSEMBLY__ */ /* * The following definitions pertain to the IO special address @@ -143,12 +142,7 @@ #define BRIDGE_REG_PTR(_base, _off) ((vo #define SN0_WIDGET_BASE(_nasid, _wid) (NODE_SWIN_BASE((_nasid), (_wid))) /* Turn on sable logging for the processors whose bits are set. */ -#ifdef SABLE -#define SABLE_LOG_TRIGGER(_map) \ - *((volatile hubreg_t *)(IO_BASE + 0x17ffff0)) = (_map) -#else #define SABLE_LOG_TRIGGER(_map) -#endif /* SABLE */ #ifndef __ASSEMBLY__ #define KERN_NMI_ADDR(nasid, slice) \ @@ -281,76 +275,6 @@ #endif /* !__ASSEMBLY__ */ #define _ARCSPROM -#ifdef _STANDALONE - -/* - * The PROM needs to pass the device base address and the - * device pci cfg space address to the device drivers during - * install. The COMPONENT->Key field is used for this purpose. - * Macros needed by SN0 device drivers to convert the - * COMPONENT->Key field to the respective base address. - * Key field looks as follows: - * - * +----------------------------------------------------+ - * |devnasid | widget |pciid |hubwidid|hstnasid | adap | - * | 2 | 1 | 1 | 1 | 2 | 1 | - * +----------------------------------------------------+ - * | | | | | | | - * 64 48 40 32 24 8 0 - * - * These are used by standalone drivers till the io infrastructure - * is in place. - */ - -#ifndef __ASSEMBLY__ - -#define uchar unsigned char - -#define KEY_DEVNASID_SHFT 48 -#define KEY_WIDID_SHFT 40 -#define KEY_PCIID_SHFT 32 -#define KEY_HUBWID_SHFT 24 -#define KEY_HSTNASID_SHFT 8 - -#define MK_SN0_KEY(nasid, widid, pciid) \ - ((((__psunsigned_t)nasid)<< KEY_DEVNASID_SHFT |\ - ((__psunsigned_t)widid) << KEY_WIDID_SHFT) |\ - ((__psunsigned_t)pciid) << KEY_PCIID_SHFT) - -#define ADD_HUBWID_KEY(key,hubwid)\ - (key|=((__psunsigned_t)hubwid << KEY_HUBWID_SHFT)) - -#define ADD_HSTNASID_KEY(key,hstnasid)\ - (key|=((__psunsigned_t)hstnasid << KEY_HSTNASID_SHFT)) - -#define GET_DEVNASID_FROM_KEY(key) ((short)(key >> KEY_DEVNASID_SHFT)) -#define GET_WIDID_FROM_KEY(key) ((uchar)(key >> KEY_WIDID_SHFT)) -#define GET_PCIID_FROM_KEY(key) ((uchar)(key >> KEY_PCIID_SHFT)) -#define GET_HUBWID_FROM_KEY(key) ((uchar)(key >> KEY_HUBWID_SHFT)) -#define GET_HSTNASID_FROM_KEY(key) ((short)(key >> KEY_HSTNASID_SHFT)) - -#define PCI_64_TARGID_SHFT 60 - -#define GET_PCIBASE_FROM_KEY(key) (NODE_SWIN_BASE(GET_DEVNASID_FROM_KEY(key),\ - GET_WIDID_FROM_KEY(key))\ - | BRIDGE_DEVIO(GET_PCIID_FROM_KEY(key))) - -#define GET_PCICFGBASE_FROM_KEY(key) \ - (NODE_SWIN_BASE(GET_DEVNASID_FROM_KEY(key),\ - GET_WIDID_FROM_KEY(key))\ - | BRIDGE_TYPE0_CFG_DEV(GET_PCIID_FROM_KEY(key))) - -#define GET_WIDBASE_FROM_KEY(key) \ - (NODE_SWIN_BASE(GET_DEVNASID_FROM_KEY(key),\ - GET_WIDID_FROM_KEY(key))) - -#define PUT_INSTALL_STATUS(c,s) c->Revision = s -#define GET_INSTALL_STATUS(c) c->Revision - -#endif /* !__ASSEMBLY__ */ - -#endif /* _STANDALONE */ - #if defined (HUB_ERR_STS_WAR) #define ERR_STS_WAR_REGISTER IIO_IIBUSERR diff --git a/include/asm-mips/sn/sn0/arch.h b/include/asm-mips/sn/sn0/arch.h index fb78773..f734f20 100644 --- a/include/asm-mips/sn/sn0/arch.h +++ b/include/asm-mips/sn/sn0/arch.h @@ -11,9 +11,6 @@ #ifndef _ASM_SN_SN0_ARCH_H #define _ASM_SN_SN0_ARCH_H -#include - -#ifndef SABLE #ifndef SN0XXL /* 128 cpu SMP max */ /* @@ -54,25 +51,16 @@ #define MAX_PREMIUM_REGIONS MAX_REGI */ #define MAX_PARTITIONS MAX_REGIONS - -#else - -#define MAX_COMPACT_NODES 4 -#define MAX_NASIDS 4 -#define MAXCPUS 8 - -#endif - #define NASID_MASK_BYTES ((MAX_NASIDS + 7) / 8) /* * Slot constants for SN0 */ -#ifdef CONFIG_SGI_SN0_N_MODE +#ifdef CONFIG_SGI_SN_N_MODE #define MAX_MEM_SLOTS 16 /* max slots per node */ -#else /* !CONFIG_SGI_SN0_N_MODE, assume M_MODE */ +#else /* !CONFIG_SGI_SN_N_MODE, assume CONFIG_SGI_SN_M_MODE */ #define MAX_MEM_SLOTS 32 /* max slots per node */ -#endif /* defined(N_MODE) */ +#endif /* CONFIG_SGI_SN_M_MODE */ #define SLOT_SHIFT (27) #define SLOT_MIN_MEM_SIZE (32*1024*1024) diff --git a/include/asm-mips/sn/sn0/hub.h b/include/asm-mips/sn/sn0/hub.h index f5dbba6..3e228f8 100644 --- a/include/asm-mips/sn/sn0/hub.h +++ b/include/asm-mips/sn/sn0/hub.h @@ -31,10 +31,6 @@ #include #include //#include -#ifdef SABLE -#define IP27_NO_HUBUART_INT 1 -#endif - /* Translation of uncached attributes */ #define UATTR_HSPEC 0 #define UATTR_IO 1 diff --git a/include/asm-mips/sn/sn0/hubio.h b/include/asm-mips/sn/sn0/hubio.h index f314da2..ef91b33 100644 --- a/include/asm-mips/sn/sn0/hubio.h +++ b/include/asm-mips/sn/sn0/hubio.h @@ -486,22 +486,6 @@ typedef union h1_icrba_u { #define ICRBN_A_CERR_SHFT 54 #define ICRBN_A_ERR_MASK 0x3ff -#if 0 /* Disabled, this causes namespace polution and break allmodconfig */ -/* - * Easy access macros. - */ -#define a_error icrba_fields_s.error -#define a_ecode icrba_fields_s.ecode -#define a_lnetuce icrba_fields_s.lnetuce -#define a_mark icrba_fields_s.mark -#define a_xerr icrba_fields_s.xerr -#define a_sidn icrba_fields_s.sidn -#define a_tnum icrba_fields_s.tnum -#define a_addr icrba_fields_s.addr -#define a_valid icrba_fields_s.valid -#define a_iow icrba_fields_s.iow -#endif - #endif /* !__ASSEMBLY__ */ #define IIO_ICRB_ADDR_SHFT 2 /* Shift to get proper address */ diff --git a/include/asm-mips/sn/sn0/hubmd.h b/include/asm-mips/sn/sn0/hubmd.h index a66def4..14c225d 100644 --- a/include/asm-mips/sn/sn0/hubmd.h +++ b/include/asm-mips/sn/sn0/hubmd.h @@ -11,7 +11,6 @@ #ifndef _ASM_SN_SN0_HUBMD_H #define _ASM_SN_SN0_HUBMD_H -#include /* * Hub Memory/Directory interface registers @@ -92,7 +91,7 @@ #define MD_UREG1_13 0x2200e8 /* uContro #define MD_UREG1_14 0x2200f0 /* uController/UART 1 register */ #define MD_UREG1_15 0x2200f8 /* uController/UART 1 register */ -#ifdef CONFIG_SGI_SN0_N_MODE +#ifdef CONFIG_SGI_SN_N_MODE #define MD_MEM_BANKS 4 /* 4 banks of memory max in N mode */ #else #define MD_MEM_BANKS 8 /* 8 banks of memory max in M mode */ diff --git a/include/asm-mips/sn/sn0/hubpi.h b/include/asm-mips/sn/sn0/hubpi.h index 355bba8..e39f5f9 100644 --- a/include/asm-mips/sn/sn0/hubpi.h +++ b/include/asm-mips/sn/sn0/hubpi.h @@ -398,24 +398,6 @@ #define PRLC_GCLK_EN (UINT64_CAST 1) /* PI_RT_FILTER_CTRL mask and shift definitions */ -#if 0 -/* - * XXX - This register's definition has changed, but it's only implemented - * in Hub 2. - */ -#define PRFC_DROP_COUNT_SHFT 27 -#define PRFC_DROP_COUNT_MASK (UINT64_CAST 0x3ff << 27) -#define PRFC_DROP_CTR_SHFT 18 -#define PRFC_DROP_CTR_MASK (UINT64_CAST 0x1ff << 18) -#define PRFC_MASK_ENABLE_SHFT 10 -#define PRFC_MASK_ENABLE_MASK (UINT64_CAST 0x7f << 10) -#define PRFC_MASK_CTR_SHFT 2 -#define PRFC_MASK_CTR_MASK (UINT64_CAST 0xff << 2) -#define PRFC_OFFSET_SHFT 0 -#define PRFC_OFFSET_MASK (UINT64_CAST 3) -#endif /* 0 */ - - /* * Bits for NACK_CNT_A/B and NACK_CMP */ diff --git a/include/asm-mips/sn/sn0/ip27.h b/include/asm-mips/sn/sn0/ip27.h index ade0e97..3c97e08 100644 --- a/include/asm-mips/sn/sn0/ip27.h +++ b/include/asm-mips/sn/sn0/ip27.h @@ -6,7 +6,7 @@ * Derived from IRIX . * * Copyright (C) 1992 - 1997, 1999 Silicon Graphics, Inc. - * Copyright (C) 1999 by Ralf Baechle + * Copyright (C) 1999, 2006 by Ralf Baechle */ #ifndef _ASM_SN_SN0_IP27_H #define _ASM_SN_SN0_IP27_H @@ -82,11 +82,4 @@ #define LED_CYCLE_SHFT 4 #define SEND_NMI(_nasid, _slice) \ REMOTE_HUB_S((_nasid), (PI_NMI_A + ((_slice) * PI_NMI_OFFSET)), 1) -/* Sanity hazzard ... Below all the Origin hacks are following. */ - -#define SN00_BRIDGE 0x9200000008000000 -#define SN00I_BRIDGE0 0x920000000b000000 -#define SN00I_BRIDGE1 0x920000000e000000 -#define SN00I_BRIDGE2 0x920000000f000000 - #endif /* _ASM_SN_SN0_IP27_H */ diff --git a/include/asm-mips/sn/sn0/sn0_fru.h b/include/asm-mips/sn/sn0/sn0_fru.h deleted file mode 100644 index 82c6377..0000000 --- a/include/asm-mips/sn/sn0/sn0_fru.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Derived from IRIX - * - * Copyright (C) 1992 - 1997, 1999 Silcon Graphics, Inc. - * Copyright (C) 1999 Ralf Baechle (ralf@gnu.org) - */ -#ifndef _ASM_SN_SN0_SN0_FRU_H -#define _ASM_SN_SN0_SN0_FRU_H - -#define MAX_DIMMS 8 /* max # of dimm banks */ -#define MAX_PCIDEV 8 /* max # of pci devices on a pci bus */ - -typedef unsigned char confidence_t; - -typedef struct kf_mem_s { - confidence_t km_confidence; /* confidence level that the memory is bad - * is this necessary ? - */ - confidence_t km_dimm[MAX_DIMMS]; - /* confidence level that dimm[i] is bad - *I think this is the right number - */ - -} kf_mem_t; - -typedef struct kf_cpu_s { - confidence_t kc_confidence; /* confidence level that cpu is bad */ - confidence_t kc_icache; /* confidence level that instr. cache is bad */ - confidence_t kc_dcache; /* confidence level that data cache is bad */ - confidence_t kc_scache; /* confidence level that sec. cache is bad */ - confidence_t kc_sysbus; /* confidence level that sysad/cmd/state bus is bad */ -} kf_cpu_t; - -typedef struct kf_pci_bus_s { - confidence_t kpb_belief; /* confidence level that the pci bus is bad */ - confidence_t kpb_pcidev_belief[MAX_PCIDEV]; - /* confidence level that the pci dev is bad */ -} kf_pci_bus_t; - -#endif /* _ASM_SN_SN0_SN0_FRU_H */ diff --git a/include/asm-mips/sni.h b/include/asm-mips/sni.h index b3bc698..b9ba54d 100644 --- a/include/asm-mips/sni.h +++ b/include/asm-mips/sni.h @@ -15,9 +15,6 @@ #define SNI_PORT_BASE 0xb4000000 /* * ASIC PCI registers for little endian configuration. */ -#ifndef __MIPSEL__ -#error "Fix me for big endian" -#endif #define PCIMT_UCONF 0xbfff0000 #define PCIMT_IOADTIMEOUT2 0xbfff0008 #define PCIMT_IOMEMCONF 0xbfff0010 @@ -51,9 +48,9 @@ #define PCIMT_INVSPACE 0xbfff00a0 #define PCIMT_PCI_CONF 0xbfff0100 /* - * Data port for the PCI bus. + * Data port for the PCI bus in IO space */ -#define PCIMT_CONFIG_DATA 0xb4000cfc +#define PCIMT_CONFIG_DATA 0x0cfc /* * Board specific registers diff --git a/include/asm-mips/socket.h b/include/asm-mips/socket.h index 0bb31e5..36ebe4e 100644 --- a/include/asm-mips/socket.h +++ b/include/asm-mips/socket.h @@ -69,6 +69,7 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 30 #define SO_SNDBUFFORCE 31 #define SO_RCVBUFFORCE 33 +#define SO_PASSSEC 34 #ifdef __KERNEL__ diff --git a/include/asm-mips/stackframe.h b/include/asm-mips/stackframe.h index c4856a8..158a4cd 100644 --- a/include/asm-mips/stackframe.h +++ b/include/asm-mips/stackframe.h @@ -10,7 +10,6 @@ #ifndef _ASM_STACKFRAME_H #define _ASM_STACKFRAME_H -#include #include #include @@ -305,7 +304,7 @@ #ifdef CONFIG_MIPS_MT_SMTC mfc0 v0, CP0_TCSTATUS ori v0, TCSTATUS_IXMT mtc0 v0, CP0_TCSTATUS - ehb + _ehb DMT 5 # dmt a1 jal mips_ihb #endif /* CONFIG_MIPS_MT_SMTC */ @@ -326,14 +325,14 @@ #ifdef CONFIG_MIPS_MT_SMTC * restore TCStatus.IXMT. */ LONG_L v1, PT_TCSTATUS(sp) - ehb + _ehb mfc0 v0, CP0_TCSTATUS andi v1, TCSTATUS_IXMT /* We know that TCStatua.IXMT should be set from above */ xori v0, v0, TCSTATUS_IXMT or v0, v0, v1 mtc0 v0, CP0_TCSTATUS - ehb + _ehb andi a1, a1, VPECONTROL_TE beqz a1, 1f emt @@ -412,7 +411,7 @@ #else /* CONFIG_MIPS_MT_SMTC */ /* Clear TKSU, leave IXMT */ xori t0, 0x00001800 mtc0 t0, CP0_TCSTATUS - ehb + _ehb /* We need to leave the global IE bit set, but clear EXL...*/ mfc0 t0, CP0_STATUS ori t0, ST0_EXL | ST0_ERL @@ -439,7 +438,7 @@ #else /* CONFIG_MIPS_MT_SMTC */ * and enable interrupts only for the * current TC, using the TCStatus register. */ - ehb + _ehb mfc0 t0,CP0_TCSTATUS /* Fortunately CU 0 is in the same place in both registers */ /* Set TCU0, TKSU (for later inversion) and IXMT */ @@ -448,7 +447,7 @@ #else /* CONFIG_MIPS_MT_SMTC */ /* Clear TKSU *and* IXMT */ xori t0, 0x00001c00 mtc0 t0, CP0_TCSTATUS - ehb + _ehb /* We need to leave the global IE bit set, but clear EXL...*/ mfc0 t0, CP0_STATUS ori t0, ST0_EXL @@ -480,7 +479,7 @@ #ifdef CONFIG_MIPS_MT_SMTC andi v1, v0, TCSTATUS_IXMT ori v0, TCSTATUS_IXMT mtc0 v0, CP0_TCSTATUS - ehb + _ehb DMT 2 # dmt v0 /* * We don't know a priori if ra is "live" @@ -496,7 +495,7 @@ #endif /* CONFIG_MIPS_MT_SMTC */ xori t0, 0x1e mtc0 t0, CP0_STATUS #ifdef CONFIG_MIPS_MT_SMTC - ehb + _ehb andi v0, v0, VPECONTROL_TE beqz v0, 2f nop /* delay slot */ diff --git a/include/asm-mips/string.h b/include/asm-mips/string.h index 907da60..436e3ad 100644 --- a/include/asm-mips/string.h +++ b/include/asm-mips/string.h @@ -10,7 +10,6 @@ #ifndef _ASM_STRING_H #define _ASM_STRING_H -#include /* * Most of the inline functions are rather naive implementations so I just diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h index 261f71d..130333d 100644 --- a/include/asm-mips/system.h +++ b/include/asm-mips/system.h @@ -12,7 +12,6 @@ #ifndef _ASM_SYSTEM_H #define _ASM_SYSTEM_H -#include #include #include diff --git a/include/asm-mips/thread_info.h b/include/asm-mips/thread_info.h index f8d97da..ae8ada5 100644 --- a/include/asm-mips/thread_info.h +++ b/include/asm-mips/thread_info.h @@ -9,7 +9,6 @@ #define _ASM_THREAD_INFO_H #ifdef __KERNEL__ -#include #ifndef __ASSEMBLY__ diff --git a/include/asm-mips/tlbflush.h b/include/asm-mips/tlbflush.h index bb4ae3c..276be77 100644 --- a/include/asm-mips/tlbflush.h +++ b/include/asm-mips/tlbflush.h @@ -1,7 +1,6 @@ #ifndef __ASM_TLBFLUSH_H #define __ASM_TLBFLUSH_H -#include #include /* diff --git a/include/asm-mips/tx4927/toshiba_rbtx4927.h b/include/asm-mips/tx4927/toshiba_rbtx4927.h index 6ce1e94..94bef03 100644 --- a/include/asm-mips/tx4927/toshiba_rbtx4927.h +++ b/include/asm-mips/tx4927/toshiba_rbtx4927.h @@ -27,7 +27,6 @@ #ifndef __ASM_TX4927_TOSHIBA_RBTX4927_H #define __ASM_TX4927_TOSHIBA_RBTX4927_H -#include #include #include #ifdef CONFIG_PCI diff --git a/include/asm-mips/types.h b/include/asm-mips/types.h index cd2813d..2b52e18 100644 --- a/include/asm-mips/types.h +++ b/include/asm-mips/types.h @@ -52,7 +52,6 @@ #define BITS_PER_LONG _MIPS_SZLONG #ifndef __ASSEMBLY__ -#include typedef __signed char s8; typedef unsigned char u8; diff --git a/include/asm-mips/uaccess.h b/include/asm-mips/uaccess.h index b96f3e0..1cdd4ee 100644 --- a/include/asm-mips/uaccess.h +++ b/include/asm-mips/uaccess.h @@ -9,7 +9,6 @@ #ifndef _ASM_UACCESS_H #define _ASM_UACCESS_H -#include #include #include #include diff --git a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h index 1068fe9..809f9f5 100644 --- a/include/asm-mips/unistd.h +++ b/include/asm-mips/unistd.h @@ -326,16 +326,17 @@ #define __NR_ppoll (__NR_Linux + 302) #define __NR_unshare (__NR_Linux + 303) #define __NR_splice (__NR_Linux + 304) #define __NR_sync_file_range (__NR_Linux + 305) +#define __NR_tee (__NR_Linux + 306) /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 305 +#define __NR_Linux_syscalls 306 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 305 +#define __NR_O32_Linux_syscalls 306 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -608,16 +609,17 @@ #define __NR_ppoll (__NR_Linux + 261) #define __NR_unshare (__NR_Linux + 262) #define __NR_splice (__NR_Linux + 263) #define __NR_sync_file_range (__NR_Linux + 264) +#define __NR_tee (__NR_Linux + 265) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 264 +#define __NR_Linux_syscalls 265 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 264 +#define __NR_64_Linux_syscalls 265 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -894,16 +896,19 @@ #define __NR_ppoll (__NR_Linux + 265) #define __NR_unshare (__NR_Linux + 266) #define __NR_splice (__NR_Linux + 267) #define __NR_sync_file_range (__NR_Linux + 268) +#define __NR_tee (__NR_Linux + 269) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 268 +#define __NR_Linux_syscalls 269 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 268 +#define __NR_N32_Linux_syscalls 269 + +#ifdef __KERNEL__ #ifndef __ASSEMBLY__ @@ -1168,9 +1173,6 @@ type name (atype a,btype b,ctype c,dtype #endif /* (_MIPS_SIM == _MIPS_SIM_NABI32) || (_MIPS_SIM == _MIPS_SIM_ABI64) */ -#ifdef __KERNEL__ - -#include #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR @@ -1197,7 +1199,6 @@ # endif # ifdef CONFIG_MIPS32_O32 # define __ARCH_WANT_COMPAT_SYS_TIME # endif -#endif #ifdef __KERNEL_SYSCALLS__ @@ -1248,4 +1249,5 @@ #endif /* !__ASSEMBLY__ */ */ #define cond_syscall(x) asm(".weak\t" #x "\n" #x "\t=\tsys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ASM_UNISTD_H */ diff --git a/include/asm-mips/vga.h b/include/asm-mips/vga.h index 34755c0..c1dd0b1 100644 --- a/include/asm-mips/vga.h +++ b/include/asm-mips/vga.h @@ -13,7 +13,7 @@ #include * access the videoram directly without any black magic. */ -#define VGA_MAP_MEM(x) (0xb0000000L + (unsigned long)(x)) +#define VGA_MAP_MEM(x,s) (0xb0000000L + (unsigned long)(x)) #define vga_readb(x) (*(x)) #define vga_writeb(x,y) (*(y) = (x)) diff --git a/include/asm-mips/vr41xx/vrc4173.h b/include/asm-mips/vr41xx/vrc4173.h index 4d41a9c..96fdcd5 100644 --- a/include/asm-mips/vr41xx/vrc4173.h +++ b/include/asm-mips/vr41xx/vrc4173.h @@ -24,7 +24,6 @@ #ifndef __NEC_VRC4173_H #define __NEC_VRC4173_H -#include #include /* diff --git a/include/asm-mips/war.h b/include/asm-mips/war.h index ad374bd..3ac146c 100644 --- a/include/asm-mips/war.h +++ b/include/asm-mips/war.h @@ -8,7 +8,6 @@ #ifndef _ASM_WAR_H #define _ASM_WAR_H -#include /* * Another R4600 erratum. Due to the lack of errata information the exact @@ -172,7 +171,8 @@ #endif * On the RM9000 there is a problem which makes the CreateDirtyExclusive * cache operation unusable on SMP systems. */ -#if defined(CONFIG_MOMENCO_JAGUAR_ATX) || defined(CONFIG_PMC_YOSEMITE) +#if defined(CONFIG_MOMENCO_JAGUAR_ATX) || defined(CONFIG_PMC_YOSEMITE) || \ + defined(CONFIG_BASLER_EXCITE) #define RM9000_CDEX_SMP_WAR 1 #endif @@ -182,7 +182,7 @@ #endif * being fetched may case spurious exceptions. */ #if defined(CONFIG_MOMENCO_JAGUAR_ATX) || defined(CONFIG_MOMENCO_OCELOT_3) || \ - defined(CONFIG_PMC_YOSEMITE) + defined(CONFIG_PMC_YOSEMITE) || defined(CONFIG_BASLER_EXCITE) #define ICACHE_REFILLS_WORKAROUND_WAR 1 #endif diff --git a/include/asm-mips/wbflush.h b/include/asm-mips/wbflush.h index c3bef50..eadc0ac 100644 --- a/include/asm-mips/wbflush.h +++ b/include/asm-mips/wbflush.h @@ -11,7 +11,6 @@ #ifndef _ASM_WBFLUSH_H #define _ASM_WBFLUSH_H -#include #ifdef CONFIG_CPU_HAS_WB diff --git a/include/asm-parisc/Kbuild b/include/asm-parisc/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-parisc/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-parisc/assembly.h b/include/asm-parisc/assembly.h index 3ce3440..1a7bfe6 100644 --- a/include/asm-parisc/assembly.h +++ b/include/asm-parisc/assembly.h @@ -48,6 +48,7 @@ #endif #define CALLEE_SAVE_FRAME_SIZE (CALLEE_REG_FRAME_SIZE + CALLEE_FLOAT_FRAME_SIZE) #ifdef CONFIG_PA20 +#define LDCW ldcw,co #define BL b,l # ifdef CONFIG_64BIT # define LEVEL 2.0w @@ -55,6 +56,7 @@ # else # define LEVEL 2.0 # endif #else +#define LDCW ldcw #define BL bl #define LEVEL 1.1 #endif diff --git a/include/asm-parisc/atomic.h b/include/asm-parisc/atomic.h index 403ea97..48bf9b8 100644 --- a/include/asm-parisc/atomic.h +++ b/include/asm-parisc/atomic.h @@ -5,7 +5,6 @@ #ifndef _ASM_PARISC_ATOMIC_H_ #define _ASM_PARISC_ATOMIC_H_ -#include #include #include diff --git a/include/asm-parisc/cache.h b/include/asm-parisc/cache.h index c831665..7d22fa2 100644 --- a/include/asm-parisc/cache.h +++ b/include/asm-parisc/cache.h @@ -5,7 +5,6 @@ #ifndef __ARCH_PARISC_CACHE_H #define __ARCH_PARISC_CACHE_H -#include /* * PA 2.0 processors have 64-byte cachelines; PA 1.1 processors have diff --git a/include/asm-parisc/cacheflush.h b/include/asm-parisc/cacheflush.h index 76b6b7d..0b459cd 100644 --- a/include/asm-parisc/cacheflush.h +++ b/include/asm-parisc/cacheflush.h @@ -1,7 +1,6 @@ #ifndef _PARISC_CACHEFLUSH_H #define _PARISC_CACHEFLUSH_H -#include #include #include /* for flush_user_dcache_range_asm() proto */ diff --git a/include/asm-parisc/compat.h b/include/asm-parisc/compat.h index 289624d..71b4eee 100644 --- a/include/asm-parisc/compat.h +++ b/include/asm-parisc/compat.h @@ -5,6 +5,7 @@ #define _ASM_PARISC_COMPAT_H */ #include #include +#include #define COMPAT_USER_HZ 100 @@ -149,4 +150,14 @@ static __inline__ void __user *compat_al return (void __user *)regs->gr[30]; } +static inline int __is_compat_task(struct task_struct *t) +{ + return personality(t->personality) == PER_LINUX32; +} + +static inline int is_compat_task(void) +{ + return __is_compat_task(current); +} + #endif /* _ASM_PARISC_COMPAT_H */ diff --git a/include/asm-parisc/dma-mapping.h b/include/asm-parisc/dma-mapping.h index 74d4ac6..1e387e1 100644 --- a/include/asm-parisc/dma-mapping.h +++ b/include/asm-parisc/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef _PARISC_DMA_MAPPING_H #define _PARISC_DMA_MAPPING_H -#include #include #include #include diff --git a/include/asm-parisc/dma.h b/include/asm-parisc/dma.h index 31fd10d..9979c3c 100644 --- a/include/asm-parisc/dma.h +++ b/include/asm-parisc/dma.h @@ -9,7 +9,6 @@ #ifndef _ASM_DMA_H #define _ASM_DMA_H -#include #include /* need byte IO */ #include diff --git a/include/asm-parisc/floppy.h b/include/asm-parisc/floppy.h index ca3aed7..da2f9c1 100644 --- a/include/asm-parisc/floppy.h +++ b/include/asm-parisc/floppy.h @@ -156,13 +156,11 @@ static int vdma_get_dma_residue(unsigned static int fd_request_irq(void) { if(can_use_virtual_dma) - return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, - "floppy", NULL); + return request_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy", NULL); else return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT|SA_SAMPLE_RANDOM, - "floppy", NULL); - + IRQF_DISABLED, "floppy", NULL); } static unsigned long dma_mem_alloc(unsigned long size) diff --git a/include/asm-parisc/hw_irq.h b/include/asm-parisc/hw_irq.h index 151426e..6707f7d 100644 --- a/include/asm-parisc/hw_irq.h +++ b/include/asm-parisc/hw_irq.h @@ -3,15 +3,6 @@ #define _ASM_HW_IRQ_H /* * linux/include/asm/hw_irq.h - * - * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar - * - * moved some of the old arch/i386/kernel/irq.h to here. VY - * - * IRQ/IPI changes taken from work by Thomas Radke - * */ -extern void hw_resend_irq(struct hw_interrupt_type *, unsigned int); - #endif diff --git a/include/asm-parisc/io.h b/include/asm-parisc/io.h index 244f6b8..b9eb245 100644 --- a/include/asm-parisc/io.h +++ b/include/asm-parisc/io.h @@ -1,7 +1,6 @@ #ifndef _ASM_IO_H #define _ASM_IO_H -#include #include #include diff --git a/include/asm-parisc/irq.h b/include/asm-parisc/irq.h index b0a30e2..5cae260 100644 --- a/include/asm-parisc/irq.h +++ b/include/asm-parisc/irq.h @@ -7,7 +7,6 @@ #ifndef _ASM_PARISC_IRQ_H #define _ASM_PARISC_IRQ_H -#include #include #include @@ -27,11 +26,6 @@ #define CPU_IRQ_MAX (CPU_IRQ_BASE + (BIT #define NR_IRQS (CPU_IRQ_MAX + 1) -/* - * IRQ line status macro IRQ_PER_CPU is used - */ -#define ARCH_HAS_IRQ_PER_CPU - static __inline__ int irq_canonicalize(int irq) { return (irq == 2) ? 9 : irq; diff --git a/include/asm-parisc/kmap_types.h b/include/asm-parisc/kmap_types.h index 6886a0c..806aae3 100644 --- a/include/asm-parisc/kmap_types.h +++ b/include/asm-parisc/kmap_types.h @@ -1,7 +1,6 @@ #ifndef _ASM_KMAP_TYPES_H #define _ASM_KMAP_TYPES_H -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-parisc/mmzone.h b/include/asm-parisc/mmzone.h index ceb9b73..c878136 100644 --- a/include/asm-parisc/mmzone.h +++ b/include/asm-parisc/mmzone.h @@ -14,11 +14,6 @@ extern struct node_map_data node_data[]; #define NODE_DATA(nid) (&node_data[nid].pg_data) -/* - * Given a kernel address, find the home node of the underlying memory. - */ -#define kvaddr_to_nid(kaddr) pfn_to_nid(__pa(kaddr) >> PAGE_SHIFT) - #define node_start_pfn(nid) (NODE_DATA(nid)->node_start_pfn) #define node_end_pfn(nid) \ ({ \ diff --git a/include/asm-parisc/page.h b/include/asm-parisc/page.h index c0dd461..0695bc9 100644 --- a/include/asm-parisc/page.h +++ b/include/asm-parisc/page.h @@ -10,7 +10,6 @@ #endif #ifdef __KERNEL__ -#include #if defined(CONFIG_PARISC_PAGE_SIZE_4KB) # define PAGE_SHIFT 12 /* 4k */ diff --git a/include/asm-parisc/param.h b/include/asm-parisc/param.h index f4694d4..07cb9b9 100644 --- a/include/asm-parisc/param.h +++ b/include/asm-parisc/param.h @@ -2,7 +2,6 @@ #ifndef _ASMPARISC_PARAM_H #define _ASMPARISC_PARAM_H #ifdef __KERNEL__ -#include # ifdef CONFIG_PA20 # define HZ 1000 /* Faster machines */ # else diff --git a/include/asm-parisc/pci.h b/include/asm-parisc/pci.h index 77bbafb..8b631f4 100644 --- a/include/asm-parisc/pci.h +++ b/include/asm-parisc/pci.h @@ -1,7 +1,6 @@ #ifndef __ASM_PARISC_PCI_H #define __ASM_PARISC_PCI_H -#include #include diff --git a/include/asm-parisc/pdc.h b/include/asm-parisc/pdc.h index 0a3face..c9b2e35 100644 --- a/include/asm-parisc/pdc.h +++ b/include/asm-parisc/pdc.h @@ -1,7 +1,6 @@ #ifndef _PARISC_PDC_H #define _PARISC_PDC_H -#include /* * PDC return values ... @@ -279,12 +278,11 @@ #define PDC_LINK_USB_ENTRY_POINTS 1 /* /* constants for OS (NVM...) */ #define OS_ID_NONE 0 /* Undefined OS ID */ #define OS_ID_HPUX 1 /* HP-UX OS */ -#define OS_ID_LINUX OS_ID_HPUX /* just use the same value as hpux */ #define OS_ID_MPEXL 2 /* MPE XL OS */ #define OS_ID_OSF 3 /* OSF OS */ #define OS_ID_HPRT 4 /* HP-RT OS */ #define OS_ID_NOVEL 5 /* NOVELL OS */ -#define OS_ID_NT 6 /* NT OS */ +#define OS_ID_LINUX 6 /* Linux */ /* constants for PDC_CHASSIS */ @@ -353,8 +351,8 @@ #endif cc_wt : 1, /* 0 = WT-Dcache, 1 = WB-Dcache */ cc_sh : 2, /* 0 = separate I/D-cache, else shared I/D-cache */ cc_cst : 3, /* 0 = incoherent D-cache, 1=coherent D-cache */ - cc_pad1 : 5, /* reserved */ - cc_assoc: 8; /* associativity of I/D-cache */ + cc_pad1 : 10, /* reserved */ + cc_hv : 3; /* hversion dependent */ }; struct pdc_tlb_cf { /* for PDC_CACHE (I/D-TLB's) */ @@ -720,6 +718,7 @@ void setup_pdc(void); /* in inventory.c int pdc_add_valid(unsigned long address); int pdc_chassis_info(struct pdc_chassis_info *chassis_info, void *led_info, unsigned long len); int pdc_chassis_disp(unsigned long disp); +int pdc_chassis_warn(unsigned long *warn); int pdc_coproc_cfg(struct pdc_coproc_cfg *pdc_coproc_info); int pdc_iodc_read(unsigned long *actcnt, unsigned long hpa, unsigned int index, void *iodc_data, unsigned int iodc_data_size); @@ -733,6 +732,7 @@ int pdc_model_cpuid(unsigned long *cpu_i int pdc_model_versions(unsigned long *versions, int id); int pdc_model_capabilities(unsigned long *capabilities); int pdc_cache_info(struct pdc_cache_info *cache); +int pdc_spaceid_bits(unsigned long *space_bits); #ifndef CONFIG_PA20 int pdc_btlb_info(struct pdc_btlb_info *btlb); int pdc_mem_map_hpa(struct pdc_memory_map *r_addr, struct pdc_module_path *mod_path); @@ -776,6 +776,18 @@ int pdc_sti_call(unsigned long func, uns extern void pdc_init(void); +static inline char * os_id_to_string(u16 os_id) { + switch(os_id) { + case OS_ID_NONE: return "No OS"; + case OS_ID_HPUX: return "HP-UX"; + case OS_ID_MPEXL: return "MPE-iX"; + case OS_ID_OSF: return "OSF"; + case OS_ID_HPRT: return "HP-RT"; + case OS_ID_NOVEL: return "Novell Netware"; + case OS_ID_LINUX: return "Linux"; + default: return "Unknown"; + } +} #endif /* __ASSEMBLY__ */ #endif /* _PARISC_PDC_H */ diff --git a/include/asm-parisc/pgtable.h b/include/asm-parisc/pgtable.h index aec089e..5066c54 100644 --- a/include/asm-parisc/pgtable.h +++ b/include/asm-parisc/pgtable.h @@ -3,7 +3,6 @@ #define _PARISC_PGTABLE_H #include -#include #include #ifndef __ASSEMBLY__ @@ -507,13 +506,13 @@ #endif /* !__ASSEMBLY__ */ /* TLB page size encoding - see table 3-1 in parisc20.pdf */ #define _PAGE_SIZE_ENCODING_4K 0 -#define _PAGE_SIZE_ENCODING_16K 1 -#define _PAGE_SIZE_ENCODING_64K 2 +#define _PAGE_SIZE_ENCODING_16K 1 +#define _PAGE_SIZE_ENCODING_64K 2 #define _PAGE_SIZE_ENCODING_256K 3 #define _PAGE_SIZE_ENCODING_1M 4 #define _PAGE_SIZE_ENCODING_4M 5 -#define _PAGE_SIZE_ENCODING_16M 6 -#define _PAGE_SIZE_ENCODING_64M 7 +#define _PAGE_SIZE_ENCODING_16M 6 +#define _PAGE_SIZE_ENCODING_64M 7 #if defined(CONFIG_PARISC_PAGE_SIZE_4KB) # define _PAGE_SIZE_ENCODING_DEFAULT _PAGE_SIZE_ENCODING_4K diff --git a/include/asm-parisc/processor.h b/include/asm-parisc/processor.h index 89f2f1c..b73626f 100644 --- a/include/asm-parisc/processor.h +++ b/include/asm-parisc/processor.h @@ -9,7 +9,6 @@ #ifndef __ASM_PARISC_PROCESSOR_H #define __ASM_PARISC_PROCESSOR_H #ifndef __ASSEMBLY__ -#include #include #include @@ -27,14 +26,12 @@ #define KERNEL_STACK_SIZE (4*PAGE_SIZE) * Default implementation of macro that returns current * instruction pointer ("program counter"). */ - -/* We cannot use MFIA as it was added for PA2.0 - prumpf - - At one point there were no "0f/0b" type local symbols in gas for - PA-RISC. This is no longer true, but this still seems like the - nicest way to implement this. */ - -#define current_text_addr() ({ void *pc; __asm__("\n\tblr 0,%0\n\tnop":"=r" (pc)); pc; }) +#ifdef CONFIG_PA20 +#define current_ia(x) __asm__("mfia %0" : "=r"(x)) +#else /* mfia added in pa2.0 */ +#define current_ia(x) __asm__("blr 0,%0\n\tnop" : "=r"(x)) +#endif +#define current_text_addr() ({ void *pc; current_ia(pc); pc; }) #define TASK_SIZE (current->thread.task_size) #define TASK_UNMAPPED_BASE (current->thread.map_base) diff --git a/include/asm-parisc/psw.h b/include/asm-parisc/psw.h index 4334d6c..5a3e23c 100644 --- a/include/asm-parisc/psw.h +++ b/include/asm-parisc/psw.h @@ -1,6 +1,5 @@ #ifndef _PARISC_PSW_H -#include #define PSW_I 0x00000001 #define PSW_D 0x00000002 diff --git a/include/asm-parisc/signal.h b/include/asm-parisc/signal.h index 25cb23e..98a82fa 100644 --- a/include/asm-parisc/signal.h +++ b/include/asm-parisc/signal.h @@ -48,7 +48,6 @@ #define SIGRTMAX _NSIG /* it's 44 under * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -69,7 +68,6 @@ #define _SA_SIGGFAULT 0x00000100 /* HPUX #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 /* obsolete -- ignored */ diff --git a/include/asm-parisc/smp.h b/include/asm-parisc/smp.h index dbdbd2e..d4c0e26 100644 --- a/include/asm-parisc/smp.h +++ b/include/asm-parisc/smp.h @@ -1,7 +1,6 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H -#include #if defined(CONFIG_SMP) diff --git a/include/asm-parisc/socket.h b/include/asm-parisc/socket.h index 1bf54dc..ce2eae1 100644 --- a/include/asm-parisc/socket.h +++ b/include/asm-parisc/socket.h @@ -48,5 +48,6 @@ #define SO_DETACH_FILTER 0x401b #define SO_ACCEPTCONN 0x401c #define SO_PEERSEC 0x401d +#define SO_PASSSEC 0x401e #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-parisc/system.h b/include/asm-parisc/system.h index a5a973c..5fe2d23 100644 --- a/include/asm-parisc/system.h +++ b/include/asm-parisc/system.h @@ -1,7 +1,6 @@ #ifndef __PARISC_SYSTEM_H #define __PARISC_SYSTEM_H -#include #include /* The program status word as bitfields. */ @@ -156,13 +155,14 @@ #ifndef CONFIG_PA20 type and dynamically select the 16-byte aligned int from the array for the semaphore. */ -#define __PA_LDCW_ALIGNMENT 16 -#define __ldcw_align(a) ({ \ - unsigned long __ret = (unsigned long) &(a)->lock[0]; \ - __ret = (__ret + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); \ - (volatile unsigned int *) __ret; \ +#define __PA_LDCW_ALIGNMENT 16 +#define __ldcw_align(a) ({ \ + unsigned long __ret = (unsigned long) &(a)->lock[0]; \ + __ret = (__ret + __PA_LDCW_ALIGNMENT - 1) \ + & ~(__PA_LDCW_ALIGNMENT - 1); \ + (volatile unsigned int *) __ret; \ }) -#define LDCW "ldcw" +#define __LDCW "ldcw" #else /*CONFIG_PA20*/ /* From: "Jim Hull" @@ -172,17 +172,18 @@ #else /*CONFIG_PA20*/ they only require "natural" alignment (4-byte for ldcw, 8-byte for ldcd). */ -#define __PA_LDCW_ALIGNMENT 4 +#define __PA_LDCW_ALIGNMENT 4 #define __ldcw_align(a) ((volatile unsigned int *)a) -#define LDCW "ldcw,co" +#define __LDCW "ldcw,co" #endif /*!CONFIG_PA20*/ /* LDCW, the only atomic read-write operation PA-RISC has. *sigh*. */ -#define __ldcw(a) ({ \ - unsigned __ret; \ - __asm__ __volatile__(LDCW " 0(%1),%0" : "=r" (__ret) : "r" (a)); \ - __ret; \ +#define __ldcw(a) ({ \ + unsigned __ret; \ + __asm__ __volatile__(__LDCW " 0(%1),%0" \ + : "=r" (__ret) : "r" (a)); \ + __ret; \ }) #ifdef CONFIG_SMP diff --git a/include/asm-parisc/tlbflush.h b/include/asm-parisc/tlbflush.h index 825994a..f662e83 100644 --- a/include/asm-parisc/tlbflush.h +++ b/include/asm-parisc/tlbflush.h @@ -3,7 +3,6 @@ #define _PARISC_TLBFLUSH_H /* TLB flushing routines.... */ -#include #include #include diff --git a/include/asm-parisc/uaccess.h b/include/asm-parisc/uaccess.h index f6c417c..d973e8b 100644 --- a/include/asm-parisc/uaccess.h +++ b/include/asm-parisc/uaccess.h @@ -172,7 +172,11 @@ ({ \ /* * The "__put_user/kernel_asm()" macros tell gcc they read from memory * instead of writing. This is because they do not write to any memory - * gcc knows about, so there are no aliasing issues. + * gcc knows about, so there are no aliasing issues. These macros must + * also be aware that "fixup_put_user_skip_[12]" are executed in the + * context of the fault, and any registers used there must be listed + * as clobbers. In this case only "r1" is used by the current routines. + * r8/r9 are already listed as err/val. */ #ifdef __LP64__ @@ -183,7 +187,8 @@ #define __put_kernel_asm(stx,x,ptr) "\t.dword\t1b,fixup_put_user_skip_1\n" \ "\t.previous" \ : "=r"(__pu_err) \ - : "r"(ptr), "r"(x), "0"(__pu_err)) + : "r"(ptr), "r"(x), "0"(__pu_err) \ + : "r1") #define __put_user_asm(stx,x,ptr) \ __asm__ __volatile__ ( \ diff --git a/include/asm-parisc/unistd.h b/include/asm-parisc/unistd.h index 0e1a30b..27bcfad 100644 --- a/include/asm-parisc/unistd.h +++ b/include/asm-parisc/unistd.h @@ -792,15 +792,11 @@ #define __NR_Linux_syscalls 294 #define HPUX_GATEWAY_ADDR 0xC0000004 #define LINUX_GATEWAY_ADDR 0x100 +#ifdef __KERNEL__ #ifndef __ASSEMBLY__ #define SYS_ify(syscall_name) __NR_##syscall_name -/* Assume all syscalls are done from PIC code just to be - * safe. The worst case scenario is that you lose a register - * and save/restore r19 across the syscall. */ -#define PIC - #ifndef ASM_LINE_SEP # define ASM_LINE_SEP ; #endif @@ -934,7 +930,6 @@ type name(type1 arg1, type2 arg2, type3 return K_INLINE_SYSCALL(name, 5, arg1, arg2, arg3, arg4, arg5); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_ALARM @@ -956,7 +951,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif /* mmap & mmap2 take 6 arguments */ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4,type5,arg5,type6,arg6) \ @@ -1056,4 +1050,5 @@ #undef STR */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ASM_PARISC_UNISTD_H_ */ diff --git a/include/asm-powerpc/Kbuild b/include/asm-powerpc/Kbuild new file mode 100644 index 0000000..ac61d7e --- /dev/null +++ b/include/asm-powerpc/Kbuild @@ -0,0 +1,10 @@ +include include/asm-generic/Kbuild.asm + +unifdef-y += a.out.h asm-compat.h bootx.h byteorder.h cputable.h elf.h \ + nvram.h param.h posix_types.h ptrace.h seccomp.h signal.h \ + termios.h types.h unistd.h + +header-y += auxvec.h ioctls.h mman.h sembuf.h siginfo.h stat.h errno.h \ + ipcbuf.h msgbuf.h shmbuf.h socket.h termbits.h fcntl.h ipc.h \ + poll.h shmparam.h sockios.h ucontext.h ioctl.h linkage.h \ + resource.h sigcontext.h statfs.h diff --git a/include/asm-powerpc/abs_addr.h b/include/asm-powerpc/abs_addr.h index c5c3259..4aa2207 100644 --- a/include/asm-powerpc/abs_addr.h +++ b/include/asm-powerpc/abs_addr.h @@ -2,7 +2,6 @@ #ifndef _ASM_POWERPC_ABS_ADDR_H #define _ASM_POWERPC_ABS_ADDR_H #ifdef __KERNEL__ -#include /* * c 2001 PPC 64 Team, IBM Corp diff --git a/include/asm-powerpc/backlight.h b/include/asm-powerpc/backlight.h index 1ba1f27..a5e9e65 100644 --- a/include/asm-powerpc/backlight.h +++ b/include/asm-powerpc/backlight.h @@ -2,30 +2,30 @@ * Routines for handling backlight control on PowerBooks * * For now, implementation resides in - * arch/powerpc/platforms/powermac/pmac_support.c + * arch/powerpc/platforms/powermac/backlight.c * */ #ifndef __ASM_POWERPC_BACKLIGHT_H #define __ASM_POWERPC_BACKLIGHT_H #ifdef __KERNEL__ -/* Abstract values */ -#define BACKLIGHT_OFF 0 -#define BACKLIGHT_MIN 1 -#define BACKLIGHT_MAX 0xf +#include +#include -struct backlight_controller { - int (*set_enable)(int enable, int level, void *data); - int (*set_level)(int level, void *data); -}; +/* For locking instructions, see the implementation file */ +extern struct backlight_device *pmac_backlight; +extern struct mutex pmac_backlight_mutex; -extern void register_backlight_controller(struct backlight_controller *ctrler, void *data, char *type); -extern void unregister_backlight_controller(struct backlight_controller *ctrler, void *data); +extern void pmac_backlight_calc_curve(struct fb_info*); +extern int pmac_backlight_curve_lookup(struct fb_info *info, int value); -extern int set_backlight_enable(int enable); -extern int get_backlight_enable(void); -extern int set_backlight_level(int level); -extern int get_backlight_level(void); +extern int pmac_has_backlight_type(const char *type); + +extern void pmac_backlight_key_up(void); +extern void pmac_backlight_key_down(void); + +extern int pmac_backlight_set_legacy_brightness(int brightness); +extern int pmac_backlight_get_legacy_brightness(void); #endif /* __KERNEL__ */ #endif diff --git a/include/asm-powerpc/bitops.h b/include/asm-powerpc/bitops.h index d1c2a44..76e2f08 100644 --- a/include/asm-powerpc/bitops.h +++ b/include/asm-powerpc/bitops.h @@ -288,8 +288,8 @@ #define __test_and_set_le_bit(nr, addr) #define __test_and_clear_le_bit(nr, addr) \ __test_and_clear_bit((nr) ^ BITOP_LE_SWIZZLE, (addr)) -#define find_first_zero_le_bit(addr, size) find_next_zero_le_bit((addr), (size), 0) -unsigned long find_next_zero_le_bit(const unsigned long *addr, +#define find_first_zero_le_bit(addr, size) generic_find_next_zero_le_bit((addr), (size), 0) +unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned long size, unsigned long offset); /* Bitmap functions for the ext2 filesystem */ @@ -309,7 +309,7 @@ #define ext2_test_bit(nr, addr) tes #define ext2_find_first_zero_bit(addr, size) \ find_first_zero_le_bit((unsigned long*)addr, size) #define ext2_find_next_zero_bit(addr, size, off) \ - find_next_zero_le_bit((unsigned long*)addr, size, off) + generic_find_next_zero_le_bit((unsigned long*)addr, size, off) /* Bitmap functions for the minix filesystem. */ diff --git a/include/asm-powerpc/cache.h b/include/asm-powerpc/cache.h index 6379c2d..642be62 100644 --- a/include/asm-powerpc/cache.h +++ b/include/asm-powerpc/cache.h @@ -3,7 +3,6 @@ #define _ASM_POWERPC_CACHE_H #ifdef __KERNEL__ -#include /* bytes per L1 cache line */ #if defined(CONFIG_8xx) || defined(CONFIG_403GCX) diff --git a/include/asm-powerpc/cputable.h b/include/asm-powerpc/cputable.h index f6265c2..1ba3c99 100644 --- a/include/asm-powerpc/cputable.h +++ b/include/asm-powerpc/cputable.h @@ -24,6 +24,9 @@ #define PPC_FEATURE_SMT 0x00004000 #define PPC_FEATURE_ICACHE_SNOOP 0x00002000 #define PPC_FEATURE_ARCH_2_05 0x00001000 +#define PPC_FEATURE_TRUE_LE 0x00000002 +#define PPC_FEATURE_PPC_LE 0x00000001 + #ifdef __KERNEL__ #ifndef __ASSEMBLY__ @@ -69,6 +72,13 @@ struct cpu_spec { /* Processor specific oprofile operations */ enum powerpc_oprofile_type oprofile_type; + /* Bit locations inside the mmcra change */ + unsigned long oprofile_mmcra_sihv; + unsigned long oprofile_mmcra_sipr; + + /* Bits to clear during an oprofile exception */ + unsigned long oprofile_mmcra_clear; + /* Name of processor class, for the ELF AT_PLATFORM entry */ char *platform; }; @@ -104,41 +114,33 @@ #define CPU_FTR_NEED_COHERENT ASM_CONST #define CPU_FTR_NO_BTIC ASM_CONST(0x0000000000040000) #define CPU_FTR_BIG_PHYS ASM_CONST(0x0000000000080000) #define CPU_FTR_NODSISRALIGN ASM_CONST(0x0000000000100000) +#define CPU_FTR_PPC_LE ASM_CONST(0x0000000000200000) +#define CPU_FTR_REAL_LE ASM_CONST(0x0000000000400000) +/* + * Add the 64-bit processor unique features in the top half of the word; + * on 32-bit, make the names available but defined to be 0. + */ #ifdef __powerpc64__ -/* Add the 64b processor unique features in the top half of the word */ -#define CPU_FTR_SLB ASM_CONST(0x0000000100000000) -#define CPU_FTR_16M_PAGE ASM_CONST(0x0000000200000000) -#define CPU_FTR_TLBIEL ASM_CONST(0x0000000400000000) -#define CPU_FTR_NOEXECUTE ASM_CONST(0x0000000800000000) -#define CPU_FTR_IABR ASM_CONST(0x0000002000000000) -#define CPU_FTR_MMCRA ASM_CONST(0x0000004000000000) -#define CPU_FTR_CTRL ASM_CONST(0x0000008000000000) -#define CPU_FTR_SMT ASM_CONST(0x0000010000000000) -#define CPU_FTR_COHERENT_ICACHE ASM_CONST(0x0000020000000000) -#define CPU_FTR_LOCKLESS_TLBIE ASM_CONST(0x0000040000000000) -#define CPU_FTR_MMCRA_SIHV ASM_CONST(0x0000080000000000) -#define CPU_FTR_CI_LARGE_PAGE ASM_CONST(0x0000100000000000) -#define CPU_FTR_PAUSE_ZERO ASM_CONST(0x0000200000000000) -#define CPU_FTR_PURR ASM_CONST(0x0000400000000000) +#define LONG_ASM_CONST(x) ASM_CONST(x) #else -/* ensure on 32b processors the flags are available for compiling but - * don't do anything */ -#define CPU_FTR_SLB ASM_CONST(0x0) -#define CPU_FTR_16M_PAGE ASM_CONST(0x0) -#define CPU_FTR_TLBIEL ASM_CONST(0x0) -#define CPU_FTR_NOEXECUTE ASM_CONST(0x0) -#define CPU_FTR_IABR ASM_CONST(0x0) -#define CPU_FTR_MMCRA ASM_CONST(0x0) -#define CPU_FTR_CTRL ASM_CONST(0x0) -#define CPU_FTR_SMT ASM_CONST(0x0) -#define CPU_FTR_COHERENT_ICACHE ASM_CONST(0x0) -#define CPU_FTR_LOCKLESS_TLBIE ASM_CONST(0x0) -#define CPU_FTR_MMCRA_SIHV ASM_CONST(0x0) -#define CPU_FTR_CI_LARGE_PAGE ASM_CONST(0x0) -#define CPU_FTR_PURR ASM_CONST(0x0) +#define LONG_ASM_CONST(x) 0 #endif +#define CPU_FTR_SLB LONG_ASM_CONST(0x0000000100000000) +#define CPU_FTR_16M_PAGE LONG_ASM_CONST(0x0000000200000000) +#define CPU_FTR_TLBIEL LONG_ASM_CONST(0x0000000400000000) +#define CPU_FTR_NOEXECUTE LONG_ASM_CONST(0x0000000800000000) +#define CPU_FTR_IABR LONG_ASM_CONST(0x0000002000000000) +#define CPU_FTR_MMCRA LONG_ASM_CONST(0x0000004000000000) +#define CPU_FTR_CTRL LONG_ASM_CONST(0x0000008000000000) +#define CPU_FTR_SMT LONG_ASM_CONST(0x0000010000000000) +#define CPU_FTR_COHERENT_ICACHE LONG_ASM_CONST(0x0000020000000000) +#define CPU_FTR_LOCKLESS_TLBIE LONG_ASM_CONST(0x0000040000000000) +#define CPU_FTR_CI_LARGE_PAGE LONG_ASM_CONST(0x0000100000000000) +#define CPU_FTR_PAUSE_ZERO LONG_ASM_CONST(0x0000200000000000) +#define CPU_FTR_PURR LONG_ASM_CONST(0x0000400000000000) + #ifndef __ASSEMBLY__ #define CPU_FTR_PPCAS_ARCH_V2_BASE (CPU_FTR_SLB | \ @@ -192,92 +194,95 @@ #define CLASSIC_PPC (!defined(CONFIG_8xx #define CPU_FTRS_PPC601 (CPU_FTR_COMMON | CPU_FTR_601 | CPU_FTR_HPTE_TABLE) #define CPU_FTRS_603 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ - CPU_FTR_MAYBE_CAN_NAP) + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) #define CPU_FTRS_604 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE) + CPU_FTR_USE_TB | CPU_FTR_604_PERF_MON | CPU_FTR_HPTE_TABLE | \ + CPU_FTR_PPC_LE) #define CPU_FTRS_740_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP) + CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) #define CPU_FTRS_740 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP) + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_PPC_LE) #define CPU_FTRS_750 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ - CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP) + CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ + CPU_FTR_PPC_LE) #define CPU_FTRS_750FX1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM) + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_NO_DPM | CPU_FTR_PPC_LE) #define CPU_FTRS_750FX2 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_NO_DPM) + CPU_FTR_NO_DPM | CPU_FTR_PPC_LE) #define CPU_FTRS_750FX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS) + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) #define CPU_FTRS_750GX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_TAU | \ CPU_FTR_HPTE_TABLE | CPU_FTR_MAYBE_CAN_NAP | \ - CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS) + CPU_FTR_DUAL_PLL_750FX | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) #define CPU_FTRS_7400_NOTAU (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \ - CPU_FTR_MAYBE_CAN_NAP) + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) #define CPU_FTRS_7400 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | CPU_FTR_L2CR | \ CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE | \ - CPU_FTR_MAYBE_CAN_NAP) + CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_PPC_LE) #define CPU_FTRS_7450_20 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ - CPU_FTR_NEED_COHERENT) + CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_7450_21 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \ - CPU_FTR_NEED_COHERENT) + CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_7450_23 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ - CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT) + CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_7455_1 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | CPU_FTR_L3CR | \ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | CPU_FTR_HAS_HIGH_BATS | \ - CPU_FTR_NEED_COHERENT) + CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_7455_20 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_L3_DISABLE_NAP | \ - CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS) + CPU_FTR_NEED_COHERENT | CPU_FTR_HAS_HIGH_BATS | CPU_FTR_PPC_LE) #define CPU_FTRS_7455 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ - CPU_FTR_NEED_COHERENT) + CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_7447_10 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ - CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC) + CPU_FTR_NEED_COHERENT | CPU_FTR_NO_BTIC | CPU_FTR_PPC_LE) #define CPU_FTRS_7447 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_L3CR | CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ - CPU_FTR_NEED_COHERENT) + CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_7447A (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | \ CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_L2CR | CPU_FTR_ALTIVEC_COMP | \ CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450 | \ CPU_FTR_NAP_DISABLE_L2_PR | CPU_FTR_HAS_HIGH_BATS | \ - CPU_FTR_NEED_COHERENT) + CPU_FTR_NEED_COHERENT | CPU_FTR_PPC_LE) #define CPU_FTRS_82XX (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB) #define CPU_FTRS_G2_LE (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_MAYBE_CAN_DOZE | \ @@ -287,13 +292,6 @@ #define CPU_FTRS_E300 (CPU_FTR_SPLIT_ID_ CPU_FTR_COMMON) #define CPU_FTRS_CLASSIC32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) -#define CPU_FTRS_POWER3_32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE) -#define CPU_FTRS_POWER4_32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_NODSISRALIGN) -#define CPU_FTRS_970_32 (CPU_FTR_COMMON | CPU_FTR_SPLIT_ID_CACHE | \ - CPU_FTR_USE_TB | CPU_FTR_HPTE_TABLE | CPU_FTR_ALTIVEC_COMP | \ - CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN) #define CPU_FTRS_8XX (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB) #define CPU_FTRS_40X (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ CPU_FTR_NODSISRALIGN) @@ -307,7 +305,7 @@ #define CPU_FTRS_E500_2 (CPU_FTR_SPLIT_I #define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN) #ifdef __powerpc64__ #define CPU_FTRS_POWER3 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ - CPU_FTR_HPTE_TABLE | CPU_FTR_IABR) + CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | CPU_FTR_PPC_LE) #define CPU_FTRS_RS64 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_IABR | \ CPU_FTR_MMCRA | CPU_FTR_CTRL) @@ -320,12 +318,12 @@ #define CPU_FTRS_POWER5 (CPU_FTR_SPLIT_I CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \ - CPU_FTR_MMCRA_SIHV | CPU_FTR_PURR) + CPU_FTR_PURR) #define CPU_FTRS_POWER6 (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ CPU_FTR_MMCRA | CPU_FTR_SMT | \ CPU_FTR_COHERENT_ICACHE | CPU_FTR_LOCKLESS_TLBIE | \ - CPU_FTR_PURR | CPU_FTR_CI_LARGE_PAGE) + CPU_FTR_PURR | CPU_FTR_CI_LARGE_PAGE | CPU_FTR_REAL_LE) #define CPU_FTRS_CELL (CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | \ CPU_FTR_HPTE_TABLE | CPU_FTR_PPCAS_ARCH_V2 | \ CPU_FTR_ALTIVEC_COMP | CPU_FTR_MMCRA | CPU_FTR_SMT | \ @@ -354,12 +352,6 @@ #if CLASSIC_PPC #else CPU_FTRS_GENERIC_32 | #endif -#ifdef CONFIG_PPC64BRIDGE - CPU_FTRS_POWER3_32 | -#endif -#ifdef CONFIG_POWER4 - CPU_FTRS_POWER4_32 | CPU_FTRS_970_32 | -#endif #ifdef CONFIG_8xx CPU_FTRS_8XX | #endif @@ -399,12 +391,6 @@ #if CLASSIC_PPC #else CPU_FTRS_GENERIC_32 & #endif -#ifdef CONFIG_PPC64BRIDGE - CPU_FTRS_POWER3_32 & -#endif -#ifdef CONFIG_POWER4 - CPU_FTRS_POWER4_32 & CPU_FTRS_970_32 & -#endif #ifdef CONFIG_8xx CPU_FTRS_8XX & #endif diff --git a/include/asm-powerpc/cputime.h b/include/asm-powerpc/cputime.h index a21185d..3108044 100644 --- a/include/asm-powerpc/cputime.h +++ b/include/asm-powerpc/cputime.h @@ -43,6 +43,7 @@ #define cputime_le(__a, __b) ((__a) <= #define cputime64_zero ((cputime64_t)0) #define cputime64_add(__a, __b) ((__a) + (__b)) +#define cputime64_sub(__a, __b) ((__a) - (__b)) #define cputime_to_cputime64(__ct) (__ct) #ifdef __KERNEL__ @@ -74,6 +75,23 @@ static inline cputime_t jiffies_to_cputi return ct; } +static inline cputime64_t jiffies64_to_cputime64(const u64 jif) +{ + cputime_t ct; + u64 sec; + + /* have to be a little careful about overflow */ + ct = jif % HZ; + sec = jif / HZ; + if (ct) { + ct *= tb_ticks_per_sec; + do_div(ct, HZ); + } + if (sec) + ct += (cputime_t) sec * tb_ticks_per_sec; + return ct; +} + static inline u64 cputime64_to_jiffies64(const cputime_t ct) { return mulhdu(ct, __cputime_jiffies_factor); diff --git a/include/asm-powerpc/delay.h b/include/asm-powerpc/delay.h index 057a609..f9200a6 100644 --- a/include/asm-powerpc/delay.h +++ b/include/asm-powerpc/delay.h @@ -17,5 +17,18 @@ #ifdef __KERNEL__ extern void __delay(unsigned long loops); extern void udelay(unsigned long usecs); +/* + * On shared processor machines the generic implementation of mdelay can + * result in large errors. While each iteration of the loop inside mdelay + * is supposed to take 1ms, the hypervisor could sleep our partition for + * longer (eg 10ms). With the right timing these errors can add up. + * + * Since there is no 32bit overflow issue on 64bit kernels, just call + * udelay directly. + */ +#ifdef CONFIG_PPC64 +#define mdelay(n) udelay((n) * 1000) +#endif + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_DELAY_H */ diff --git a/include/asm-powerpc/dma-mapping.h b/include/asm-powerpc/dma-mapping.h index 2ac63f5..2ab9baf 100644 --- a/include/asm-powerpc/dma-mapping.h +++ b/include/asm-powerpc/dma-mapping.h @@ -8,7 +8,6 @@ #ifndef _ASM_DMA_MAPPING_H #define _ASM_DMA_MAPPING_H #ifdef __KERNEL__ -#include #include #include /* need struct page definitions */ diff --git a/include/asm-powerpc/dma.h b/include/asm-powerpc/dma.h index 4bb57fe..7a4374b 100644 --- a/include/asm-powerpc/dma.h +++ b/include/asm-powerpc/dma.h @@ -22,7 +22,6 @@ #ifdef __KERNEL__ * with a grain of salt. */ -#include #include #include #include diff --git a/include/asm-powerpc/eeh.h b/include/asm-powerpc/eeh.h index 868c713..4df3e80 100644 --- a/include/asm-powerpc/eeh.h +++ b/include/asm-powerpc/eeh.h @@ -21,7 +21,6 @@ #ifndef _PPC64_EEH_H #define _PPC64_EEH_H #ifdef __KERNEL__ -#include #include #include #include @@ -293,8 +292,6 @@ #undef EEH_CHECK_ALIGN static inline u8 eeh_inb(unsigned long port) { u8 val; - if (!_IO_IS_VALID(port)) - return ~0; val = in_8((u8 __iomem *)(port+pci_io_base)); if (EEH_POSSIBLE_ERROR(val, u8)) return eeh_check_failure((void __iomem *)(port), val); @@ -303,15 +300,12 @@ static inline u8 eeh_inb(unsigned long p static inline void eeh_outb(u8 val, unsigned long port) { - if (_IO_IS_VALID(port)) - out_8((u8 __iomem *)(port+pci_io_base), val); + out_8((u8 __iomem *)(port+pci_io_base), val); } static inline u16 eeh_inw(unsigned long port) { u16 val; - if (!_IO_IS_VALID(port)) - return ~0; val = in_le16((u16 __iomem *)(port+pci_io_base)); if (EEH_POSSIBLE_ERROR(val, u16)) return eeh_check_failure((void __iomem *)(port), val); @@ -320,15 +314,12 @@ static inline u16 eeh_inw(unsigned long static inline void eeh_outw(u16 val, unsigned long port) { - if (_IO_IS_VALID(port)) - out_le16((u16 __iomem *)(port+pci_io_base), val); + out_le16((u16 __iomem *)(port+pci_io_base), val); } static inline u32 eeh_inl(unsigned long port) { u32 val; - if (!_IO_IS_VALID(port)) - return ~0; val = in_le32((u32 __iomem *)(port+pci_io_base)); if (EEH_POSSIBLE_ERROR(val, u32)) return eeh_check_failure((void __iomem *)(port), val); @@ -337,8 +328,7 @@ static inline u32 eeh_inl(unsigned long static inline void eeh_outl(u32 val, unsigned long port) { - if (_IO_IS_VALID(port)) - out_le32((u32 __iomem *)(port+pci_io_base), val); + out_le32((u32 __iomem *)(port+pci_io_base), val); } /* in-string eeh macros */ diff --git a/include/asm-powerpc/eeh_event.h b/include/asm-powerpc/eeh_event.h index 93d55a2..dc6bf0f 100644 --- a/include/asm-powerpc/eeh_event.h +++ b/include/asm-powerpc/eeh_event.h @@ -18,8 +18,8 @@ * Copyright (c) 2005 Linas Vepstas */ -#ifndef ASM_PPC64_EEH_EVENT_H -#define ASM_PPC64_EEH_EVENT_H +#ifndef ASM_POWERPC_EEH_EVENT_H +#define ASM_POWERPC_EEH_EVENT_H #ifdef __KERNEL__ /** EEH event -- structure holding pci controller data that describes @@ -39,7 +39,7 @@ struct eeh_event { * @dev pci device * * This routine builds a PCI error event which will be delivered - * to all listeners on the peh_notifier_chain. + * to all listeners on the eeh_notifier_chain. * * This routine can be called within an interrupt context; * the actual event will be delivered in a normal context @@ -51,7 +51,7 @@ int eeh_send_failure_event (struct devic int time_unavail); /* Main recovery function */ -void handle_eeh_events (struct eeh_event *); +struct pci_dn * handle_eeh_events (struct eeh_event *); #endif /* __KERNEL__ */ -#endif /* ASM_PPC64_EEH_EVENT_H */ +#endif /* ASM_POWERPC_EEH_EVENT_H */ diff --git a/include/asm-powerpc/elf.h b/include/asm-powerpc/elf.h index 94d228f..9a83a98 100644 --- a/include/asm-powerpc/elf.h +++ b/include/asm-powerpc/elf.h @@ -3,14 +3,14 @@ #define _ASM_POWERPC_ELF_H #ifdef __KERNEL__ #include /* for task_struct */ +#include +#include #endif #include #include #include #include -#include -#include /* PowerPC relocations defined by the ABIs */ #define R_PPC_NONE 0 @@ -129,7 +129,7 @@ #else /* Assumption: ELF_ARCH == EM_PPC and ELF_CLASS == ELFCLASS32 */ typedef elf_greg_t32 elf_greg_t; typedef elf_gregset_t32 elf_gregset_t; -# define elf_addr_t u32 +# define elf_addr_t __u32 #endif /* ELF_ARCH */ /* Floating point registers */ @@ -161,6 +161,7 @@ #ifdef __powerpc64__ typedef elf_vrreg_t elf_vrregset_t32[ELF_NVRREG32]; #endif +#ifdef __KERNEL__ /* * This is used to ensure we don't load something for the wrong architecture. */ @@ -176,8 +177,6 @@ #define ELF_EXEC_PAGESIZE PAGE_SIZE #define ELF_ET_DYN_BASE (0x08000000) -#ifdef __KERNEL__ - /* Common routine for both 32-bit and 64-bit processes */ static inline void ppc_elf_core_copy_regs(elf_gregset_t elf_regs, struct pt_regs *regs) @@ -294,7 +293,7 @@ do { \ NEW_AUX_ENT(AT_DCACHEBSIZE, dcache_bsize); \ NEW_AUX_ENT(AT_ICACHEBSIZE, icache_bsize); \ NEW_AUX_ENT(AT_UCACHEBSIZE, ucache_bsize); \ - VDSO_AUX_ENT(AT_SYSINFO_EHDR, current->thread.vdso_base) \ + VDSO_AUX_ENT(AT_SYSINFO_EHDR, current->mm->context.vdso_base) \ } while (0) /* PowerPC64 relocations defined by the ABIs */ diff --git a/include/asm-powerpc/floppy.h b/include/asm-powerpc/floppy.h index 608164c..fd242a2 100644 --- a/include/asm-powerpc/floppy.h +++ b/include/asm-powerpc/floppy.h @@ -11,7 +11,6 @@ #ifndef __ASM_POWERPC_FLOPPY_H #define __ASM_POWERPC_FLOPPY_H #ifdef __KERNEL__ -#include #include #define fd_inb(port) inb_p(port) @@ -28,8 +27,7 @@ #define fd_enable_irq() enable_i #define fd_disable_irq() disable_irq(FLOPPY_IRQ) #define fd_cacheflush(addr,size) /* nothing */ #define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt, \ - SA_INTERRUPT|SA_SAMPLE_RANDOM, \ - "floppy", NULL) + IRQF_DISABLED, "floppy", NULL) #define fd_free_irq() free_irq(FLOPPY_IRQ, NULL); #ifdef CONFIG_PCI diff --git a/include/asm-powerpc/hvcall.h b/include/asm-powerpc/hvcall.h index 6cc7e1f..0d3c4e8 100644 --- a/include/asm-powerpc/hvcall.h +++ b/include/asm-powerpc/hvcall.h @@ -102,6 +102,15 @@ #define H_N (1UL<<(63-61)) #define H_PP1 (1UL<<(63-62)) #define H_PP2 (1UL<<(63-63)) +/* VASI States */ +#define H_VASI_INVALID 0 +#define H_VASI_ENABLED 1 +#define H_VASI_ABORTED 2 +#define H_VASI_SUSPENDING 3 +#define H_VASI_SUSPENDED 4 +#define H_VASI_RESUMED 5 +#define H_VASI_COMPLETED 6 + /* DABRX flags */ #define H_DABRX_HYPERVISOR (1UL<<(63-61)) #define H_DABRX_KERNEL (1UL<<(63-62)) @@ -190,6 +199,7 @@ #define H_MANAGE_TRACE 0x1C0 #define H_QUERY_INT_STATE 0x1E4 #define H_POLL_PENDING 0x1D8 #define H_JOIN 0x298 +#define H_VASI_STATE 0x2A4 #define H_ENABLE_CRQ 0x2B0 #ifndef __ASSEMBLY__ diff --git a/include/asm-powerpc/hw_irq.h b/include/asm-powerpc/hw_irq.h index 26b89d8..d403592 100644 --- a/include/asm-powerpc/hw_irq.h +++ b/include/asm-powerpc/hw_irq.h @@ -6,7 +6,6 @@ #define _ASM_POWERPC_HW_IRQ_H #ifdef __KERNEL__ -#include #include #include #include @@ -87,27 +86,27 @@ #endif /* CONFIG_PPC_ISERIES */ #define mask_irq(irq) \ ({ \ irq_desc_t *desc = get_irq_desc(irq); \ - if (desc->handler && desc->handler->disable) \ - desc->handler->disable(irq); \ + if (desc->chip && desc->chip->disable) \ + desc->chip->disable(irq); \ }) #define unmask_irq(irq) \ ({ \ irq_desc_t *desc = get_irq_desc(irq); \ - if (desc->handler && desc->handler->enable) \ - desc->handler->enable(irq); \ + if (desc->chip && desc->chip->enable) \ + desc->chip->enable(irq); \ }) #define ack_irq(irq) \ ({ \ irq_desc_t *desc = get_irq_desc(irq); \ - if (desc->handler && desc->handler->ack) \ - desc->handler->ack(irq); \ + if (desc->chip && desc->chip->ack) \ + desc->chip->ack(irq); \ }) -/* Should we handle this via lost interrupts and IPIs or should we don't care like - * we do now ? --BenH. +/* + * interrupt-retrigger: should we handle this via lost interrupts and IPIs + * or should we not care like we do now ? --BenH. */ struct hw_interrupt_type; -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_HW_IRQ_H */ diff --git a/include/asm-powerpc/i8259.h b/include/asm-powerpc/i8259.h index 0392159..c80e113 100644 --- a/include/asm-powerpc/i8259.h +++ b/include/asm-powerpc/i8259.h @@ -4,11 +4,13 @@ #ifdef __KERNEL__ #include -extern struct hw_interrupt_type i8259_pic; - +#ifdef CONFIG_PPC_MERGE +extern void i8259_init(struct device_node *node, unsigned long intack_addr); +extern unsigned int i8259_irq(struct pt_regs *regs); +#else extern void i8259_init(unsigned long intack_addr, int offset); extern int i8259_irq(struct pt_regs *regs); -extern int i8259_irq_cascade(struct pt_regs *regs, void *unused); +#endif #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_I8259_H */ diff --git a/include/asm-powerpc/ide.h b/include/asm-powerpc/ide.h index da5f640..b09b42a 100644 --- a/include/asm-powerpc/ide.h +++ b/include/asm-powerpc/ide.h @@ -22,7 +22,6 @@ #endif #endif #ifndef __powerpc64__ -#include #include #include #include diff --git a/include/asm-powerpc/immap_86xx.h b/include/asm-powerpc/immap_86xx.h new file mode 100644 index 0000000..d905b66 --- /dev/null +++ b/include/asm-powerpc/immap_86xx.h @@ -0,0 +1,199 @@ +/* + * MPC86xx Internal Memory Map + * + * Author: Jeff Brown + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __ASM_POWERPC_IMMAP_86XX_H__ +#define __ASM_POWERPC_IMMAP_86XX_H__ +#ifdef __KERNEL__ + +/* Eventually this should define all the IO block registers in 86xx */ + +/* PCI Registers */ +typedef struct ccsr_pci { + uint cfg_addr; /* 0x.000 - PCI Configuration Address Register */ + uint cfg_data; /* 0x.004 - PCI Configuration Data Register */ + uint int_ack; /* 0x.008 - PCI Interrupt Acknowledge Register */ + char res1[3060]; + uint potar0; /* 0x.c00 - PCI Outbound Transaction Address Register 0 */ + uint potear0; /* 0x.c04 - PCI Outbound Translation Extended Address Register 0 */ + uint powbar0; /* 0x.c08 - PCI Outbound Window Base Address Register 0 */ + char res2[4]; + uint powar0; /* 0x.c10 - PCI Outbound Window Attributes Register 0 */ + char res3[12]; + uint potar1; /* 0x.c20 - PCI Outbound Transaction Address Register 1 */ + uint potear1; /* 0x.c24 - PCI Outbound Translation Extended Address Register 1 */ + uint powbar1; /* 0x.c28 - PCI Outbound Window Base Address Register 1 */ + char res4[4]; + uint powar1; /* 0x.c30 - PCI Outbound Window Attributes Register 1 */ + char res5[12]; + uint potar2; /* 0x.c40 - PCI Outbound Transaction Address Register 2 */ + uint potear2; /* 0x.c44 - PCI Outbound Translation Extended Address Register 2 */ + uint powbar2; /* 0x.c48 - PCI Outbound Window Base Address Register 2 */ + char res6[4]; + uint powar2; /* 0x.c50 - PCI Outbound Window Attributes Register 2 */ + char res7[12]; + uint potar3; /* 0x.c60 - PCI Outbound Transaction Address Register 3 */ + uint potear3; /* 0x.c64 - PCI Outbound Translation Extended Address Register 3 */ + uint powbar3; /* 0x.c68 - PCI Outbound Window Base Address Register 3 */ + char res8[4]; + uint powar3; /* 0x.c70 - PCI Outbound Window Attributes Register 3 */ + char res9[12]; + uint potar4; /* 0x.c80 - PCI Outbound Transaction Address Register 4 */ + uint potear4; /* 0x.c84 - PCI Outbound Translation Extended Address Register 4 */ + uint powbar4; /* 0x.c88 - PCI Outbound Window Base Address Register 4 */ + char res10[4]; + uint powar4; /* 0x.c90 - PCI Outbound Window Attributes Register 4 */ + char res11[268]; + uint pitar3; /* 0x.da0 - PCI Inbound Translation Address Register 3 */ + char res12[4]; + uint piwbar3; /* 0x.da8 - PCI Inbound Window Base Address Register 3 */ + uint piwbear3; /* 0x.dac - PCI Inbound Window Base Extended Address Register 3 */ + uint piwar3; /* 0x.db0 - PCI Inbound Window Attributes Register 3 */ + char res13[12]; + uint pitar2; /* 0x.dc0 - PCI Inbound Translation Address Register 2 */ + char res14[4]; + uint piwbar2; /* 0x.dc8 - PCI Inbound Window Base Address Register 2 */ + uint piwbear2; /* 0x.dcc - PCI Inbound Window Base Extended Address Register 2 */ + uint piwar2; /* 0x.dd0 - PCI Inbound Window Attributes Register 2 */ + char res15[12]; + uint pitar1; /* 0x.de0 - PCI Inbound Translation Address Register 1 */ + char res16[4]; + uint piwbar1; /* 0x.de8 - PCI Inbound Window Base Address Register 1 */ + char res17[4]; + uint piwar1; /* 0x.df0 - PCI Inbound Window Attributes Register 1 */ + char res18[12]; + uint err_dr; /* 0x.e00 - PCI Error Detect Register */ + uint err_cap_dr; /* 0x.e04 - PCI Error Capture Disable Register */ + uint err_en; /* 0x.e08 - PCI Error Enable Register */ + uint err_attrib; /* 0x.e0c - PCI Error Attributes Capture Register */ + uint err_addr; /* 0x.e10 - PCI Error Address Capture Register */ + uint err_ext_addr; /* 0x.e14 - PCI Error Extended Address Capture Register */ + uint err_dl; /* 0x.e18 - PCI Error Data Low Capture Register */ + uint err_dh; /* 0x.e1c - PCI Error Data High Capture Register */ + uint gas_timr; /* 0x.e20 - PCI Gasket Timer Register */ + uint pci_timr; /* 0x.e24 - PCI Timer Register */ + char res19[472]; +} ccsr_pci_t; + +/* PCI Express Registers */ +typedef struct ccsr_pex { + uint pex_config_addr; /* 0x.000 - PCI Express Configuration Address Register */ + uint pex_config_data; /* 0x.004 - PCI Express Configuration Data Register */ + char res1[4]; + uint pex_otb_cpl_tor; /* 0x.00c - PCI Express Outbound completion timeout register */ + uint pex_conf_tor; /* 0x.010 - PCI Express configuration timeout register */ + char res2[12]; + uint pex_pme_mes_dr; /* 0x.020 - PCI Express PME and message detect register */ + uint pex_pme_mes_disr; /* 0x.024 - PCI Express PME and message disable register */ + uint pex_pme_mes_ier; /* 0x.028 - PCI Express PME and message interrupt enable register */ + uint pex_pmcr; /* 0x.02c - PCI Express power management command register */ + char res3[3024]; + uint pexotar0; /* 0x.c00 - PCI Express outbound translation address register 0 */ + uint pexotear0; /* 0x.c04 - PCI Express outbound translation extended address register 0*/ + char res4[8]; + uint pexowar0; /* 0x.c10 - PCI Express outbound window attributes register 0*/ + char res5[12]; + uint pexotar1; /* 0x.c20 - PCI Express outbound translation address register 1 */ + uint pexotear1; /* 0x.c24 - PCI Express outbound translation extended address register 1*/ + uint pexowbar1; /* 0x.c28 - PCI Express outbound window base address register 1*/ + char res6[4]; + uint pexowar1; /* 0x.c30 - PCI Express outbound window attributes register 1*/ + char res7[12]; + uint pexotar2; /* 0x.c40 - PCI Express outbound translation address register 2 */ + uint pexotear2; /* 0x.c44 - PCI Express outbound translation extended address register 2*/ + uint pexowbar2; /* 0x.c48 - PCI Express outbound window base address register 2*/ + char res8[4]; + uint pexowar2; /* 0x.c50 - PCI Express outbound window attributes register 2*/ + char res9[12]; + uint pexotar3; /* 0x.c60 - PCI Express outbound translation address register 3 */ + uint pexotear3; /* 0x.c64 - PCI Express outbound translation extended address register 3*/ + uint pexowbar3; /* 0x.c68 - PCI Express outbound window base address register 3*/ + char res10[4]; + uint pexowar3; /* 0x.c70 - PCI Express outbound window attributes register 3*/ + char res11[12]; + uint pexotar4; /* 0x.c80 - PCI Express outbound translation address register 4 */ + uint pexotear4; /* 0x.c84 - PCI Express outbound translation extended address register 4*/ + uint pexowbar4; /* 0x.c88 - PCI Express outbound window base address register 4*/ + char res12[4]; + uint pexowar4; /* 0x.c90 - PCI Express outbound window attributes register 4*/ + char res13[12]; + char res14[256]; + uint pexitar3; /* 0x.da0 - PCI Express inbound translation address register 3 */ + char res15[4]; + uint pexiwbar3; /* 0x.da8 - PCI Express inbound window base address register 3 */ + uint pexiwbear3; /* 0x.dac - PCI Express inbound window base extended address register 3 */ + uint pexiwar3; /* 0x.db0 - PCI Express inbound window attributes register 3 */ + char res16[12]; + uint pexitar2; /* 0x.dc0 - PCI Express inbound translation address register 2 */ + char res17[4]; + uint pexiwbar2; /* 0x.dc8 - PCI Express inbound window base address register 2 */ + uint pexiwbear2; /* 0x.dcc - PCI Express inbound window base extended address register 2 */ + uint pexiwar2; /* 0x.dd0 - PCI Express inbound window attributes register 2 */ + char res18[12]; + uint pexitar1; /* 0x.de0 - PCI Express inbound translation address register 2 */ + char res19[4]; + uint pexiwbar1; /* 0x.de8 - PCI Express inbound window base address register 2 */ + uint pexiwbear1; /* 0x.dec - PCI Express inbound window base extended address register 2 */ + uint pexiwar1; /* 0x.df0 - PCI Express inbound window attributes register 2 */ + char res20[12]; + uint pex_err_dr; /* 0x.e00 - PCI Express error detect register */ + char res21[4]; + uint pex_err_en; /* 0x.e08 - PCI Express error interrupt enable register */ + char res22[4]; + uint pex_err_disr; /* 0x.e10 - PCI Express error disable register */ + char res23[12]; + uint pex_err_cap_stat; /* 0x.e20 - PCI Express error capture status register */ + char res24[4]; + uint pex_err_cap_r0; /* 0x.e28 - PCI Express error capture register 0 */ + uint pex_err_cap_r1; /* 0x.e2c - PCI Express error capture register 0 */ + uint pex_err_cap_r2; /* 0x.e30 - PCI Express error capture register 0 */ + uint pex_err_cap_r3; /* 0x.e34 - PCI Express error capture register 0 */ +} ccsr_pex_t; + +/* Global Utility Registers */ +typedef struct ccsr_guts { + uint porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */ + uint porbmsr; /* 0x.0004 - POR Boot Mode Status Register */ + uint porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */ + uint pordevsr; /* 0x.000c - POR I/O Device Status Register */ + uint pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */ + char res1[12]; + uint gpporcr; /* 0x.0020 - General-Purpose POR Configuration Register */ + char res2[12]; + uint gpiocr; /* 0x.0030 - GPIO Control Register */ + char res3[12]; + uint gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */ + char res4[12]; + uint gpindr; /* 0x.0050 - General-Purpose Input Data Register */ + char res5[12]; + uint pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */ + char res6[12]; + uint devdisr; /* 0x.0070 - Device Disable Control */ + char res7[12]; + uint powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */ + char res8[12]; + uint mcpsumr; /* 0x.0090 - Machine Check Summary Register */ + char res9[12]; + uint pvr; /* 0x.00a0 - Processor Version Register */ + uint svr; /* 0x.00a4 - System Version Register */ + char res10[3416]; + uint clkocr; /* 0x.0e00 - Clock Out Select Register */ + char res11[12]; + uint ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */ + char res12[12]; + uint lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */ + char res13[61916]; +} ccsr_guts_t; + +#endif /* __ASM_POWERPC_IMMAP_86XX_H__ */ +#endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/io.h b/include/asm-powerpc/io.h index f1c2469..a9496f3 100644 --- a/include/asm-powerpc/io.h +++ b/include/asm-powerpc/io.h @@ -40,12 +40,6 @@ #define SLOW_DOWN_IO extern unsigned long isa_io_base; extern unsigned long pci_io_base; -extern unsigned long io_page_mask; - -#define MAX_ISA_PORT 0x10000 - -#define _IO_IS_VALID(port) ((port) >= MAX_ISA_PORT || (1 << (port>>PAGE_SHIFT)) \ - & io_page_mask) #ifdef CONFIG_PPC_ISERIES /* __raw_* accessors aren't supported on iSeries */ diff --git a/include/asm-powerpc/iommu.h b/include/asm-powerpc/iommu.h index 18ca29e..a5e9864 100644 --- a/include/asm-powerpc/iommu.h +++ b/include/asm-powerpc/iommu.h @@ -22,7 +22,6 @@ #ifndef _ASM_IOMMU_H #define _ASM_IOMMU_H #ifdef __KERNEL__ -#include #include #include #include @@ -67,7 +66,8 @@ #endif /* CONFIG_PPC_MULTIPLATFORM */ /* Initializes an iommu_table based in values set in the passed-in * structure */ -extern struct iommu_table *iommu_init_table(struct iommu_table * tbl); +extern struct iommu_table *iommu_init_table(struct iommu_table * tbl, + int nid); extern int iommu_map_sg(struct device *dev, struct iommu_table *tbl, struct scatterlist *sglist, int nelems, unsigned long mask, @@ -76,7 +76,8 @@ extern void iommu_unmap_sg(struct iommu_ int nelems, enum dma_data_direction direction); extern void *iommu_alloc_coherent(struct iommu_table *tbl, size_t size, - dma_addr_t *dma_handle, unsigned long mask, gfp_t flag); + dma_addr_t *dma_handle, unsigned long mask, + gfp_t flag, int node); extern void iommu_free_coherent(struct iommu_table *tbl, size_t size, void *vaddr, dma_addr_t dma_handle); extern dma_addr_t iommu_map_single(struct iommu_table *tbl, void *vaddr, diff --git a/include/asm-powerpc/irq.h b/include/asm-powerpc/irq.h index 7bc6d73..e057547 100644 --- a/include/asm-powerpc/irq.h +++ b/include/asm-powerpc/irq.h @@ -11,30 +11,12 @@ #define _ASM_POWERPC_IRQ_H #include #include +#include +#include #include #include -/* this number is used when no interrupt has been assigned */ -#define NO_IRQ (-1) - -/* - * These constants are used for passing information about interrupt - * signal polarity and level/edge sensing to the low-level PIC chip - * drivers. - */ -#define IRQ_SENSE_MASK 0x1 -#define IRQ_SENSE_LEVEL 0x1 /* interrupt on active level */ -#define IRQ_SENSE_EDGE 0x0 /* interrupt triggered by edge */ - -#define IRQ_POLARITY_MASK 0x2 -#define IRQ_POLARITY_POSITIVE 0x2 /* high level or low->high edge */ -#define IRQ_POLARITY_NEGATIVE 0x0 /* low level or high->low edge */ - -/* - * IRQ line status macro IRQ_PER_CPU is used - */ -#define ARCH_HAS_IRQ_PER_CPU #define get_irq_desc(irq) (&irq_desc[(irq)]) @@ -42,50 +24,325 @@ #define get_irq_desc(irq) (&irq_desc[(ir #define for_each_irq(i) \ for ((i) = 0; (i) < NR_IRQS; ++(i)) -#ifdef CONFIG_PPC64 +extern atomic_t ppc_n_lost_interrupts; -/* - * Maximum number of interrupt sources that we can handle. +#ifdef CONFIG_PPC_MERGE + +/* This number is used when no interrupt has been assigned */ +#define NO_IRQ (0) + +/* This is a special irq number to return from get_irq() to tell that + * no interrupt happened _and_ ignore it (don't count it as bad). Some + * platforms like iSeries rely on that. */ +#define NO_IRQ_IGNORE ((unsigned int)-1) + +/* Total number of virq in the platform (make it a CONFIG_* option ? */ #define NR_IRQS 512 -/* Interrupt numbers are virtual in case they are sparsely - * distributed by the hardware. +/* Number of irqs reserved for the legacy controller */ +#define NUM_ISA_INTERRUPTS 16 + +/* This type is the placeholder for a hardware interrupt number. It has to + * be big enough to enclose whatever representation is used by a given + * platform. + */ +typedef unsigned long irq_hw_number_t; + +/* Interrupt controller "host" data structure. This could be defined as a + * irq domain controller. That is, it handles the mapping between hardware + * and virtual interrupt numbers for a given interrupt domain. The host + * structure is generally created by the PIC code for a given PIC instance + * (though a host can cover more than one PIC if they have a flat number + * model). It's the host callbacks that are responsible for setting the + * irq_chip on a given irq_desc after it's been mapped. + * + * The host code and data structures are fairly agnostic to the fact that + * we use an open firmware device-tree. We do have references to struct + * device_node in two places: in irq_find_host() to find the host matching + * a given interrupt controller node, and of course as an argument to its + * counterpart host->ops->match() callback. However, those are treated as + * generic pointers by the core and the fact that it's actually a device-node + * pointer is purely a convention between callers and implementation. This + * code could thus be used on other architectures by replacing those two + * by some sort of arch-specific void * "token" used to identify interrupt + * controllers. + */ +struct irq_host; +struct radix_tree_root; + +/* Functions below are provided by the host and called whenever a new mapping + * is created or an old mapping is disposed. The host can then proceed to + * whatever internal data structures management is required. It also needs + * to setup the irq_desc when returning from map(). + */ +struct irq_host_ops { + /* Match an interrupt controller device node to a host, returns + * 1 on a match + */ + int (*match)(struct irq_host *h, struct device_node *node); + + /* Create or update a mapping between a virtual irq number and a hw + * irq number. This can be called several times for the same mapping + * but with different flags, though unmap shall always be called + * before the virq->hw mapping is changed. + */ + int (*map)(struct irq_host *h, unsigned int virq, + irq_hw_number_t hw, unsigned int flags); + + /* Dispose of such a mapping */ + void (*unmap)(struct irq_host *h, unsigned int virq); + + /* Translate device-tree interrupt specifier from raw format coming + * from the firmware to a irq_hw_number_t (interrupt line number) and + * trigger flags that can be passed to irq_create_mapping(). + * If no translation is provided, raw format is assumed to be one cell + * for interrupt line and default sense. + */ + int (*xlate)(struct irq_host *h, struct device_node *ctrler, + u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_flags); +}; + +struct irq_host { + struct list_head link; + + /* type of reverse mapping technique */ + unsigned int revmap_type; +#define IRQ_HOST_MAP_LEGACY 0 /* legacy 8259, gets irqs 1..15 */ +#define IRQ_HOST_MAP_NOMAP 1 /* no fast reverse mapping */ +#define IRQ_HOST_MAP_LINEAR 2 /* linear map of interrupts */ +#define IRQ_HOST_MAP_TREE 3 /* radix tree */ + union { + struct { + unsigned int size; + unsigned int *revmap; + } linear; + struct radix_tree_root tree; + } revmap_data; + struct irq_host_ops *ops; + void *host_data; + irq_hw_number_t inval_irq; +}; + +/* The main irq map itself is an array of NR_IRQ entries containing the + * associate host and irq number. An entry with a host of NULL is free. + * An entry can be allocated if it's free, the allocator always then sets + * hwirq first to the host's invalid irq number and then fills ops. + */ +struct irq_map_entry { + irq_hw_number_t hwirq; + struct irq_host *host; +}; + +extern struct irq_map_entry irq_map[NR_IRQS]; + + +/*** + * irq_alloc_host - Allocate a new irq_host data structure + * @node: device-tree node of the interrupt controller + * @revmap_type: type of reverse mapping to use + * @revmap_arg: for IRQ_HOST_MAP_LINEAR linear only: size of the map + * @ops: map/unmap host callbacks + * @inval_irq: provide a hw number in that host space that is always invalid + * + * Allocates and initialize and irq_host structure. Note that in the case of + * IRQ_HOST_MAP_LEGACY, the map() callback will be called before this returns + * for all legacy interrupts except 0 (which is always the invalid irq for + * a legacy controller). For a IRQ_HOST_MAP_LINEAR, the map is allocated by + * this call as well. For a IRQ_HOST_MAP_TREE, the radix tree will be allocated + * later during boot automatically (the reverse mapping will use the slow path + * until that happens). + */ +extern struct irq_host *irq_alloc_host(unsigned int revmap_type, + unsigned int revmap_arg, + struct irq_host_ops *ops, + irq_hw_number_t inval_irq); + + +/*** + * irq_find_host - Locates a host for a given device node + * @node: device-tree node of the interrupt controller + */ +extern struct irq_host *irq_find_host(struct device_node *node); + + +/*** + * irq_set_default_host - Set a "default" host + * @host: default host pointer + * + * For convenience, it's possible to set a "default" host that will be used + * whenever NULL is passed to irq_create_mapping(). It makes life easier for + * platforms that want to manipulate a few hard coded interrupt numbers that + * aren't properly represented in the device-tree. + */ +extern void irq_set_default_host(struct irq_host *host); + + +/*** + * irq_set_virq_count - Set the maximum number of virt irqs + * @count: number of linux virtual irqs, capped with NR_IRQS + * + * This is mainly for use by platforms like iSeries who want to program + * the virtual irq number in the controller to avoid the reverse mapping + */ +extern void irq_set_virq_count(unsigned int count); + + +/*** + * irq_create_mapping - Map a hardware interrupt into linux virq space + * @host: host owning this hardware interrupt or NULL for default host + * @hwirq: hardware irq number in that host space + * @flags: flags passed to the controller. contains the trigger type among + * others. Use IRQ_TYPE_* defined in include/linux/irq.h + * + * Only one mapping per hardware interrupt is permitted. Returns a linux + * virq number. The flags can be used to provide sense information to the + * controller (typically extracted from the device-tree). If no information + * is passed, the controller defaults will apply (for example, xics can only + * do edge so flags are irrelevant for some pseries specific irqs). + * + * The device-tree generally contains the trigger info in an encoding that is + * specific to a given type of controller. In that case, you can directly use + * host->ops->trigger_xlate() to translate that. + * + * It is recommended that new PICs that don't have existing OF bindings chose + * to use a representation of triggers identical to linux. + */ +extern unsigned int irq_create_mapping(struct irq_host *host, + irq_hw_number_t hwirq, + unsigned int flags); + + +/*** + * irq_dispose_mapping - Unmap an interrupt + * @virq: linux virq number of the interrupt to unmap + */ +extern void irq_dispose_mapping(unsigned int virq); + +/*** + * irq_find_mapping - Find a linux virq from an hw irq number. + * @host: host owning this hardware interrupt + * @hwirq: hardware irq number in that host space + * + * This is a slow path, for use by generic code. It's expected that an + * irq controller implementation directly calls the appropriate low level + * mapping function. + */ +extern unsigned int irq_find_mapping(struct irq_host *host, + irq_hw_number_t hwirq); + + +/*** + * irq_radix_revmap - Find a linux virq from a hw irq number. + * @host: host owning this hardware interrupt + * @hwirq: hardware irq number in that host space + * + * This is a fast path, for use by irq controller code that uses radix tree + * revmaps */ -extern unsigned int virt_irq_to_real_map[NR_IRQS]; +extern unsigned int irq_radix_revmap(struct irq_host *host, + irq_hw_number_t hwirq); -/* The maximum virtual IRQ number that we support. This - * can be set by the platform and will be reduced by the - * value of __irq_offset_value. It defaults to and is - * capped by (NR_IRQS - 1). +/*** + * irq_linear_revmap - Find a linux virq from a hw irq number. + * @host: host owning this hardware interrupt + * @hwirq: hardware irq number in that host space + * + * This is a fast path, for use by irq controller code that uses linear + * revmaps. It does fallback to the slow path if the revmap doesn't exist + * yet and will create the revmap entry with appropriate locking */ -extern unsigned int virt_irq_max; -/* Create a mapping for a real_irq if it doesn't already exist. - * Return the virtual irq as a convenience. +extern unsigned int irq_linear_revmap(struct irq_host *host, + irq_hw_number_t hwirq); + + + +/*** + * irq_alloc_virt - Allocate virtual irq numbers + * @host: host owning these new virtual irqs + * @count: number of consecutive numbers to allocate + * @hint: pass a hint number, the allocator will try to use a 1:1 mapping + * + * This is a low level function that is used internally by irq_create_mapping() + * and that can be used by some irq controllers implementations for things + * like allocating ranges of numbers for MSIs. The revmaps are left untouched. + */ +extern unsigned int irq_alloc_virt(struct irq_host *host, + unsigned int count, + unsigned int hint); + +/*** + * irq_free_virt - Free virtual irq numbers + * @virq: virtual irq number of the first interrupt to free + * @count: number of interrupts to free + * + * This function is the opposite of irq_alloc_virt. It will not clear reverse + * maps, this should be done previously by unmap'ing the interrupt. In fact, + * all interrupts covered by the range being freed should have been unmapped + * prior to calling this. + */ +extern void irq_free_virt(unsigned int virq, unsigned int count); + + +/* -- OF helpers -- */ + +/* irq_create_of_mapping - Map a hardware interrupt into linux virq space + * @controller: Device node of the interrupt controller + * @inspec: Interrupt specifier from the device-tree + * @intsize: Size of the interrupt specifier from the device-tree + * + * This function is identical to irq_create_mapping except that it takes + * as input informations straight from the device-tree (typically the results + * of the of_irq_map_*() functions */ -int virt_irq_create_mapping(unsigned int real_irq); -void virt_irq_init(void); +extern unsigned int irq_create_of_mapping(struct device_node *controller, + u32 *intspec, unsigned int intsize); -static inline unsigned int virt_irq_to_real(unsigned int virt_irq) + +/* irq_of_parse_and_map - Parse nad Map an interrupt into linux virq space + * @device: Device node of the device whose interrupt is to be mapped + * @index: Index of the interrupt to map + * + * This function is a wrapper that chains of_irq_map_one() and + * irq_create_of_mapping() to make things easier to callers + */ +extern unsigned int irq_of_parse_and_map(struct device_node *dev, int index); + +/* -- End OF helpers -- */ + +/*** + * irq_early_init - Init irq remapping subsystem + */ +extern void irq_early_init(void); + +static __inline__ int irq_canonicalize(int irq) { - return virt_irq_to_real_map[virt_irq]; + return irq; } -extern unsigned int real_irq_to_virt_slowpath(unsigned int real_irq); + +#else /* CONFIG_PPC_MERGE */ + +/* This number is used when no interrupt has been assigned */ +#define NO_IRQ (-1) +#define NO_IRQ_IGNORE (-2) + /* - * List of interrupt controllers. + * These constants are used for passing information about interrupt + * signal polarity and level/edge sensing to the low-level PIC chip + * drivers. */ -#define IC_INVALID 0 -#define IC_OPEN_PIC 1 -#define IC_PPC_XIC 2 -#define IC_CELL_PIC 3 -#define IC_ISERIES 4 +#define IRQ_SENSE_MASK 0x1 +#define IRQ_SENSE_LEVEL 0x1 /* interrupt on active level */ +#define IRQ_SENSE_EDGE 0x0 /* interrupt triggered by edge */ -extern u64 ppc64_interrupt_controller; +#define IRQ_POLARITY_MASK 0x2 +#define IRQ_POLARITY_POSITIVE 0x2 /* high level or low->high edge */ +#define IRQ_POLARITY_NEGATIVE 0x0 /* low level or high->low edge */ -#else /* 32-bit */ #if defined(CONFIG_40x) #include @@ -348,6 +605,92 @@ #define SIU_INT_PC2 ((uint)0x3d+CPM_IRQ #define SIU_INT_PC1 ((uint)0x3e+CPM_IRQ_OFFSET) #define SIU_INT_PC0 ((uint)0x3f+CPM_IRQ_OFFSET) +#elif defined(CONFIG_PPC_86xx) +#include + +#define NR_EPIC_INTS 48 +#ifndef NR_8259_INTS +#define NR_8259_INTS 16 /*ULI 1575 can route 12 interrupts */ +#endif +#define NUM_8259_INTERRUPTS NR_8259_INTS + +#ifndef I8259_OFFSET +#define I8259_OFFSET 0 +#endif + +#define NR_IRQS 256 + +/* Internal IRQs on MPC86xx OpenPIC */ + +#ifndef MPC86xx_OPENPIC_IRQ_OFFSET +#define MPC86xx_OPENPIC_IRQ_OFFSET NR_8259_INTS +#endif + +/* The 48 internal sources */ +#define MPC86xx_IRQ_NULL ( 0 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_MCM ( 1 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_DDR ( 2 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_LBC ( 3 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_DMA0 ( 4 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_DMA1 ( 5 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_DMA2 ( 6 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_DMA3 ( 7 + MPC86xx_OPENPIC_IRQ_OFFSET) + +/* no 10,11 */ +#define MPC86xx_IRQ_UART2 (12 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC1_TX (13 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC1_RX (14 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC3_TX (15 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC3_RX (16 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC3_ERROR (17 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC1_ERROR (18 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC2_TX (19 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC2_RX (20 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC4_TX (21 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC4_RX (22 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC4_ERROR (23 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_TSEC2_ERROR (24 + MPC86xx_OPENPIC_IRQ_OFFSET) +/* no 25 */ +#define MPC86xx_IRQ_UART1 (26 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_IIC (27 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_PERFMON (28 + MPC86xx_OPENPIC_IRQ_OFFSET) +/* no 29,30,31 */ +#define MPC86xx_IRQ_SRIO_ERROR (32 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_SRIO_OUT_BELL (33 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_SRIO_IN_BELL (34 + MPC86xx_OPENPIC_IRQ_OFFSET) +/* no 35,36 */ +#define MPC86xx_IRQ_SRIO_OUT_MSG1 (37 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_SRIO_IN_MSG1 (38 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_SRIO_OUT_MSG2 (39 + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_SRIO_IN_MSG2 (40 + MPC86xx_OPENPIC_IRQ_OFFSET) + +/* The 12 external interrupt lines */ +#define MPC86xx_IRQ_EXT_BASE 48 +#define MPC86xx_IRQ_EXT0 (0 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT1 (1 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT2 (2 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT3 (3 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT4 (4 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT5 (5 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT6 (6 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT7 (7 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT8 (8 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT9 (9 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT10 (10 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) +#define MPC86xx_IRQ_EXT11 (11 + MPC86xx_IRQ_EXT_BASE \ + + MPC86xx_OPENPIC_IRQ_OFFSET) + #else /* CONFIG_40x + CONFIG_8xx */ /* * this is the # irq's for all ppc arch's (pmac/chrp/prep) @@ -432,16 +775,11 @@ #define SIU_INT_PC0 ((uint)0x3f + CPM_I #endif /* CONFIG_8260 */ -#endif +#endif /* Whatever way too big #ifdef */ #define NR_MASK_WORDS ((NR_IRQS + 31) / 32) /* pedantic: these are long because they are used with set_bit --RR */ extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; -extern atomic_t ppc_n_lost_interrupts; - -#define virt_irq_create_mapping(x) (x) - -#endif /* * Because many systems have two overlapping names spaces for @@ -480,6 +818,7 @@ static __inline__ int irq_canonicalize(i irq = 9; return irq; } +#endif /* CONFIG_PPC_MERGE */ extern int distribute_irqs; @@ -499,9 +838,8 @@ extern struct thread_info *softirq_ctx[N extern void irq_ctx_init(void); extern void call_do_softirq(struct thread_info *tp); -extern int call___do_IRQ(int irq, struct pt_regs *regs, - struct thread_info *tp); - +extern int call_handle_irq(int irq, void *p1, void *p2, + struct thread_info *tp, void *func); #else #define irq_ctx_init() diff --git a/include/asm-powerpc/irqflags.h b/include/asm-powerpc/irqflags.h new file mode 100644 index 0000000..7970cba --- /dev/null +++ b/include/asm-powerpc/irqflags.h @@ -0,0 +1,31 @@ +/* + * include/asm-powerpc/irqflags.h + * + * IRQ flags handling + * + * This file gets included from lowlevel asm headers too, to provide + * wrapped versions of the local_irq_*() APIs, based on the + * raw_local_irq_*() macros from the lowlevel headers. + */ +#ifndef _ASM_IRQFLAGS_H +#define _ASM_IRQFLAGS_H + +/* + * Get definitions for raw_local_save_flags(x), etc. + */ +#include + +/* + * Do the CPU's IRQ-state tracing from assembly code. We call a + * C function, so save all the C-clobbered registers: + */ +#ifdef CONFIG_TRACE_IRQFLAGS + +#error No support on PowerPC yet for CONFIG_TRACE_IRQFLAGS + +#else +# define TRACE_IRQS_ON +# define TRACE_IRQS_OFF +#endif + +#endif diff --git a/include/asm-powerpc/iseries/iommu.h b/include/asm-powerpc/iseries/iommu.h new file mode 100644 index 0000000..0edbfe1 --- /dev/null +++ b/include/asm-powerpc/iseries/iommu.h @@ -0,0 +1,35 @@ +#ifndef _ASM_POWERPC_ISERIES_IOMMU_H +#define _ASM_POWERPC_ISERIES_IOMMU_H + +/* + * Copyright (C) 2005 Stephen Rothwell, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the: + * Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA + */ + +struct device_node; +struct iommu_table; + +/* Creates table for an individual device node */ +extern void iommu_devnode_init_iSeries(struct device_node *dn); + +/* Get table parameters from HV */ +extern void iommu_table_getparms_iSeries(unsigned long busno, + unsigned char slotno, unsigned char virtbus, + struct iommu_table *tbl); + +#endif /* _ASM_POWERPC_ISERIES_IOMMU_H */ diff --git a/include/asm-powerpc/iseries/iseries_io.h b/include/asm-powerpc/iseries/iseries_io.h index 496aa85..f29009b 100644 --- a/include/asm-powerpc/iseries/iseries_io.h +++ b/include/asm-powerpc/iseries/iseries_io.h @@ -1,7 +1,6 @@ #ifndef _ASM_POWERPC_ISERIES_ISERIES_IO_H #define _ASM_POWERPC_ISERIES_ISERIES_IO_H -#include #ifdef CONFIG_PPC_ISERIES #include diff --git a/include/asm-powerpc/iseries/it_lp_queue.h b/include/asm-powerpc/iseries/it_lp_queue.h index b7c6fc1..284c5a7 100644 --- a/include/asm-powerpc/iseries/it_lp_queue.h +++ b/include/asm-powerpc/iseries/it_lp_queue.h @@ -29,20 +29,20 @@ #include struct HvLpEvent; -#define ITMaxLpQueues 8 +#define IT_LP_MAX_QUEUES 8 -#define NotUsed 0 // Queue will not be used by PLIC -#define DedicatedIo 1 // Queue dedicated to IO processor specified -#define DedicatedLp 2 // Queue dedicated to LP specified -#define Shared 3 // Queue shared for both IO and LP +#define IT_LP_NOT_USED 0 /* Queue will not be used by PLIC */ +#define IT_LP_DEDICATED_IO 1 /* Queue dedicated to IO processor specified */ +#define IT_LP_DEDICATED_LP 2 /* Queue dedicated to LP specified */ +#define IT_LP_SHARED 3 /* Queue shared for both IO and LP */ -#define LpEventStackSize 4096 -#define LpEventMaxSize 256 -#define LpEventAlign 64 +#define IT_LP_EVENT_STACK_SIZE 4096 +#define IT_LP_EVENT_MAX_SIZE 256 +#define IT_LP_EVENT_ALIGN 64 struct hvlpevent_queue { /* - * The xSlicCurEventPtr is the pointer to the next event stack entry + * The hq_current_event is the pointer to the next event stack entry * that will become valid. The OS must peek at this entry to determine * if it is valid. PLIC will set the valid indicator as the very last * store into that entry. @@ -52,23 +52,23 @@ struct hvlpevent_queue { * location again. * * If the event stack fills and there are overflow events, then PLIC - * will set the xPlicOverflowIntPending flag in which case the OS will + * will set the hq_overflow_pending flag in which case the OS will * have to fetch the additional LP events once they have drained the * event stack. * * The first 16-bytes are known by both the OS and PLIC. The remainder * of the cache line is for use by the OS. */ - u8 xPlicOverflowIntPending;// 0x00 Overflow events are pending - u8 xPlicStatus; // 0x01 DedicatedIo or DedicatedLp or NotUsed - u16 xSlicLogicalProcIndex; // 0x02 Logical Proc Index for correlation - u8 xPlicRsvd[12]; // 0x04 - char *xSlicCurEventPtr; // 0x10 - char *xSlicLastValidEventPtr; // 0x18 - char *xSlicEventStackPtr; // 0x20 - u8 xIndex; // 0x28 unique sequential index. - u8 xSlicRsvd[3]; // 0x29-2b - spinlock_t lock; + u8 hq_overflow_pending; /* 0x00 Overflow events are pending */ + u8 hq_status; /* 0x01 DedicatedIo or DedicatedLp or NotUsed */ + u16 hq_proc_index; /* 0x02 Logical Proc Index for correlation */ + u8 hq_reserved1[12]; /* 0x04 */ + char *hq_current_event; /* 0x10 */ + char *hq_last_event; /* 0x18 */ + char *hq_event_stack; /* 0x20 */ + u8 hq_index; /* 0x28 unique sequential index. */ + u8 hq_reserved2[3]; /* 0x29-2b */ + spinlock_t hq_lock; }; extern struct hvlpevent_queue hvlpevent_queue; diff --git a/include/asm-powerpc/kdebug.h b/include/asm-powerpc/kdebug.h index c01786a..532bfee 100644 --- a/include/asm-powerpc/kdebug.h +++ b/include/asm-powerpc/kdebug.h @@ -18,6 +18,8 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); extern struct atomic_notifier_head powerpc_die_chain; /* Grossly misnamed. */ diff --git a/include/asm-powerpc/kdump.h b/include/asm-powerpc/kdump.h index a87aed0..dc1574c 100644 --- a/include/asm-powerpc/kdump.h +++ b/include/asm-powerpc/kdump.h @@ -1,13 +1,40 @@ #ifndef _PPC64_KDUMP_H #define _PPC64_KDUMP_H +/* Kdump kernel runs at 32 MB, change at your peril. */ +#define KDUMP_KERNELBASE 0x2000000 + /* How many bytes to reserve at zero for kdump. The reserve limit should - * be greater or equal to the trampoline's end address. */ + * be greater or equal to the trampoline's end address. + * Reserve to the end of the FWNMI area, see head_64.S */ #define KDUMP_RESERVE_LIMIT 0x8000 +#ifdef CONFIG_CRASH_DUMP + +#define PHYSICAL_START KDUMP_KERNELBASE #define KDUMP_TRAMPOLINE_START 0x0100 #define KDUMP_TRAMPOLINE_END 0x3000 -extern void kdump_setup(void); +#define KDUMP_MIN_TCE_ENTRIES 2048 + +#else /* !CONFIG_CRASH_DUMP */ + +#define PHYSICAL_START 0x0 + +#endif /* CONFIG_CRASH_DUMP */ + +#ifndef __ASSEMBLY__ +#ifdef CONFIG_CRASH_DUMP + +extern void reserve_kdump_trampoline(void); +extern void setup_kdump_trampoline(void); + +#else /* !CONFIG_CRASH_DUMP */ + +static inline void reserve_kdump_trampoline(void) { ; } +static inline void setup_kdump_trampoline(void) { ; } + +#endif /* CONFIG_CRASH_DUMP */ +#endif /* __ASSEMBLY__ */ #endif /* __PPC64_KDUMP_H */ diff --git a/include/asm-powerpc/kexec.h b/include/asm-powerpc/kexec.h index 6a2af2f..8f7fd5c 100644 --- a/include/asm-powerpc/kexec.h +++ b/include/asm-powerpc/kexec.h @@ -31,9 +31,10 @@ #else #define KEXEC_ARCH KEXEC_ARCH_PPC #endif +#ifndef __ASSEMBLY__ + #ifdef CONFIG_KEXEC -#ifndef __ASSEMBLY__ #ifdef __powerpc64__ /* * This function is responsible for capturing register states if coming @@ -111,9 +112,13 @@ #define MAX_NOTE_BYTES 1024 #ifdef __powerpc64__ extern void kexec_smp_wait(void); /* get and clear naca physid, wait for master to copy new code to 0 */ -extern void __init kexec_setup(void); extern int crashing_cpu; extern void crash_send_ipi(void (*crash_ipi_callback)(struct pt_regs *)); +extern cpumask_t cpus_in_sr; +static inline int kexec_sr_activated(int cpu) +{ + return cpu_isset(cpu,cpus_in_sr); +} #endif /* __powerpc64 __ */ struct kimage; @@ -123,8 +128,22 @@ extern int default_machine_kexec_prepare extern void default_machine_crash_shutdown(struct pt_regs *regs); extern void machine_kexec_simple(struct kimage *image); +extern void crash_kexec_secondary(struct pt_regs *regs); +extern int overlaps_crashkernel(unsigned long start, unsigned long size); +extern void reserve_crashkernel(void); + +#else /* !CONFIG_KEXEC */ +static inline int kexec_sr_activated(int cpu) { return 0; } +static inline void crash_kexec_secondary(struct pt_regs *regs) { } + +static inline int overlaps_crashkernel(unsigned long start, unsigned long size) +{ + return 0; +} + +static inline void reserve_crashkernel(void) { ; } -#endif /* ! __ASSEMBLY__ */ #endif /* CONFIG_KEXEC */ +#endif /* ! __ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_KEXEC_H */ diff --git a/include/asm-powerpc/kprobes.h b/include/asm-powerpc/kprobes.h index f466bc8..2d0af52 100644 --- a/include/asm-powerpc/kprobes.h +++ b/include/asm-powerpc/kprobes.h @@ -50,6 +50,8 @@ #define is_trap(instr) (IS_TW(instr) || IS_TWI(instr) || IS_TDI(instr)) #define ARCH_SUPPORTS_KRETPROBES +#define ARCH_INACTIVE_KPROBE_COUNT 1 + void kretprobe_trampoline(void); extern void arch_remove_kprobe(struct kprobe *p); diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h index 0f9254c..c17c137 100644 --- a/include/asm-powerpc/machdep.h +++ b/include/asm-powerpc/machdep.h @@ -9,7 +9,6 @@ #ifdef __KERNEL__ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -82,6 +81,8 @@ #ifdef CONFIG_PPC64 void (*tce_free)(struct iommu_table *tbl, long index, long npages); + unsigned long (*tce_get)(struct iommu_table *tbl, + long index); void (*tce_flush)(struct iommu_table *tbl); void (*iommu_dev_setup)(struct pci_dev *dev); void (*iommu_bus_setup)(struct pci_bus *bus); @@ -96,7 +97,7 @@ #endif /* CONFIG_PPC64 */ void (*show_percpuinfo)(struct seq_file *m, int i); void (*init_IRQ)(void); - int (*get_irq)(struct pt_regs *); + unsigned int (*get_irq)(struct pt_regs *); #ifdef CONFIG_KEXEC void (*kexec_cpu_down)(int crash_shutdown, int secondary); #endif @@ -238,6 +239,11 @@ #ifdef CONFIG_KEXEC */ void (*machine_kexec)(struct kimage *image); #endif /* CONFIG_KEXEC */ + +#ifdef CONFIG_PCI_MSI + int (*enable_msi)(struct pci_dev *pdev); + void (*disable_msi)(struct pci_dev *pdev); +#endif /* CONFIG_PCI_MSI */ }; extern void power4_idle(void); diff --git a/include/asm-powerpc/mmu.h b/include/asm-powerpc/mmu.h index 31f7219..c3fc7a2 100644 --- a/include/asm-powerpc/mmu.h +++ b/include/asm-powerpc/mmu.h @@ -96,6 +96,8 @@ #define HPTE_R_RPN ASM_CONST(0x3fffffff #define HPTE_R_FLAGS ASM_CONST(0x00000000000003ff) #define HPTE_R_PP ASM_CONST(0x0000000000000003) #define HPTE_R_N ASM_CONST(0x0000000000000004) +#define HPTE_R_C ASM_CONST(0x0000000000000080) +#define HPTE_R_R ASM_CONST(0x0000000000000100) /* Values for PP (assumes Ks=0, Kp=1) */ /* pp0 will always be 0 for linux */ @@ -163,6 +165,16 @@ #ifndef __ASSEMBLY__ extern struct mmu_psize_def mmu_psize_defs[MMU_PAGE_COUNT]; extern int mmu_linear_psize; extern int mmu_virtual_psize; +extern int mmu_vmalloc_psize; +extern int mmu_io_psize; + +/* + * If the processor supports 64k normal pages but not 64k cache + * inhibited pages, we have to be prepared to switch processes + * to use 4k pages when they create cache-inhibited mappings. + * If this is the case, mmu_ci_restrictions will be set to 1. + */ +extern int mmu_ci_restrictions; #ifdef CONFIG_HUGETLB_PAGE /* @@ -226,7 +238,6 @@ extern int hash_huge_page(struct mm_stru unsigned long ea, unsigned long vsid, int local, unsigned long trap); -extern void htab_finish_init(void); extern int htab_bolt_mapping(unsigned long vstart, unsigned long vend, unsigned long pstart, unsigned long mode, int psize); @@ -254,6 +265,7 @@ extern long iSeries_hpte_insert(unsigned extern void stabs_alloc(void); extern void slb_initialize(void); +extern void slb_flush_and_rebolt(void); extern void stab_initialize(unsigned long stab); #endif /* __ASSEMBLY__ */ @@ -357,9 +369,12 @@ typedef unsigned long mm_context_id_t; typedef struct { mm_context_id_t id; + u16 user_psize; /* page size index */ + u16 sllp; /* SLB entry page size encoding */ #ifdef CONFIG_HUGETLB_PAGE u16 low_htlb_areas, high_htlb_areas; #endif + unsigned long vdso_base; } mm_context_t; diff --git a/include/asm-powerpc/mmu_context.h b/include/asm-powerpc/mmu_context.h index 1b8a25f..083ac91 100644 --- a/include/asm-powerpc/mmu_context.h +++ b/include/asm-powerpc/mmu_context.h @@ -20,20 +20,18 @@ #include * 2 of the License, or (at your option) any later version. */ -/* - * Getting into a kernel thread, there is no valid user segment, mark - * paca->pgdir NULL so that SLB miss on user addresses will fault - */ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) { -#ifdef CONFIG_PPC_64K_PAGES - get_paca()->pgdir = NULL; -#endif /* CONFIG_PPC_64K_PAGES */ } +/* + * The proto-VSID space has 2^35 - 1 segments available for user mappings. + * Each segment contains 2^28 bytes. Each context maps 2^44 bytes, + * so we can support 2^19-1 contexts (19 == 35 + 28 - 44). + */ #define NO_CONTEXT 0 -#define MAX_CONTEXT (0x100000-1) +#define MAX_CONTEXT ((1UL << 19) - 1) extern int init_new_context(struct task_struct *tsk, struct mm_struct *mm); extern void destroy_context(struct mm_struct *mm); @@ -52,13 +50,8 @@ static inline void switch_mm(struct mm_s cpu_set(smp_processor_id(), next->cpu_vm_mask); /* No need to flush userspace segments if the mm doesnt change */ -#ifdef CONFIG_PPC_64K_PAGES - if (prev == next && get_paca()->pgdir == next->pgd) - return; -#else if (prev == next) return; -#endif /* CONFIG_PPC_64K_PAGES */ #ifdef CONFIG_ALTIVEC if (cpu_has_feature(CPU_FTR_ALTIVEC)) diff --git a/include/asm-powerpc/mmzone.h b/include/asm-powerpc/mmzone.h index 88d70ba..d484ca9 100644 --- a/include/asm-powerpc/mmzone.h +++ b/include/asm-powerpc/mmzone.h @@ -8,7 +8,6 @@ #ifndef _ASM_MMZONE_H_ #define _ASM_MMZONE_H_ #ifdef __KERNEL__ -#include /* * generic non-linear memory support: diff --git a/include/asm-powerpc/mpc86xx.h b/include/asm-powerpc/mpc86xx.h new file mode 100644 index 0000000..f260382 --- /dev/null +++ b/include/asm-powerpc/mpc86xx.h @@ -0,0 +1,42 @@ +/* + * MPC86xx definitions + * + * Author: Jeff Brown + * + * Copyright 2004 Freescale Semiconductor, Inc + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_POWERPC_MPC86xx_H__ +#define __ASM_POWERPC_MPC86xx_H__ + +#include + +#ifdef CONFIG_PPC_86xx + +#define _IO_BASE isa_io_base +#define _ISA_MEM_BASE isa_mem_base +#ifdef CONFIG_PCI +#define PCI_DRAM_OFFSET pci_dram_offset +#else +#define PCI_DRAM_OFFSET 0 +#endif + +#define CPU0_BOOT_RELEASE 0x01000000 +#define CPU1_BOOT_RELEASE 0x02000000 +#define CPU_ALL_RELEASED (CPU0_BOOT_RELEASE | CPU1_BOOT_RELEASE) +#define MCM_PORT_CONFIG_OFFSET 0x1010 + +/* Offset from CCSRBAR */ +#define MPC86xx_OPENPIC_OFFSET (0x40000) +#define MPC86xx_MCM_OFFSET (0x00000) +#define MPC86xx_MCM_SIZE (0x02000) + +#endif /* CONFIG_PPC_86xx */ +#endif /* __ASM_POWERPC_MPC86xx_H__ */ +#endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/mpic.h b/include/asm-powerpc/mpic.h index 6b9e781..eb241c9 100644 --- a/include/asm-powerpc/mpic.h +++ b/include/asm-powerpc/mpic.h @@ -22,6 +22,10 @@ #define MPIC_GREG_GCONF_RESET 0x80000 #define MPIC_GREG_GCONF_8259_PTHROU_DIS 0x20000000 #define MPIC_GREG_GCONF_BASE_MASK 0x000fffff #define MPIC_GREG_GLOBAL_CONF_1 0x00030 +#define MPIC_GREG_GLOBAL_CONF_1_SIE 0x08000000 +#define MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK 0x70000000 +#define MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO(r) \ + (((r) << 28) & MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK) #define MPIC_GREG_VENDOR_0 0x00040 #define MPIC_GREG_VENDOR_1 0x00050 #define MPIC_GREG_VENDOR_2 0x00060 @@ -110,9 +114,6 @@ #define MPIC_VEC_TIMER_2 249 #define MPIC_VEC_TIMER_1 248 #define MPIC_VEC_TIMER_0 247 -/* Type definition of the cascade handler */ -typedef int (*mpic_cascade_t)(struct pt_regs *regs, void *data); - #ifdef CONFIG_MPIC_BROKEN_U3 /* Fixup table entry */ struct mpic_irq_fixup @@ -128,10 +129,19 @@ #endif /* CONFIG_MPIC_BROKEN_U3 */ /* The instance data of a given MPIC */ struct mpic { + /* The device node of the interrupt controller */ + struct device_node *of_node; + + /* The remapper for this MPIC */ + struct irq_host *irqhost; + /* The "linux" controller struct */ - hw_irq_controller hc_irq; + struct irq_chip hc_irq; +#ifdef CONFIG_MPIC_BROKEN_U3 + struct irq_chip hc_ht_irq; +#endif #ifdef CONFIG_SMP - hw_irq_controller hc_ipi; + struct irq_chip hc_ipi; #endif const char *name; /* Flags */ @@ -140,20 +150,12 @@ #endif unsigned int isu_size; unsigned int isu_shift; unsigned int isu_mask; - /* Offset of irq vector numbers */ - unsigned int irq_offset; unsigned int irq_count; - /* Offset of ipi vector numbers */ - unsigned int ipi_offset; /* Number of sources */ unsigned int num_sources; /* Number of CPUs */ unsigned int num_cpus; - /* cascade handler */ - mpic_cascade_t cascade; - void *cascade_data; - unsigned int cascade_vec; - /* senses array */ + /* default senses array */ unsigned char *senses; unsigned int senses_count; @@ -209,14 +211,11 @@ #define MPIC_WANTS_RESET 0x00000010 * The values in the array start at the first source of the MPIC, * that is senses[0] correspond to linux irq "irq_offset". */ -extern struct mpic *mpic_alloc(unsigned long phys_addr, +extern struct mpic *mpic_alloc(struct device_node *node, + unsigned long phys_addr, unsigned int flags, unsigned int isu_size, - unsigned int irq_offset, unsigned int irq_count, - unsigned int ipi_offset, - unsigned char *senses, - unsigned int senses_num, const char *name); /* Assign ISUs, to call before mpic_init() @@ -228,22 +227,27 @@ extern struct mpic *mpic_alloc(unsigned extern void mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, unsigned long phys_addr); +/* Set default sense codes + * + * @mpic: controller + * @senses: array of sense codes + * @count: size of above array + * + * Optionally provide an array (indexed on hardware interrupt numbers + * for this MPIC) of default sense codes for the chip. Those are linux + * sense codes IRQ_TYPE_* + * + * The driver gets ownership of the pointer, don't dispose of it or + * anything like that. __init only. + */ +extern void mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count); + + /* Initialize the controller. After this has been called, none of the above * should be called again for this mpic */ extern void mpic_init(struct mpic *mpic); -/* Setup a cascade. Currently, only one cascade is supported this - * way, though you can always do a normal request_irq() and add - * other cascades this way. You should call this _after_ having - * added all the ISUs - * - * @irq_no: "linux" irq number of the cascade (that is offset'ed vector) - * @handler: cascade handler function - */ -extern void mpic_setup_cascade(unsigned int irq_no, mpic_cascade_t hanlder, - void *data); - /* * All of the following functions must only be used after the * ISUs have been assigned and the controller fully initialized @@ -280,12 +284,15 @@ extern void mpic_send_ipi(unsigned int i void smp_mpic_message_pass(int target, int msg); /* Fetch interrupt from a given mpic */ -extern int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs); +extern unsigned int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs); /* This one gets to the primary mpic */ -extern int mpic_get_irq(struct pt_regs *regs); +extern unsigned int mpic_get_irq(struct pt_regs *regs); + +/* Set the EPIC clock ratio */ +void mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio); -/* global mpic for pSeries */ -extern struct mpic *pSeries_mpic; +/* Enable/Disable EPIC serial interrupt mode */ +void mpic_set_serial_int(struct mpic *mpic, int enable); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_MPIC_H */ diff --git a/include/asm-powerpc/of_device.h b/include/asm-powerpc/of_device.h index 6249a7c..c5c0b0b 100644 --- a/include/asm-powerpc/of_device.h +++ b/include/asm-powerpc/of_device.h @@ -9,7 +9,7 @@ #include /* * The of_platform_bus_type is a bus type used by drivers that do not * attach to a macio or similar bus but still use OF probing - * mecanism + * mechanism */ extern struct bus_type of_platform_bus_type; diff --git a/include/asm-powerpc/paca.h b/include/asm-powerpc/paca.h index 706325f..2d4585f 100644 --- a/include/asm-powerpc/paca.h +++ b/include/asm-powerpc/paca.h @@ -16,7 +16,6 @@ #ifndef _ASM_POWERPC_PACA_H #define _ASM_POWERPC_PACA_H #ifdef __KERNEL__ -#include #include #include #include @@ -79,11 +78,9 @@ #endif /* CONFIG_PPC_ISERIES */ u64 exmc[10]; /* used for machine checks */ u64 exslb[10]; /* used for SLB/segment table misses * on the linear mapping */ -#ifdef CONFIG_PPC_64K_PAGES - pgd_t *pgdir; -#endif /* CONFIG_PPC_64K_PAGES */ mm_context_t context; + u16 vmalloc_sllp; u16 slb_cache[SLB_CACHE_ENTRIES]; u16 slb_cache_ptr; diff --git a/include/asm-powerpc/page.h b/include/asm-powerpc/page.h index 2fbeceb..fb597b3 100644 --- a/include/asm-powerpc/page.h +++ b/include/asm-powerpc/page.h @@ -11,8 +11,8 @@ #define _ASM_POWERPC_PAGE_H */ #ifdef __KERNEL__ -#include #include +#include /* * On PPC32 page size is 4K. For PPC64 we support either 4K or 64K software @@ -52,13 +52,6 @@ #define PAGE_MASK (~((1 << PAGE_SHI * If you want to test if something's a kernel address, use is_kernel_addr(). */ -#ifdef CONFIG_CRASH_DUMP -/* Kdump kernel runs at 32 MB, change at your peril. */ -#define PHYSICAL_START 0x2000000 -#else -#define PHYSICAL_START 0x0 -#endif - #define PAGE_OFFSET ASM_CONST(CONFIG_KERNEL_START) #define KERNELBASE (PAGE_OFFSET + PHYSICAL_START) @@ -198,6 +191,9 @@ extern void copy_user_page(void *to, voi struct page *p); extern int page_is_ram(unsigned long pfn); +struct vm_area_struct; +extern const char *arch_vma_name(struct vm_area_struct *vma); + #include #endif /* __ASSEMBLY__ */ diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h index 38de92d..4f55573 100644 --- a/include/asm-powerpc/pci-bridge.h +++ b/include/asm-powerpc/pci-bridge.h @@ -6,6 +6,7 @@ #ifndef CONFIG_PPC64 #include #else +#include #include #include @@ -22,6 +23,7 @@ #include struct pci_controller { struct pci_bus *bus; char is_dynamic; + int node; void *arch_data; struct list_head list_node; @@ -78,12 +80,6 @@ #endif struct iommu_table *iommu_table; /* for phb's or bridges */ struct pci_dev *pcidev; /* back-pointer to the pci device */ struct device_node *node; /* back-pointer to the device_node */ -#ifdef CONFIG_PPC_ISERIES - struct list_head Device_List; - int Irq; /* Assigned IRQ */ - int Flags; /* Possible flags(disable/bist)*/ - u8 LogicalSlot; /* Hv Slot Index for Tces */ -#endif u32 config_space[16]; /* saved PCI config space */ }; @@ -171,6 +167,12 @@ #define PCI_PROBE_NONE -1 /* Don't look #define PCI_PROBE_NORMAL 0 /* Do normal PCI probing */ #define PCI_PROBE_DEVTREE 1 /* Instantiate from device tree */ +#ifdef CONFIG_NUMA +#define PHB_SET_NODE(PHB, NODE) ((PHB)->node = (NODE)) +#else +#define PHB_SET_NODE(PHB, NODE) ((PHB)->node = -1) +#endif + #endif /* CONFIG_PPC64 */ #endif /* __KERNEL__ */ #endif diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h index 5d2c9e6..46afd29 100644 --- a/include/asm-powerpc/pci.h +++ b/include/asm-powerpc/pci.h @@ -242,7 +242,7 @@ #if defined(CONFIG_PPC_MULTIPLATFORM) || #define HAVE_ARCH_PCI_RESOURCE_TO_USER extern void pci_resource_to_user(const struct pci_dev *dev, int bar, const struct resource *rsrc, - u64 *start, u64 *end); + resource_size_t *start, resource_size_t *end); #endif /* CONFIG_PPC_MULTIPLATFORM || CONFIG_PPC32 */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/percpu.h b/include/asm-powerpc/percpu.h index 184a7a4..2f2e302 100644 --- a/include/asm-powerpc/percpu.h +++ b/include/asm-powerpc/percpu.h @@ -14,6 +14,7 @@ #include #define __per_cpu_offset(cpu) (paca[cpu].data_offset) #define __my_cpu_offset() get_paca()->data_offset +#define per_cpu_offset(x) (__per_cpu_offset(x)) /* Separate out the type, so (int[3], foo) works. */ #define DEFINE_PER_CPU(type, name) \ @@ -22,6 +23,7 @@ #define DEFINE_PER_CPU(type, name) \ /* var is in discarded region: offset to particular copy we want */ #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset(cpu))) #define __get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __my_cpu_offset())) +#define __raw_get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __my_cpu_offset())) /* A macro to avoid #include hell... */ #define percpu_modcopy(pcpudst, src, size) \ @@ -41,6 +43,7 @@ #define DEFINE_PER_CPU(type, name) \ #define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var +#define __raw_get_cpu_var(var) per_cpu__##var #endif /* SMP */ diff --git a/include/asm-powerpc/pgtable-4k.h b/include/asm-powerpc/pgtable-4k.h index b2e1862..e703615 100644 --- a/include/asm-powerpc/pgtable-4k.h +++ b/include/asm-powerpc/pgtable-4k.h @@ -78,6 +78,8 @@ #define pte_iterate_hashed_subpages(rpte #define pte_iterate_hashed_end() } while(0) +#define pte_pagesize_index(pte) MMU_PAGE_4K + /* * 4-level page tables related bits */ diff --git a/include/asm-powerpc/pgtable-64k.h b/include/asm-powerpc/pgtable-64k.h index 6539150..4b7126c 100644 --- a/include/asm-powerpc/pgtable-64k.h +++ b/include/asm-powerpc/pgtable-64k.h @@ -90,6 +90,8 @@ #define pte_iterate_hashed_subpages(rpte #define pte_iterate_hashed_end() } while(0); } } while(0) +#define pte_pagesize_index(pte) \ + (((pte) & _PAGE_COMBO)? MMU_PAGE_4K: MMU_PAGE_64K) #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-powerpc/pgtable.h b/include/asm-powerpc/pgtable.h index e9f1f46..8dbf5ad 100644 --- a/include/asm-powerpc/pgtable.h +++ b/include/asm-powerpc/pgtable.h @@ -12,7 +12,6 @@ #else */ #ifndef __ASSEMBLY__ -#include #include #include /* For TASK_SIZE */ #include @@ -47,8 +46,8 @@ #endif /* * Define the address range of the vmalloc VM area. */ -#define VMALLOC_START (0xD000000000000000ul) -#define VMALLOC_SIZE (0x80000000000UL) +#define VMALLOC_START ASM_CONST(0xD000000000000000) +#define VMALLOC_SIZE ASM_CONST(0x80000000000) #define VMALLOC_END (VMALLOC_START + VMALLOC_SIZE) /* @@ -413,12 +412,6 @@ static inline void set_pte_at(struct mm_ flush_tlb_pending(); } pte = __pte(pte_val(pte) & ~_PAGE_HPTEFLAGS); - -#ifdef CONFIG_PPC_64K_PAGES - if (mmu_virtual_psize != MMU_PAGE_64K) - pte = __pte(pte_val(pte) | _PAGE_COMBO); -#endif /* CONFIG_PPC_64K_PAGES */ - *ptep = pte; } diff --git a/include/asm-powerpc/pmac_pfunc.h b/include/asm-powerpc/pmac_pfunc.h index cef6130..1330d6a 100644 --- a/include/asm-powerpc/pmac_pfunc.h +++ b/include/asm-powerpc/pmac_pfunc.h @@ -205,7 +205,7 @@ extern void pmf_do_irq(struct pmf_functi * * The args array contains as many arguments as is required by the function, * this is dependent on the function you are calling, unfortunately Apple - * mecanism provides no way to encode that so you have to get it right at + * mechanism provides no way to encode that so you have to get it right at * the call site. Some functions require no args, in which case, you can * pass NULL. * diff --git a/include/asm-powerpc/ppc_asm.h b/include/asm-powerpc/ppc_asm.h index dd1c0a9..a940cfe 100644 --- a/include/asm-powerpc/ppc_asm.h +++ b/include/asm-powerpc/ppc_asm.h @@ -5,7 +5,6 @@ #ifndef _ASM_POWERPC_PPC_ASM_H #define _ASM_POWERPC_PPC_ASM_H #include -#include #include #ifndef __ASSEMBLY__ diff --git a/include/asm-powerpc/processor.h b/include/asm-powerpc/processor.h index 93f83ef..22e54a2 100644 --- a/include/asm-powerpc/processor.h +++ b/include/asm-powerpc/processor.h @@ -149,11 +149,11 @@ #endif unsigned int val; /* Floating point status */ } fpscr; int fpexc_mode; /* floating-point exception mode */ + unsigned int align_ctl; /* alignment handling control */ #ifdef CONFIG_PPC64 unsigned long start_tb; /* Start purr when proc switched in */ unsigned long accum_tb; /* Total accumilated purr for process */ #endif - unsigned long vdso_base; /* base of the vDSO library */ unsigned long dabr; /* Data address breakpoint register */ #ifdef CONFIG_ALTIVEC /* Complete AltiVec register set */ @@ -190,7 +190,7 @@ #define INIT_THREAD { \ .fs = KERNEL_DS, \ .fpr = {0}, \ .fpscr = { .val = 0, }, \ - .fpexc_mode = MSR_FE0|MSR_FE1, \ + .fpexc_mode = 0, \ } #endif @@ -212,6 +212,18 @@ #define SET_FPEXC_CTL(tsk, val) set_fpex extern int get_fpexc_mode(struct task_struct *tsk, unsigned long adr); extern int set_fpexc_mode(struct task_struct *tsk, unsigned int val); +#define GET_ENDIAN(tsk, adr) get_endian((tsk), (adr)) +#define SET_ENDIAN(tsk, val) set_endian((tsk), (val)) + +extern int get_endian(struct task_struct *tsk, unsigned long adr); +extern int set_endian(struct task_struct *tsk, unsigned int val); + +#define GET_UNALIGN_CTL(tsk, adr) get_unalign_ctl((tsk), (adr)) +#define SET_UNALIGN_CTL(tsk, val) set_unalign_ctl((tsk), (val)) + +extern int get_unalign_ctl(struct task_struct *tsk, unsigned long adr); +extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val); + static inline unsigned int __unpack_fe01(unsigned long msr_bits) { return ((msr_bits & MSR_FE0) >> 10) | ((msr_bits & MSR_FE1) >> 8); diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h index 97ef1cd..b095a28 100644 --- a/include/asm-powerpc/prom.h +++ b/include/asm-powerpc/prom.h @@ -15,7 +15,6 @@ #ifdef __KERNEL__ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -65,11 +64,6 @@ struct boot_param_header typedef u32 phandle; typedef u32 ihandle; -struct interrupt_info { - int line; - int sense; /* +ve/-ve logic, edge or level, etc. */ -}; - struct property { char *name; int length; @@ -82,8 +76,6 @@ struct device_node { char *type; phandle node; phandle linux_phandle; - int n_intrs; - struct interrupt_info *intrs; char *full_name; struct property *properties; @@ -168,8 +160,8 @@ extern void unflatten_device_tree(void); extern void early_init_devtree(void *); extern int device_is_compatible(struct device_node *device, const char *); extern int machine_is_compatible(const char *compat); -extern unsigned char *get_property(struct device_node *node, const char *name, - int *lenp); +extern void *get_property(struct device_node *node, const char *name, + int *lenp); extern void print_properties(struct device_node *node); extern int prom_n_addr_cells(struct device_node* np); extern int prom_n_size_cells(struct device_node* np); @@ -205,6 +197,15 @@ extern int release_OF_resource(struct de */ +/* Helper to read a big number */ +static inline u64 of_read_number(u32 *cell, int size) +{ + u64 r = 0; + while (size--) + r = (r << 32) | *(cell++); + return r; +} + /* Translate an OF address block into a CPU physical address */ #define OF_BAD_ADDR ((u64)-1) @@ -230,7 +231,94 @@ extern int of_address_to_resource(struct extern int of_pci_address_to_resource(struct device_node *dev, int bar, struct resource *r); +/* Parse the ibm,dma-window property of an OF node into the busno, phys and + * size parameters. + */ +void of_parse_dma_window(struct device_node *dn, unsigned char *dma_window_prop, + unsigned long *busno, unsigned long *phys, unsigned long *size); + extern void kdump_move_device_tree(void); +/* CPU OF node matching */ +struct device_node *of_get_cpu_node(int cpu, unsigned int *thread); + + +/* + * OF interrupt mapping + */ + +/* This structure is returned when an interrupt is mapped. The controller + * field needs to be put() after use + */ + +#define OF_MAX_IRQ_SPEC 4 /* We handle specifiers of at most 4 cells */ + +struct of_irq { + struct device_node *controller; /* Interrupt controller node */ + u32 size; /* Specifier size */ + u32 specifier[OF_MAX_IRQ_SPEC]; /* Specifier copy */ +}; + +/*** + * of_irq_map_init - Initialize the irq remapper + * @flags: flags defining workarounds to enable + * + * Some machines have bugs in the device-tree which require certain workarounds + * to be applied. Call this before any interrupt mapping attempts to enable + * those workarounds. + */ +#define OF_IMAP_OLDWORLD_MAC 0x00000001 +#define OF_IMAP_NO_PHANDLE 0x00000002 + +extern void of_irq_map_init(unsigned int flags); + +/*** + * of_irq_map_raw - Low level interrupt tree parsing + * @parent: the device interrupt parent + * @intspec: interrupt specifier ("interrupts" property of the device) + * @addr: address specifier (start of "reg" property of the device) + * @out_irq: structure of_irq filled by this function + * + * Returns 0 on success and a negative number on error + * + * This function is a low-level interrupt tree walking function. It + * can be used to do a partial walk with synthetized reg and interrupts + * properties, for example when resolving PCI interrupts when no device + * node exist for the parent. + * + */ + +extern int of_irq_map_raw(struct device_node *parent, u32 *intspec, u32 *addr, + struct of_irq *out_irq); + + +/*** + * of_irq_map_one - Resolve an interrupt for a device + * @device: the device whose interrupt is to be resolved + * @index: index of the interrupt to resolve + * @out_irq: structure of_irq filled by this function + * + * This function resolves an interrupt, walking the tree, for a given + * device-tree node. It's the high level pendant to of_irq_map_raw(). + * It also implements the workarounds for OldWolrd Macs. + */ +extern int of_irq_map_one(struct device_node *device, int index, + struct of_irq *out_irq); + +/*** + * of_irq_map_pci - Resolve the interrupt for a PCI device + * @pdev: the device whose interrupt is to be resolved + * @out_irq: structure of_irq filled by this function + * + * This function resolves the PCI interrupt for a given PCI device. If a + * device-node exists for a given pci_dev, it will use normal OF tree + * walking. If not, it will implement standard swizzling and walk up the + * PCI tree until an device-node is found, at which point it will finish + * resolving using the OF tree walking. + */ +struct pci_dev; +extern int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq); + + #endif /* __KERNEL__ */ #endif /* _POWERPC_PROM_H */ diff --git a/include/asm-powerpc/ptrace.h b/include/asm-powerpc/ptrace.h index 9c550b3..dc4cb9c 100644 --- a/include/asm-powerpc/ptrace.h +++ b/include/asm-powerpc/ptrace.h @@ -229,13 +229,13 @@ #endif /* __powerpc64__ */ #define PTRACE_GET_DEBUGREG 25 #define PTRACE_SET_DEBUGREG 26 -#ifdef __powerpc64__ /* Additional PTRACE requests implemented on PowerPC. */ #define PPC_PTRACE_GETREGS 0x99 /* Get GPRs 0 - 31 */ #define PPC_PTRACE_SETREGS 0x98 /* Set GPRs 0 - 31 */ #define PPC_PTRACE_GETFPREGS 0x97 /* Get FPRs 0 - 31 */ #define PPC_PTRACE_SETFPREGS 0x96 /* Set FPRs 0 - 31 */ +#ifdef __powerpc64__ /* Calls to trace a 64bit program from a 32bit program */ #define PPC_PTRACE_PEEKTEXT_3264 0x95 #define PPC_PTRACE_PEEKDATA_3264 0x94 diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h index bd467bf..cf73475 100644 --- a/include/asm-powerpc/reg.h +++ b/include/asm-powerpc/reg.h @@ -93,8 +93,8 @@ #define MSR_RI __MASK(MSR_RI_LG) /* Rec #define MSR_LE __MASK(MSR_LE_LG) /* Little Endian */ #ifdef CONFIG_PPC64 -#define MSR_ MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF -#define MSR_KERNEL MSR_ | MSR_SF | MSR_HV +#define MSR_ MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF |MSR_HV +#define MSR_KERNEL MSR_ | MSR_SF #define MSR_USER32 MSR_ | MSR_PR | MSR_EE #define MSR_USER64 MSR_USER32 | MSR_SF @@ -153,7 +153,7 @@ #define CTRL_RUNLATCH 0x1 #define SPRN_DABR 0x3F5 /* Data Address Breakpoint Register */ #define DABR_TRANSLATION (1UL << 2) #define SPRN_DAR 0x013 /* Data Address Register */ -#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ +#define SPRN_DSISR 0x012 /* Data Storage Interrupt Status Register */ #define DSISR_NOHPTE 0x40000000 /* no translation found */ #define DSISR_PROTFAULT 0x08000000 /* protection fault */ #define DSISR_ISSTORE 0x02000000 /* access was a store */ @@ -258,16 +258,16 @@ #define SPRN_HID2 0x3F8 /* Hardware Imp #define SPRN_IABR 0x3F2 /* Instruction Address Breakpoint Register */ #define SPRN_HID4 0x3F4 /* 970 HID4 */ #define SPRN_HID5 0x3F6 /* 970 HID5 */ -#define SPRN_HID6 0x3F9 /* BE HID 6 */ -#define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */ -#define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */ -#define SPRN_TSC_CELL 0x399 /* Thread switch control on Cell */ -#define TSC_CELL_DEC_ENABLE_0 0x400000 /* Decrementer Interrupt */ -#define TSC_CELL_DEC_ENABLE_1 0x200000 /* Decrementer Interrupt */ -#define TSC_CELL_EE_ENABLE 0x100000 /* External Interrupt */ -#define TSC_CELL_EE_BOOST 0x080000 /* External Interrupt Boost */ -#define SPRN_TSC 0x3FD /* Thread switch control on others */ -#define SPRN_TST 0x3FC /* Thread switch timeout on others */ +#define SPRN_HID6 0x3F9 /* BE HID 6 */ +#define HID6_LB (0x0F<<12) /* Concurrent Large Page Modes */ +#define HID6_DLP (1<<20) /* Disable all large page modes (4K only) */ +#define SPRN_TSC_CELL 0x399 /* Thread switch control on Cell */ +#define TSC_CELL_DEC_ENABLE_0 0x400000 /* Decrementer Interrupt */ +#define TSC_CELL_DEC_ENABLE_1 0x200000 /* Decrementer Interrupt */ +#define TSC_CELL_EE_ENABLE 0x100000 /* External Interrupt */ +#define TSC_CELL_EE_BOOST 0x080000 /* External Interrupt Boost */ +#define SPRN_TSC 0x3FD /* Thread switch control on others */ +#define SPRN_TST 0x3FC /* Thread switch timeout on others */ #if !defined(SPRN_IAC1) && !defined(SPRN_IAC2) #define SPRN_IAC1 0x3F4 /* Instruction Address Compare 1 */ #define SPRN_IAC2 0x3F5 /* Instruction Address Compare 2 */ @@ -362,7 +362,7 @@ #define SPRN_PIR 0x3FF /* Processor Iden #endif #define SPRN_PTEHI 0x3D5 /* 981 7450 PTE HI word (S/W TLB load) */ #define SPRN_PTELO 0x3D6 /* 982 7450 PTE LO word (S/W TLB load) */ -#define SPRN_PURR 0x135 /* Processor Utilization of Resources Reg */ +#define SPRN_PURR 0x135 /* Processor Utilization of Resources Reg */ #define SPRN_PVR 0x11F /* Processor Version Register */ #define SPRN_RPA 0x3D6 /* Required Physical Address Register */ #define SPRN_SDA 0x3BF /* Sampled Data Address Register */ @@ -386,6 +386,8 @@ #define SRR1_WAKEEE 0x00200000 /* Ext #define SRR1_WAKEMT 0x00280000 /* mtctrl */ #define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */ #define SRR1_WAKETHERM 0x00100000 /* Thermal management interrupt */ +#define SPRN_HSRR0 0x13A /* Save/Restore Register 0 */ +#define SPRN_HSRR1 0x13B /* Save/Restore Register 1 */ #ifndef SPRN_SVR #define SPRN_SVR 0x11E /* System Version Register */ @@ -443,6 +445,10 @@ #define SPRN_MMCRA 0x312 #define MMCRA_SIHV 0x10000000UL /* state of MSR HV when SIAR set */ #define MMCRA_SIPR 0x08000000UL /* state of MSR PR when SIAR set */ #define MMCRA_SAMPLE_ENABLE 0x00000001UL /* enable sampling */ +#define POWER6_MMCRA_SIHV 0x0000040000000000ULL +#define POWER6_MMCRA_SIPR 0x0000020000000000ULL +#define POWER6_MMCRA_THRM 0x00000020UL +#define POWER6_MMCRA_OTHER 0x0000000EUL #define SPRN_PMC1 787 #define SPRN_PMC2 788 #define SPRN_PMC3 789 @@ -495,6 +501,19 @@ #define MMCR0_PMC2_ITLB 0x7 #define MMCR0_PMC2_LOADMISSTIME 0x5 #endif +/* + * An mtfsf instruction with the L bit set. On CPUs that support this a + * full 64bits of FPSCR is restored and on other CPUs it is ignored. + * + * Until binutils gets the new form of mtfsf, hardwire the instruction. + */ +#ifdef CONFIG_PPC64 +#define MTFSF_L(REG) \ + .long (0xfc00058e | ((0xff) << 17) | ((REG) << 11) | (1 << 25)) +#else +#define MTFSF_L(REG) mtfsf 0xff, (REG) +#endif + /* Processor Version Register (PVR) field extraction */ #define PVR_VER(pvr) (((pvr) >> 16) & 0xFFFF) /* Version field */ @@ -559,20 +578,20 @@ #define PVR_8260 PVR_8240 /* 64-bit processors */ /* XXX the prefix should be PVR_, we'll do a global sweep to fix it one day */ -#define PV_NORTHSTAR 0x0033 -#define PV_PULSAR 0x0034 -#define PV_POWER4 0x0035 -#define PV_ICESTAR 0x0036 -#define PV_SSTAR 0x0037 -#define PV_POWER4p 0x0038 +#define PV_NORTHSTAR 0x0033 +#define PV_PULSAR 0x0034 +#define PV_POWER4 0x0035 +#define PV_ICESTAR 0x0036 +#define PV_SSTAR 0x0037 +#define PV_POWER4p 0x0038 #define PV_970 0x0039 -#define PV_POWER5 0x003A +#define PV_POWER5 0x003A #define PV_POWER5p 0x003B #define PV_970FX 0x003C -#define PV_630 0x0040 -#define PV_630p 0x0041 -#define PV_970MP 0x0044 -#define PV_BE 0x0070 +#define PV_630 0x0040 +#define PV_630p 0x0041 +#define PV_970MP 0x0044 +#define PV_BE 0x0070 /* * Number of entries in the SLB. If this ever changes we should handle diff --git a/include/asm-powerpc/rtas.h b/include/asm-powerpc/rtas.h index f43c683..a33c6ac 100644 --- a/include/asm-powerpc/rtas.h +++ b/include/asm-powerpc/rtas.h @@ -24,6 +24,7 @@ #define RTAS_INSTANTIATE_MAX (1UL<<30) / #define RTAS_RMOBUF_MAX (64 * 1024) /* RTAS return status codes */ +#define RTAS_NOT_SUSPENDABLE -9004 #define RTAS_BUSY -2 /* RTAS Busy */ #define RTAS_EXTENDED_DELAY_MIN 9900 #define RTAS_EXTENDED_DELAY_MAX 9905 @@ -177,12 +178,11 @@ extern unsigned long rtas_get_boot_time( extern void rtas_get_rtc_time(struct rtc_time *rtc_time); extern int rtas_set_rtc_time(struct rtc_time *rtc_time); -/* Given an RTAS status code of 9900..9905 compute the hinted delay */ -unsigned int rtas_extended_busy_delay_time(int status); -static inline int rtas_is_extended_busy(int status) -{ - return status >= 9900 && status <= 9909; -} +extern unsigned int rtas_busy_delay_time(int status); +extern unsigned int rtas_busy_delay(int status); + +extern int early_init_dt_scan_rtas(unsigned long node, + const char *uname, int depth, void *data); extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal); diff --git a/include/asm-powerpc/rwsem.h b/include/asm-powerpc/rwsem.h index 2c2fe96..e929145 100644 --- a/include/asm-powerpc/rwsem.h +++ b/include/asm-powerpc/rwsem.h @@ -28,24 +28,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_AC #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; -#endif }; -/* - * initialisation - */ -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 -#else -#define __RWSEM_DEBUG_INIT /* */ -#endif - #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEBUG_INIT } + LIST_HEAD_INIT((name).wait_list) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) @@ -60,9 +47,6 @@ static inline void init_rwsem(struct rw_ sem->count = RWSEM_UNLOCKED_VALUE; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif } /* diff --git a/include/asm-powerpc/signal.h b/include/asm-powerpc/signal.h index a4d8f86..a8c7bab 100644 --- a/include/asm-powerpc/signal.h +++ b/include/asm-powerpc/signal.h @@ -63,7 +63,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK is not currently supported, but will allow sigaltstack(2). - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -83,7 +82,6 @@ #define SA_RESETHAND 0x80000000U #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000u /* dummy -- ignored */ #define SA_RESTORER 0x04000000U diff --git a/include/asm-powerpc/smp.h b/include/asm-powerpc/smp.h index 4a716f7..068f119 100644 --- a/include/asm-powerpc/smp.h +++ b/include/asm-powerpc/smp.h @@ -17,7 +17,6 @@ #ifndef _ASM_POWERPC_SMP_H #define _ASM_POWERPC_SMP_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-powerpc/smu.h b/include/asm-powerpc/smu.h index 2dc9363..51e65fc 100644 --- a/include/asm-powerpc/smu.h +++ b/include/asm-powerpc/smu.h @@ -5,7 +5,6 @@ #define _SMU_H * Definitions for talking to the SMU chip in newer G5 PowerMacs */ #ifdef __KERNEL__ -#include #include #endif #include diff --git a/include/asm-powerpc/socket.h b/include/asm-powerpc/socket.h index e4b8177..c8b1da5 100644 --- a/include/asm-powerpc/socket.h +++ b/include/asm-powerpc/socket.h @@ -55,5 +55,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h index 7cfcff3..c02d105 100644 --- a/include/asm-powerpc/spu.h +++ b/include/asm-powerpc/spu.h @@ -24,9 +24,8 @@ #ifndef _SPU_H #define _SPU_H #ifdef __KERNEL__ -#include -#include #include +#include #define LS_SIZE (256 * 1024) #define LS_ADDR_MASK (LS_SIZE - 1) @@ -118,12 +117,12 @@ struct spu { struct list_head sched_list; int number; int nid; + unsigned int irqs[3]; u32 isrc; u32 node; u64 flags; u64 dar; u64 dsisr; - struct kref kref; size_t ls_size; unsigned int slb_replace; struct mm_struct *mm; @@ -135,7 +134,6 @@ struct spu { int class_0_pending; spinlock_t register_lock; - u32 stop_code; void (* wbox_callback)(struct spu *spu); void (* ibox_callback)(struct spu *spu); void (* stop_callback)(struct spu *spu); @@ -144,6 +142,8 @@ struct spu { char irq_c0[8]; char irq_c1[8]; char irq_c2[8]; + + struct sys_device sysdev; }; struct spu *spu_alloc(void); @@ -182,29 +182,6 @@ static inline void unregister_spu_syscal #endif /* MODULE */ -/* access to priv1 registers */ -void spu_int_mask_and(struct spu *spu, int class, u64 mask); -void spu_int_mask_or(struct spu *spu, int class, u64 mask); -void spu_int_mask_set(struct spu *spu, int class, u64 mask); -u64 spu_int_mask_get(struct spu *spu, int class); -void spu_int_stat_clear(struct spu *spu, int class, u64 stat); -u64 spu_int_stat_get(struct spu *spu, int class); -void spu_int_route_set(struct spu *spu, u64 route); -u64 spu_mfc_dar_get(struct spu *spu); -u64 spu_mfc_dsisr_get(struct spu *spu); -void spu_mfc_dsisr_set(struct spu *spu, u64 dsisr); -void spu_mfc_sdr_set(struct spu *spu, u64 sdr); -void spu_mfc_sr1_set(struct spu *spu, u64 sr1); -u64 spu_mfc_sr1_get(struct spu *spu); -void spu_mfc_tclass_id_set(struct spu *spu, u64 tclass_id); -u64 spu_mfc_tclass_id_get(struct spu *spu); -void spu_tlb_invalidate(struct spu *spu); -void spu_resource_allocation_groupID_set(struct spu *spu, u64 id); -u64 spu_resource_allocation_groupID_get(struct spu *spu); -void spu_resource_allocation_enable_set(struct spu *spu, u64 enable); -u64 spu_resource_allocation_enable_get(struct spu *spu); - - /* * This defines the Local Store, Problem Area and Privlege Area of an SPU. */ diff --git a/include/asm-powerpc/spu_csa.h b/include/asm-powerpc/spu_csa.h index ba18d7d..964c2d3 100644 --- a/include/asm-powerpc/spu_csa.h +++ b/include/asm-powerpc/spu_csa.h @@ -86,10 +86,18 @@ struct spu_lscsa { struct spu_reg128 event_mask; struct spu_reg128 srr0; struct spu_reg128 stopped_status; - struct spu_reg128 pad[119]; /* 'ls' must be page-aligned. */ - unsigned char ls[LS_SIZE]; + + /* + * 'ls' must be page-aligned on all configurations. + * Since we don't want to rely on having the spu-gcc + * installed to build the kernel and this structure + * is used in the SPU-side code, make it 64k-page + * aligned for now. + */ + unsigned char ls[LS_SIZE] __attribute__((aligned(65536))); }; +#ifndef __SPU__ /* * struct spu_problem_collapsed - condensed problem state area, w/o pads. */ @@ -250,6 +258,7 @@ extern int spu_restore(struct spu_state extern int spu_switch(struct spu_state *prev, struct spu_state *new, struct spu *spu); +#endif /* !__SPU__ */ #endif /* __KERNEL__ */ #endif /* !__ASSEMBLY__ */ #endif /* _SPU_CSA_H_ */ diff --git a/include/asm-powerpc/spu_priv1.h b/include/asm-powerpc/spu_priv1.h new file mode 100644 index 0000000..300c458 --- /dev/null +++ b/include/asm-powerpc/spu_priv1.h @@ -0,0 +1,182 @@ +/* + * Defines an spu hypervisor abstraction layer. + * + * Copyright 2006 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(_SPU_PRIV1_H) +#define _SPU_PRIV1_H +#if defined(__KERNEL__) + +struct spu; + +/* access to priv1 registers */ + +struct spu_priv1_ops +{ + void (*int_mask_and) (struct spu *spu, int class, u64 mask); + void (*int_mask_or) (struct spu *spu, int class, u64 mask); + void (*int_mask_set) (struct spu *spu, int class, u64 mask); + u64 (*int_mask_get) (struct spu *spu, int class); + void (*int_stat_clear) (struct spu *spu, int class, u64 stat); + u64 (*int_stat_get) (struct spu *spu, int class); + void (*cpu_affinity_set) (struct spu *spu, int cpu); + u64 (*mfc_dar_get) (struct spu *spu); + u64 (*mfc_dsisr_get) (struct spu *spu); + void (*mfc_dsisr_set) (struct spu *spu, u64 dsisr); + void (*mfc_sdr_set) (struct spu *spu, u64 sdr); + void (*mfc_sr1_set) (struct spu *spu, u64 sr1); + u64 (*mfc_sr1_get) (struct spu *spu); + void (*mfc_tclass_id_set) (struct spu *spu, u64 tclass_id); + u64 (*mfc_tclass_id_get) (struct spu *spu); + void (*tlb_invalidate) (struct spu *spu); + void (*resource_allocation_groupID_set) (struct spu *spu, u64 id); + u64 (*resource_allocation_groupID_get) (struct spu *spu); + void (*resource_allocation_enable_set) (struct spu *spu, u64 enable); + u64 (*resource_allocation_enable_get) (struct spu *spu); +}; + +extern const struct spu_priv1_ops* spu_priv1_ops; + +static inline void +spu_int_mask_and (struct spu *spu, int class, u64 mask) +{ + spu_priv1_ops->int_mask_and(spu, class, mask); +} + +static inline void +spu_int_mask_or (struct spu *spu, int class, u64 mask) +{ + spu_priv1_ops->int_mask_or(spu, class, mask); +} + +static inline void +spu_int_mask_set (struct spu *spu, int class, u64 mask) +{ + spu_priv1_ops->int_mask_set(spu, class, mask); +} + +static inline u64 +spu_int_mask_get (struct spu *spu, int class) +{ + return spu_priv1_ops->int_mask_get(spu, class); +} + +static inline void +spu_int_stat_clear (struct spu *spu, int class, u64 stat) +{ + spu_priv1_ops->int_stat_clear(spu, class, stat); +} + +static inline u64 +spu_int_stat_get (struct spu *spu, int class) +{ + return spu_priv1_ops->int_stat_get (spu, class); +} + +static inline void +spu_cpu_affinity_set (struct spu *spu, int cpu) +{ + spu_priv1_ops->cpu_affinity_set(spu, cpu); +} + +static inline u64 +spu_mfc_dar_get (struct spu *spu) +{ + return spu_priv1_ops->mfc_dar_get(spu); +} + +static inline u64 +spu_mfc_dsisr_get (struct spu *spu) +{ + return spu_priv1_ops->mfc_dsisr_get(spu); +} + +static inline void +spu_mfc_dsisr_set (struct spu *spu, u64 dsisr) +{ + spu_priv1_ops->mfc_dsisr_set(spu, dsisr); +} + +static inline void +spu_mfc_sdr_set (struct spu *spu, u64 sdr) +{ + spu_priv1_ops->mfc_sdr_set(spu, sdr); +} + +static inline void +spu_mfc_sr1_set (struct spu *spu, u64 sr1) +{ + spu_priv1_ops->mfc_sr1_set(spu, sr1); +} + +static inline u64 +spu_mfc_sr1_get (struct spu *spu) +{ + return spu_priv1_ops->mfc_sr1_get(spu); +} + +static inline void +spu_mfc_tclass_id_set (struct spu *spu, u64 tclass_id) +{ + spu_priv1_ops->mfc_tclass_id_set(spu, tclass_id); +} + +static inline u64 +spu_mfc_tclass_id_get (struct spu *spu) +{ + return spu_priv1_ops->mfc_tclass_id_get(spu); +} + +static inline void +spu_tlb_invalidate (struct spu *spu) +{ + spu_priv1_ops->tlb_invalidate(spu); +} + +static inline void +spu_resource_allocation_groupID_set (struct spu *spu, u64 id) +{ + spu_priv1_ops->resource_allocation_groupID_set(spu, id); +} + +static inline u64 +spu_resource_allocation_groupID_get (struct spu *spu) +{ + return spu_priv1_ops->resource_allocation_groupID_get(spu); +} + +static inline void +spu_resource_allocation_enable_set (struct spu *spu, u64 enable) +{ + spu_priv1_ops->resource_allocation_enable_set(spu, enable); +} + +static inline u64 +spu_resource_allocation_enable_get (struct spu *spu) +{ + return spu_priv1_ops->resource_allocation_enable_get(spu); +} + +/* The declarations folowing are put here for convenience + * and only intended to be used by the platform setup code + * for initializing spu_priv1_ops. + */ + +extern const struct spu_priv1_ops spu_priv1_mmio_ops; + +#endif /* __KERNEL__ */ +#endif diff --git a/include/asm-powerpc/systbl.h b/include/asm-powerpc/systbl.h new file mode 100644 index 0000000..eac85ce --- /dev/null +++ b/include/asm-powerpc/systbl.h @@ -0,0 +1,306 @@ +/* + * List of powerpc syscalls. For the meaning of the _SPU suffix see + * arch/powerpc/platforms/cell/spu_callbacks.c + */ + +SYSCALL(restart_syscall) +SYSCALL(exit) +PPC_SYS(fork) +SYSCALL_SPU(read) +SYSCALL_SPU(write) +COMPAT_SYS_SPU(open) +SYSCALL_SPU(close) +COMPAT_SYS_SPU(waitpid) +COMPAT_SYS_SPU(creat) +SYSCALL_SPU(link) +SYSCALL_SPU(unlink) +COMPAT_SYS(execve) +SYSCALL_SPU(chdir) +COMPAT_SYS_SPU(time) +SYSCALL_SPU(mknod) +SYSCALL_SPU(chmod) +SYSCALL_SPU(lchown) +SYSCALL(ni_syscall) +OLDSYS(stat) +SYSX_SPU(sys_lseek,ppc32_lseek,sys_lseek) +SYSCALL_SPU(getpid) +COMPAT_SYS(mount) +SYSX(sys_ni_syscall,sys_oldumount,sys_oldumount) +SYSCALL_SPU(setuid) +SYSCALL_SPU(getuid) +COMPAT_SYS_SPU(stime) +COMPAT_SYS(ptrace) +SYSCALL_SPU(alarm) +OLDSYS(fstat) +COMPAT_SYS(pause) +COMPAT_SYS(utime) +SYSCALL(ni_syscall) +SYSCALL(ni_syscall) +COMPAT_SYS_SPU(access) +COMPAT_SYS_SPU(nice) +SYSCALL(ni_syscall) +SYSCALL_SPU(sync) +COMPAT_SYS_SPU(kill) +SYSCALL_SPU(rename) +COMPAT_SYS_SPU(mkdir) +SYSCALL_SPU(rmdir) +SYSCALL_SPU(dup) +SYSCALL_SPU(pipe) +COMPAT_SYS_SPU(times) +SYSCALL(ni_syscall) +SYSCALL_SPU(brk) +SYSCALL_SPU(setgid) +SYSCALL_SPU(getgid) +SYSCALL(signal) +SYSCALL_SPU(geteuid) +SYSCALL_SPU(getegid) +SYSCALL(acct) +SYSCALL(umount) +SYSCALL(ni_syscall) +COMPAT_SYS_SPU(ioctl) +COMPAT_SYS_SPU(fcntl) +SYSCALL(ni_syscall) +COMPAT_SYS_SPU(setpgid) +SYSCALL(ni_syscall) +SYSX(sys_ni_syscall,sys_olduname, sys_olduname) +COMPAT_SYS_SPU(umask) +SYSCALL_SPU(chroot) +SYSCALL(ustat) +SYSCALL_SPU(dup2) +SYSCALL_SPU(getppid) +SYSCALL_SPU(getpgrp) +SYSCALL_SPU(setsid) +SYS32ONLY(sigaction) +SYSCALL_SPU(sgetmask) +COMPAT_SYS_SPU(ssetmask) +SYSCALL_SPU(setreuid) +SYSCALL_SPU(setregid) +SYS32ONLY(sigsuspend) +COMPAT_SYS(sigpending) +COMPAT_SYS_SPU(sethostname) +COMPAT_SYS_SPU(setrlimit) +COMPAT_SYS(old_getrlimit) +COMPAT_SYS_SPU(getrusage) +COMPAT_SYS_SPU(gettimeofday) +COMPAT_SYS_SPU(settimeofday) +COMPAT_SYS_SPU(getgroups) +COMPAT_SYS_SPU(setgroups) +SYSX(sys_ni_syscall,sys_ni_syscall,ppc_select) +SYSCALL_SPU(symlink) +OLDSYS(lstat) +COMPAT_SYS_SPU(readlink) +SYSCALL(uselib) +SYSCALL(swapon) +SYSCALL(reboot) +SYSX(sys_ni_syscall,old32_readdir,old_readdir) +SYSCALL_SPU(mmap) +SYSCALL_SPU(munmap) +SYSCALL_SPU(truncate) +SYSCALL_SPU(ftruncate) +SYSCALL_SPU(fchmod) +SYSCALL_SPU(fchown) +COMPAT_SYS_SPU(getpriority) +COMPAT_SYS_SPU(setpriority) +SYSCALL(ni_syscall) +COMPAT_SYS(statfs) +COMPAT_SYS(fstatfs) +SYSCALL(ni_syscall) +COMPAT_SYS_SPU(socketcall) +COMPAT_SYS_SPU(syslog) +COMPAT_SYS_SPU(setitimer) +COMPAT_SYS_SPU(getitimer) +COMPAT_SYS_SPU(newstat) +COMPAT_SYS_SPU(newlstat) +COMPAT_SYS_SPU(newfstat) +SYSX(sys_ni_syscall,sys_uname,sys_uname) +SYSCALL(ni_syscall) +SYSCALL_SPU(vhangup) +SYSCALL(ni_syscall) +SYSCALL(ni_syscall) +COMPAT_SYS_SPU(wait4) +SYSCALL(swapoff) +COMPAT_SYS_SPU(sysinfo) +COMPAT_SYS(ipc) +SYSCALL_SPU(fsync) +SYS32ONLY(sigreturn) +PPC_SYS(clone) +COMPAT_SYS_SPU(setdomainname) +PPC_SYS_SPU(newuname) +SYSCALL(ni_syscall) +COMPAT_SYS_SPU(adjtimex) +SYSCALL_SPU(mprotect) +SYSX(sys_ni_syscall,compat_sys_sigprocmask,sys_sigprocmask) +SYSCALL(ni_syscall) +SYSCALL(init_module) +SYSCALL(delete_module) +SYSCALL(ni_syscall) +SYSCALL(quotactl) +COMPAT_SYS_SPU(getpgid) +SYSCALL_SPU(fchdir) +SYSCALL_SPU(bdflush) +COMPAT_SYS(sysfs) +SYSX_SPU(ppc64_personality,ppc64_personality,sys_personality) +SYSCALL(ni_syscall) +SYSCALL_SPU(setfsuid) +SYSCALL_SPU(setfsgid) +SYSCALL_SPU(llseek) +COMPAT_SYS_SPU(getdents) +SYSX_SPU(sys_select,ppc32_select,ppc_select) +SYSCALL_SPU(flock) +SYSCALL_SPU(msync) +COMPAT_SYS_SPU(readv) +COMPAT_SYS_SPU(writev) +COMPAT_SYS_SPU(getsid) +SYSCALL_SPU(fdatasync) +COMPAT_SYS(sysctl) +SYSCALL_SPU(mlock) +SYSCALL_SPU(munlock) +SYSCALL_SPU(mlockall) +SYSCALL_SPU(munlockall) +COMPAT_SYS_SPU(sched_setparam) +COMPAT_SYS_SPU(sched_getparam) +COMPAT_SYS_SPU(sched_setscheduler) +COMPAT_SYS_SPU(sched_getscheduler) +SYSCALL_SPU(sched_yield) +COMPAT_SYS_SPU(sched_get_priority_max) +COMPAT_SYS_SPU(sched_get_priority_min) +COMPAT_SYS_SPU(sched_rr_get_interval) +COMPAT_SYS_SPU(nanosleep) +SYSCALL_SPU(mremap) +SYSCALL_SPU(setresuid) +SYSCALL_SPU(getresuid) +SYSCALL(ni_syscall) +SYSCALL_SPU(poll) +COMPAT_SYS(nfsservctl) +SYSCALL_SPU(setresgid) +SYSCALL_SPU(getresgid) +COMPAT_SYS_SPU(prctl) +COMPAT_SYS(rt_sigreturn) +COMPAT_SYS(rt_sigaction) +COMPAT_SYS(rt_sigprocmask) +COMPAT_SYS(rt_sigpending) +COMPAT_SYS(rt_sigtimedwait) +COMPAT_SYS(rt_sigqueueinfo) +COMPAT_SYS(rt_sigsuspend) +COMPAT_SYS_SPU(pread64) +COMPAT_SYS_SPU(pwrite64) +SYSCALL_SPU(chown) +SYSCALL_SPU(getcwd) +SYSCALL_SPU(capget) +SYSCALL_SPU(capset) +COMPAT_SYS(sigaltstack) +SYSX_SPU(sys_sendfile64,compat_sys_sendfile,sys_sendfile) +SYSCALL(ni_syscall) +SYSCALL(ni_syscall) +PPC_SYS(vfork) +COMPAT_SYS_SPU(getrlimit) +COMPAT_SYS_SPU(readahead) +SYS32ONLY(mmap2) +SYS32ONLY(truncate64) +SYS32ONLY(ftruncate64) +SYSX(sys_ni_syscall,sys_stat64,sys_stat64) +SYSX(sys_ni_syscall,sys_lstat64,sys_lstat64) +SYSX(sys_ni_syscall,sys_fstat64,sys_fstat64) +SYSCALL(pciconfig_read) +SYSCALL(pciconfig_write) +SYSCALL(pciconfig_iobase) +SYSCALL(ni_syscall) +SYSCALL_SPU(getdents64) +SYSCALL_SPU(pivot_root) +SYSX(sys_ni_syscall,compat_sys_fcntl64,sys_fcntl64) +SYSCALL_SPU(madvise) +SYSCALL_SPU(mincore) +SYSCALL_SPU(gettid) +SYSCALL_SPU(tkill) +SYSCALL_SPU(setxattr) +SYSCALL_SPU(lsetxattr) +SYSCALL_SPU(fsetxattr) +SYSCALL_SPU(getxattr) +SYSCALL_SPU(lgetxattr) +SYSCALL_SPU(fgetxattr) +SYSCALL_SPU(listxattr) +SYSCALL_SPU(llistxattr) +SYSCALL_SPU(flistxattr) +SYSCALL_SPU(removexattr) +SYSCALL_SPU(lremovexattr) +SYSCALL_SPU(fremovexattr) +COMPAT_SYS_SPU(futex) +COMPAT_SYS_SPU(sched_setaffinity) +COMPAT_SYS_SPU(sched_getaffinity) +SYSCALL(ni_syscall) +SYSCALL(ni_syscall) +SYS32ONLY(sendfile64) +COMPAT_SYS_SPU(io_setup) +SYSCALL_SPU(io_destroy) +COMPAT_SYS_SPU(io_getevents) +COMPAT_SYS_SPU(io_submit) +SYSCALL_SPU(io_cancel) +SYSCALL(set_tid_address) +SYSX_SPU(sys_fadvise64,ppc32_fadvise64,sys_fadvise64) +SYSCALL(exit_group) +SYSX(sys_lookup_dcookie,ppc32_lookup_dcookie,sys_lookup_dcookie) +SYSCALL_SPU(epoll_create) +SYSCALL_SPU(epoll_ctl) +SYSCALL_SPU(epoll_wait) +SYSCALL_SPU(remap_file_pages) +SYSX_SPU(sys_timer_create,compat_sys_timer_create,sys_timer_create) +COMPAT_SYS_SPU(timer_settime) +COMPAT_SYS_SPU(timer_gettime) +SYSCALL_SPU(timer_getoverrun) +SYSCALL_SPU(timer_delete) +COMPAT_SYS_SPU(clock_settime) +COMPAT_SYS_SPU(clock_gettime) +COMPAT_SYS_SPU(clock_getres) +COMPAT_SYS_SPU(clock_nanosleep) +SYSX(ppc64_swapcontext,ppc32_swapcontext,ppc_swapcontext) +COMPAT_SYS_SPU(tgkill) +COMPAT_SYS_SPU(utimes) +COMPAT_SYS_SPU(statfs64) +COMPAT_SYS_SPU(fstatfs64) +SYSX(sys_ni_syscall, ppc_fadvise64_64, ppc_fadvise64_64) +PPC_SYS_SPU(rtas) +OLDSYS(debug_setcontext) +SYSCALL(ni_syscall) +SYSCALL(ni_syscall) +COMPAT_SYS(mbind) +COMPAT_SYS(get_mempolicy) +COMPAT_SYS(set_mempolicy) +COMPAT_SYS(mq_open) +SYSCALL(mq_unlink) +COMPAT_SYS(mq_timedsend) +COMPAT_SYS(mq_timedreceive) +COMPAT_SYS(mq_notify) +COMPAT_SYS(mq_getsetattr) +COMPAT_SYS(kexec_load) +COMPAT_SYS(add_key) +COMPAT_SYS(request_key) +COMPAT_SYS(keyctl) +COMPAT_SYS(waitid) +COMPAT_SYS(ioprio_set) +COMPAT_SYS(ioprio_get) +SYSCALL(inotify_init) +SYSCALL(inotify_add_watch) +SYSCALL(inotify_rm_watch) +SYSCALL(spu_run) +SYSCALL(spu_create) +COMPAT_SYS(pselect6) +COMPAT_SYS(ppoll) +SYSCALL_SPU(unshare) +SYSCALL_SPU(splice) +SYSCALL_SPU(tee) +SYSCALL_SPU(vmsplice) +COMPAT_SYS_SPU(openat) +SYSCALL_SPU(mkdirat) +SYSCALL_SPU(mknodat) +SYSCALL_SPU(fchownat) +COMPAT_SYS_SPU(futimesat) +SYSX_SPU(sys_newfstatat, sys_fstatat64, sys_fstatat64) +SYSCALL_SPU(unlinkat) +SYSCALL_SPU(renameat) +SYSCALL_SPU(linkat) +SYSCALL_SPU(symlinkat) +SYSCALL_SPU(readlinkat) +SYSCALL_SPU(fchmodat) +SYSCALL_SPU(faccessat) +COMPAT_SYS_SPU(get_robust_list) +COMPAT_SYS_SPU(set_robust_list) diff --git a/include/asm-powerpc/tce.h b/include/asm-powerpc/tce.h index 6fa200a..c9483ad 100644 --- a/include/asm-powerpc/tce.h +++ b/include/asm-powerpc/tce.h @@ -35,32 +35,15 @@ #define TCE_SHIFT 12 #define TCE_PAGE_SIZE (1 << TCE_SHIFT) #define TCE_PAGE_FACTOR (PAGE_SHIFT - TCE_SHIFT) - -/* tce_entry - * Used by pSeries (SMP) and iSeries/pSeries LPAR, but there it's - * abstracted so layout is irrelevant. - */ -union tce_entry { - unsigned long te_word; - struct { - unsigned int tb_cacheBits :6; /* Cache hash bits - not used */ - unsigned int tb_rsvd :6; - unsigned long tb_rpn :40; /* Real page number */ - unsigned int tb_valid :1; /* Tce is valid (vb only) */ - unsigned int tb_allio :1; /* Tce is valid for all lps (vb only) */ - unsigned int tb_lpindex :8; /* LpIndex for user of TCE (vb only) */ - unsigned int tb_pciwr :1; /* Write allowed (pci only) */ - unsigned int tb_rdwr :1; /* Read allowed (pci), Write allowed (vb) */ - } te_bits; -#define te_cacheBits te_bits.tb_cacheBits -#define te_rpn te_bits.tb_rpn -#define te_valid te_bits.tb_valid -#define te_allio te_bits.tb_allio -#define te_lpindex te_bits.tb_lpindex -#define te_pciwr te_bits.tb_pciwr -#define te_rdwr te_bits.tb_rdwr -}; - +#define TCE_ENTRY_SIZE 8 /* each TCE is 64 bits */ + +#define TCE_RPN_MASK 0xfffffffffful /* 40-bit RPN (4K pages) */ +#define TCE_RPN_SHIFT 12 +#define TCE_VALID 0x800 /* TCE valid */ +#define TCE_ALLIO 0x400 /* TCE valid for all lpars */ +#define TCE_PCI_WRITE 0x2 /* write from PCI allowed */ +#define TCE_PCI_READ 0x1 /* read from PCI allowed */ +#define TCE_VB_WRITE 0x1 /* write from VB allowed */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_TCE_H */ diff --git a/include/asm-powerpc/thread_info.h b/include/asm-powerpc/thread_info.h index 88b553c..d339e2e 100644 --- a/include/asm-powerpc/thread_info.h +++ b/include/asm-powerpc/thread_info.h @@ -21,7 +21,6 @@ #endif #define THREAD_SIZE (1 << THREAD_SHIFT) #ifndef __ASSEMBLY__ -#include #include #include #include diff --git a/include/asm-powerpc/time.h b/include/asm-powerpc/time.h index 912118d..dcde441 100644 --- a/include/asm-powerpc/time.h +++ b/include/asm-powerpc/time.h @@ -14,13 +14,13 @@ #ifndef __POWERPC_TIME_H #define __POWERPC_TIME_H #ifdef __KERNEL__ -#include #include #include #include -#ifdef CONFIG_PPC64 +#ifdef CONFIG_PPC_ISERIES #include +#include #include #endif @@ -178,7 +178,8 @@ #else #ifdef CONFIG_PPC_ISERIES int cur_dec; - if (get_lppaca()->shared_proc) { + if (firmware_has_feature(FW_FEATURE_ISERIES) && + get_lppaca()->shared_proc) { get_lppaca()->virtual_decr = val; cur_dec = get_dec(); if (cur_dec > val) diff --git a/include/asm-powerpc/timex.h b/include/asm-powerpc/timex.h index c02d15a..3b9a8e7 100644 --- a/include/asm-powerpc/timex.h +++ b/include/asm-powerpc/timex.h @@ -7,7 +7,6 @@ #ifdef __KERNEL__ * PowerPC architecture timex specifications */ -#include #include #define CLOCK_TICK_RATE 1024000 /* Underlying HZ */ diff --git a/include/asm-powerpc/tlb.h b/include/asm-powerpc/tlb.h index 601a53c..4e2a834 100644 --- a/include/asm-powerpc/tlb.h +++ b/include/asm-powerpc/tlb.h @@ -13,7 +13,6 @@ #ifndef _ASM_POWERPC_TLB_H #define _ASM_POWERPC_TLB_H #ifdef __KERNEL__ -#include #ifndef __powerpc64__ #include #endif diff --git a/include/asm-powerpc/tlbflush.h b/include/asm-powerpc/tlbflush.h index a2998ee..93c7d0c 100644 --- a/include/asm-powerpc/tlbflush.h +++ b/include/asm-powerpc/tlbflush.h @@ -17,7 +17,6 @@ #define _ASM_POWERPC_TLBFLUSH_H */ #ifdef __KERNEL__ -#include struct mm_struct; diff --git a/include/asm-powerpc/todc.h b/include/asm-powerpc/todc.h new file mode 100644 index 0000000..60a8c39 --- /dev/null +++ b/include/asm-powerpc/todc.h @@ -0,0 +1,487 @@ +/* + * Definitions for the M48Txx and mc146818 series of Time of day/Real Time + * Clock chips. + * + * Author: Mark A. Greer + * + * 2001 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +/* + * Support for the M48T37/M48T59/.../mc146818 Real Time Clock chips. + * Purpose is to make one generic file that handles all of these chips instead + * of every platform implementing the same code over & over again. + */ + +#ifndef __PPC_KERNEL_TODC_H +#define __PPC_KERNEL_TODC_H + +typedef struct { + uint rtc_type; /* your particular chip */ + + /* + * Following are the addresses of the AS0, AS1, and DATA registers + * of these chips. Note that these are board-specific. + */ + unsigned int nvram_as0; + unsigned int nvram_as1; + unsigned int nvram_data; + + /* + * Define bits to stop external set of regs from changing so + * the chip can be read/written reliably. + */ + unsigned char enable_read; + unsigned char enable_write; + + /* + * Following is the number of AS0 address bits. This is normally + * 8 but some bad hardware routes address lines incorrectly. + */ + int as0_bits; + + int nvram_size; /* Size of NVRAM on chip */ + int sw_flags; /* Software control flags */ + + /* Following are the register offsets for the particular chip */ + int year; + int month; + int day_of_month; + int day_of_week; + int hours; + int minutes; + int seconds; + int control_b; + int control_a; + int watchdog; + int interrupts; + int alarm_date; + int alarm_hour; + int alarm_minutes; + int alarm_seconds; + int century; + int flags; + + /* + * Some RTC chips have their NVRAM buried behind a addr/data pair of + * regs on the first level/clock registers. The following fields + * are the addresses for those addr/data regs. + */ + int nvram_addr_reg; + int nvram_data_reg; +} todc_info_t; + +/* + * Define the types of TODC/RTC variants that are supported in + * arch/ppc/kernel/todc_time.c + * Make a new one of these for any chip somehow differs from what's already + * defined. That way, if you ever need to put in code to touch those + * bits/registers in todc_time.c, you can put it inside an + * 'if (todc_info->rtc_type == TODC_TYPE_XXX)' so you won't break + * anyone else. + */ +#define TODC_TYPE_MK48T35 1 +#define TODC_TYPE_MK48T37 2 +#define TODC_TYPE_MK48T59 3 +#define TODC_TYPE_DS1693 4 /* Dallas DS1693 RTC */ +#define TODC_TYPE_DS1743 5 /* Dallas DS1743 RTC */ +#define TODC_TYPE_DS1746 6 /* Dallas DS1746 RTC */ +#define TODC_TYPE_DS1747 7 /* Dallas DS1747 RTC */ +#define TODC_TYPE_DS1501 8 /* Dallas DS1501 RTC */ +#define TODC_TYPE_DS1643 9 /* Dallas DS1643 RTC */ +#define TODC_TYPE_PC97307 10 /* PC97307 internal RTC */ +#define TODC_TYPE_DS1557 11 /* Dallas DS1557 RTC */ +#define TODC_TYPE_DS17285 12 /* Dallas DS17285 RTC */ +#define TODC_TYPE_DS1553 13 /* Dallas DS1553 RTC */ +#define TODC_TYPE_MC146818 100 /* Leave room for m48txx's */ + +/* + * Bit to clear/set to enable reads/writes to the chip + */ +#define TODC_MK48TXX_CNTL_A_R 0x40 +#define TODC_MK48TXX_CNTL_A_W 0x80 +#define TODC_MK48TXX_DAY_CB 0x80 + +#define TODC_DS1501_CNTL_B_TE 0x80 + +/* + * Define flag bits used by todc routines. + */ +#define TODC_FLAG_2_LEVEL_NVRAM 0x00000001 + +/* + * Define the values for the various RTC's that should to into the todc_info + * table. + * Note: The XXX_NVRAM_SIZE, XXX_NVRAM_ADDR_REG, and XXX_NVRAM_DATA_REG only + * matter if XXX_SW_FLAGS has TODC_FLAG_2_LEVEL_NVRAM set. + */ +#define TODC_TYPE_MK48T35_NVRAM_SIZE 0x7ff8 +#define TODC_TYPE_MK48T35_SW_FLAGS 0 +#define TODC_TYPE_MK48T35_YEAR 0x7fff +#define TODC_TYPE_MK48T35_MONTH 0x7ffe +#define TODC_TYPE_MK48T35_DOM 0x7ffd /* Day of Month */ +#define TODC_TYPE_MK48T35_DOW 0x7ffc /* Day of Week */ +#define TODC_TYPE_MK48T35_HOURS 0x7ffb +#define TODC_TYPE_MK48T35_MINUTES 0x7ffa +#define TODC_TYPE_MK48T35_SECONDS 0x7ff9 +#define TODC_TYPE_MK48T35_CNTL_B 0x7ff9 +#define TODC_TYPE_MK48T35_CNTL_A 0x7ff8 +#define TODC_TYPE_MK48T35_WATCHDOG 0x0000 +#define TODC_TYPE_MK48T35_INTERRUPTS 0x0000 +#define TODC_TYPE_MK48T35_ALARM_DATE 0x0000 +#define TODC_TYPE_MK48T35_ALARM_HOUR 0x0000 +#define TODC_TYPE_MK48T35_ALARM_MINUTES 0x0000 +#define TODC_TYPE_MK48T35_ALARM_SECONDS 0x0000 +#define TODC_TYPE_MK48T35_CENTURY 0x0000 +#define TODC_TYPE_MK48T35_FLAGS 0x0000 +#define TODC_TYPE_MK48T35_NVRAM_ADDR_REG 0 +#define TODC_TYPE_MK48T35_NVRAM_DATA_REG 0 + +#define TODC_TYPE_MK48T37_NVRAM_SIZE 0x7ff0 +#define TODC_TYPE_MK48T37_SW_FLAGS 0 +#define TODC_TYPE_MK48T37_YEAR 0x7fff +#define TODC_TYPE_MK48T37_MONTH 0x7ffe +#define TODC_TYPE_MK48T37_DOM 0x7ffd /* Day of Month */ +#define TODC_TYPE_MK48T37_DOW 0x7ffc /* Day of Week */ +#define TODC_TYPE_MK48T37_HOURS 0x7ffb +#define TODC_TYPE_MK48T37_MINUTES 0x7ffa +#define TODC_TYPE_MK48T37_SECONDS 0x7ff9 +#define TODC_TYPE_MK48T37_CNTL_B 0x7ff9 +#define TODC_TYPE_MK48T37_CNTL_A 0x7ff8 +#define TODC_TYPE_MK48T37_WATCHDOG 0x7ff7 +#define TODC_TYPE_MK48T37_INTERRUPTS 0x7ff6 +#define TODC_TYPE_MK48T37_ALARM_DATE 0x7ff5 +#define TODC_TYPE_MK48T37_ALARM_HOUR 0x7ff4 +#define TODC_TYPE_MK48T37_ALARM_MINUTES 0x7ff3 +#define TODC_TYPE_MK48T37_ALARM_SECONDS 0x7ff2 +#define TODC_TYPE_MK48T37_CENTURY 0x7ff1 +#define TODC_TYPE_MK48T37_FLAGS 0x7ff0 +#define TODC_TYPE_MK48T37_NVRAM_ADDR_REG 0 +#define TODC_TYPE_MK48T37_NVRAM_DATA_REG 0 + +#define TODC_TYPE_MK48T59_NVRAM_SIZE 0x1ff0 +#define TODC_TYPE_MK48T59_SW_FLAGS 0 +#define TODC_TYPE_MK48T59_YEAR 0x1fff +#define TODC_TYPE_MK48T59_MONTH 0x1ffe +#define TODC_TYPE_MK48T59_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_MK48T59_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_MK48T59_HOURS 0x1ffb +#define TODC_TYPE_MK48T59_MINUTES 0x1ffa +#define TODC_TYPE_MK48T59_SECONDS 0x1ff9 +#define TODC_TYPE_MK48T59_CNTL_B 0x1ff9 +#define TODC_TYPE_MK48T59_CNTL_A 0x1ff8 +#define TODC_TYPE_MK48T59_WATCHDOG 0x1fff +#define TODC_TYPE_MK48T59_INTERRUPTS 0x1fff +#define TODC_TYPE_MK48T59_ALARM_DATE 0x1fff +#define TODC_TYPE_MK48T59_ALARM_HOUR 0x1fff +#define TODC_TYPE_MK48T59_ALARM_MINUTES 0x1fff +#define TODC_TYPE_MK48T59_ALARM_SECONDS 0x1fff +#define TODC_TYPE_MK48T59_CENTURY 0x1fff +#define TODC_TYPE_MK48T59_FLAGS 0x1fff +#define TODC_TYPE_MK48T59_NVRAM_ADDR_REG 0 +#define TODC_TYPE_MK48T59_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1501_NVRAM_SIZE 0x100 +#define TODC_TYPE_DS1501_SW_FLAGS TODC_FLAG_2_LEVEL_NVRAM +#define TODC_TYPE_DS1501_YEAR (TODC_TYPE_DS1501_NVRAM_SIZE + 0x06) +#define TODC_TYPE_DS1501_MONTH (TODC_TYPE_DS1501_NVRAM_SIZE + 0x05) +#define TODC_TYPE_DS1501_DOM (TODC_TYPE_DS1501_NVRAM_SIZE + 0x04) +#define TODC_TYPE_DS1501_DOW (TODC_TYPE_DS1501_NVRAM_SIZE + 0x03) +#define TODC_TYPE_DS1501_HOURS (TODC_TYPE_DS1501_NVRAM_SIZE + 0x02) +#define TODC_TYPE_DS1501_MINUTES (TODC_TYPE_DS1501_NVRAM_SIZE + 0x01) +#define TODC_TYPE_DS1501_SECONDS (TODC_TYPE_DS1501_NVRAM_SIZE + 0x00) +#define TODC_TYPE_DS1501_CNTL_B (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0f) +#define TODC_TYPE_DS1501_CNTL_A (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0f) +#define TODC_TYPE_DS1501_WATCHDOG (TODC_TYPE_DS1501_NVRAM_SIZE + 0xff) +#define TODC_TYPE_DS1501_INTERRUPTS (TODC_TYPE_DS1501_NVRAM_SIZE + 0xff) +#define TODC_TYPE_DS1501_ALARM_DATE (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0b) +#define TODC_TYPE_DS1501_ALARM_HOUR (TODC_TYPE_DS1501_NVRAM_SIZE + 0x0a) +#define TODC_TYPE_DS1501_ALARM_MINUTES (TODC_TYPE_DS1501_NVRAM_SIZE + 0x09) +#define TODC_TYPE_DS1501_ALARM_SECONDS (TODC_TYPE_DS1501_NVRAM_SIZE + 0x08) +#define TODC_TYPE_DS1501_CENTURY (TODC_TYPE_DS1501_NVRAM_SIZE + 0x07) +#define TODC_TYPE_DS1501_FLAGS (TODC_TYPE_DS1501_NVRAM_SIZE + 0xff) +#define TODC_TYPE_DS1501_NVRAM_ADDR_REG 0x10 +#define TODC_TYPE_DS1501_NVRAM_DATA_REG 0x13 + +#define TODC_TYPE_DS1553_NVRAM_SIZE 0x1ff0 +#define TODC_TYPE_DS1553_SW_FLAGS 0 +#define TODC_TYPE_DS1553_YEAR 0x1fff +#define TODC_TYPE_DS1553_MONTH 0x1ffe +#define TODC_TYPE_DS1553_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_DS1553_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_DS1553_HOURS 0x1ffb +#define TODC_TYPE_DS1553_MINUTES 0x1ffa +#define TODC_TYPE_DS1553_SECONDS 0x1ff9 +#define TODC_TYPE_DS1553_CNTL_B 0x1ff9 +#define TODC_TYPE_DS1553_CNTL_A 0x1ff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1553_WATCHDOG 0x1ff7 +#define TODC_TYPE_DS1553_INTERRUPTS 0x1ff6 +#define TODC_TYPE_DS1553_ALARM_DATE 0x1ff5 +#define TODC_TYPE_DS1553_ALARM_HOUR 0x1ff4 +#define TODC_TYPE_DS1553_ALARM_MINUTES 0x1ff3 +#define TODC_TYPE_DS1553_ALARM_SECONDS 0x1ff2 +#define TODC_TYPE_DS1553_CENTURY 0x1ff8 +#define TODC_TYPE_DS1553_FLAGS 0x1ff0 +#define TODC_TYPE_DS1553_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1553_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1557_NVRAM_SIZE 0x7fff0 +#define TODC_TYPE_DS1557_SW_FLAGS 0 +#define TODC_TYPE_DS1557_YEAR 0x7ffff +#define TODC_TYPE_DS1557_MONTH 0x7fffe +#define TODC_TYPE_DS1557_DOM 0x7fffd /* Day of Month */ +#define TODC_TYPE_DS1557_DOW 0x7fffc /* Day of Week */ +#define TODC_TYPE_DS1557_HOURS 0x7fffb +#define TODC_TYPE_DS1557_MINUTES 0x7fffa +#define TODC_TYPE_DS1557_SECONDS 0x7fff9 +#define TODC_TYPE_DS1557_CNTL_B 0x7fff9 +#define TODC_TYPE_DS1557_CNTL_A 0x7fff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1557_WATCHDOG 0x7fff7 +#define TODC_TYPE_DS1557_INTERRUPTS 0x7fff6 +#define TODC_TYPE_DS1557_ALARM_DATE 0x7fff5 +#define TODC_TYPE_DS1557_ALARM_HOUR 0x7fff4 +#define TODC_TYPE_DS1557_ALARM_MINUTES 0x7fff3 +#define TODC_TYPE_DS1557_ALARM_SECONDS 0x7fff2 +#define TODC_TYPE_DS1557_CENTURY 0x7fff8 +#define TODC_TYPE_DS1557_FLAGS 0x7fff0 +#define TODC_TYPE_DS1557_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1557_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1643_NVRAM_SIZE 0x1ff8 +#define TODC_TYPE_DS1643_SW_FLAGS 0 +#define TODC_TYPE_DS1643_YEAR 0x1fff +#define TODC_TYPE_DS1643_MONTH 0x1ffe +#define TODC_TYPE_DS1643_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_DS1643_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_DS1643_HOURS 0x1ffb +#define TODC_TYPE_DS1643_MINUTES 0x1ffa +#define TODC_TYPE_DS1643_SECONDS 0x1ff9 +#define TODC_TYPE_DS1643_CNTL_B 0x1ff9 +#define TODC_TYPE_DS1643_CNTL_A 0x1ff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1643_WATCHDOG 0x1fff +#define TODC_TYPE_DS1643_INTERRUPTS 0x1fff +#define TODC_TYPE_DS1643_ALARM_DATE 0x1fff +#define TODC_TYPE_DS1643_ALARM_HOUR 0x1fff +#define TODC_TYPE_DS1643_ALARM_MINUTES 0x1fff +#define TODC_TYPE_DS1643_ALARM_SECONDS 0x1fff +#define TODC_TYPE_DS1643_CENTURY 0x1ff8 +#define TODC_TYPE_DS1643_FLAGS 0x1fff +#define TODC_TYPE_DS1643_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1643_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1693_NVRAM_SIZE 0 /* Not handled yet */ +#define TODC_TYPE_DS1693_SW_FLAGS 0 +#define TODC_TYPE_DS1693_YEAR 0x09 +#define TODC_TYPE_DS1693_MONTH 0x08 +#define TODC_TYPE_DS1693_DOM 0x07 /* Day of Month */ +#define TODC_TYPE_DS1693_DOW 0x06 /* Day of Week */ +#define TODC_TYPE_DS1693_HOURS 0x04 +#define TODC_TYPE_DS1693_MINUTES 0x02 +#define TODC_TYPE_DS1693_SECONDS 0x00 +#define TODC_TYPE_DS1693_CNTL_B 0x0b +#define TODC_TYPE_DS1693_CNTL_A 0x0a +#define TODC_TYPE_DS1693_WATCHDOG 0xff +#define TODC_TYPE_DS1693_INTERRUPTS 0xff +#define TODC_TYPE_DS1693_ALARM_DATE 0x49 +#define TODC_TYPE_DS1693_ALARM_HOUR 0x05 +#define TODC_TYPE_DS1693_ALARM_MINUTES 0x03 +#define TODC_TYPE_DS1693_ALARM_SECONDS 0x01 +#define TODC_TYPE_DS1693_CENTURY 0x48 +#define TODC_TYPE_DS1693_FLAGS 0xff +#define TODC_TYPE_DS1693_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1693_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1743_NVRAM_SIZE 0x1ff8 +#define TODC_TYPE_DS1743_SW_FLAGS 0 +#define TODC_TYPE_DS1743_YEAR 0x1fff +#define TODC_TYPE_DS1743_MONTH 0x1ffe +#define TODC_TYPE_DS1743_DOM 0x1ffd /* Day of Month */ +#define TODC_TYPE_DS1743_DOW 0x1ffc /* Day of Week */ +#define TODC_TYPE_DS1743_HOURS 0x1ffb +#define TODC_TYPE_DS1743_MINUTES 0x1ffa +#define TODC_TYPE_DS1743_SECONDS 0x1ff9 +#define TODC_TYPE_DS1743_CNTL_B 0x1ff9 +#define TODC_TYPE_DS1743_CNTL_A 0x1ff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1743_WATCHDOG 0x1fff +#define TODC_TYPE_DS1743_INTERRUPTS 0x1fff +#define TODC_TYPE_DS1743_ALARM_DATE 0x1fff +#define TODC_TYPE_DS1743_ALARM_HOUR 0x1fff +#define TODC_TYPE_DS1743_ALARM_MINUTES 0x1fff +#define TODC_TYPE_DS1743_ALARM_SECONDS 0x1fff +#define TODC_TYPE_DS1743_CENTURY 0x1ff8 +#define TODC_TYPE_DS1743_FLAGS 0x1fff +#define TODC_TYPE_DS1743_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1743_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1746_NVRAM_SIZE 0x1fff8 +#define TODC_TYPE_DS1746_SW_FLAGS 0 +#define TODC_TYPE_DS1746_YEAR 0x1ffff +#define TODC_TYPE_DS1746_MONTH 0x1fffe +#define TODC_TYPE_DS1746_DOM 0x1fffd /* Day of Month */ +#define TODC_TYPE_DS1746_DOW 0x1fffc /* Day of Week */ +#define TODC_TYPE_DS1746_HOURS 0x1fffb +#define TODC_TYPE_DS1746_MINUTES 0x1fffa +#define TODC_TYPE_DS1746_SECONDS 0x1fff9 +#define TODC_TYPE_DS1746_CNTL_B 0x1fff9 +#define TODC_TYPE_DS1746_CNTL_A 0x1fff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1746_WATCHDOG 0x00000 +#define TODC_TYPE_DS1746_INTERRUPTS 0x00000 +#define TODC_TYPE_DS1746_ALARM_DATE 0x00000 +#define TODC_TYPE_DS1746_ALARM_HOUR 0x00000 +#define TODC_TYPE_DS1746_ALARM_MINUTES 0x00000 +#define TODC_TYPE_DS1746_ALARM_SECONDS 0x00000 +#define TODC_TYPE_DS1746_CENTURY 0x00000 +#define TODC_TYPE_DS1746_FLAGS 0x00000 +#define TODC_TYPE_DS1746_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1746_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS1747_NVRAM_SIZE 0x7fff8 +#define TODC_TYPE_DS1747_SW_FLAGS 0 +#define TODC_TYPE_DS1747_YEAR 0x7ffff +#define TODC_TYPE_DS1747_MONTH 0x7fffe +#define TODC_TYPE_DS1747_DOM 0x7fffd /* Day of Month */ +#define TODC_TYPE_DS1747_DOW 0x7fffc /* Day of Week */ +#define TODC_TYPE_DS1747_HOURS 0x7fffb +#define TODC_TYPE_DS1747_MINUTES 0x7fffa +#define TODC_TYPE_DS1747_SECONDS 0x7fff9 +#define TODC_TYPE_DS1747_CNTL_B 0x7fff9 +#define TODC_TYPE_DS1747_CNTL_A 0x7fff8 /* control_a R/W regs */ +#define TODC_TYPE_DS1747_WATCHDOG 0x00000 +#define TODC_TYPE_DS1747_INTERRUPTS 0x00000 +#define TODC_TYPE_DS1747_ALARM_DATE 0x00000 +#define TODC_TYPE_DS1747_ALARM_HOUR 0x00000 +#define TODC_TYPE_DS1747_ALARM_MINUTES 0x00000 +#define TODC_TYPE_DS1747_ALARM_SECONDS 0x00000 +#define TODC_TYPE_DS1747_CENTURY 0x00000 +#define TODC_TYPE_DS1747_FLAGS 0x00000 +#define TODC_TYPE_DS1747_NVRAM_ADDR_REG 0 +#define TODC_TYPE_DS1747_NVRAM_DATA_REG 0 + +#define TODC_TYPE_DS17285_NVRAM_SIZE (0x1000-0x80) /* 4Kx8 NVRAM (minus RTC regs) */ +#define TODC_TYPE_DS17285_SW_FLAGS TODC_FLAG_2_LEVEL_NVRAM +#define TODC_TYPE_DS17285_SECONDS (TODC_TYPE_DS17285_NVRAM_SIZE + 0x00) +#define TODC_TYPE_DS17285_ALARM_SECONDS (TODC_TYPE_DS17285_NVRAM_SIZE + 0x01) +#define TODC_TYPE_DS17285_MINUTES (TODC_TYPE_DS17285_NVRAM_SIZE + 0x02) +#define TODC_TYPE_DS17285_ALARM_MINUTES (TODC_TYPE_DS17285_NVRAM_SIZE + 0x03) +#define TODC_TYPE_DS17285_HOURS (TODC_TYPE_DS17285_NVRAM_SIZE + 0x04) +#define TODC_TYPE_DS17285_ALARM_HOUR (TODC_TYPE_DS17285_NVRAM_SIZE + 0x05) +#define TODC_TYPE_DS17285_DOW (TODC_TYPE_DS17285_NVRAM_SIZE + 0x06) +#define TODC_TYPE_DS17285_DOM (TODC_TYPE_DS17285_NVRAM_SIZE + 0x07) +#define TODC_TYPE_DS17285_MONTH (TODC_TYPE_DS17285_NVRAM_SIZE + 0x08) +#define TODC_TYPE_DS17285_YEAR (TODC_TYPE_DS17285_NVRAM_SIZE + 0x09) +#define TODC_TYPE_DS17285_CNTL_A (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0A) +#define TODC_TYPE_DS17285_CNTL_B (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0B) +#define TODC_TYPE_DS17285_CNTL_C (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0C) +#define TODC_TYPE_DS17285_CNTL_D (TODC_TYPE_DS17285_NVRAM_SIZE + 0x0D) +#define TODC_TYPE_DS17285_WATCHDOG 0 +#define TODC_TYPE_DS17285_INTERRUPTS 0 +#define TODC_TYPE_DS17285_ALARM_DATE 0 +#define TODC_TYPE_DS17285_CENTURY 0 +#define TODC_TYPE_DS17285_FLAGS 0 +#define TODC_TYPE_DS17285_NVRAM_ADDR_REG 0x50 +#define TODC_TYPE_DS17285_NVRAM_DATA_REG 0x53 + +#define TODC_TYPE_MC146818_NVRAM_SIZE 0 /* XXXX */ +#define TODC_TYPE_MC146818_SW_FLAGS 0 +#define TODC_TYPE_MC146818_YEAR 0x09 +#define TODC_TYPE_MC146818_MONTH 0x08 +#define TODC_TYPE_MC146818_DOM 0x07 /* Day of Month */ +#define TODC_TYPE_MC146818_DOW 0x06 /* Day of Week */ +#define TODC_TYPE_MC146818_HOURS 0x04 +#define TODC_TYPE_MC146818_MINUTES 0x02 +#define TODC_TYPE_MC146818_SECONDS 0x00 +#define TODC_TYPE_MC146818_CNTL_B 0x0a +#define TODC_TYPE_MC146818_CNTL_A 0x0b /* control_a R/W regs */ +#define TODC_TYPE_MC146818_WATCHDOG 0 +#define TODC_TYPE_MC146818_INTERRUPTS 0x0c +#define TODC_TYPE_MC146818_ALARM_DATE 0xff +#define TODC_TYPE_MC146818_ALARM_HOUR 0x05 +#define TODC_TYPE_MC146818_ALARM_MINUTES 0x03 +#define TODC_TYPE_MC146818_ALARM_SECONDS 0x01 +#define TODC_TYPE_MC146818_CENTURY 0xff +#define TODC_TYPE_MC146818_FLAGS 0xff +#define TODC_TYPE_MC146818_NVRAM_ADDR_REG 0 +#define TODC_TYPE_MC146818_NVRAM_DATA_REG 0 + +#define TODC_TYPE_PC97307_NVRAM_SIZE 0 /* No NVRAM? */ +#define TODC_TYPE_PC97307_SW_FLAGS 0 +#define TODC_TYPE_PC97307_YEAR 0x09 +#define TODC_TYPE_PC97307_MONTH 0x08 +#define TODC_TYPE_PC97307_DOM 0x07 /* Day of Month */ +#define TODC_TYPE_PC97307_DOW 0x06 /* Day of Week */ +#define TODC_TYPE_PC97307_HOURS 0x04 +#define TODC_TYPE_PC97307_MINUTES 0x02 +#define TODC_TYPE_PC97307_SECONDS 0x00 +#define TODC_TYPE_PC97307_CNTL_B 0x0a +#define TODC_TYPE_PC97307_CNTL_A 0x0b /* control_a R/W regs */ +#define TODC_TYPE_PC97307_WATCHDOG 0x0c +#define TODC_TYPE_PC97307_INTERRUPTS 0x0d +#define TODC_TYPE_PC97307_ALARM_DATE 0xff +#define TODC_TYPE_PC97307_ALARM_HOUR 0x05 +#define TODC_TYPE_PC97307_ALARM_MINUTES 0x03 +#define TODC_TYPE_PC97307_ALARM_SECONDS 0x01 +#define TODC_TYPE_PC97307_CENTURY 0xff +#define TODC_TYPE_PC97307_FLAGS 0xff +#define TODC_TYPE_PC97307_NVRAM_ADDR_REG 0 +#define TODC_TYPE_PC97307_NVRAM_DATA_REG 0 + +/* + * Define macros to allocate and init the todc_info_t table that will + * be used by the todc_time.c routines. + */ +#define TODC_ALLOC() \ + static todc_info_t todc_info_alloc; \ + todc_info_t *todc_info = &todc_info_alloc; + +#define TODC_INIT(clock_type, as0, as1, data, bits) { \ + todc_info->rtc_type = clock_type; \ + \ + todc_info->nvram_as0 = (unsigned int)(as0); \ + todc_info->nvram_as1 = (unsigned int)(as1); \ + todc_info->nvram_data = (unsigned int)(data); \ + \ + todc_info->as0_bits = (bits); \ + \ + todc_info->nvram_size = clock_type ##_NVRAM_SIZE; \ + todc_info->sw_flags = clock_type ##_SW_FLAGS; \ + \ + todc_info->year = clock_type ##_YEAR; \ + todc_info->month = clock_type ##_MONTH; \ + todc_info->day_of_month = clock_type ##_DOM; \ + todc_info->day_of_week = clock_type ##_DOW; \ + todc_info->hours = clock_type ##_HOURS; \ + todc_info->minutes = clock_type ##_MINUTES; \ + todc_info->seconds = clock_type ##_SECONDS; \ + todc_info->control_b = clock_type ##_CNTL_B; \ + todc_info->control_a = clock_type ##_CNTL_A; \ + todc_info->watchdog = clock_type ##_WATCHDOG; \ + todc_info->interrupts = clock_type ##_INTERRUPTS; \ + todc_info->alarm_date = clock_type ##_ALARM_DATE; \ + todc_info->alarm_hour = clock_type ##_ALARM_HOUR; \ + todc_info->alarm_minutes = clock_type ##_ALARM_MINUTES; \ + todc_info->alarm_seconds = clock_type ##_ALARM_SECONDS; \ + todc_info->century = clock_type ##_CENTURY; \ + todc_info->flags = clock_type ##_FLAGS; \ + \ + todc_info->nvram_addr_reg = clock_type ##_NVRAM_ADDR_REG; \ + todc_info->nvram_data_reg = clock_type ##_NVRAM_DATA_REG; \ +} + +extern todc_info_t *todc_info; + +unsigned char todc_direct_read_val(int addr); +void todc_direct_write_val(int addr, unsigned char val); +unsigned char todc_m48txx_read_val(int addr); +void todc_m48txx_write_val(int addr, unsigned char val); +unsigned char todc_mc146818_read_val(int addr); +void todc_mc146818_write_val(int addr, unsigned char val); + +long todc_time_init(void); +void todc_get_rtc_time(struct rtc_time *); +int todc_set_rtc_time(struct rtc_time *); +void todc_calibrate_decr(void); + +#endif /* __PPC_KERNEL_TODC_H */ diff --git a/include/asm-powerpc/topology.h b/include/asm-powerpc/topology.h index 87362a0..bbc3844 100644 --- a/include/asm-powerpc/topology.h +++ b/include/asm-powerpc/topology.h @@ -2,7 +2,6 @@ #ifndef _ASM_POWERPC_TOPOLOGY_H #define _ASM_POWERPC_TOPOLOGY_H #ifdef __KERNEL__ -#include struct sys_device; struct device_node; @@ -32,8 +31,13 @@ static inline int node_to_first_cpu(int int of_node_to_nid(struct device_node *device); -#define pcibus_to_node(node) (-1) -#define pcibus_to_cpumask(bus) (cpu_online_map) +struct pci_bus; +extern int pcibus_to_node(struct pci_bus *bus); + +#define pcibus_to_cpumask(bus) (pcibus_to_node(bus) == -1 ? \ + CPU_MASK_ALL : \ + node_to_cpumask(pcibus_to_node(bus)) \ + ) /* sched_domains SD_NODE_INIT for PPC64 machines */ #define SD_NODE_INIT (struct sched_domain) { \ @@ -89,5 +93,10 @@ #include #endif /* CONFIG_NUMA */ +#ifdef CONFIG_SMP +#include +#define smt_capable() (cpu_has_feature(CPU_FTR_SMT)) +#endif + #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_TOPOLOGY_H */ diff --git a/include/asm-powerpc/tsi108.h b/include/asm-powerpc/tsi108.h new file mode 100644 index 0000000..c4c278d --- /dev/null +++ b/include/asm-powerpc/tsi108.h @@ -0,0 +1,109 @@ +/* + * include/asm-ppc/tsi108.h + * + * common routine and memory layout for Tundra TSI108(Grendel) host bridge + * memory controller. + * + * Author: Jacob Pan (jacob.pan@freescale.com) + * Alex Bounine (alexandreb@tundra.com) + * 2004 (c) Freescale Semiconductor Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ +#ifndef __PPC_KERNEL_TSI108_H +#define __PPC_KERNEL_TSI108_H + +#include + +/* Size of entire register space */ +#define TSI108_REG_SIZE (0x10000) + +/* Sizes of register spaces for individual blocks */ +#define TSI108_HLP_SIZE 0x1000 +#define TSI108_PCI_SIZE 0x1000 +#define TSI108_CLK_SIZE 0x1000 +#define TSI108_PB_SIZE 0x1000 +#define TSI108_SD_SIZE 0x1000 +#define TSI108_DMA_SIZE 0x1000 +#define TSI108_ETH_SIZE 0x1000 +#define TSI108_I2C_SIZE 0x400 +#define TSI108_MPIC_SIZE 0x400 +#define TSI108_UART0_SIZE 0x200 +#define TSI108_GPIO_SIZE 0x200 +#define TSI108_UART1_SIZE 0x200 + +/* Offsets within Tsi108(A) CSR space for individual blocks */ +#define TSI108_HLP_OFFSET 0x0000 +#define TSI108_PCI_OFFSET 0x1000 +#define TSI108_CLK_OFFSET 0x2000 +#define TSI108_PB_OFFSET 0x3000 +#define TSI108_SD_OFFSET 0x4000 +#define TSI108_DMA_OFFSET 0x5000 +#define TSI108_ETH_OFFSET 0x6000 +#define TSI108_I2C_OFFSET 0x7000 +#define TSI108_MPIC_OFFSET 0x7400 +#define TSI108_UART0_OFFSET 0x7800 +#define TSI108_GPIO_OFFSET 0x7A00 +#define TSI108_UART1_OFFSET 0x7C00 + +/* Tsi108 registers used by common code components */ +#define TSI108_PCI_CSR (0x004) +#define TSI108_PCI_IRP_CFG_CTL (0x180) +#define TSI108_PCI_IRP_STAT (0x184) +#define TSI108_PCI_IRP_ENABLE (0x188) +#define TSI108_PCI_IRP_INTAD (0x18C) + +#define TSI108_PCI_IRP_STAT_P_INT (0x00400000) +#define TSI108_PCI_IRP_ENABLE_P_INT (0x00400000) + +#define TSI108_CG_PWRUP_STATUS (0x234) + +#define TSI108_PB_ISR (0x00C) +#define TSI108_PB_ERRCS (0x404) +#define TSI108_PB_AERR (0x408) + +#define TSI108_PB_ERRCS_ES (1 << 1) +#define TSI108_PB_ISR_PBS_RD_ERR (1 << 8) + +#define TSI108_PCI_CFG_BASE_PHYS (0xfb000000) +#define TSI108_PCI_CFG_SIZE (0x01000000) +/* Global variables */ + +extern u32 tsi108_pci_cfg_base; +/* Exported functions */ + +extern int tsi108_bridge_init(struct pci_controller *hose, uint phys_csr_base); +extern unsigned long tsi108_get_mem_size(void); +extern unsigned long tsi108_get_cpu_clk(void); +extern unsigned long tsi108_get_sdc_clk(void); +extern int tsi108_direct_write_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 val); +extern int tsi108_direct_read_config(struct pci_bus *bus, unsigned int devfn, + int offset, int len, u32 * val); +extern void tsi108_clear_pci_error(u32 pci_cfg_base); + +extern phys_addr_t get_csrbase(void); + +typedef struct { + u32 regs; /* hw registers base address */ + u32 phyregs; /* phy registers base address */ + u16 phy; /* phy address */ + u16 irq_num; /* irq number */ + u8 mac_addr[6]; /* phy mac address */ +} hw_info; + +extern u32 get_vir_csrbase(void); +extern u32 tsi108_csr_vir_base; + +extern inline u32 tsi108_read_reg(u32 reg_offset) +{ + return in_be32((volatile u32 *)(tsi108_csr_vir_base + reg_offset)); +} + +extern inline void tsi108_write_reg(u32 reg_offset, u32 val) +{ + out_be32((volatile u32 *)(tsi108_csr_vir_base + reg_offset), val); +} + +#endif /* __PPC_KERNEL_TSI108_H */ diff --git a/include/asm-powerpc/types.h b/include/asm-powerpc/types.h index baabba9..d6fb56b 100644 --- a/include/asm-powerpc/types.h +++ b/include/asm-powerpc/types.h @@ -64,7 +64,6 @@ #endif #ifndef __ASSEMBLY__ -#include typedef signed char s8; typedef unsigned char u8; diff --git a/include/asm-powerpc/udbg.h b/include/asm-powerpc/udbg.h index 5c4236c..55e5784 100644 --- a/include/asm-powerpc/udbg.h +++ b/include/asm-powerpc/udbg.h @@ -23,7 +23,8 @@ extern int udbg_write(const char *s, int extern int udbg_read(char *buf, int buflen); extern void register_early_udbg_console(void); -extern void udbg_printf(const char *fmt, ...); +extern void udbg_printf(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); extern void udbg_progress(char *s, unsigned short hex); extern void udbg_init_uart(void __iomem *comport, unsigned int speed, @@ -41,7 +42,8 @@ extern void __init udbg_init_debug_lpar( extern void __init udbg_init_pmac_realmode(void); extern void __init udbg_init_maple_realmode(void); extern void __init udbg_init_iseries(void); -extern void __init udbg_init_rtas(void); +extern void __init udbg_init_rtas_panel(void); +extern void __init udbg_init_rtas_console(void); #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_UDBG_H */ diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index edde246..eb66eae 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -324,12 +324,12 @@ #define __NR_faccessat 298 #define __NR_get_robust_list 299 #define __NR_set_robust_list 300 +#ifdef __KERNEL__ + #define __NR_syscalls 301 -#ifdef __KERNEL__ #define __NR__exit __NR_exit #define NR_syscalls __NR_syscalls -#endif #ifndef __ASSEMBLY__ @@ -441,9 +441,7 @@ type name(type1 arg1, type2 arg2, type3 __syscall_nr(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ } -#ifdef __KERNEL__ -#include #include #include #include @@ -499,8 +497,8 @@ #else #define cond_syscall(x) asm(".weak\t." #x "\n\t.set\t." #x ",.sys_ni_syscall") #endif -#endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ +#endif /* __KERNEL__ */ #endif /* _ASM_PPC_UNISTD_H_ */ diff --git a/include/asm-powerpc/vga.h b/include/asm-powerpc/vga.h index f8d350a..a2eac40 100644 --- a/include/asm-powerpc/vga.h +++ b/include/asm-powerpc/vga.h @@ -12,7 +12,6 @@ #ifdef __KERNEL__ #include -#include #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_MDA_CONSOLE) @@ -42,9 +41,9 @@ #endif /* !CONFIG_VGA_CONSOLE && !CONFIG extern unsigned long vgacon_remap_base; #ifdef __powerpc64__ -#define VGA_MAP_MEM(x) ((unsigned long) ioremap((x), 0)) +#define VGA_MAP_MEM(x,s) ((unsigned long) ioremap((x), s)) #else -#define VGA_MAP_MEM(x) (x + vgacon_remap_base) +#define VGA_MAP_MEM(x,s) (x + vgacon_remap_base) #endif #define vga_readb(x) (*(x)) diff --git a/include/asm-powerpc/vio.h b/include/asm-powerpc/vio.h index 0544ece..dc9bd10 100644 --- a/include/asm-powerpc/vio.h +++ b/include/asm-powerpc/vio.h @@ -15,7 +15,6 @@ #ifndef _ASM_POWERPC_VIO_H #define _ASM_POWERPC_VIO_H #ifdef __KERNEL__ -#include #include #include #include @@ -64,32 +63,22 @@ struct vio_driver { struct device_driver driver; }; -struct vio_bus_ops { - int (*match)(const struct vio_device_id *id, const struct vio_dev *dev); - void (*unregister_device)(struct vio_dev *); - void (*release_device)(struct device *); -}; - extern struct dma_mapping_ops vio_dma_ops; extern struct bus_type vio_bus_type; -extern struct vio_dev vio_bus_device; extern int vio_register_driver(struct vio_driver *drv); extern void vio_unregister_driver(struct vio_driver *drv); -extern struct vio_dev * __devinit vio_register_device(struct vio_dev *viodev); extern void __devinit vio_unregister_device(struct vio_dev *dev); -extern int vio_bus_init(struct vio_bus_ops *); - -#ifdef CONFIG_PPC_PSERIES struct device_node; extern struct vio_dev * __devinit vio_register_device_node( struct device_node *node_vdev); -extern struct vio_dev *vio_find_node(struct device_node *vnode); -extern const void *vio_get_attribute(struct vio_dev *vdev, void *which, +extern const void *vio_get_attribute(struct vio_dev *vdev, char *which, int *length); +#ifdef CONFIG_PPC_PSERIES +extern struct vio_dev *vio_find_node(struct device_node *vnode); extern int vio_enable_interrupts(struct vio_dev *dev); extern int vio_disable_interrupts(struct vio_dev *dev); #endif diff --git a/include/asm-ppc/amigahw.h b/include/asm-ppc/amigahw.h index 8c98945..90fd127 100644 --- a/include/asm-ppc/amigahw.h +++ b/include/asm-ppc/amigahw.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef __ASMPPC_AMIGAHW_H #define __ASMPPC_AMIGAHW_H -#include #include #undef CHIP_PHYSADDR diff --git a/include/asm-ppc/bootinfo.h b/include/asm-ppc/bootinfo.h index 93d955c..2ace4a7 100644 --- a/include/asm-ppc/bootinfo.h +++ b/include/asm-ppc/bootinfo.h @@ -9,7 +9,6 @@ #ifdef __KERNEL__ #ifndef _PPC_BOOTINFO_H #define _PPC_BOOTINFO_H -#include #include #if defined(CONFIG_APUS) && !defined(__BOOTER__) diff --git a/include/asm-ppc/commproc.h b/include/asm-ppc/commproc.h index 31f3629..3247bea 100644 --- a/include/asm-ppc/commproc.h +++ b/include/asm-ppc/commproc.h @@ -17,7 +17,6 @@ #ifndef __CPM_8XX__ #define __CPM_8XX__ -#include #include #include diff --git a/include/asm-ppc/floppy.h b/include/asm-ppc/floppy.h index 8ccd4a2..d3963ca 100644 --- a/include/asm-ppc/floppy.h +++ b/include/asm-ppc/floppy.h @@ -96,13 +96,11 @@ static int vdma_get_dma_residue(unsigned static int fd_request_irq(void) { if (can_use_virtual_dma) - return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, - "floppy", NULL); + return request_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy", NULL); else return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT|SA_SAMPLE_RANDOM, - "floppy", NULL); - + IRQF_DISABLED, "floppy", NULL); } static int vdma_dma_setup(char *addr, unsigned long size, int mode, int io) diff --git a/include/asm-ppc/ibm403.h b/include/asm-ppc/ibm403.h index bf6efa0..c9c5d53 100644 --- a/include/asm-ppc/ibm403.h +++ b/include/asm-ppc/ibm403.h @@ -12,7 +12,6 @@ #ifdef __KERNEL__ #ifndef __ASM_IBM403_H__ #define __ASM_IBM403_H__ -#include #if defined(CONFIG_403GCX) diff --git a/include/asm-ppc/ibm44x.h b/include/asm-ppc/ibm44x.h index 3acc382..7818b54 100644 --- a/include/asm-ppc/ibm44x.h +++ b/include/asm-ppc/ibm44x.h @@ -17,7 +17,6 @@ #ifdef __KERNEL__ #ifndef __ASM_IBM44x_H__ #define __ASM_IBM44x_H__ -#include #ifndef NR_BOARD_IRQS #define NR_BOARD_IRQS 0 diff --git a/include/asm-ppc/ibm4xx.h b/include/asm-ppc/ibm4xx.h index 38f9971..cf62b69 100644 --- a/include/asm-ppc/ibm4xx.h +++ b/include/asm-ppc/ibm4xx.h @@ -14,7 +14,6 @@ #ifdef __KERNEL__ #ifndef __ASM_IBM4XX_H__ #define __ASM_IBM4XX_H__ -#include #include #ifdef CONFIG_40x diff --git a/include/asm-ppc/io.h b/include/asm-ppc/io.h index b919d8f..89c6f1b 100644 --- a/include/asm-ppc/io.h +++ b/include/asm-ppc/io.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef _PPC_IO_H #define _PPC_IO_H -#include #include #include diff --git a/include/asm-ppc/machdep.h b/include/asm-ppc/machdep.h index e1a0a7b..da77467 100644 --- a/include/asm-ppc/machdep.h +++ b/include/asm-ppc/machdep.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef _PPC_MACHDEP_H #define _PPC_MACHDEP_H -#include #include #include diff --git a/include/asm-ppc/mmu.h b/include/asm-ppc/mmu.h index 9205db4..14584e5 100644 --- a/include/asm-ppc/mmu.h +++ b/include/asm-ppc/mmu.h @@ -6,7 +6,6 @@ #ifdef __KERNEL__ #ifndef _PPC_MMU_H_ #define _PPC_MMU_H_ -#include #ifndef __ASSEMBLY__ @@ -24,25 +23,18 @@ extern phys_addr_t fixup_bigphys_addr(ph #define PHYS_FMT "%16Lx" #endif -/* Default "unsigned long" context */ -typedef unsigned long mm_context_t; +typedef struct { + unsigned long id; + unsigned long vdso_base; +} mm_context_t; /* Hardware Page Table Entry */ typedef struct _PTE { -#ifdef CONFIG_PPC64BRIDGE - unsigned long long vsid:52; - unsigned long api:5; - unsigned long :5; - unsigned long h:1; - unsigned long v:1; - unsigned long long rpn:52; -#else /* CONFIG_PPC64BRIDGE */ unsigned long v:1; /* Entry is valid */ unsigned long vsid:24; /* Virtual segment identifier */ unsigned long h:1; /* Hash algorithm indicator */ unsigned long api:6; /* Abbreviated page index */ unsigned long rpn:20; /* Real (physical) page number */ -#endif /* CONFIG_PPC64BRIDGE */ unsigned long :3; /* Unused */ unsigned long r:1; /* Referenced */ unsigned long c:1; /* Changed */ @@ -83,11 +75,7 @@ typedef struct _P601_BATU { /* Upper par } P601_BATU; typedef struct _BATU { /* Upper part of BAT (all except 601) */ -#ifdef CONFIG_PPC64BRIDGE - unsigned long long bepi:47; -#else /* CONFIG_PPC64BRIDGE */ unsigned long bepi:15; /* Effective page index (virtual address) */ -#endif /* CONFIG_PPC64BRIDGE */ unsigned long :4; /* Unused */ unsigned long bl:11; /* Block size mask */ unsigned long vs:1; /* Supervisor valid */ @@ -102,11 +90,7 @@ typedef struct _P601_BATL { /* Lower par } P601_BATL; typedef struct _BATL { /* Lower part of BAT (all except 601) */ -#ifdef CONFIG_PPC64BRIDGE - unsigned long long brpn:47; -#else /* CONFIG_PPC64BRIDGE */ unsigned long brpn:15; /* Real page index (physical address) */ -#endif /* CONFIG_PPC64BRIDGE */ unsigned long :10; /* Unused */ unsigned long w:1; /* Write-thru cache */ unsigned long i:1; /* Cache inhibit */ diff --git a/include/asm-ppc/mmu_context.h b/include/asm-ppc/mmu_context.h index 4f152cc..2bc8589 100644 --- a/include/asm-ppc/mmu_context.h +++ b/include/asm-ppc/mmu_context.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef __PPC_MMU_CONTEXT_H #define __PPC_MMU_CONTEXT_H -#include #include #include #include @@ -71,7 +70,7 @@ #define FIRST_CONTEXT 1 #else /* PPC 6xx, 7xx CPUs */ -#define NO_CONTEXT ((mm_context_t) -1) +#define NO_CONTEXT ((unsigned long) -1) #define LAST_CONTEXT 32767 #define FIRST_CONTEXT 1 #endif @@ -86,7 +85,7 @@ #endif * can be used for debugging on all processors (if you happen to have * an Abatron). */ -extern void set_context(mm_context_t context, pgd_t *pgd); +extern void set_context(unsigned long contextid, pgd_t *pgd); /* * Bitmap of contexts in use. @@ -99,7 +98,7 @@ extern unsigned long context_map[]; * Its use is an optimization only, we can't rely on this context * number to be free, but it usually will be. */ -extern mm_context_t next_mmu_context; +extern unsigned long next_mmu_context; /* * If we don't have sufficient contexts to give one to every task @@ -118,9 +117,9 @@ #endif */ static inline void get_mmu_context(struct mm_struct *mm) { - mm_context_t ctx; + unsigned long ctx; - if (mm->context != NO_CONTEXT) + if (mm->context.id != NO_CONTEXT) return; #ifdef FEW_CONTEXTS while (atomic_dec_if_positive(&nr_free_contexts) < 0) @@ -133,7 +132,7 @@ #endif ctx = 0; } next_mmu_context = (ctx + 1) & LAST_CONTEXT; - mm->context = ctx; + mm->context.id = ctx; #ifdef FEW_CONTEXTS context_mm[ctx] = mm; #endif @@ -142,7 +141,12 @@ #endif /* * Set up the context for a new address space. */ -#define init_new_context(tsk,mm) (((mm)->context = NO_CONTEXT), 0) +static inline int init_new_context(struct task_struct *t, struct mm_struct *mm) +{ + mm->context.id = NO_CONTEXT; + mm->context.vdso_base = 0; + return 0; +} /* * We're finished using the context for an address space. @@ -150,9 +154,9 @@ #define init_new_context(tsk,mm) (((mm)- static inline void destroy_context(struct mm_struct *mm) { preempt_disable(); - if (mm->context != NO_CONTEXT) { - clear_bit(mm->context, context_map); - mm->context = NO_CONTEXT; + if (mm->context.id != NO_CONTEXT) { + clear_bit(mm->context.id, context_map); + mm->context.id = NO_CONTEXT; #ifdef FEW_CONTEXTS atomic_inc(&nr_free_contexts); #endif @@ -180,7 +184,7 @@ #endif /* CONFIG_ALTIVEC */ /* Setup new userspace context */ get_mmu_context(next); - set_context(next->context, next->pgd); + set_context(next->context.id, next->pgd); } #define deactivate_mm(tsk,mm) do { } while (0) diff --git a/include/asm-ppc/mpc8260.h b/include/asm-ppc/mpc8260.h index 6ba69a8..4b93481 100644 --- a/include/asm-ppc/mpc8260.h +++ b/include/asm-ppc/mpc8260.h @@ -8,7 +8,6 @@ #ifdef __KERNEL__ #ifndef __ASM_PPC_MPC8260_H__ #define __ASM_PPC_MPC8260_H__ -#include #ifdef CONFIG_8260 diff --git a/include/asm-ppc/mpc83xx.h b/include/asm-ppc/mpc83xx.h index 3c23fc4..02ed2c3 100644 --- a/include/asm-ppc/mpc83xx.h +++ b/include/asm-ppc/mpc83xx.h @@ -17,7 +17,6 @@ #ifdef __KERNEL__ #ifndef __ASM_MPC83xx_H__ #define __ASM_MPC83xx_H__ -#include #include #ifdef CONFIG_83xx diff --git a/include/asm-ppc/mpc85xx.h b/include/asm-ppc/mpc85xx.h index f47002a..9b48511 100644 --- a/include/asm-ppc/mpc85xx.h +++ b/include/asm-ppc/mpc85xx.h @@ -17,7 +17,6 @@ #ifdef __KERNEL__ #ifndef __ASM_MPC85xx_H__ #define __ASM_MPC85xx_H__ -#include #include #ifdef CONFIG_85xx @@ -28,6 +27,9 @@ #endif #if defined(CONFIG_MPC8555_CDS) || defined(CONFIG_MPC8548_CDS) #include #endif +#ifdef CONFIG_MPC85xx_CDS +#include +#endif #ifdef CONFIG_MPC8560_ADS #include #endif diff --git a/include/asm-ppc/mpc8xx.h b/include/asm-ppc/mpc8xx.h index 3515a7f..adcce33 100644 --- a/include/asm-ppc/mpc8xx.h +++ b/include/asm-ppc/mpc8xx.h @@ -8,7 +8,6 @@ #ifdef __KERNEL__ #ifndef __CONFIG_8xx_DEFS #define __CONFIG_8xx_DEFS -#include #ifdef CONFIG_8xx diff --git a/include/asm-ppc/mv64x60.h b/include/asm-ppc/mv64x60.h index 4f2405b..663edbe 100644 --- a/include/asm-ppc/mv64x60.h +++ b/include/asm-ppc/mv64x60.h @@ -17,7 +17,6 @@ #include #include #include #include -#include #include #include diff --git a/include/asm-ppc/ocp.h b/include/asm-ppc/ocp.h index 983116f..16dbc7d 100644 --- a/include/asm-ppc/ocp.h +++ b/include/asm-ppc/ocp.h @@ -26,8 +26,6 @@ #define __OCP_H__ #include #include -#include -#include #include #include diff --git a/include/asm-ppc/open_pic.h b/include/asm-ppc/open_pic.h index ec2f466..a4fe962 100644 --- a/include/asm-ppc/open_pic.h +++ b/include/asm-ppc/open_pic.h @@ -12,7 +12,6 @@ #ifndef _PPC_KERNEL_OPEN_PIC_H #define _PPC_KERNEL_OPEN_PIC_H -#include #include #define OPENPIC_SIZE 0x40000 diff --git a/include/asm-ppc/page.h b/include/asm-ppc/page.h index 0fb68a0..fe95c82 100644 --- a/include/asm-ppc/page.h +++ b/include/asm-ppc/page.h @@ -1,7 +1,6 @@ #ifndef _PPC_PAGE_H #define _PPC_PAGE_H -#include #include /* PAGE_SHIFT determines the page size */ @@ -15,7 +14,6 @@ #define PAGE_SIZE (ASM_CONST(1) << PAGE_ #define PAGE_MASK (~((1 << PAGE_SHIFT) - 1)) #ifdef __KERNEL__ -#include /* This must match what is in arch/ppc/Makefile */ #define PAGE_OFFSET CONFIG_KERNEL_START @@ -172,7 +170,7 @@ #endif /* __ASSEMBLY__ */ #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) -/* We do define AT_SYSINFO_EHDR but don't use the gate mecanism */ +/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */ #define __HAVE_ARCH_GATE_AREA 1 #include diff --git a/include/asm-ppc/pc_serial.h b/include/asm-ppc/pc_serial.h index 8f994f9..81a2d0f 100644 --- a/include/asm-ppc/pc_serial.h +++ b/include/asm-ppc/pc_serial.h @@ -9,7 +9,6 @@ * anyone using any of those on a PPC platform. -- paulus */ -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-ppc/pci.h b/include/asm-ppc/pci.h index 61434ed..11ffaaa 100644 --- a/include/asm-ppc/pci.h +++ b/include/asm-ppc/pci.h @@ -133,7 +133,7 @@ extern pgprot_t pci_phys_mem_access_prot #define HAVE_ARCH_PCI_RESOURCE_TO_USER extern void pci_resource_to_user(const struct pci_dev *dev, int bar, const struct resource *rsrc, - u64 *start, u64 *end); + resource_size_t *start, resource_size_t *end); #endif /* __KERNEL__ */ diff --git a/include/asm-ppc/pgalloc.h b/include/asm-ppc/pgalloc.h index bdefd1c..44d88a9 100644 --- a/include/asm-ppc/pgalloc.h +++ b/include/asm-ppc/pgalloc.h @@ -2,7 +2,6 @@ #ifdef __KERNEL__ #ifndef _PPC_PGALLOC_H #define _PPC_PGALLOC_H -#include #include extern void __bad_pte(pmd_t *pmd); diff --git a/include/asm-ppc/pgtable.h b/include/asm-ppc/pgtable.h index 570b355..51fa7c6 100644 --- a/include/asm-ppc/pgtable.h +++ b/include/asm-ppc/pgtable.h @@ -4,7 +4,6 @@ #define _PPC_PGTABLE_H #include -#include #ifndef __ASSEMBLY__ #include @@ -663,7 +662,7 @@ #endif return (old & _PAGE_ACCESSED) != 0; } #define ptep_test_and_clear_young(__vma, __addr, __ptep) \ - __ptep_test_and_clear_young((__vma)->vm_mm->context, __addr, __ptep) + __ptep_test_and_clear_young((__vma)->vm_mm->context.id, __addr, __ptep) #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY static inline int ptep_test_and_clear_dirty(struct vm_area_struct *vma, diff --git a/include/asm-ppc/ppc4xx_dma.h b/include/asm-ppc/ppc4xx_dma.h index 46a086f..935d1e0 100644 --- a/include/asm-ppc/ppc4xx_dma.h +++ b/include/asm-ppc/ppc4xx_dma.h @@ -24,7 +24,6 @@ #ifdef __KERNEL__ #ifndef __ASMPPC_PPC4xx_DMA_H #define __ASMPPC_PPC4xx_DMA_H -#include #include #include #include diff --git a/include/asm-ppc/ppc4xx_pic.h b/include/asm-ppc/ppc4xx_pic.h index c16c7f8..e442612 100644 --- a/include/asm-ppc/ppc4xx_pic.h +++ b/include/asm-ppc/ppc4xx_pic.h @@ -17,7 +17,6 @@ #ifndef __PPC4XX_PIC_H__ #define __PPC4XX_PIC_H__ -#include #include #include diff --git a/include/asm-ppc/serial.h b/include/asm-ppc/serial.h index b74af54..8a59f88 100644 --- a/include/asm-ppc/serial.h +++ b/include/asm-ppc/serial.h @@ -6,7 +6,6 @@ #ifdef __KERNEL__ #ifndef __ASM_SERIAL_H__ #define __ASM_SERIAL_H__ -#include #if defined(CONFIG_EV64260) #include diff --git a/include/asm-ppc/smp.h b/include/asm-ppc/smp.h index 30e9268..0b7fa89 100644 --- a/include/asm-ppc/smp.h +++ b/include/asm-ppc/smp.h @@ -10,7 +10,6 @@ #ifdef __KERNEL__ #ifndef _PPC_SMP_H #define _PPC_SMP_H -#include #include #include #include diff --git a/include/asm-ppc/time.h b/include/asm-ppc/time.h index c861123..f7eadf6 100644 --- a/include/asm-ppc/time.h +++ b/include/asm-ppc/time.h @@ -9,7 +9,6 @@ #ifdef __KERNEL__ #ifndef __ASM_TIME_H__ #define __ASM_TIME_H__ -#include #include #include #include diff --git a/include/asm-s390/Kbuild b/include/asm-s390/Kbuild new file mode 100644 index 0000000..ed8955f --- /dev/null +++ b/include/asm-s390/Kbuild @@ -0,0 +1,4 @@ +include include/asm-generic/Kbuild.asm + +unifdef-y += cmb.h debug.h +header-y += dasd.h qeth.h tape390.h ucontext.h vtoc.h z90crypt.h diff --git a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h index ca092ff..0ddcdba 100644 --- a/include/asm-s390/bitops.h +++ b/include/asm-s390/bitops.h @@ -12,7 +12,9 @@ #define _S390_BITOPS_H * Copyright (C) 1992, Linus Torvalds * */ -#include + +#ifdef __KERNEL__ + #include /* @@ -51,19 +53,6 @@ #include * with operation of the form "set_bit(bitnr, flags)". */ -/* set ALIGN_CS to 1 if the SMP safe bit operations should - * align the address to 4 byte boundary. It seems to work - * without the alignment. - */ -#ifdef __KERNEL__ -#define ALIGN_CS 0 -#else -#define ALIGN_CS 1 -#ifndef CONFIG_SMP -#error "bitops won't work without CONFIG_SMP" -#endif -#endif - /* bitmap tables from arch/S390/kernel/bitmap.S */ extern const char _oi_bitmap[]; extern const char _ni_bitmap[]; @@ -122,10 +111,6 @@ static inline void set_bit_cs(unsigned l unsigned long addr, old, new, mask; addr = (unsigned long) ptr; -#if ALIGN_CS == 1 - nr += (addr & __BITOPS_ALIGN) << 3; /* add alignment to bit number */ - addr ^= addr & __BITOPS_ALIGN; /* align address to 8 */ -#endif /* calculate address for CS */ addr += (nr ^ (nr & (__BITOPS_WORDSIZE - 1))) >> 3; /* make OR mask */ @@ -142,10 +127,6 @@ static inline void clear_bit_cs(unsigned unsigned long addr, old, new, mask; addr = (unsigned long) ptr; -#if ALIGN_CS == 1 - nr += (addr & __BITOPS_ALIGN) << 3; /* add alignment to bit number */ - addr ^= addr & __BITOPS_ALIGN; /* align address to 8 */ -#endif /* calculate address for CS */ addr += (nr ^ (nr & (__BITOPS_WORDSIZE - 1))) >> 3; /* make AND mask */ @@ -162,10 +143,6 @@ static inline void change_bit_cs(unsigne unsigned long addr, old, new, mask; addr = (unsigned long) ptr; -#if ALIGN_CS == 1 - nr += (addr & __BITOPS_ALIGN) << 3; /* add alignment to bit number */ - addr ^= addr & __BITOPS_ALIGN; /* align address to 8 */ -#endif /* calculate address for CS */ addr += (nr ^ (nr & (__BITOPS_WORDSIZE - 1))) >> 3; /* make XOR mask */ @@ -183,10 +160,6 @@ test_and_set_bit_cs(unsigned long nr, vo unsigned long addr, old, new, mask; addr = (unsigned long) ptr; -#if ALIGN_CS == 1 - nr += (addr & __BITOPS_ALIGN) << 3; /* add alignment to bit number */ - addr ^= addr & __BITOPS_ALIGN; /* align address to 8 */ -#endif /* calculate address for CS */ addr += (nr ^ (nr & (__BITOPS_WORDSIZE - 1))) >> 3; /* make OR/test mask */ @@ -206,10 +179,6 @@ test_and_clear_bit_cs(unsigned long nr, unsigned long addr, old, new, mask; addr = (unsigned long) ptr; -#if ALIGN_CS == 1 - nr += (addr & __BITOPS_ALIGN) << 3; /* add alignment to bit number */ - addr ^= addr & __BITOPS_ALIGN; /* align address to 8 */ -#endif /* calculate address for CS */ addr += (nr ^ (nr & (__BITOPS_WORDSIZE - 1))) >> 3; /* make AND/test mask */ @@ -229,10 +198,6 @@ test_and_change_bit_cs(unsigned long nr, unsigned long addr, old, new, mask; addr = (unsigned long) ptr; -#if ALIGN_CS == 1 - nr += (addr & __BITOPS_ALIGN) << 3; /* add alignment to bit number */ - addr ^= addr & __BITOPS_ALIGN; /* align address to 8 */ -#endif /* calculate address for CS */ addr += (nr ^ (nr & (__BITOPS_WORDSIZE - 1))) >> 3; /* make XOR/test mask */ @@ -835,8 +800,6 @@ #include #include -#ifdef __KERNEL__ - /* * ATTENTION: intel byte ordering convention for ext2 and minix !! * bit 0 is the LSB of addr; bit 31 is the MSB of addr; diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h index 089cf56..2b16193 100644 --- a/include/asm-s390/cio.h +++ b/include/asm-s390/cio.h @@ -276,6 +276,8 @@ extern void wait_cons_dev(void); extern void clear_all_subchannels(void); +extern void css_schedule_reprobe(void); + #endif #endif diff --git a/include/asm-s390/cmb.h b/include/asm-s390/cmb.h index dae1dd4..241756f 100644 --- a/include/asm-s390/cmb.h +++ b/include/asm-s390/cmb.h @@ -44,10 +44,6 @@ struct cmbdata { #define BIODASDCMFENABLE _IO(DASD_IOCTL_LETTER,32) /* enable channel measurement */ #define BIODASDCMFDISABLE _IO(DASD_IOCTL_LETTER,33) -/* reset channel measurement block */ -#define BIODASDRESETCMB _IO(DASD_IOCTL_LETTER,34) -/* read channel measurement data */ -#define BIODASDREADCMB _IOWR(DASD_IOCTL_LETTER,32,u64) /* read channel measurement data */ #define BIODASDREADALLCMB _IOWR(DASD_IOCTL_LETTER,33,struct cmbdata) diff --git a/include/asm-s390/dasd.h b/include/asm-s390/dasd.h index 1630c26..c042f95 100644 --- a/include/asm-s390/dasd.h +++ b/include/asm-s390/dasd.h @@ -68,10 +68,12 @@ #define DASD_FORMAT_CDL 2 * 0x00: default features * 0x01: readonly (ro) * 0x02: use diag discipline (diag) + * 0x04: set the device initially online (internal use only) */ -#define DASD_FEATURE_DEFAULT 0 -#define DASD_FEATURE_READONLY 1 -#define DASD_FEATURE_USEDIAG 2 +#define DASD_FEATURE_DEFAULT 0x00 +#define DASD_FEATURE_READONLY 0x01 +#define DASD_FEATURE_USEDIAG 0x02 +#define DASD_FEATURE_INITIAL_ONLINE 0x04 #define DASD_PARTN_BITS 2 diff --git a/include/asm-s390/debug.h b/include/asm-s390/debug.h index 23450ed..7f1ef99 100644 --- a/include/asm-s390/debug.h +++ b/include/asm-s390/debug.h @@ -9,7 +9,6 @@ #ifndef DEBUG_H #define DEBUG_H -#include #include #include diff --git a/include/asm-s390/hardirq.h b/include/asm-s390/hardirq.h index 6792c55..e84b7ef 100644 --- a/include/asm-s390/hardirq.h +++ b/include/asm-s390/hardirq.h @@ -12,7 +12,6 @@ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include #include diff --git a/include/asm-s390/idals.h b/include/asm-s390/idals.h index 8038858..e82c10e 100644 --- a/include/asm-s390/idals.h +++ b/include/asm-s390/idals.h @@ -13,7 +13,6 @@ #ifndef _S390_IDALS_H #define _S390_IDALS_H -#include #include #include #include diff --git a/include/asm-s390/io.h b/include/asm-s390/io.h index b05825d..d4614b3 100644 --- a/include/asm-s390/io.h +++ b/include/asm-s390/io.h @@ -86,20 +86,25 @@ #define bus_to_virt phys_to_virt #define readb(addr) (*(volatile unsigned char *) __io_virt(addr)) #define readw(addr) (*(volatile unsigned short *) __io_virt(addr)) #define readl(addr) (*(volatile unsigned int *) __io_virt(addr)) +#define readq(addr) (*(volatile unsigned long long *) __io_virt(addr)) #define readb_relaxed(addr) readb(addr) #define readw_relaxed(addr) readw(addr) #define readl_relaxed(addr) readl(addr) +#define readq_relaxed(addr) readq(addr) #define __raw_readb readb #define __raw_readw readw #define __raw_readl readl +#define __raw_readq readq #define writeb(b,addr) (*(volatile unsigned char *) __io_virt(addr) = (b)) #define writew(b,addr) (*(volatile unsigned short *) __io_virt(addr) = (b)) #define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b)) +#define writeq(b,addr) (*(volatile unsigned long long *) __io_virt(addr) = (b)) #define __raw_writeb writeb #define __raw_writew writew #define __raw_writel writel +#define __raw_writeq writeq #define memset_io(a,b,c) memset(__io_virt(a),(b),(c)) #define memcpy_fromio(a,b,c) memcpy((a),__io_virt(b),(c)) diff --git a/include/asm-s390/irq.h b/include/asm-s390/irq.h index 916a1aa..bd1a721 100644 --- a/include/asm-s390/irq.h +++ b/include/asm-s390/irq.h @@ -21,10 +21,6 @@ enum interruption_class { #define touch_nmi_watchdog() do { } while(0) -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif /* __KERNEL__ */ #endif diff --git a/include/asm-s390/irqflags.h b/include/asm-s390/irqflags.h new file mode 100644 index 0000000..65f4db6 --- /dev/null +++ b/include/asm-s390/irqflags.h @@ -0,0 +1,50 @@ +/* + * include/asm-s390/irqflags.h + * + * Copyright (C) IBM Corp. 2006 + * Author(s): Heiko Carstens + */ + +#ifndef __ASM_IRQFLAGS_H +#define __ASM_IRQFLAGS_H + +#ifdef __KERNEL__ + +/* interrupt control.. */ +#define raw_local_irq_enable() ({ \ + unsigned long __dummy; \ + __asm__ __volatile__ ( \ + "stosm 0(%1),0x03" \ + : "=m" (__dummy) : "a" (&__dummy) : "memory" ); \ + }) + +#define raw_local_irq_disable() ({ \ + unsigned long __flags; \ + __asm__ __volatile__ ( \ + "stnsm 0(%1),0xfc" : "=m" (__flags) : "a" (&__flags) ); \ + __flags; \ + }) + +#define raw_local_save_flags(x) \ + __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x), "m" (x) ) + +#define raw_local_irq_restore(x) \ + __asm__ __volatile__("ssm 0(%0)" : : "a" (&x), "m" (x) : "memory") + +#define raw_irqs_disabled() \ +({ \ + unsigned long flags; \ + local_save_flags(flags); \ + !((flags >> __FLAG_SHIFT) & 3); \ +}) + +static inline int raw_irqs_disabled_flags(unsigned long flags) +{ + return !((flags >> __FLAG_SHIFT) & 3); +} + +/* For spinlocks etc */ +#define raw_local_irq_save(x) ((x) = raw_local_irq_disable()) + +#endif /* __KERNEL__ */ +#endif /* __ASM_IRQFLAGS_H */ diff --git a/include/asm-s390/local.h b/include/asm-s390/local.h index cf81890..86745a1 100644 --- a/include/asm-s390/local.h +++ b/include/asm-s390/local.h @@ -1,7 +1,6 @@ #ifndef _ASM_LOCAL_H #define _ASM_LOCAL_H -#include #include #include diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h index bea7279..596c8b1 100644 --- a/include/asm-s390/lowcore.h +++ b/include/asm-s390/lowcore.h @@ -124,7 +124,6 @@ #endif /* __s390x__ */ #ifndef __ASSEMBLY__ -#include #include #include #include diff --git a/include/asm-s390/page.h b/include/asm-s390/page.h index 3b1138a..b2628dc 100644 --- a/include/asm-s390/page.h +++ b/include/asm-s390/page.h @@ -9,7 +9,6 @@ #ifndef _S390_PAGE_H #define _S390_PAGE_H -#include #include /* PAGE_SHIFT determines the page size */ @@ -20,6 +19,7 @@ #define PAGE_DEFAULT_ACC 0 #define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4) #ifdef __KERNEL__ +#include #ifndef __ASSEMBLY__ #ifndef __s390x__ @@ -189,9 +189,9 @@ #define virt_addr_valid(kaddr) pfn_valid #define VM_DATA_DEFAULT_FLAGS (VM_READ | VM_WRITE | VM_EXEC | \ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC) -#endif /* __KERNEL__ */ - #include #include +#endif /* __KERNEL__ */ + #endif /* _S390_PAGE_H */ diff --git a/include/asm-s390/percpu.h b/include/asm-s390/percpu.h index 436d216..28b3517 100644 --- a/include/asm-s390/percpu.h +++ b/include/asm-s390/percpu.h @@ -40,7 +40,9 @@ #define DEFINE_PER_CPU(type, name) \ __typeof__(type) per_cpu__##name #define __get_cpu_var(var) __reloc_hide(var,S390_lowcore.percpu_offset) +#define __raw_get_cpu_var(var) __reloc_hide(var,S390_lowcore.percpu_offset) #define per_cpu(var,cpu) __reloc_hide(var,__per_cpu_offset[cpu]) +#define per_cpu_offset(x) (__per_cpu_offset[x]) /* A macro to avoid #include hell... */ #define percpu_modcopy(pcpudst, src, size) \ @@ -57,6 +59,7 @@ #define DEFINE_PER_CPU(type, name) \ __typeof__(type) per_cpu__##name #define __get_cpu_var(var) __reloc_hide(var,0) +#define __raw_get_cpu_var(var) __reloc_hide(var,0) #define per_cpu(var,cpu) __reloc_hide(var,0) #endif /* SMP */ diff --git a/include/asm-s390/pgalloc.h b/include/asm-s390/pgalloc.h index e28aaf2..3002fda 100644 --- a/include/asm-s390/pgalloc.h +++ b/include/asm-s390/pgalloc.h @@ -13,7 +13,6 @@ #ifndef _S390_PGALLOC_H #define _S390_PGALLOC_H -#include #include #include #include diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h index 859b5e9..2431238 100644 --- a/include/asm-s390/pgtable.h +++ b/include/asm-s390/pgtable.h @@ -657,13 +657,6 @@ ({ __pte; \ }) -#define SetPageUptodate(_page) \ - do { \ - struct page *__page = (_page); \ - if (!test_and_set_bit(PG_uptodate, &__page->flags)) \ - page_test_and_clear_dirty(_page); \ - } while (0) - #ifdef __s390x__ #define pfn_pmd(pfn, pgprot) \ diff --git a/include/asm-s390/posix_types.h b/include/asm-s390/posix_types.h index 61788de..b94c988 100644 --- a/include/asm-s390/posix_types.h +++ b/include/asm-s390/posix_types.h @@ -76,24 +76,36 @@ #endif /* !defined } __kernel_fsid_t; -#if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) - -#ifndef _S390_BITOPS_H -#include -#endif - -#undef __FD_SET -#define __FD_SET(fd,fdsetp) set_bit((fd),(fdsetp)->fds_bits) - -#undef __FD_CLR -#define __FD_CLR(fd,fdsetp) clear_bit((fd),(fdsetp)->fds_bits) - -#undef __FD_ISSET -#define __FD_ISSET(fd,fdsetp) test_bit((fd),(fdsetp)->fds_bits) +#ifdef __KERNEL__ + +#undef __FD_SET +static inline void __FD_SET(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] |= (1UL<<_rem); +} + +#undef __FD_CLR +static inline void __FD_CLR(unsigned long fd, __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + fdsetp->fds_bits[_tmp] &= ~(1UL<<_rem); +} + +#undef __FD_ISSET +static inline int __FD_ISSET(unsigned long fd, const __kernel_fd_set *fdsetp) +{ + unsigned long _tmp = fd / __NFDBITS; + unsigned long _rem = fd % __NFDBITS; + return (fdsetp->fds_bits[_tmp] & (1UL<<_rem)) != 0; +} #undef __FD_ZERO -#define __FD_ZERO(fdsetp) (memset ((fdsetp), 0, sizeof(*(fd_set *)(fdsetp)))) +#define __FD_ZERO(fdsetp) \ + ((void) memset ((__ptr_t) (fdsetp), 0, sizeof (__kernel_fd_set))) -#endif /* defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)*/ +#endif /* __KERNEL__ */ #endif diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h index a949cc0..4d75d77 100644 --- a/include/asm-s390/ptrace.h +++ b/include/asm-s390/ptrace.h @@ -181,11 +181,8 @@ #define ACR_SIZE 4 #define PTRACE_OLDSETOPTIONS 21 #ifndef __ASSEMBLY__ -#include #include #include -#include -#include typedef union { @@ -301,6 +298,9 @@ typedef struct } s390_regs; #ifdef __KERNEL__ +#include +#include + /* * The pt_regs struct defines the way the registers are stored on * the stack during a system call. diff --git a/include/asm-s390/rwsem.h b/include/asm-s390/rwsem.h index 0422a08..13ec169 100644 --- a/include/asm-s390/rwsem.h +++ b/include/asm-s390/rwsem.h @@ -61,6 +61,9 @@ struct rw_semaphore { signed long count; spinlock_t wait_lock; struct list_head wait_list; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif }; #ifndef __s390x__ @@ -80,8 +83,16 @@ #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_W /* * initialisation */ + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } +#else +# define __RWSEM_DEP_MAP_INIT(lockname) +#endif + #define __RWSEM_INITIALIZER(name) \ -{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) } +{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \ + __RWSEM_DEP_MAP_INIT(name) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) @@ -93,6 +104,17 @@ static inline void init_rwsem(struct rw_ INIT_LIST_HEAD(&sem->wait_list); } +extern void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_rwsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_rwsem((sem), #sem, &__key); \ +} while (0) + + /* * lock for reading */ @@ -155,7 +177,7 @@ #endif /* __s390x__ */ /* * lock for writing */ -static inline void __down_write(struct rw_semaphore *sem) +static inline void __down_write_nested(struct rw_semaphore *sem, int subclass) { signed long old, new, tmp; @@ -181,6 +203,11 @@ #endif /* __s390x__ */ rwsem_down_write_failed(sem); } +static inline void __down_write(struct rw_semaphore *sem) +{ + __down_write_nested(sem, 0); +} + /* * trylock for writing -- returns 1 if successful, 0 if contention */ diff --git a/include/asm-s390/semaphore.h b/include/asm-s390/semaphore.h index 702cf43..32cdc69 100644 --- a/include/asm-s390/semaphore.h +++ b/include/asm-s390/semaphore.h @@ -37,7 +37,8 @@ #define DECLARE_MUTEX_LOCKED(name) __DEC static inline void sema_init (struct semaphore *sem, int val) { - *sem = (struct semaphore) __SEMAPHORE_INITIALIZER((*sem),val); + atomic_set(&sem->count, val); + init_waitqueue_head(&sem->wait); } static inline void init_MUTEX (struct semaphore *sem) diff --git a/include/asm-s390/sfp-machine.h b/include/asm-s390/sfp-machine.h index 3c79b53..de69dfa 100644 --- a/include/asm-s390/sfp-machine.h +++ b/include/asm-s390/sfp-machine.h @@ -25,7 +25,6 @@ #ifndef _SFP_MACHINE_H #define _SFP_MACHINE_H -#include #define _FP_W_TYPE_SIZE 32 #define _FP_W_TYPE unsigned long diff --git a/include/asm-s390/signal.h b/include/asm-s390/signal.h index 7084626..f6cfddb 100644 --- a/include/asm-s390/signal.h +++ b/include/asm-s390/signal.h @@ -84,7 +84,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -104,7 +103,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h index 444dae5..6576460 100644 --- a/include/asm-s390/smp.h +++ b/include/asm-s390/smp.h @@ -10,7 +10,6 @@ #ifndef __ASM_SMP_H #define __ASM_SMP_H -#include #include #include #include diff --git a/include/asm-s390/socket.h b/include/asm-s390/socket.h index 15a5298..1778a49 100644 --- a/include/asm-s390/socket.h +++ b/include/asm-s390/socket.h @@ -56,5 +56,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h index 6a89dbb..9ab186f 100644 --- a/include/asm-s390/system.h +++ b/include/asm-s390/system.h @@ -11,7 +11,6 @@ #ifndef __ASM_SYSTEM_H #define __ASM_SYSTEM_H -#include #include #include #include @@ -302,34 +301,6 @@ #define smp_mb__after_clear_bit() s #define set_mb(var, value) do { var = value; mb(); } while (0) #define set_wmb(var, value) do { var = value; wmb(); } while (0) -/* interrupt control.. */ -#define local_irq_enable() ({ \ - unsigned long __dummy; \ - __asm__ __volatile__ ( \ - "stosm 0(%1),0x03" \ - : "=m" (__dummy) : "a" (&__dummy) : "memory" ); \ - }) - -#define local_irq_disable() ({ \ - unsigned long __flags; \ - __asm__ __volatile__ ( \ - "stnsm 0(%1),0xfc" : "=m" (__flags) : "a" (&__flags) ); \ - __flags; \ - }) - -#define local_save_flags(x) \ - __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x), "m" (x) ) - -#define local_irq_restore(x) \ - __asm__ __volatile__("ssm 0(%0)" : : "a" (&x), "m" (x) : "memory") - -#define irqs_disabled() \ -({ \ - unsigned long flags; \ - local_save_flags(flags); \ - !((flags >> __FLAG_SHIFT) & 3); \ -}) - #ifdef __s390x__ #define __ctl_load(array, low, high) ({ \ @@ -443,8 +414,7 @@ #define __ctl_clear_bit(cr, bit) ({ \ }) #endif /* __s390x__ */ -/* For spinlocks etc */ -#define local_irq_save(x) ((x) = local_irq_disable()) +#include /* * Use to set psw mask except for the first byte which @@ -483,4 +453,3 @@ #define arch_align_stack(x) (x) #endif /* __KERNEL__ */ #endif - diff --git a/include/asm-s390/thread_info.h b/include/asm-s390/thread_info.h index 8e0c7ed..0a51891 100644 --- a/include/asm-s390/thread_info.h +++ b/include/asm-s390/thread_info.h @@ -63,6 +63,7 @@ #define INIT_THREAD_INFO(tsk) \ .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ + .preempt_count = 1, \ .restart_block = { \ .fn = do_no_restart_syscall, \ }, \ diff --git a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h index 1bb73b0..73cd85b 100644 --- a/include/asm-s390/tlbflush.h +++ b/include/asm-s390/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _S390_TLBFLUSH_H #define _S390_TLBFLUSH_H -#include #include #include diff --git a/include/asm-s390/types.h b/include/asm-s390/types.h index 5738ad6..ae2951c 100644 --- a/include/asm-s390/types.h +++ b/include/asm-s390/types.h @@ -58,7 +58,6 @@ #endif #ifndef __ASSEMBLY__ -#include typedef signed char s8; typedef unsigned char u8; diff --git a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h index 41c2792..aa7a243 100644 --- a/include/asm-s390/unistd.h +++ b/include/asm-s390/unistd.h @@ -392,11 +392,11 @@ #define __NR_newfstatat 293 #endif -/* user-visible error numbers are in the range -1 - -122: see */ +#ifdef __KERNEL__ #define __syscall_return(type, res) \ do { \ - if ((unsigned long)(res) >= (unsigned long)(-125)) { \ + if ((unsigned long)(res) >= (unsigned long)(-4095)) {\ errno = -(res); \ res = -1; \ } \ @@ -546,7 +546,6 @@ type name(type1 arg1, type2 arg2, type3 __syscall_return(type,__res); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_ALARM @@ -573,11 +572,9 @@ # ifdef CONFIG_COMPAT # define __ARCH_WANT_COMPAT_SYS_TIME # define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND # endif -#endif #ifdef __KERNEL_SYSCALLS__ -#include #include #include #include @@ -625,7 +622,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -635,4 +632,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _ASM_S390_UNISTD_H_ */ diff --git a/include/asm-s390/vtoc.h b/include/asm-s390/vtoc.h index d1de5b7..3a5267d 100644 --- a/include/asm-s390/vtoc.h +++ b/include/asm-s390/vtoc.h @@ -177,27 +177,27 @@ struct vtoc_format7_label } __attribute__ ((packed)); struct vtoc_cms_label { - u8 label_id[4]; /* Label identifier */ - u8 vol_id[6]; /* Volid */ - u16 version_id; /* Version identifier */ - u32 block_size; /* Disk block size */ - u32 origin_ptr; /* Disk origin pointer */ - u32 usable_count; /* Number of usable cylinders/blocks */ - u32 formatted_count; /* Maximum number of formatted cylinders/ + __u8 label_id[4]; /* Label identifier */ + __u8 vol_id[6]; /* Volid */ + __u16 version_id; /* Version identifier */ + __u32 block_size; /* Disk block size */ + __u32 origin_ptr; /* Disk origin pointer */ + __u32 usable_count; /* Number of usable cylinders/blocks */ + __u32 formatted_count; /* Maximum number of formatted cylinders/ * blocks */ - u32 block_count; /* Disk size in CMS blocks */ - u32 used_count; /* Number of CMS blocks in use */ - u32 fst_size; /* File Status Table (FST) size */ - u32 fst_count; /* Number of FSTs per CMS block */ - u8 format_date[6]; /* Disk FORMAT date */ - u8 reserved1[2]; - u32 disk_offset; /* Disk offset when reserved*/ - u32 map_block; /* Allocation Map Block with next hole */ - u32 hblk_disp; /* Displacement into HBLK data of next hole */ - u32 user_disp; /* Displacement into user part of Allocation + __u32 block_count; /* Disk size in CMS blocks */ + __u32 used_count; /* Number of CMS blocks in use */ + __u32 fst_size; /* File Status Table (FST) size */ + __u32 fst_count; /* Number of FSTs per CMS block */ + __u8 format_date[6]; /* Disk FORMAT date */ + __u8 reserved1[2]; + __u32 disk_offset; /* Disk offset when reserved*/ + __u32 map_block; /* Allocation Map Block with next hole */ + __u32 hblk_disp; /* Displacement into HBLK data of next hole */ + __u32 user_disp; /* Displacement into user part of Allocation * map */ - u8 reserved2[4]; - u8 segment_name[8]; /* Name of shared segment */ + __u8 reserved2[4]; + __u8 segment_name[8]; /* Name of shared segment */ } __attribute__ ((packed)); #endif /* _ASM_S390_VTOC_H */ diff --git a/include/asm-s390/z90crypt.h b/include/asm-s390/z90crypt.h new file mode 100644 index 0000000..31a2439 --- /dev/null +++ b/include/asm-s390/z90crypt.h @@ -0,0 +1,212 @@ +/* + * include/asm-s390/z90crypt.h + * + * z90crypt 1.3.3 (user-visible header) + * + * Copyright (C) 2001, 2005 IBM Corporation + * Author(s): Robert Burroughs + * Eric Rossman (edrossma@us.ibm.com) + * + * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_S390_Z90CRYPT_H +#define __ASM_S390_Z90CRYPT_H +#include + +#define z90crypt_VERSION 1 +#define z90crypt_RELEASE 3 // 2 = PCIXCC, 3 = rewrite for coding standards +#define z90crypt_VARIANT 3 // 3 = CEX2A support + +/** + * struct ica_rsa_modexpo + * + * Requirements: + * - outputdatalength is at least as large as inputdatalength. + * - All key parts are right justified in their fields, padded on + * the left with zeroes. + * - length(b_key) = inputdatalength + * - length(n_modulus) = inputdatalength + */ +struct ica_rsa_modexpo { + char __user * inputdata; + unsigned int inputdatalength; + char __user * outputdata; + unsigned int outputdatalength; + char __user * b_key; + char __user * n_modulus; +}; + +/** + * struct ica_rsa_modexpo_crt + * + * Requirements: + * - inputdatalength is even. + * - outputdatalength is at least as large as inputdatalength. + * - All key parts are right justified in their fields, padded on + * the left with zeroes. + * - length(bp_key) = inputdatalength/2 + 8 + * - length(bq_key) = inputdatalength/2 + * - length(np_key) = inputdatalength/2 + 8 + * - length(nq_key) = inputdatalength/2 + * - length(u_mult_inv) = inputdatalength/2 + 8 + */ +struct ica_rsa_modexpo_crt { + char __user * inputdata; + unsigned int inputdatalength; + char __user * outputdata; + unsigned int outputdatalength; + char __user * bp_key; + char __user * bq_key; + char __user * np_prime; + char __user * nq_prime; + char __user * u_mult_inv; +}; + +#define Z90_IOCTL_MAGIC 'z' // NOTE: Need to allocate from linux folks + +/** + * Interface notes: + * + * The ioctl()s which are implemented (along with relevant details) + * are: + * + * ICARSAMODEXPO + * Perform an RSA operation using a Modulus-Exponent pair + * This takes an ica_rsa_modexpo struct as its arg. + * + * NOTE: please refer to the comments preceding this structure + * for the implementation details for the contents of the + * block + * + * ICARSACRT + * Perform an RSA operation using a Chinese-Remainder Theorem key + * This takes an ica_rsa_modexpo_crt struct as its arg. + * + * NOTE: please refer to the comments preceding this structure + * for the implementation details for the contents of the + * block + * + * Z90STAT_TOTALCOUNT + * Return an integer count of all device types together. + * + * Z90STAT_PCICACOUNT + * Return an integer count of all PCICAs. + * + * Z90STAT_PCICCCOUNT + * Return an integer count of all PCICCs. + * + * Z90STAT_PCIXCCMCL2COUNT + * Return an integer count of all MCL2 PCIXCCs. + * + * Z90STAT_PCIXCCMCL3COUNT + * Return an integer count of all MCL3 PCIXCCs. + * + * Z90STAT_CEX2CCOUNT + * Return an integer count of all CEX2Cs. + * + * Z90STAT_CEX2ACOUNT + * Return an integer count of all CEX2As. + * + * Z90STAT_REQUESTQ_COUNT + * Return an integer count of the number of entries waiting to be + * sent to a device. + * + * Z90STAT_PENDINGQ_COUNT + * Return an integer count of the number of entries sent to a + * device awaiting the reply. + * + * Z90STAT_TOTALOPEN_COUNT + * Return an integer count of the number of open file handles. + * + * Z90STAT_DOMAIN_INDEX + * Return the integer value of the Cryptographic Domain. + * + * Z90STAT_STATUS_MASK + * Return an 64 element array of unsigned chars for the status of + * all devices. + * 0x01: PCICA + * 0x02: PCICC + * 0x03: PCIXCC_MCL2 + * 0x04: PCIXCC_MCL3 + * 0x05: CEX2C + * 0x06: CEX2A + * 0x0d: device is disabled via the proc filesystem + * + * Z90STAT_QDEPTH_MASK + * Return an 64 element array of unsigned chars for the queue + * depth of all devices. + * + * Z90STAT_PERDEV_REQCNT + * Return an 64 element array of unsigned integers for the number + * of successfully completed requests per device since the device + * was detected and made available. + * + * ICAZ90STATUS (deprecated) + * Return some device driver status in a ica_z90_status struct + * This takes an ica_z90_status struct as its arg. + * + * NOTE: this ioctl() is deprecated, and has been replaced with + * single ioctl()s for each type of status being requested + * + * Z90STAT_PCIXCCCOUNT (deprecated) + * Return an integer count of all PCIXCCs (MCL2 + MCL3). + * This is DEPRECATED now that MCL3 PCIXCCs are treated differently from + * MCL2 PCIXCCs. + * + * Z90QUIESCE (not recommended) + * Quiesce the driver. This is intended to stop all new + * requests from being processed. Its use is NOT recommended, + * except in circumstances where there is no other way to stop + * callers from accessing the driver. Its original use was to + * allow the driver to be "drained" of work in preparation for + * a system shutdown. + * + * NOTE: once issued, this ban on new work cannot be undone + * except by unloading and reloading the driver. + */ + +/** + * Supported ioctl calls + */ +#define ICARSAMODEXPO _IOC(_IOC_READ|_IOC_WRITE, Z90_IOCTL_MAGIC, 0x05, 0) +#define ICARSACRT _IOC(_IOC_READ|_IOC_WRITE, Z90_IOCTL_MAGIC, 0x06, 0) + +/* DEPRECATED status calls (bound for removal at some point) */ +#define ICAZ90STATUS _IOR(Z90_IOCTL_MAGIC, 0x10, struct ica_z90_status) +#define Z90STAT_PCIXCCCOUNT _IOR(Z90_IOCTL_MAGIC, 0x43, int) + +/* unrelated to ICA callers */ +#define Z90QUIESCE _IO(Z90_IOCTL_MAGIC, 0x11) + +/* New status calls */ +#define Z90STAT_TOTALCOUNT _IOR(Z90_IOCTL_MAGIC, 0x40, int) +#define Z90STAT_PCICACOUNT _IOR(Z90_IOCTL_MAGIC, 0x41, int) +#define Z90STAT_PCICCCOUNT _IOR(Z90_IOCTL_MAGIC, 0x42, int) +#define Z90STAT_PCIXCCMCL2COUNT _IOR(Z90_IOCTL_MAGIC, 0x4b, int) +#define Z90STAT_PCIXCCMCL3COUNT _IOR(Z90_IOCTL_MAGIC, 0x4c, int) +#define Z90STAT_CEX2CCOUNT _IOR(Z90_IOCTL_MAGIC, 0x4d, int) +#define Z90STAT_CEX2ACOUNT _IOR(Z90_IOCTL_MAGIC, 0x4e, int) +#define Z90STAT_REQUESTQ_COUNT _IOR(Z90_IOCTL_MAGIC, 0x44, int) +#define Z90STAT_PENDINGQ_COUNT _IOR(Z90_IOCTL_MAGIC, 0x45, int) +#define Z90STAT_TOTALOPEN_COUNT _IOR(Z90_IOCTL_MAGIC, 0x46, int) +#define Z90STAT_DOMAIN_INDEX _IOR(Z90_IOCTL_MAGIC, 0x47, int) +#define Z90STAT_STATUS_MASK _IOR(Z90_IOCTL_MAGIC, 0x48, char[64]) +#define Z90STAT_QDEPTH_MASK _IOR(Z90_IOCTL_MAGIC, 0x49, char[64]) +#define Z90STAT_PERDEV_REQCNT _IOR(Z90_IOCTL_MAGIC, 0x4a, int[64]) + +#endif /* __ASM_S390_Z90CRYPT_H */ diff --git a/include/asm-sh/Kbuild b/include/asm-sh/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-sh/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-sh/bug.h b/include/asm-sh/bug.h index 70508a3..1b4fc52 100644 --- a/include/asm-sh/bug.h +++ b/include/asm-sh/bug.h @@ -1,7 +1,6 @@ #ifndef __ASM_SH_BUG_H #define __ASM_SH_BUG_H -#include #ifdef CONFIG_BUG /* diff --git a/include/asm-sh/checksum.h b/include/asm-sh/checksum.h index 5ebd0f2..fa03b30 100644 --- a/include/asm-sh/checksum.h +++ b/include/asm-sh/checksum.h @@ -9,7 +9,6 @@ #define __ASM_SH_CHECKSUM_H * Copyright (C) 1999 by Kaz Kojima & Niibe Yutaka */ -#include #include /* diff --git a/include/asm-sh/dma-mapping.h b/include/asm-sh/dma-mapping.h index 48f1f42..124968f 100644 --- a/include/asm-sh/dma-mapping.h +++ b/include/asm-sh/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef __ASM_SH_DMA_MAPPING_H #define __ASM_SH_DMA_MAPPING_H -#include #include #include #include diff --git a/include/asm-sh/dma.h b/include/asm-sh/dma.h index a118a0d..e62a6d0 100644 --- a/include/asm-sh/dma.h +++ b/include/asm-sh/dma.h @@ -11,7 +11,6 @@ #ifndef __ASM_SH_DMA_H #define __ASM_SH_DMA_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-sh/fixmap.h b/include/asm-sh/fixmap.h index 509224b..412bcca 100644 --- a/include/asm-sh/fixmap.h +++ b/include/asm-sh/fixmap.h @@ -13,7 +13,6 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H -#include #include #include #ifdef CONFIG_HIGHMEM diff --git a/include/asm-sh/floppy.h b/include/asm-sh/floppy.h index 38d7a29..dc1ad46 100644 --- a/include/asm-sh/floppy.h +++ b/include/asm-sh/floppy.h @@ -146,13 +146,11 @@ static int vdma_get_dma_residue(unsigned static int fd_request_irq(void) { if(can_use_virtual_dma) - return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, - "floppy", NULL); + return request_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy", NULL); else return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT|SA_SAMPLE_RANDOM, - "floppy", NULL); - + IRQF_DISABLED, "floppy", NULL); } static unsigned long dma_mem_alloc(unsigned long size) diff --git a/include/asm-sh/hardirq.h b/include/asm-sh/hardirq.h index f2fdf0f..715ee23 100644 --- a/include/asm-sh/hardirq.h +++ b/include/asm-sh/hardirq.h @@ -1,7 +1,6 @@ #ifndef __ASM_SH_HARDIRQ_H #define __ASM_SH_HARDIRQ_H -#include #include #include diff --git a/include/asm-sh/hd64461/hd64461.h b/include/asm-sh/hd64461/hd64461.h index c457ca2..87f13d2 100644 --- a/include/asm-sh/hd64461/hd64461.h +++ b/include/asm-sh/hd64461/hd64461.h @@ -5,7 +5,6 @@ #define __ASM_SH_HD64461 * Copyright (C) 2000 YAEGASHI Takeshi * Hitachi HD64461 companion chip support */ -#include /* Constants for PCMCIA mappings */ #define HD64461_PCC_WINDOW 0x01000000 diff --git a/include/asm-sh/hd64465/hd64465.h b/include/asm-sh/hd64465/hd64465.h index c672032..cfd0e80 100644 --- a/include/asm-sh/hd64465/hd64465.h +++ b/include/asm-sh/hd64465/hd64465.h @@ -11,7 +11,6 @@ #define _ASM_SH_HD64465_ 1 * Derived from which bore the message: * Copyright (C) 2000 YAEGASHI Takeshi */ -#include #include #include diff --git a/include/asm-sh/hw_irq.h b/include/asm-sh/hw_irq.h index 1d934fb..fed2661 100644 --- a/include/asm-sh/hw_irq.h +++ b/include/asm-sh/hw_irq.h @@ -1,9 +1,4 @@ #ifndef __ASM_SH_HW_IRQ_H #define __ASM_SH_HW_IRQ_H -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) -{ - /* Nothing to do */ -} - #endif /* __ASM_SH_HW_IRQ_H */ diff --git a/include/asm-sh/ide.h b/include/asm-sh/ide.h index 711dad4..9f8e914 100644 --- a/include/asm-sh/ide.h +++ b/include/asm-sh/ide.h @@ -14,7 +14,6 @@ #define __ASM_SH_IDE_H #ifdef __KERNEL__ -#include #define ide_default_io_ctl(base) (0) diff --git a/include/asm-sh/io.h b/include/asm-sh/io.h index 2c3afe7..894e64b 100644 --- a/include/asm-sh/io.h +++ b/include/asm-sh/io.h @@ -23,7 +23,6 @@ #define __ASM_SH_IO_H * inb by default expands to _inb, but the machine specific code may * define it to __inb if it chooses. */ -#include #include #include #include diff --git a/include/asm-sh/irq.h b/include/asm-sh/irq.h index 42b8394..611e67c 100644 --- a/include/asm-sh/irq.h +++ b/include/asm-sh/irq.h @@ -11,7 +11,6 @@ #define __ASM_SH_IRQ_H * */ -#include #include #include /* for pt_regs */ diff --git a/include/asm-sh/keyboard.h b/include/asm-sh/keyboard.h index 1103df0..31dcc4f 100644 --- a/include/asm-sh/keyboard.h +++ b/include/asm-sh/keyboard.h @@ -5,7 +5,6 @@ #define __ASM_SH_KEYBOARD_H */ #include -#include #include #ifdef CONFIG_SH_MPC1211 diff --git a/include/asm-sh/kmap_types.h b/include/asm-sh/kmap_types.h index 2492ba0..84d565c 100644 --- a/include/asm-sh/kmap_types.h +++ b/include/asm-sh/kmap_types.h @@ -3,7 +3,6 @@ #define __SH_KMAP_TYPES_H /* Dummy header just to define km_type. */ -#include #ifdef CONFIG_DEBUG_HIGHMEM # define D(n) __KM_FENCE_##n , diff --git a/include/asm-sh/machvec.h b/include/asm-sh/machvec.h index 550c50a..550501f 100644 --- a/include/asm-sh/machvec.h +++ b/include/asm-sh/machvec.h @@ -10,7 +10,6 @@ #ifndef _ASM_SH_MACHVEC_H #define _ASM_SH_MACHVEC_H 1 -#include #include #include diff --git a/include/asm-sh/machvec_init.h b/include/asm-sh/machvec_init.h index 9e7de80..e397798 100644 --- a/include/asm-sh/machvec_init.h +++ b/include/asm-sh/machvec_init.h @@ -12,7 +12,6 @@ #ifndef __SH_MACHVEC_INIT_H #define __SH_MACHVEC_INIT_H -#include /* * In a GENERIC kernel, we have lots of these vectors floating about, diff --git a/include/asm-sh/mpc1211/dma.h b/include/asm-sh/mpc1211/dma.h index 0a2fdab..e506d1a 100644 --- a/include/asm-sh/mpc1211/dma.h +++ b/include/asm-sh/mpc1211/dma.h @@ -8,7 +8,6 @@ #ifndef _ASM_MPC1211_DMA_H #define _ASM_MPC1211_DMA_H -#include #include /* And spinlocks */ #include /* need byte IO */ #include diff --git a/include/asm-sh/mpc1211/keyboard.h b/include/asm-sh/mpc1211/keyboard.h index 5f0b908..71ef4cf 100644 --- a/include/asm-sh/mpc1211/keyboard.h +++ b/include/asm-sh/mpc1211/keyboard.h @@ -57,7 +57,7 @@ #define kbd_pause() do { } while(0) #define AUX_IRQ 12 #define aux_request_irq(hand, dev_id) \ - request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS2 Mouse", dev_id) + request_irq(AUX_IRQ, hand, IRQF_SHARED, "PS2 Mouse", dev_id) #define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) diff --git a/include/asm-sh/overdrive/overdrive.h b/include/asm-sh/overdrive/overdrive.h index aa62ae6..fc746c2 100644 --- a/include/asm-sh/overdrive/overdrive.h +++ b/include/asm-sh/overdrive/overdrive.h @@ -6,7 +6,6 @@ * */ -#include #ifndef __OVERDRIVE_H__ #define __OVERDRIVE_H__ diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h index 9c89287..a5559e3 100644 --- a/include/asm-sh/page.h +++ b/include/asm-sh/page.h @@ -13,7 +13,6 @@ #define __ASM_SH_PAGE_H [ P4 control ] 0xE0000000 */ -#include /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 diff --git a/include/asm-sh/pgtable.h b/include/asm-sh/pgtable.h index bb0efb3..dcd23a0 100644 --- a/include/asm-sh/pgtable.h +++ b/include/asm-sh/pgtable.h @@ -8,7 +8,6 @@ #include * Copyright (C) 2002, 2003, 2004 Paul Mundt */ -#include #include /* diff --git a/include/asm-sh/rwsem.h b/include/asm-sh/rwsem.h index 0262d3d..9d2aea5 100644 --- a/include/asm-sh/rwsem.h +++ b/include/asm-sh/rwsem.h @@ -25,24 +25,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_AC #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; -#endif }; -/* - * initialisation - */ -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 -#else -#define __RWSEM_DEBUG_INIT /* */ -#endif - #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEBUG_INIT } + LIST_HEAD_INIT((name).wait_list) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) @@ -57,9 +44,6 @@ static inline void init_rwsem(struct rw_ sem->count = RWSEM_UNLOCKED_VALUE; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif } /* diff --git a/include/asm-sh/serial.h b/include/asm-sh/serial.h index f51e232..8734590 100644 --- a/include/asm-sh/serial.h +++ b/include/asm-sh/serial.h @@ -7,7 +7,6 @@ #ifndef _ASM_SERIAL_H #define _ASM_SERIAL_H -#include #include #ifdef CONFIG_SH_EC3104 diff --git a/include/asm-sh/signal.h b/include/asm-sh/signal.h index d6e8eb0..5c5c1e8 100644 --- a/include/asm-sh/signal.h +++ b/include/asm-sh/signal.h @@ -75,7 +75,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -95,7 +94,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-sh/smp.h b/include/asm-sh/smp.h index f19a8b3..f57c4fe 100644 --- a/include/asm-sh/smp.h +++ b/include/asm-sh/smp.h @@ -10,7 +10,6 @@ #ifndef __ASM_SH_SMP_H #define __ASM_SH_SMP_H -#include #include #include diff --git a/include/asm-sh/socket.h b/include/asm-sh/socket.h index 553904f..ca70362 100644 --- a/include/asm-sh/socket.h +++ b/include/asm-sh/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* __ASM_SH_SOCKET_H */ diff --git a/include/asm-sh/system.h b/include/asm-sh/system.h index bb03304..ce2e606 100644 --- a/include/asm-sh/system.h +++ b/include/asm-sh/system.h @@ -6,14 +6,13 @@ #define __ASM_SH_SYSTEM_H * Copyright (C) 2002 Paul Mundt */ -#include /* * switch_to() should switch tasks to task nr n, first */ #define switch_to(prev, next, last) do { \ - task_t *__last; \ + struct task_struct *__last; \ register unsigned long *__ts1 __asm__ ("r1") = &prev->thread.sp; \ register unsigned long *__ts2 __asm__ ("r2") = &prev->thread.pc; \ register unsigned long *__ts4 __asm__ ("r4") = (unsigned long *)prev; \ diff --git a/include/asm-sh/types.h b/include/asm-sh/types.h index 488552f..3c09dd4 100644 --- a/include/asm-sh/types.h +++ b/include/asm-sh/types.h @@ -35,7 +35,6 @@ #define BITS_PER_LONG 32 #ifndef __ASSEMBLY__ -#include typedef __signed__ char s8; typedef unsigned char u8; diff --git a/include/asm-sh/unistd.h b/include/asm-sh/unistd.h index 05520ce..76b5430 100644 --- a/include/asm-sh/unistd.h +++ b/include/asm-sh/unistd.h @@ -304,6 +304,8 @@ #define __NR_inotify_rm_watch 292 #define NR_syscalls 293 +#ifdef __KERNEL__ + /* user-visible error numbers are in the range -1 - -124: see */ #define __syscall_return(type, res) \ @@ -420,7 +422,6 @@ __asm__ __volatile__ ("trapa #0x16" \ __syscall_return(type,__sc0); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -443,7 +444,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -513,7 +513,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -525,4 +525,5 @@ #ifndef cond_syscall #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") #endif +#endif /* __KERNEL__ */ #endif /* __ASM_SH_UNISTD_H */ diff --git a/include/asm-sh/watchdog.h b/include/asm-sh/watchdog.h index f0cf4be..09ca419 100644 --- a/include/asm-sh/watchdog.h +++ b/include/asm-sh/watchdog.h @@ -13,7 +13,6 @@ #define __ASM_SH_WATCHDOG_H #ifdef __KERNEL__ #include -#include #include #include diff --git a/include/asm-sh64/Kbuild b/include/asm-sh64/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-sh64/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-sh64/bug.h b/include/asm-sh64/bug.h index 5d659ec..81f722e 100644 --- a/include/asm-sh64/bug.h +++ b/include/asm-sh64/bug.h @@ -1,7 +1,6 @@ #ifndef __ASM_SH64_BUG_H #define __ASM_SH64_BUG_H -#include /* * Tell the user there is some problem, then force a segfault (in process diff --git a/include/asm-sh64/dma-mapping.h b/include/asm-sh64/dma-mapping.h index cc9a2e8..a74a49e 100644 --- a/include/asm-sh64/dma-mapping.h +++ b/include/asm-sh64/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef __ASM_SH_DMA_MAPPING_H #define __ASM_SH_DMA_MAPPING_H -#include #include #include #include diff --git a/include/asm-sh64/hardirq.h b/include/asm-sh64/hardirq.h index ad2330e..555fd7a 100644 --- a/include/asm-sh64/hardirq.h +++ b/include/asm-sh64/hardirq.h @@ -1,7 +1,6 @@ #ifndef __ASM_SH64_HARDIRQ_H #define __ASM_SH64_HARDIRQ_H -#include #include #include diff --git a/include/asm-sh64/hw_irq.h b/include/asm-sh64/hw_irq.h index ae718d1..ebb3908 100644 --- a/include/asm-sh64/hw_irq.h +++ b/include/asm-sh64/hw_irq.h @@ -11,6 +11,5 @@ #define __ASM_SH64_HW_IRQ_H * Copyright (C) 2000, 2001 Paolo Alberelli * */ -static __inline__ void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { /* Nothing to do */ } #endif /* __ASM_SH64_HW_IRQ_H */ diff --git a/include/asm-sh64/ide.h b/include/asm-sh64/ide.h index 852f50a..c9d84d5 100644 --- a/include/asm-sh64/ide.h +++ b/include/asm-sh64/ide.h @@ -15,7 +15,6 @@ #define __ASM_SH64_IDE_H #ifdef __KERNEL__ -#include /* Without this, the initialisation of PCI IDE cards end up calling * ide_init_hwif_ports, which won't work. */ diff --git a/include/asm-sh64/irq.h b/include/asm-sh64/irq.h index f815b43..1ca49e2 100644 --- a/include/asm-sh64/irq.h +++ b/include/asm-sh64/irq.h @@ -12,7 +12,6 @@ #define __ASM_SH64_IRQ_H * */ -#include /* * Encoded IRQs are not considered worth to be supported. diff --git a/include/asm-sh64/keyboard.h b/include/asm-sh64/keyboard.h index 733e2bb..1fab96d 100644 --- a/include/asm-sh64/keyboard.h +++ b/include/asm-sh64/keyboard.h @@ -65,7 +65,7 @@ #define AUX_IRQ (START_EXT_IRQS + 6) /* #endif #define aux_request_irq(hand, dev_id) \ - request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS2 Mouse", dev_id) + request_irq(AUX_IRQ, hand, IRQF_SHARED, "PS2 Mouse", dev_id) #define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) diff --git a/include/asm-sh64/mmu_context.h b/include/asm-sh64/mmu_context.h index 991cfda..8c860da 100644 --- a/include/asm-sh64/mmu_context.h +++ b/include/asm-sh64/mmu_context.h @@ -26,7 +26,6 @@ #ifndef __ASSEMBLY__ */ extern unsigned long mmu_context_cache; -#include #include diff --git a/include/asm-sh64/page.h b/include/asm-sh64/page.h index e4937cd..34fb347 100644 --- a/include/asm-sh64/page.h +++ b/include/asm-sh64/page.h @@ -17,7 +17,6 @@ #define __ASM_SH64_PAGE_H * */ -#include /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 diff --git a/include/asm-sh64/param.h b/include/asm-sh64/param.h index d18cc87..f409adb 100644 --- a/include/asm-sh64/param.h +++ b/include/asm-sh64/param.h @@ -12,7 +12,6 @@ #ifndef __ASM_SH64_PARAM_H #define __ASM_SH64_PARAM_H -#include #ifdef __KERNEL__ # ifdef CONFIG_SH_WDT diff --git a/include/asm-sh64/pgtable.h b/include/asm-sh64/pgtable.h index 57af6b3..54c7821 100644 --- a/include/asm-sh64/pgtable.h +++ b/include/asm-sh64/pgtable.h @@ -22,7 +22,6 @@ #ifndef __ASSEMBLY__ #include #include #include -#include struct vm_area_struct; diff --git a/include/asm-sh64/signal.h b/include/asm-sh64/signal.h index 2400dc6..a5a2820 100644 --- a/include/asm-sh64/signal.h +++ b/include/asm-sh64/signal.h @@ -74,7 +74,6 @@ #define SIGRTMAX (_NSIG-1) * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -94,7 +93,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-sh64/system.h b/include/asm-sh64/system.h index 3002e98..7606f6e 100644 --- a/include/asm-sh64/system.h +++ b/include/asm-sh64/system.h @@ -14,7 +14,6 @@ #define __ASM_SH64_SYSTEM_H * */ -#include #include #include diff --git a/include/asm-sh64/unistd.h b/include/asm-sh64/unistd.h index 1f8f394..9a1590f 100644 --- a/include/asm-sh64/unistd.h +++ b/include/asm-sh64/unistd.h @@ -344,6 +344,8 @@ #define __NR_inotify_init 318 #define __NR_inotify_add_watch 319 #define __NR_inotify_rm_watch 320 +#ifdef __KERNEL__ + #define NR_syscalls 321 /* user-visible error numbers are in the range -1 - -125: see */ @@ -486,7 +488,6 @@ __asm__ __volatile__ ("!dummy %0 %1 %2 % __syscall_return(type,__sc0); \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT @@ -509,7 +510,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -550,7 +550,7 @@ static inline pid_t wait(int * wait_stat { return waitpid(-1,wait_stat,0); } -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -562,4 +562,5 @@ #ifndef cond_syscall #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") #endif +#endif /* __KERNEL__ */ #endif /* __ASM_SH64_UNISTD_H */ diff --git a/include/asm-sparc/Kbuild b/include/asm-sparc/Kbuild new file mode 100644 index 0000000..e2a57fd --- /dev/null +++ b/include/asm-sparc/Kbuild @@ -0,0 +1,6 @@ +include include/asm-generic/Kbuild.asm + +unifdef-y += fbio.h perfctr.h psr.h +header-y += apc.h asi.h auxio.h bpp.h head.h ipc.h jsflash.h \ + openpromio.h pbm.h pconf.h pgtsun4.h reg.h traps.h \ + turbosparc.h vfc_ioctls.h winmacro.h diff --git a/include/asm-sparc/asmmacro.h b/include/asm-sparc/asmmacro.h index 0d4b65b..a619a4d 100644 --- a/include/asm-sparc/asmmacro.h +++ b/include/asm-sparc/asmmacro.h @@ -6,7 +6,6 @@ #ifndef _SPARC_ASMMACRO_H #define _SPARC_ASMMACRO_H -#include #include #include diff --git a/include/asm-sparc/atomic.h b/include/asm-sparc/atomic.h index e103317..731fa56 100644 --- a/include/asm-sparc/atomic.h +++ b/include/asm-sparc/atomic.h @@ -10,7 +10,6 @@ #ifndef __ARCH_SPARC_ATOMIC__ #define __ARCH_SPARC_ATOMIC__ -#include typedef struct { volatile int counter; } atomic_t; diff --git a/include/asm-sparc/bugs.h b/include/asm-sparc/bugs.h index e652f89..a0f939b 100644 --- a/include/asm-sparc/bugs.h +++ b/include/asm-sparc/bugs.h @@ -5,7 +5,6 @@ */ #include -#include extern unsigned long loops_per_jiffy; diff --git a/include/asm-sparc/cacheflush.h b/include/asm-sparc/cacheflush.h index 4901217..fc632f8 100644 --- a/include/asm-sparc/cacheflush.h +++ b/include/asm-sparc/cacheflush.h @@ -1,7 +1,6 @@ #ifndef _SPARC_CACHEFLUSH_H #define _SPARC_CACHEFLUSH_H -#include #include /* Common for other includes */ // #include from pgalloc.h // #include from pgalloc.h diff --git a/include/asm-sparc/delay.h b/include/asm-sparc/delay.h index 7ec8e9f..48aa70e 100644 --- a/include/asm-sparc/delay.h +++ b/include/asm-sparc/delay.h @@ -7,7 +7,6 @@ #ifndef __SPARC_DELAY_H #define __SPARC_DELAY_H -#include #include static inline void __delay(unsigned long loops) diff --git a/include/asm-sparc/dma-mapping.h b/include/asm-sparc/dma-mapping.h index d7c3b0f..6db83dc 100644 --- a/include/asm-sparc/dma-mapping.h +++ b/include/asm-sparc/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef _ASM_SPARC_DMA_MAPPING_H #define _ASM_SPARC_DMA_MAPPING_H -#include #ifdef CONFIG_PCI #include diff --git a/include/asm-sparc/dma.h b/include/asm-sparc/dma.h index 8ec206a..407b361 100644 --- a/include/asm-sparc/dma.h +++ b/include/asm-sparc/dma.h @@ -7,7 +7,6 @@ #ifndef _ASM_SPARC_DMA_H #define _ASM_SPARC_DMA_H -#include #include #include diff --git a/include/asm-sparc/ebus.h b/include/asm-sparc/ebus.h index 2d6a997..5465288 100644 --- a/include/asm-sparc/ebus.h +++ b/include/asm-sparc/ebus.h @@ -13,13 +13,14 @@ #ifndef _LINUX_IOPORT_H #include #endif #include +#include +#include struct linux_ebus_child { struct linux_ebus_child *next; struct linux_ebus_device *parent; struct linux_ebus *bus; - int prom_node; - char prom_name[64]; + struct device_node *prom_node; struct resource resource[PROMREG_MAX]; int num_addrs; unsigned int irqs[PROMINTR_MAX]; @@ -27,27 +28,27 @@ struct linux_ebus_child { }; struct linux_ebus_device { + struct of_device ofdev; struct linux_ebus_device *next; struct linux_ebus_child *children; struct linux_ebus *bus; - int prom_node; - char prom_name[64]; + struct device_node *prom_node; struct resource resource[PROMREG_MAX]; int num_addrs; unsigned int irqs[PROMINTR_MAX]; int num_irqs; }; +#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev) struct linux_ebus { + struct of_device ofdev; struct linux_ebus *next; struct linux_ebus_device *devices; struct linux_pbm_info *parent; struct pci_dev *self; - int prom_node; - char prom_name[64]; - struct linux_prom_ebus_ranges ebus_ranges[PROMREG_MAX]; - int num_ebus_ranges; + struct device_node *prom_node; }; +#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev) struct linux_ebus_dma { unsigned int dcsr; diff --git a/include/asm-sparc/elf.h b/include/asm-sparc/elf.h index 4a71d7c..83a3dd1 100644 --- a/include/asm-sparc/elf.h +++ b/include/asm-sparc/elf.h @@ -6,7 +6,6 @@ #define __ASMSPARC_ELF_H * ELF register definitions.. */ -#include #include #ifdef __KERNEL__ diff --git a/include/asm-sparc/fixmap.h b/include/asm-sparc/fixmap.h index 9de52b4..f18fc07 100644 --- a/include/asm-sparc/fixmap.h +++ b/include/asm-sparc/fixmap.h @@ -13,7 +13,6 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H -#include #include #include #ifdef CONFIG_HIGHMEM diff --git a/include/asm-sparc/floppy.h b/include/asm-sparc/floppy.h index 7a941b8..c53b332 100644 --- a/include/asm-sparc/floppy.h +++ b/include/asm-sparc/floppy.h @@ -271,7 +271,8 @@ static int sun_fd_request_irq(void) if(!once) { once = 1; - error = request_fast_irq(FLOPPY_IRQ, floppy_hardint, SA_INTERRUPT, "floppy"); + error = request_fast_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy"); return ((error == 0) ? 0 : -1); } else return 0; } diff --git a/include/asm-sparc/hardirq.h b/include/asm-sparc/hardirq.h index 2a668c4..4f63ed8 100644 --- a/include/asm-sparc/hardirq.h +++ b/include/asm-sparc/hardirq.h @@ -7,7 +7,6 @@ #ifndef __SPARC_HARDIRQ_H #define __SPARC_HARDIRQ_H -#include #include #include #include diff --git a/include/asm-sparc/ide.h b/include/asm-sparc/ide.h index 64d8103..a6d735a 100644 --- a/include/asm-sparc/ide.h +++ b/include/asm-sparc/ide.h @@ -11,7 +11,6 @@ #define _SPARC_IDE_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-sparc/io.h b/include/asm-sparc/io.h index a42df20..cab0b85 100644 --- a/include/asm-sparc/io.h +++ b/include/asm-sparc/io.h @@ -249,6 +249,22 @@ extern void __iomem *ioremap(unsigned lo #define ioremap_nocache(X,Y) ioremap((X),(Y)) extern void iounmap(volatile void __iomem *addr); +#define ioread8(X) readb(X) +#define ioread16(X) readw(X) +#define ioread32(X) readl(X) +#define iowrite8(val,X) writeb(val,X) +#define iowrite16(val,X) writew(val,X) +#define iowrite32(val,X) writel(val,X) + +/* Create a virtual mapping cookie for an IO port range */ +extern void __iomem *ioport_map(unsigned long port, unsigned int nr); +extern void ioport_unmap(void __iomem *); + +/* Create a virtual mapping cookie for a PCI BAR (memory or IO) */ +struct pci_dev; +extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); +extern void pci_iounmap(struct pci_dev *dev, void __iomem *); + /* * Bus number may be in res->flags... somewhere. */ diff --git a/include/asm-sparc/irq.h b/include/asm-sparc/irq.h index cee356b..3141ddf 100644 --- a/include/asm-sparc/irq.h +++ b/include/asm-sparc/irq.h @@ -7,7 +7,6 @@ #ifndef _SPARC_IRQ_H #define _SPARC_IRQ_H -#include #include #include /* For NR_CPUS */ #include @@ -17,8 +16,6 @@ #include #define __irq_ino(irq) irq #define __irq_pil(irq) irq -BTFIXUPDEF_CALL(char *, __irq_itoa, unsigned int) -#define __irq_itoa(irq) BTFIXUP_CALL(__irq_itoa)(irq) #define NR_IRQS 16 @@ -184,8 +181,4 @@ #define SUN4M_INT_SBUSBITS 0x00003F80 #define SUN4M_INT_SBUS(x) (1 << (x+7)) #define SUN4M_INT_VME(x) (1 << (x)) -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif diff --git a/include/asm-sparc/mostek.h b/include/asm-sparc/mostek.h index 59b86bc..bd92a78 100644 --- a/include/asm-sparc/mostek.h +++ b/include/asm-sparc/mostek.h @@ -9,7 +9,6 @@ #ifndef _SPARC_MOSTEK_H #define _SPARC_MOSTEK_H -#include #include #include diff --git a/include/asm-sparc/of_device.h b/include/asm-sparc/of_device.h new file mode 100644 index 0000000..80ea31f --- /dev/null +++ b/include/asm-sparc/of_device.h @@ -0,0 +1,79 @@ +#ifndef _ASM_SPARC_OF_DEVICE_H +#define _ASM_SPARC_OF_DEVICE_H +#ifdef __KERNEL__ + +#include +#include +#include +#include + +extern struct bus_type ebus_bus_type; +extern struct bus_type sbus_bus_type; +extern struct bus_type of_bus_type; + +/* + * The of_device is a kind of "base class" that is a superset of + * struct device for use by devices attached to an OF node and + * probed using OF properties. + */ +struct of_device +{ + struct device_node *node; + struct device dev; + struct resource resource[PROMREG_MAX]; + unsigned int irqs[PROMINTR_MAX]; + int num_irqs; + + void *sysdata; + + int slot; + int portid; + int clock_freq; +}; +#define to_of_device(d) container_of(d, struct of_device, dev) + +extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name); +extern void of_iounmap(void __iomem *base, unsigned long size); + +extern struct of_device *of_find_device_by_node(struct device_node *); + +extern const struct of_device_id *of_match_device( + const struct of_device_id *matches, const struct of_device *dev); + +extern struct of_device *of_dev_get(struct of_device *dev); +extern void of_dev_put(struct of_device *dev); + +/* + * An of_platform_driver driver is attached to a basic of_device on + * the ISA, EBUS, and SBUS busses on sparc64. + */ +struct of_platform_driver +{ + char *name; + struct of_device_id *match_table; + struct module *owner; + + int (*probe)(struct of_device* dev, const struct of_device_id *match); + int (*remove)(struct of_device* dev); + + int (*suspend)(struct of_device* dev, pm_message_t state); + int (*resume)(struct of_device* dev); + int (*shutdown)(struct of_device* dev); + + struct device_driver driver; +}; +#define to_of_platform_driver(drv) container_of(drv,struct of_platform_driver, driver) + +extern int of_register_driver(struct of_platform_driver *drv, + struct bus_type *bus); +extern void of_unregister_driver(struct of_platform_driver *drv); +extern int of_device_register(struct of_device *ofdev); +extern void of_device_unregister(struct of_device *ofdev); +extern struct of_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent, + struct bus_type *bus); +extern void of_release_dev(struct device *dev); + +#endif /* __KERNEL__ */ +#endif /* _ASM_SPARC_OF_DEVICE_H */ diff --git a/include/asm-sparc/page.h b/include/asm-sparc/page.h index ec3274b..5bab8a7 100644 --- a/include/asm-sparc/page.h +++ b/include/asm-sparc/page.h @@ -8,7 +8,6 @@ #ifndef _SPARC_PAGE_H #define _SPARC_PAGE_H -#include #ifdef CONFIG_SUN4 #define PAGE_SHIFT 13 #else diff --git a/include/asm-sparc/pbm.h b/include/asm-sparc/pbm.h index 0aba3a8..fedd9c6 100644 --- a/include/asm-sparc/pbm.h +++ b/include/asm-sparc/pbm.h @@ -22,6 +22,7 @@ #define __SPARC_PBM_H #include #include +#include struct linux_pbm_info { int prom_node; @@ -40,7 +41,7 @@ struct linux_pbm_info { */ struct pcidev_cookie { struct linux_pbm_info *pbm; - int prom_node; + struct device_node *prom_node; }; #endif /* !(__SPARC_PBM_H) */ diff --git a/include/asm-sparc/pgalloc.h b/include/asm-sparc/pgalloc.h index 126800a..a449cd4 100644 --- a/include/asm-sparc/pgalloc.h +++ b/include/asm-sparc/pgalloc.h @@ -2,7 +2,6 @@ #ifndef _SPARC_PGALLOC_H #define _SPARC_PGALLOC_H -#include #include #include diff --git a/include/asm-sparc/pgtable.h b/include/asm-sparc/pgtable.h index 9eea8f4..226c647 100644 --- a/include/asm-sparc/pgtable.h +++ b/include/asm-sparc/pgtable.h @@ -11,7 +11,6 @@ #define _SPARC_PGTABLE_H #include -#include #include #include #include diff --git a/include/asm-sparc/prom.h b/include/asm-sparc/prom.h new file mode 100644 index 0000000..86c13dc --- /dev/null +++ b/include/asm-sparc/prom.h @@ -0,0 +1,104 @@ +#ifndef _SPARC_PROM_H +#define _SPARC_PROM_H +#ifdef __KERNEL__ + + +/* + * Definitions for talking to the Open Firmware PROM on + * Power Macintosh computers. + * + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. + * Updates for SPARC32 by David S. Miller + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +typedef u32 phandle; +typedef u32 ihandle; + +struct property { + char *name; + int length; + void *value; + struct property *next; + unsigned long _flags; + unsigned int unique_id; +}; + +struct device_node { + char *name; + char *type; + phandle node; + char *path_component_name; + char *full_name; + + struct property *properties; + struct property *deadprops; /* removed properties */ + struct device_node *parent; + struct device_node *child; + struct device_node *sibling; + struct device_node *next; /* next device of same type */ + struct device_node *allnext; /* next in list of all nodes */ + struct proc_dir_entry *pde; /* this node's proc directory */ + struct kref kref; + unsigned long _flags; + void *data; + unsigned int unique_id; +}; + +/* flag descriptions */ +#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ + +#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) +#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) + +#define OF_BAD_ADDR ((u64)-1) + +static inline void set_node_proc_entry(struct device_node *dn, struct proc_dir_entry *de) +{ + dn->pde = de; +} + +extern struct device_node *of_find_node_by_name(struct device_node *from, + const char *name); +#define for_each_node_by_name(dn, name) \ + for (dn = of_find_node_by_name(NULL, name); dn; \ + dn = of_find_node_by_name(dn, name)) +extern struct device_node *of_find_node_by_type(struct device_node *from, + const char *type); +#define for_each_node_by_type(dn, type) \ + for (dn = of_find_node_by_type(NULL, type); dn; \ + dn = of_find_node_by_type(dn, type)) +extern struct device_node *of_find_compatible_node(struct device_node *from, + const char *type, const char *compat); +extern struct device_node *of_find_node_by_path(const char *path); +extern struct device_node *of_find_node_by_phandle(phandle handle); +extern struct device_node *of_get_parent(const struct device_node *node); +extern struct device_node *of_get_next_child(const struct device_node *node, + struct device_node *prev); +extern struct property *of_find_property(struct device_node *np, + const char *name, + int *lenp); +extern int of_device_is_compatible(struct device_node *device, const char *); +extern void *of_get_property(struct device_node *node, const char *name, + int *lenp); +extern int of_set_property(struct device_node *node, const char *name, void *val, int len); +extern int of_getintprop_default(struct device_node *np, + const char *name, + int def); +extern int of_n_addr_cells(struct device_node *np); +extern int of_n_size_cells(struct device_node *np); + +extern void prom_build_devicetree(void); + +#endif /* __KERNEL__ */ +#endif /* _SPARC_PROM_H */ diff --git a/include/asm-sparc/sbus.h b/include/asm-sparc/sbus.h index a13cddc..d036e44 100644 --- a/include/asm-sparc/sbus.h +++ b/include/asm-sparc/sbus.h @@ -11,7 +11,8 @@ #include #include #include -/* #include */ /* Unused since we use opaque iommu (|io-unit) */ +#include +#include #include /* We scan which devices are on the SBus using the PROM node device @@ -42,18 +43,19 @@ struct sbus_bus; /* Linux SBUS device tables */ struct sbus_dev { - struct sbus_bus *bus; /* Back ptr to sbus */ - struct sbus_dev *next; /* next device on this SBus or null */ - struct sbus_dev *child; /* For ledma and espdma on sun4m */ - struct sbus_dev *parent; /* Parent device if not toplevel */ - int prom_node; /* PROM device tree node for this device */ - char prom_name[64]; /* PROM device name */ + struct of_device ofdev; + struct sbus_bus *bus; + struct sbus_dev *next; + struct sbus_dev *child; + struct sbus_dev *parent; + int prom_node; + char prom_name[64]; int slot; struct resource resource[PROMREG_MAX]; struct linux_prom_registers reg_addrs[PROMREG_MAX]; - int num_registers, ranges_applied; + int num_registers; struct linux_prom_ranges device_ranges[PROMREG_MAX]; int num_device_ranges; @@ -61,9 +63,11 @@ struct sbus_dev { unsigned int irqs[4]; int num_irqs; }; +#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev) /* This struct describes the SBus(s) found on this machine. */ struct sbus_bus { + struct of_device ofdev; void *iommu; /* Opaque IOMMU cookie */ struct sbus_dev *devices; /* Link to devices on this SBus */ struct sbus_bus *next; /* next SBus, if more than one SBus */ @@ -77,6 +81,7 @@ struct sbus_bus { int devid; int board; }; +#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev) extern struct sbus_bus *sbus_root; @@ -102,6 +107,7 @@ #define for_all_sbusdev(device, bus) \ #define sbus_can_dma_64bit(sdev) (0) /* actually, sparc_cpu_model==sun4d */ #define sbus_can_burst64(sdev) (0) /* actually, sparc_cpu_model==sun4d */ extern void sbus_set_sbus64(struct sbus_dev *, int); +extern void sbus_fill_device_irq(struct sbus_dev *); /* These yield IOMMU mappings in consistent mode. */ extern void *sbus_alloc_consistent(struct sbus_dev *, long, u32 *dma_addrp); @@ -139,4 +145,10 @@ extern void sbus_dma_sync_sg_for_device( BTFIXUPDEF_CALL(unsigned int, sbint_to_irq, struct sbus_dev *sdev, unsigned int) #define sbint_to_irq(sdev, sbint) BTFIXUP_CALL(sbint_to_irq)(sdev, sbint) +extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *); +extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *); +extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *); +extern int sbus_arch_preinit(void); +extern void sbus_arch_postinit(void); + #endif /* !(_SPARC_SBUS_H) */ diff --git a/include/asm-sparc/sfp-machine.h b/include/asm-sparc/sfp-machine.h index b4ca2d9..ecfc86a 100644 --- a/include/asm-sparc/sfp-machine.h +++ b/include/asm-sparc/sfp-machine.h @@ -25,7 +25,6 @@ #ifndef _SFP_MACHINE_H #define _SFP_MACHINE_H -#include #define _FP_W_TYPE_SIZE 32 #define _FP_W_TYPE unsigned long diff --git a/include/asm-sparc/signal.h b/include/asm-sparc/signal.h index aa9960a..0ae5084 100644 --- a/include/asm-sparc/signal.h +++ b/include/asm-sparc/signal.h @@ -132,16 +132,13 @@ #define _SV_IGNCHILD 8u /* Do not se * usage of signal stacks by using the (now obsolete) sa_restorer field in * the sigaction structure as a stack pointer. This is now possible due to * the changes in signal handling. LBT 010493. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_SHIRQ flag is for shared interrupt support on PCI and EISA. */ #define SA_NOCLDSTOP _SV_IGNCHILD #define SA_STACK _SV_SSTACK #define SA_ONSTACK _SV_SSTACK #define SA_RESTART _SV_INTR #define SA_ONESHOT _SV_RESET -#define SA_INTERRUPT 0x10u #define SA_NOMASK 0x20u #define SA_NOCLDWAIT 0x100u #define SA_SIGINFO 0x200u diff --git a/include/asm-sparc/smp.h b/include/asm-sparc/smp.h index 98c46e3..b9da9a6 100644 --- a/include/asm-sparc/smp.h +++ b/include/asm-sparc/smp.h @@ -6,7 +6,6 @@ #ifndef _SPARC_SMP_H #define _SPARC_SMP_H -#include #include #include #include @@ -146,6 +145,8 @@ #define raw_smp_processor_id() (current #define prof_multiplier(__cpu) cpu_data(__cpu).multiplier #define prof_counter(__cpu) cpu_data(__cpu).counter +void smp_setup_cpu_possible_map(void); + #endif /* !(__ASSEMBLY__) */ /* Sparc specific messages. */ @@ -162,7 +163,11 @@ #define MBOX_IDLECPU 0xFC #define MBOX_IDLECPU2 0xFD #define MBOX_STOPCPU2 0xFE -#endif /* SMP */ +#else /* SMP */ + +#define smp_setup_cpu_possible_map() do { } while (0) + +#endif /* !(SMP) */ #define NO_PROC_ID 0xFF diff --git a/include/asm-sparc/socket.h b/include/asm-sparc/socket.h index 4e0ce3a..f6c4e5b 100644 --- a/include/asm-sparc/socket.h +++ b/include/asm-sparc/socket.h @@ -48,6 +48,7 @@ #define SO_TIMESTAMP 0x001d #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 0x001e +#define SO_PASSSEC 0x001f /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/include/asm-sparc/spinlock.h b/include/asm-sparc/spinlock.h index 3350c90..1c75474 100644 --- a/include/asm-sparc/spinlock.h +++ b/include/asm-sparc/spinlock.h @@ -154,6 +154,9 @@ #define __raw_write_unlock(rw) do { (rw) #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) #define __raw_read_trylock(lock) generic__raw_read_trylock(lock) +#define __raw_read_can_lock(rw) (!((rw)->lock & 0xff)) +#define __raw_write_can_lock(rw) (!(rw)->lock) + #endif /* !(__ASSEMBLY__) */ #endif /* __SPARC_SPINLOCK_H */ diff --git a/include/asm-sparc/system.h b/include/asm-sparc/system.h index 58dd162..cb7dda1 100644 --- a/include/asm-sparc/system.h +++ b/include/asm-sparc/system.h @@ -1,10 +1,8 @@ /* $Id: system.h,v 1.86 2001/10/30 04:57:10 davem Exp $ */ -#include #ifndef __SPARC_SYSTEM_H #define __SPARC_SYSTEM_H -#include #include #include /* NR_CPUS */ #include diff --git a/include/asm-sparc/timer.h b/include/asm-sparc/timer.h index b16eb73..cb1fa1d 100644 --- a/include/asm-sparc/timer.h +++ b/include/asm-sparc/timer.h @@ -4,7 +4,6 @@ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) */ -#include #ifndef _SPARC_TIMER_H #define _SPARC_TIMER_H diff --git a/include/asm-sparc/tlbflush.h b/include/asm-sparc/tlbflush.h index 5643ca3..4a3b666 100644 --- a/include/asm-sparc/tlbflush.h +++ b/include/asm-sparc/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _SPARC_TLBFLUSH_H #define _SPARC_TLBFLUSH_H -#include #include // #include diff --git a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h index 45a5765..2553762 100644 --- a/include/asm-sparc/unistd.h +++ b/include/asm-sparc/unistd.h @@ -319,6 +319,7 @@ #define __NR_unshare 299 #define __NR_set_robust_list 300 #define __NR_get_robust_list 301 +#ifdef __KERNEL__ /* WARNING: You MAY NOT add syscall numbers larger than 301, since * all of the syscall tables in the Sparc kernel are * sized to have 301 entries (starting at zero). Therefore @@ -455,7 +456,6 @@ errno = -__res; \ return -1; \ } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 @@ -477,7 +477,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGSUSPEND -#endif #ifdef __KERNEL_SYSCALLS__ @@ -534,4 +533,5 @@ #endif /* __KERNEL_SYSCALLS__ */ */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _SPARC_UNISTD_H */ diff --git a/include/asm-sparc/vac-ops.h b/include/asm-sparc/vac-ops.h index 9e01723..ab6f53b 100644 --- a/include/asm-sparc/vac-ops.h +++ b/include/asm-sparc/vac-ops.h @@ -8,7 +8,6 @@ #define _SPARC_VAC_OPS_H * Copyright (C) 1994, David S. Miller (davem@caip.rutgers.edu) */ -#include #include #include #include diff --git a/include/asm-sparc/winmacro.h b/include/asm-sparc/winmacro.h index 557257e..096f3d3 100644 --- a/include/asm-sparc/winmacro.h +++ b/include/asm-sparc/winmacro.h @@ -7,7 +7,6 @@ #ifndef _SPARC_WINMACRO_H #define _SPARC_WINMACRO_H -#include #include /* Store the register window onto the 8-byte aligned area starting diff --git a/include/asm-sparc64/Kbuild b/include/asm-sparc64/Kbuild new file mode 100644 index 0000000..c78d44b --- /dev/null +++ b/include/asm-sparc64/Kbuild @@ -0,0 +1,10 @@ +include include/asm-generic/Kbuild.asm + +ALTARCH := sparc +ARCHDEF := defined __sparc__ && defined __arch64__ +ALTARCHDEF := defined __sparc__ && !defined __arch64__ + +unifdef-y := fbio.h perfctr.h +header-y += apb.h asi.h bbc.h bpp.h display7seg.h envctrl.h floppy.h \ + ipc.h kdebug.h mostek.h openprom.h openpromio.h parport.h \ + pconf.h psrcompat.h pstate.h reg.h uctx.h utrap.h watchdog.h diff --git a/include/asm-sparc64/atomic.h b/include/asm-sparc64/atomic.h index 468eb48..2f0bec2 100644 --- a/include/asm-sparc64/atomic.h +++ b/include/asm-sparc64/atomic.h @@ -8,7 +8,6 @@ #ifndef __ARCH_SPARC64_ATOMIC__ #define __ARCH_SPARC64_ATOMIC__ -#include #include typedef struct { volatile int counter; } atomic_t; diff --git a/include/asm-sparc64/bitops.h b/include/asm-sparc64/bitops.h index 71944b0..3d5e1af 100644 --- a/include/asm-sparc64/bitops.h +++ b/include/asm-sparc64/bitops.h @@ -7,7 +7,6 @@ #ifndef _SPARC64_BITOPS_H #define _SPARC64_BITOPS_H -#include #include #include diff --git a/include/asm-sparc64/bugs.h b/include/asm-sparc64/bugs.h index 360dd04..120422f 100644 --- a/include/asm-sparc64/bugs.h +++ b/include/asm-sparc64/bugs.h @@ -4,7 +4,6 @@ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) */ -#include extern unsigned long loops_per_jiffy; diff --git a/include/asm-sparc64/cacheflush.h b/include/asm-sparc64/cacheflush.h index b3f6165..745d1ab 100644 --- a/include/asm-sparc64/cacheflush.h +++ b/include/asm-sparc64/cacheflush.h @@ -1,7 +1,6 @@ #ifndef _SPARC64_CACHEFLUSH_H #define _SPARC64_CACHEFLUSH_H -#include #include #ifndef __ASSEMBLY__ diff --git a/include/asm-sparc64/cpudata.h b/include/asm-sparc64/cpudata.h index 9d6a6db..f2cc941 100644 --- a/include/asm-sparc64/cpudata.h +++ b/include/asm-sparc64/cpudata.h @@ -74,8 +74,10 @@ struct trap_per_cpu { unsigned long tsb_huge; unsigned long tsb_huge_temp; -/* Dcache line 8: Unused, needed to keep trap_block a power-of-2 in size. */ - unsigned long __pad2[4]; +/* Dcache line 8: IRQ work list, and keep trap_block a power-of-2 in size. */ + unsigned int irq_worklist; + unsigned int __pad1; + unsigned long __pad2[3]; } __attribute__((aligned(64))); extern struct trap_per_cpu trap_block[NR_CPUS]; extern void init_cur_cpu_trap(struct thread_info *); @@ -119,6 +121,7 @@ #define TRAP_PER_CPU_CPU_MONDO_BLOCK_PA #define TRAP_PER_CPU_CPU_LIST_PA 0xc8 #define TRAP_PER_CPU_TSB_HUGE 0xd0 #define TRAP_PER_CPU_TSB_HUGE_TEMP 0xd8 +#define TRAP_PER_CPU_IRQ_WORKLIST 0xe0 #define TRAP_BLOCK_SZ_SHIFT 8 @@ -171,11 +174,8 @@ #define TRAP_LOAD_PGD_PHYS(DEST, TMP) \ /* Clobbers TMP, loads local processor's IRQ work area into DEST. */ #define TRAP_LOAD_IRQ_WORK(DEST, TMP) \ - __GET_CPUID(TMP) \ - sethi %hi(__irq_work), DEST; \ - sllx TMP, 6, TMP; \ - or DEST, %lo(__irq_work), DEST; \ - add DEST, TMP, DEST; + TRAP_LOAD_TRAP_BLOCK(DEST, TMP) \ + add DEST, TRAP_PER_CPU_IRQ_WORKLIST, DEST; /* Clobbers TMP, loads DEST with current thread info pointer. */ #define TRAP_LOAD_THREAD_REG(DEST, TMP) \ @@ -211,9 +211,10 @@ #define TRAP_LOAD_PGD_PHYS(DEST, TMP) \ TRAP_LOAD_TRAP_BLOCK(DEST, TMP) \ ldx [DEST + TRAP_PER_CPU_PGD_PADDR], DEST; +/* Clobbers TMP, loads local processor's IRQ work area into DEST. */ #define TRAP_LOAD_IRQ_WORK(DEST, TMP) \ - sethi %hi(__irq_work), DEST; \ - or DEST, %lo(__irq_work), DEST; + TRAP_LOAD_TRAP_BLOCK(DEST, TMP) \ + add DEST, TRAP_PER_CPU_IRQ_WORKLIST, DEST; #define TRAP_LOAD_THREAD_REG(DEST, TMP) \ TRAP_LOAD_TRAP_BLOCK(DEST, TMP) \ diff --git a/include/asm-sparc64/delay.h b/include/asm-sparc64/delay.h index 2901ea0..a4aae6f 100644 --- a/include/asm-sparc64/delay.h +++ b/include/asm-sparc64/delay.h @@ -11,7 +11,6 @@ #ifndef __SPARC64_DELAY_H #define __SPARC64_DELAY_H -#include #include #include diff --git a/include/asm-sparc64/dma-mapping.h b/include/asm-sparc64/dma-mapping.h index a8d39f2..27c46fb 100644 --- a/include/asm-sparc64/dma-mapping.h +++ b/include/asm-sparc64/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef _ASM_SPARC64_DMA_MAPPING_H #define _ASM_SPARC64_DMA_MAPPING_H -#include #ifdef CONFIG_PCI @@ -161,6 +160,63 @@ static inline void dma_free_coherent(str BUG(); } +static inline void +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + #endif /* PCI */ + +/* Now for the API extensions over the pci_ one */ + +#define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) +#define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +#define dma_is_consistent(d) (1) + +static inline int +dma_get_cache_alignment(void) +{ + /* no easy way to get cache size on all processors, so return + * the maximum possible, to be safe */ + return (1 << INTERNODE_CACHE_SHIFT); +} + +static inline void +dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything, that's all the pci API can do */ + dma_sync_single_for_cpu(dev, dma_handle, offset+size, direction); +} + +static inline void +dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + /* just sync everything, that's all the pci API can do */ + dma_sync_single_for_device(dev, dma_handle, offset+size, direction); +} + +static inline void +dma_cache_sync(void *vaddr, size_t size, + enum dma_data_direction direction) +{ + /* could define this in terms of the dma_cache ... operations, + * but if you get this on a platform, you should convert the platform + * to using the generic device DMA API */ + BUG(); +} + #endif /* _ASM_SPARC64_DMA_MAPPING_H */ diff --git a/include/asm-sparc64/dma.h b/include/asm-sparc64/dma.h index 1aab3c8..27f6597 100644 --- a/include/asm-sparc64/dma.h +++ b/include/asm-sparc64/dma.h @@ -7,7 +7,6 @@ #ifndef _ASM_SPARC64_DMA_H #define _ASM_SPARC64_DMA_H -#include #include #include #include diff --git a/include/asm-sparc64/ebus.h b/include/asm-sparc64/ebus.h index 7a408a0..a4afe9d 100644 --- a/include/asm-sparc64/ebus.h +++ b/include/asm-sparc64/ebus.h @@ -10,13 +10,14 @@ #define __SPARC64_EBUS_H #include #include +#include +#include struct linux_ebus_child { struct linux_ebus_child *next; struct linux_ebus_device *parent; struct linux_ebus *bus; - int prom_node; - char prom_name[64]; + struct device_node *prom_node; struct resource resource[PROMREG_MAX]; int num_addrs; unsigned int irqs[PROMINTR_MAX]; @@ -24,32 +25,29 @@ struct linux_ebus_child { }; struct linux_ebus_device { + struct of_device ofdev; struct linux_ebus_device *next; struct linux_ebus_child *children; struct linux_ebus *bus; - int prom_node; - char prom_name[64]; + struct device_node *prom_node; struct resource resource[PROMREG_MAX]; int num_addrs; unsigned int irqs[PROMINTR_MAX]; int num_irqs; }; +#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev) struct linux_ebus { + struct of_device ofdev; struct linux_ebus *next; struct linux_ebus_device *devices; struct pci_pbm_info *parent; struct pci_dev *self; int index; int is_rio; - int prom_node; - char prom_name[64]; - struct linux_prom_ebus_ranges ebus_ranges[PROMREG_MAX]; - int num_ebus_ranges; - struct linux_prom_ebus_intmap ebus_intmap[PROMREG_MAX]; - int num_ebus_intmap; - struct linux_prom_ebus_intmask ebus_intmask; + struct device_node *prom_node; }; +#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev) struct ebus_dma_info { spinlock_t lock; diff --git a/include/asm-sparc64/fhc.h b/include/asm-sparc64/fhc.h index f29eaa2..9e7f1b0 100644 --- a/include/asm-sparc64/fhc.h +++ b/include/asm-sparc64/fhc.h @@ -10,6 +10,7 @@ #define _SPARC64_FHC_H #include #include +#include #include struct linux_fhc; @@ -34,8 +35,7 @@ struct linux_central { unsigned long clkregs; unsigned long clkver; int slots; - int prom_node; - char prom_name[64]; + struct device_node *prom_node; struct linux_prom_ranges central_ranges[PROMREG_MAX]; int num_central_ranges; @@ -112,8 +112,7 @@ struct linux_fhc { struct fhc_regs fhc_regs; int board; int jtag_master; - int prom_node; - char prom_name[64]; + struct device_node *prom_node; struct linux_prom_ranges fhc_ranges[PROMREG_MAX]; int num_fhc_ranges; diff --git a/include/asm-sparc64/floppy.h b/include/asm-sparc64/floppy.h index 6a95d5d..abf1500 100644 --- a/include/asm-sparc64/floppy.h +++ b/include/asm-sparc64/floppy.h @@ -10,7 +10,6 @@ #ifndef __ASM_SPARC64_FLOPPY_H #define __ASM_SPARC64_FLOPPY_H -#include #include #include @@ -209,7 +208,55 @@ static void sun_fd_enable_dma(void) pdma_areasize = pdma_size; } -extern irqreturn_t sparc_floppy_irq(int, void *, struct pt_regs *); +irqreturn_t sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs) +{ + if (likely(doing_pdma)) { + void __iomem *stat = (void __iomem *) fdc_status; + unsigned char *vaddr = pdma_vaddr; + unsigned long size = pdma_size; + u8 val; + + while (size) { + val = readb(stat); + if (unlikely(!(val & 0x80))) { + pdma_vaddr = vaddr; + pdma_size = size; + return IRQ_HANDLED; + } + if (unlikely(!(val & 0x20))) { + pdma_vaddr = vaddr; + pdma_size = size; + doing_pdma = 0; + goto main_interrupt; + } + if (val & 0x40) { + /* read */ + *vaddr++ = readb(stat + 1); + } else { + unsigned char data = *vaddr++; + + /* write */ + writeb(data, stat + 1); + } + size--; + } + + pdma_vaddr = vaddr; + pdma_size = size; + + /* Send Terminal Count pulse to floppy controller. */ + val = readb(auxio_register); + val |= AUXIO_AUX1_FTCNT; + writeb(val, auxio_register); + val &= ~AUXIO_AUX1_FTCNT; + writeb(val, auxio_register); + + doing_pdma = 0; + } + +main_interrupt: + return floppy_interrupt(irq, dev_cookie, regs); +} static int sun_fd_request_irq(void) { @@ -220,7 +267,7 @@ static int sun_fd_request_irq(void) once = 1; error = request_irq(FLOPPY_IRQ, sparc_floppy_irq, - SA_INTERRUPT, "floppy", NULL); + IRQF_DISABLED, "floppy", NULL); return ((error == 0) ? 0 : -1); } @@ -499,15 +546,14 @@ #endif /* CONFIG_PCI */ #ifdef CONFIG_PCI static int __init ebus_fdthree_p(struct linux_ebus_device *edev) { - if (!strcmp(edev->prom_name, "fdthree")) + if (!strcmp(edev->prom_node->name, "fdthree")) return 1; - if (!strcmp(edev->prom_name, "floppy")) { - char compat[16]; - prom_getstring(edev->prom_node, - "compatible", - compat, sizeof(compat)); - compat[15] = '\0'; - if (!strcmp(compat, "fdthree")) + if (!strcmp(edev->prom_node->name, "floppy")) { + char *compat; + + compat = of_get_property(edev->prom_node, + "compatible", NULL); + if (compat && !strcmp(compat, "fdthree")) return 1; } return 0; @@ -525,12 +571,12 @@ static unsigned long __init isa_floppy_i for_each_isa(isa_br) { for_each_isadev(isa_dev, isa_br) { - if (!strcmp(isa_dev->prom_name, "dma")) { + if (!strcmp(isa_dev->prom_node->name, "dma")) { struct sparc_isa_device *child = isa_dev->child; while (child) { - if (!strcmp(child->prom_name, + if (!strcmp(child->prom_node->name, "floppy")) { isa_dev = child; goto isa_done; @@ -615,6 +661,7 @@ #ifdef CONFIG_PCI struct linux_ebus_device *edev = NULL; unsigned long config = 0; void __iomem *auxio_reg; + char *state_prop; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { @@ -631,9 +678,8 @@ #else #endif } - prom_getproperty(edev->prom_node, "status", - state, sizeof(state)); - if (!strncmp(state, "disabled", 8)) + state_prop = of_get_property(edev->prom_node, "status", NULL); + if (state_prop && !strncmp(state_prop, "disabled", 8)) return 0; FLOPPY_IRQ = edev->irqs[0]; @@ -704,7 +750,7 @@ #endif */ for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_name, "ecpp")) { + if (!strcmp(edev->prom_node->name, "ecpp")) { config = edev->resource[1].start; goto config_done; } diff --git a/include/asm-sparc64/hardirq.h b/include/asm-sparc64/hardirq.h index f0cf713..7c29fd1 100644 --- a/include/asm-sparc64/hardirq.h +++ b/include/asm-sparc64/hardirq.h @@ -12,6 +12,8 @@ #define __ARCH_IRQ_STAT #define local_softirq_pending() \ (local_cpu_data().__softirq_pending) +void ack_bad_irq(unsigned int irq); + #define HARDIRQ_BITS 8 #endif /* !(__SPARC64_HARDIRQ_H) */ diff --git a/include/asm-sparc64/hw_irq.h b/include/asm-sparc64/hw_irq.h index 153cae2..599b3b0 100644 --- a/include/asm-sparc64/hw_irq.h +++ b/include/asm-sparc64/hw_irq.h @@ -1,6 +1,6 @@ #ifndef __ASM_SPARC64_HW_IRQ_H #define __ASM_SPARC64_HW_IRQ_H -/* Dummy include. */ +extern void hw_resend_irq(struct hw_interrupt_type *handler, unsigned int virt_irq); #endif diff --git a/include/asm-sparc64/ide.h b/include/asm-sparc64/ide.h index c393f81..55149cf 100644 --- a/include/asm-sparc64/ide.h +++ b/include/asm-sparc64/ide.h @@ -10,7 +10,6 @@ #define _SPARC64_IDE_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h index de33d6e..905e59b 100644 --- a/include/asm-sparc64/irq.h +++ b/include/asm-sparc64/irq.h @@ -8,7 +8,6 @@ #ifndef _SPARC64_IRQ_H #define _SPARC64_IRQ_H -#include #include #include #include @@ -16,58 +15,6 @@ #include #include #include -struct ino_bucket; - -#define MAX_IRQ_DESC_ACTION 4 - -struct irq_desc { - void (*pre_handler)(struct ino_bucket *, void *, void *); - void *pre_handler_arg1; - void *pre_handler_arg2; - u32 action_active_mask; - struct irqaction action[MAX_IRQ_DESC_ACTION]; -}; - -/* You should not mess with this directly. That's the job of irq.c. - * - * If you make changes here, please update hand coded assembler of - * the vectored interrupt trap handler in entry.S -DaveM - * - * This is currently one DCACHE line, two buckets per L2 cache - * line. Keep this in mind please. - */ -struct ino_bucket { - /* Next handler in per-CPU PIL worklist. We know that - * bucket pointers have the high 32-bits clear, so to - * save space we only store the bits we need. - */ -/*0x00*/unsigned int irq_chain; - - /* PIL to schedule this IVEC at. */ -/*0x04*/unsigned char pil; - - /* If an IVEC arrives while irq_info is NULL, we - * set this to notify request_irq() about the event. - */ -/*0x05*/unsigned char pending; - - /* Miscellaneous flags. */ -/*0x06*/unsigned char flags; - - /* Currently unused. */ -/*0x07*/unsigned char __pad; - - /* Reference to IRQ descriptor for this bucket. */ -/*0x08*/struct irq_desc *irq_info; - - /* Sun5 Interrupt Clear Register. */ -/*0x10*/unsigned long iclr; - - /* Sun5 Interrupt Mapping Register. */ -/*0x18*/unsigned long imap; - -}; - /* IMAP/ICLR register defines */ #define IMAP_VALID 0x80000000 /* IRQ Enabled */ #define IMAP_TID_UPA 0x7c000000 /* UPA TargetID */ @@ -85,36 +32,20 @@ #define ICLR_IDLE 0x00000000 /* Idle st #define ICLR_TRANSMIT 0x00000001 /* Transmit state */ #define ICLR_PENDING 0x00000003 /* Pending state */ -/* Only 8-bits are available, be careful. -DaveM */ -#define IBF_PCI 0x02 /* PSYCHO/SABRE/SCHIZO PCI interrupt. */ -#define IBF_ACTIVE 0x04 /* Interrupt is active and has a handler.*/ -#define IBF_INPROGRESS 0x10 /* IRQ is being serviced. */ - -#define NUM_IVECS (IMAP_INR + 1) -extern struct ino_bucket ivector_table[NUM_IVECS]; - -#define __irq_ino(irq) \ - (((struct ino_bucket *)(unsigned long)(irq)) - &ivector_table[0]) -#define __irq_pil(irq) ((struct ino_bucket *)(unsigned long)(irq))->pil -#define __bucket(irq) ((struct ino_bucket *)(unsigned long)(irq)) -#define __irq(bucket) ((unsigned int)(unsigned long)(bucket)) - -static __inline__ char *__irq_itoa(unsigned int irq) -{ - static char buff[16]; - - sprintf(buff, "%d,%x", __irq_pil(irq), (unsigned int)__irq_ino(irq)); - return buff; -} - -#define NR_IRQS 16 +/* The largest number of unique interrupt sources we support. + * If this needs to ever be larger than 255, you need to change + * the type of ino_bucket->virt_irq as appropriate. + * + * ino_bucket->virt_irq allocation is made during {sun4v_,}build_irq(). + */ +#define NR_IRQS 255 +extern void irq_install_pre_handler(int virt_irq, + void (*func)(unsigned int, void *, void *), + void *arg1, void *arg2); #define irq_canonicalize(irq) (irq) -extern void disable_irq(unsigned int); -#define disable_irq_nosync disable_irq -extern void enable_irq(unsigned int); -extern unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap); -extern unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino, int pil, unsigned char flags); +extern unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap); +extern unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino); extern unsigned int sbus_build_irq(void *sbus, unsigned int ino); static __inline__ void set_softint(unsigned long bits) @@ -140,8 +71,4 @@ static __inline__ unsigned long get_soft return retval; } -struct irqaction; -struct pt_regs; -int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif diff --git a/include/asm-sparc64/isa.h b/include/asm-sparc64/isa.h index 4601bbf..d9728b9 100644 --- a/include/asm-sparc64/isa.h +++ b/include/asm-sparc64/isa.h @@ -9,37 +9,32 @@ #define __SPARC64_ISA_H #include #include +#include +#include struct sparc_isa_bridge; struct sparc_isa_device { + struct of_device ofdev; struct sparc_isa_device *next; struct sparc_isa_device *child; struct sparc_isa_bridge *bus; - int prom_node; - char prom_name[64]; - char compatible[64]; + struct device_node *prom_node; struct resource resource; unsigned int irq; }; +#define to_isa_device(d) container_of(d, struct sparc_isa_device, ofdev.dev) struct sparc_isa_bridge { + struct of_device ofdev; struct sparc_isa_bridge *next; struct sparc_isa_device *devices; struct pci_pbm_info *parent; struct pci_dev *self; int index; - int prom_node; - char prom_name[64]; -#define linux_prom_isa_ranges linux_prom_ebus_ranges - struct linux_prom_isa_ranges isa_ranges[PROMREG_MAX]; - int num_isa_ranges; -#define linux_prom_isa_intmap linux_prom_ebus_intmap - struct linux_prom_isa_intmap isa_intmap[PROMREG_MAX]; - int num_isa_intmap; -#define linux_prom_isa_intmask linux_prom_ebus_intmask - struct linux_prom_isa_intmap isa_intmask; + struct device_node *prom_node; }; +#define to_isa_bridge(d) container_of(d, struct sparc_isa_bridge, ofdev.dev) extern struct sparc_isa_bridge *isa_chain; diff --git a/include/asm-sparc64/kdebug.h b/include/asm-sparc64/kdebug.h index 4040d12..11251bd 100644 --- a/include/asm-sparc64/kdebug.h +++ b/include/asm-sparc64/kdebug.h @@ -17,6 +17,8 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); extern struct atomic_notifier_head sparc64die_chain; extern void bad_trap(struct pt_regs *, long); diff --git a/include/asm-sparc64/kprobes.h b/include/asm-sparc64/kprobes.h index e4efe65..15065af 100644 --- a/include/asm-sparc64/kprobes.h +++ b/include/asm-sparc64/kprobes.h @@ -1,7 +1,6 @@ #ifndef _SPARC64_KPROBES_H #define _SPARC64_KPROBES_H -#include #include #include @@ -13,6 +12,7 @@ #define MAX_INSN_SIZE 2 #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry #define arch_remove_kprobe(p) do {} while (0) +#define ARCH_INACTIVE_KPROBE_COUNT 0 /* Architecture specific copy of original instruction*/ struct arch_specific_insn { diff --git a/include/asm-sparc64/mc146818rtc.h b/include/asm-sparc64/mc146818rtc.h index 75bd572..e9c0fcc 100644 --- a/include/asm-sparc64/mc146818rtc.h +++ b/include/asm-sparc64/mc146818rtc.h @@ -4,7 +4,6 @@ #ifndef __ASM_SPARC64_MC146818RTC_H #define __ASM_SPARC64_MC146818RTC_H -#include #include #ifndef RTC_PORT diff --git a/include/asm-sparc64/mmu.h b/include/asm-sparc64/mmu.h index 2d4f2ea..70af4b6 100644 --- a/include/asm-sparc64/mmu.h +++ b/include/asm-sparc64/mmu.h @@ -1,7 +1,6 @@ #ifndef __MMU_H #define __MMU_H -#include #include #include #include diff --git a/include/asm-sparc64/of_device.h b/include/asm-sparc64/of_device.h new file mode 100644 index 0000000..a62c7b9 --- /dev/null +++ b/include/asm-sparc64/of_device.h @@ -0,0 +1,80 @@ +#ifndef _ASM_SPARC64_OF_DEVICE_H +#define _ASM_SPARC64_OF_DEVICE_H +#ifdef __KERNEL__ + +#include +#include +#include +#include + +extern struct bus_type isa_bus_type; +extern struct bus_type ebus_bus_type; +extern struct bus_type sbus_bus_type; +extern struct bus_type of_bus_type; + +/* + * The of_device is a kind of "base class" that is a superset of + * struct device for use by devices attached to an OF node and + * probed using OF properties. + */ +struct of_device +{ + struct device_node *node; + struct device dev; + struct resource resource[PROMREG_MAX]; + unsigned int irqs[PROMINTR_MAX]; + int num_irqs; + + void *sysdata; + + int slot; + int portid; + int clock_freq; +}; +#define to_of_device(d) container_of(d, struct of_device, dev) + +extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name); +extern void of_iounmap(void __iomem *base, unsigned long size); + +extern struct of_device *of_find_device_by_node(struct device_node *); + +extern const struct of_device_id *of_match_device( + const struct of_device_id *matches, const struct of_device *dev); + +extern struct of_device *of_dev_get(struct of_device *dev); +extern void of_dev_put(struct of_device *dev); + +/* + * An of_platform_driver driver is attached to a basic of_device on + * the ISA, EBUS, and SBUS busses on sparc64. + */ +struct of_platform_driver +{ + char *name; + struct of_device_id *match_table; + struct module *owner; + + int (*probe)(struct of_device* dev, const struct of_device_id *match); + int (*remove)(struct of_device* dev); + + int (*suspend)(struct of_device* dev, pm_message_t state); + int (*resume)(struct of_device* dev); + int (*shutdown)(struct of_device* dev); + + struct device_driver driver; +}; +#define to_of_platform_driver(drv) container_of(drv,struct of_platform_driver, driver) + +extern int of_register_driver(struct of_platform_driver *drv, + struct bus_type *bus); +extern void of_unregister_driver(struct of_platform_driver *drv); +extern int of_device_register(struct of_device *ofdev); +extern void of_device_unregister(struct of_device *ofdev); +extern struct of_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent, + struct bus_type *bus); +extern void of_release_dev(struct device *dev); + +#endif /* __KERNEL__ */ +#endif /* _ASM_SPARC64_OF_DEVICE_H */ diff --git a/include/asm-sparc64/oplib.h b/include/asm-sparc64/oplib.h index c754676..a68b0bb 100644 --- a/include/asm-sparc64/oplib.h +++ b/include/asm-sparc64/oplib.h @@ -9,7 +9,6 @@ #ifndef __SPARC64_OPLIB_H #define __SPARC64_OPLIB_H -#include #include /* OBP version string. */ @@ -324,8 +323,9 @@ extern int prom_pathtoinode(const char * extern int prom_inst2pkg(int); /* CPU probing helpers. */ -int cpu_find_by_instance(int instance, int *prom_node, int *mid); -int cpu_find_by_mid(int mid, int *prom_node); +struct device_node; +int cpu_find_by_instance(int instance, struct device_node **dev_node, int *mid); +int cpu_find_by_mid(int mid, struct device_node **prom_node); /* Client interface level routines. */ extern void prom_set_trap_table(unsigned long tba); diff --git a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h index aabb219..fdf0ceb 100644 --- a/include/asm-sparc64/page.h +++ b/include/asm-sparc64/page.h @@ -3,7 +3,6 @@ #ifndef _SPARC64_PAGE_H #define _SPARC64_PAGE_H -#include #include #if defined(CONFIG_SPARC64_PAGE_SIZE_8KB) diff --git a/include/asm-sparc64/param.h b/include/asm-sparc64/param.h index a1cd497..f0125cf 100644 --- a/include/asm-sparc64/param.h +++ b/include/asm-sparc64/param.h @@ -1,7 +1,6 @@ #ifndef _ASMSPARC64_PARAM_H #define _ASMSPARC64_PARAM_H -#include #ifdef __KERNEL__ # define HZ CONFIG_HZ /* Internal kernel timer frequency */ diff --git a/include/asm-sparc64/parport.h b/include/asm-sparc64/parport.h index 56b5197..d389587 100644 --- a/include/asm-sparc64/parport.h +++ b/include/asm-sparc64/parport.h @@ -67,18 +67,17 @@ static __inline__ unsigned int get_dma_r static int ebus_ecpp_p(struct linux_ebus_device *edev) { - if (!strcmp(edev->prom_name, "ecpp")) + if (!strcmp(edev->prom_node->name, "ecpp")) return 1; - if (!strcmp(edev->prom_name, "parallel")) { - char compat[19]; - prom_getstring(edev->prom_node, - "compatible", - compat, sizeof(compat)); - compat[18] = '\0'; - if (!strcmp(compat, "ecpp")) - return 1; - if (!strcmp(compat, "ns87317-ecpp") && - !strcmp(compat + 13, "ecpp")) + if (!strcmp(edev->prom_node->name, "parallel")) { + char *compat; + + compat = of_get_property(edev->prom_node, + "compatible", NULL); + if (compat && + (!strcmp(compat, "ecpp") || + !strcmp(compat, "ns87317-ecpp") || + !strcmp(compat + 13, "ecpp"))) return 1; } return 0; @@ -94,12 +93,12 @@ static int parport_isa_probe(int count) struct sparc_isa_device *child; unsigned long base; - if (strcmp(isa_dev->prom_name, "dma")) + if (strcmp(isa_dev->prom_node->name, "dma")) continue; child = isa_dev->child; while (child) { - if (!strcmp(child->prom_name, "parallel")) + if (!strcmp(child->prom_node->name, "parallel")) break; child = child->next; } diff --git a/include/asm-sparc64/pbm.h b/include/asm-sparc64/pbm.h index 1396f11..dcfa762 100644 --- a/include/asm-sparc64/pbm.h +++ b/include/asm-sparc64/pbm.h @@ -15,6 +15,8 @@ #include #include #include #include +#include +#include #include /* The abstraction used here is that there are PCI controllers, @@ -153,16 +155,15 @@ #define PBM_CHIP_TYPE_TOMATILLO 5 int chip_revision; /* Name used for top-level resources. */ - char name[64]; + char *name; /* OBP specific information. */ - int prom_node; - char prom_name[64]; - struct linux_prom_pci_ranges pbm_ranges[PROM_PCIRNG_MAX]; + struct device_node *prom_node; + struct linux_prom_pci_ranges *pbm_ranges; int num_pbm_ranges; - struct linux_prom_pci_intmap pbm_intmap[PROM_PCIIMAP_MAX]; + struct linux_prom_pci_intmap *pbm_intmap; int num_pbm_intmap; - struct linux_prom_pci_intmask pbm_intmask; + struct linux_prom_pci_intmask *pbm_intmask; u64 ino_bitmap; /* PBM I/O and Memory space resources. */ @@ -209,7 +210,6 @@ struct pci_controller_info { /* Operations which are controller specific. */ void (*scan_bus)(struct pci_controller_info *); - unsigned int (*irq_build)(struct pci_pbm_info *, struct pci_dev *, unsigned int); void (*base_address_update)(struct pci_dev *, int); void (*resource_adjust)(struct pci_dev *, struct resource *, struct resource *); @@ -217,8 +217,6 @@ struct pci_controller_info { struct pci_ops *pci_ops; unsigned int pci_first_busno; unsigned int pci_last_busno; - - void *starfire_cookie; }; /* PCI devices which are not bridges have this placed in their pci_dev @@ -227,8 +225,8 @@ struct pci_controller_info { */ struct pcidev_cookie { struct pci_pbm_info *pbm; - char prom_name[64]; - int prom_node; + struct device_node *prom_node; + struct of_device *op; struct linux_prom_pci_registers prom_regs[PROMREG_MAX]; int num_prom_regs; struct linux_prom_pci_registers prom_assignments[PROMREG_MAX]; diff --git a/include/asm-sparc64/percpu.h b/include/asm-sparc64/percpu.h index baef13b..ced8cbd 100644 --- a/include/asm-sparc64/percpu.h +++ b/include/asm-sparc64/percpu.h @@ -11,6 +11,7 @@ extern unsigned long __per_cpu_base; extern unsigned long __per_cpu_shift; #define __per_cpu_offset(__cpu) \ (__per_cpu_base + ((unsigned long)(__cpu) << __per_cpu_shift)) +#define per_cpu_offset(x) (__per_cpu_offset(x)) /* Separate out the type, so (int[3], foo) works. */ #define DEFINE_PER_CPU(type, name) \ @@ -21,6 +22,7 @@ register unsigned long __local_per_cpu_o /* var is in discarded region: offset to particular copy we want */ #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset(cpu))) #define __get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __local_per_cpu_offset)) +#define __raw_get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __local_per_cpu_offset)) /* A macro to avoid #include hell... */ #define percpu_modcopy(pcpudst, src, size) \ @@ -37,6 +39,7 @@ #define DEFINE_PER_CPU(type, name) \ #define per_cpu(var, cpu) (*((void)cpu, &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var +#define __raw_get_cpu_var(var) per_cpu__##var #endif /* SMP */ diff --git a/include/asm-sparc64/pgalloc.h b/include/asm-sparc64/pgalloc.h index 12e4a27..010f9cd 100644 --- a/include/asm-sparc64/pgalloc.h +++ b/include/asm-sparc64/pgalloc.h @@ -2,7 +2,6 @@ #ifndef _SPARC64_PGALLOC_H #define _SPARC64_PGALLOC_H -#include #include #include #include diff --git a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h index cd464f4..03f5bc9 100644 --- a/include/asm-sparc64/pgtable.h +++ b/include/asm-sparc64/pgtable.h @@ -14,7 +14,6 @@ #define _SPARC64_PGTABLE_H #include -#include #include #include #include @@ -757,6 +756,8 @@ extern unsigned long *sparc64_valid_addr #define kern_addr_valid(addr) \ (test_bit(__pa((unsigned long)(addr))>>22, sparc64_valid_addr_bitmap)) +extern int page_in_phys_avail(unsigned long paddr); + extern int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot); diff --git a/include/asm-sparc64/pil.h b/include/asm-sparc64/pil.h index 79f827e..7292774 100644 --- a/include/asm-sparc64/pil.h +++ b/include/asm-sparc64/pil.h @@ -5,9 +5,9 @@ #define _SPARC64_PIL_H /* To avoid some locking problems, we hard allocate certain PILs * for SMP cross call messages that must do a etrap/rtrap. * - * A cli() does not block the cross call delivery, so when SMP - * locking is an issue we reschedule the event into a PIL interrupt - * which is blocked by cli(). + * A local_irq_disable() does not block the cross call delivery, so + * when SMP locking is an issue we reschedule the event into a PIL + * interrupt which is blocked by local_irq_disable(). * * In fact any XCALL which has to etrap/rtrap has a problem because * it is difficult to prevent rtrap from running BH's, and that would @@ -17,6 +17,7 @@ #define PIL_SMP_CALL_FUNC 1 #define PIL_SMP_RECEIVE_SIGNAL 2 #define PIL_SMP_CAPTURE 3 #define PIL_SMP_CTX_NEW_VERSION 4 +#define PIL_DEVICE_IRQ 5 #ifndef __ASSEMBLY__ #define PIL_RESERVED(PIL) ((PIL) == PIL_SMP_CALL_FUNC || \ diff --git a/include/asm-sparc64/processor.h b/include/asm-sparc64/processor.h index c6896b8..66dd2fa 100644 --- a/include/asm-sparc64/processor.h +++ b/include/asm-sparc64/processor.h @@ -13,7 +13,6 @@ #define __ASM_SPARC64_PROCESSOR_H */ #define current_text_addr() ({ void *pc; __asm__("rd %%pc, %0" : "=r" (pc)); pc; }) -#include #include #include #include diff --git a/include/asm-sparc64/prom.h b/include/asm-sparc64/prom.h new file mode 100644 index 0000000..99671ed --- /dev/null +++ b/include/asm-sparc64/prom.h @@ -0,0 +1,112 @@ +#ifndef _SPARC64_PROM_H +#define _SPARC64_PROM_H +#ifdef __KERNEL__ + + +/* + * Definitions for talking to the Open Firmware PROM on + * Power Macintosh computers. + * + * Copyright (C) 1996-2005 Paul Mackerras. + * + * Updates for PPC64 by Peter Bergner & David Engebretsen, IBM Corp. + * Updates for SPARC64 by David S. Miller + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +typedef u32 phandle; +typedef u32 ihandle; + +struct property { + char *name; + int length; + void *value; + struct property *next; + unsigned long _flags; + unsigned int unique_id; +}; + +struct of_irq_controller; +struct device_node { + char *name; + char *type; + phandle node; + char *path_component_name; + char *full_name; + + struct property *properties; + struct property *deadprops; /* removed properties */ + struct device_node *parent; + struct device_node *child; + struct device_node *sibling; + struct device_node *next; /* next device of same type */ + struct device_node *allnext; /* next in list of all nodes */ + struct proc_dir_entry *pde; /* this node's proc directory */ + struct kref kref; + unsigned long _flags; + void *data; + unsigned int unique_id; + + struct of_irq_controller *irq_trans; +}; + +struct of_irq_controller { + unsigned int (*irq_build)(struct device_node *, unsigned int, void *); + void *data; +}; + +/* flag descriptions */ +#define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ + +#define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) +#define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) + +#define OF_BAD_ADDR ((u64)-1) + +static inline void set_node_proc_entry(struct device_node *dn, struct proc_dir_entry *de) +{ + dn->pde = de; +} + +extern struct device_node *of_find_node_by_name(struct device_node *from, + const char *name); +#define for_each_node_by_name(dn, name) \ + for (dn = of_find_node_by_name(NULL, name); dn; \ + dn = of_find_node_by_name(dn, name)) +extern struct device_node *of_find_node_by_type(struct device_node *from, + const char *type); +#define for_each_node_by_type(dn, type) \ + for (dn = of_find_node_by_type(NULL, type); dn; \ + dn = of_find_node_by_type(dn, type)) +extern struct device_node *of_find_compatible_node(struct device_node *from, + const char *type, const char *compat); +extern struct device_node *of_find_node_by_path(const char *path); +extern struct device_node *of_find_node_by_phandle(phandle handle); +extern struct device_node *of_get_parent(const struct device_node *node); +extern struct device_node *of_get_next_child(const struct device_node *node, + struct device_node *prev); +extern struct property *of_find_property(struct device_node *np, + const char *name, + int *lenp); +extern int of_device_is_compatible(struct device_node *device, const char *); +extern void *of_get_property(struct device_node *node, const char *name, + int *lenp); +extern int of_set_property(struct device_node *node, const char *name, void *val, int len); +extern int of_getintprop_default(struct device_node *np, + const char *name, + int def); +extern int of_n_addr_cells(struct device_node *np); +extern int of_n_size_cells(struct device_node *np); + +extern void prom_build_devicetree(void); + +#endif /* __KERNEL__ */ +#endif /* _SPARC64_PROM_H */ diff --git a/include/asm-sparc64/sbus.h b/include/asm-sparc64/sbus.h index 48279e1..7efd49d 100644 --- a/include/asm-sparc64/sbus.h +++ b/include/asm-sparc64/sbus.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -42,18 +44,19 @@ struct sbus_bus; /* Linux SBUS device tables */ struct sbus_dev { - struct sbus_bus *bus; /* Our toplevel parent SBUS */ - struct sbus_dev *next; /* Chain of siblings */ - struct sbus_dev *child; /* Chain of children */ - struct sbus_dev *parent;/* Parent device if not toplevel*/ - int prom_node; /* OBP node of this device */ - char prom_name[64]; /* OBP device name property */ - int slot; /* SBUS slot number */ + struct of_device ofdev; + struct sbus_bus *bus; + struct sbus_dev *next; + struct sbus_dev *child; + struct sbus_dev *parent; + int prom_node; + char prom_name[64]; + int slot; struct resource resource[PROMREG_MAX]; struct linux_prom_registers reg_addrs[PROMREG_MAX]; - int num_registers, ranges_applied; + int num_registers; struct linux_prom_ranges device_ranges[PROMREG_MAX]; int num_device_ranges; @@ -61,9 +64,11 @@ struct sbus_dev { unsigned int irqs[4]; int num_irqs; }; +#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev) /* This struct describes the SBus(s) found on this machine. */ struct sbus_bus { + struct of_device ofdev; void *iommu; /* Opaque IOMMU cookie */ struct sbus_dev *devices; /* Tree of SBUS devices */ struct sbus_bus *next; /* Next SBUS in system */ @@ -75,8 +80,8 @@ struct sbus_bus { int num_sbus_ranges; int portid; - void *starfire_cookie; }; +#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev) extern struct sbus_bus *sbus_root; @@ -95,6 +100,7 @@ #define for_all_sbusdev(device, bus) \ #define sbus_can_dma_64bit(sdev) (1) #define sbus_can_burst64(sdev) (1) extern void sbus_set_sbus64(struct sbus_dev *, int); +extern void sbus_fill_device_irq(struct sbus_dev *); /* These yield IOMMU mappings in consistent mode. */ extern void *sbus_alloc_consistent(struct sbus_dev *, size_t, dma_addr_t *dma_addrp); @@ -119,4 +125,10 @@ extern void sbus_dma_sync_sg_for_cpu(str #define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu extern void sbus_dma_sync_sg_for_device(struct sbus_dev *, struct scatterlist *, int, int); +extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *); +extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *); +extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *); +extern int sbus_arch_preinit(void); +extern void sbus_arch_postinit(void); + #endif /* !(_SPARC64_SBUS_H) */ diff --git a/include/asm-sparc64/siginfo.h b/include/asm-sparc64/siginfo.h index df17e47..c96e6c3 100644 --- a/include/asm-sparc64/siginfo.h +++ b/include/asm-sparc64/siginfo.h @@ -11,7 +11,6 @@ #include #ifdef __KERNEL__ -#include #include #ifdef CONFIG_COMPAT diff --git a/include/asm-sparc64/signal.h b/include/asm-sparc64/signal.h index e3059bb..9968871 100644 --- a/include/asm-sparc64/signal.h +++ b/include/asm-sparc64/signal.h @@ -6,7 +6,6 @@ #include #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#include #include #include #include @@ -134,16 +133,13 @@ #define _SV_IGNCHILD 8u /* Do not se * usage of signal stacks by using the (now obsolete) sa_restorer field in * the sigaction structure as a stack pointer. This is now possible due to * the changes in signal handling. LBT 010493. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) - * SA_SHIRQ flag is for shared interrupt support on PCI and EISA. */ #define SA_NOCLDSTOP _SV_IGNCHILD #define SA_STACK _SV_SSTACK #define SA_ONSTACK _SV_SSTACK #define SA_RESTART _SV_INTR #define SA_ONESHOT _SV_RESET -#define SA_INTERRUPT 0x10u #define SA_NOMASK 0x20u #define SA_NOCLDWAIT 0x100u #define SA_SIGINFO 0x200u diff --git a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h index 89d86ec..388249b 100644 --- a/include/asm-sparc64/smp.h +++ b/include/asm-sparc64/smp.h @@ -6,7 +6,6 @@ #ifndef _SPARC64_SMP_H #define _SPARC64_SMP_H -#include #include #include #include diff --git a/include/asm-sparc64/socket.h b/include/asm-sparc64/socket.h index 59987da..754d46a 100644 --- a/include/asm-sparc64/socket.h +++ b/include/asm-sparc64/socket.h @@ -48,6 +48,7 @@ #define SO_TIMESTAMP 0x001d #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_PEERSEC 0x001e +#define SO_PASSSEC 0x001f /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 diff --git a/include/asm-sparc64/spinlock.h b/include/asm-sparc64/spinlock.h index 508c416..bd5ffc7 100644 --- a/include/asm-sparc64/spinlock.h +++ b/include/asm-sparc64/spinlock.h @@ -6,7 +6,6 @@ #ifndef __SPARC64_SPINLOCK_H #define __SPARC64_SPINLOCK_H -#include #include /* For NR_CPUS */ #ifndef __ASSEMBLY__ diff --git a/include/asm-sparc64/starfire.h b/include/asm-sparc64/starfire.h index b606cb2..48b50b5 100644 --- a/include/asm-sparc64/starfire.h +++ b/include/asm-sparc64/starfire.h @@ -14,7 +14,7 @@ extern int this_is_starfire; extern void check_if_starfire(void); extern void starfire_cpu_setup(void); extern int starfire_hard_smp_processor_id(void); -extern void *starfire_hookup(int); +extern void starfire_hookup(int); extern unsigned int starfire_translate(unsigned long imap, unsigned int upaid); #endif diff --git a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h index a18ec87..4ca6860 100644 --- a/include/asm-sparc64/system.h +++ b/include/asm-sparc64/system.h @@ -2,7 +2,6 @@ #ifndef __SPARC64_SYSTEM_H #define __SPARC64_SYSTEM_H -#include #include #include #include diff --git a/include/asm-sparc64/timer.h b/include/asm-sparc64/timer.h index edc8e08..d435594 100644 --- a/include/asm-sparc64/timer.h +++ b/include/asm-sparc64/timer.h @@ -9,7 +9,6 @@ #define _SPARC64_TIMER_H #include -#include struct sparc64_tick_ops { void (*init_tick)(unsigned long); diff --git a/include/asm-sparc64/tlb.h b/include/asm-sparc64/tlb.h index 61c0188..7af1e11 100644 --- a/include/asm-sparc64/tlb.h +++ b/include/asm-sparc64/tlb.h @@ -1,7 +1,6 @@ #ifndef _SPARC64_TLB_H #define _SPARC64_TLB_H -#include #include #include #include diff --git a/include/asm-sparc64/tlbflush.h b/include/asm-sparc64/tlbflush.h index e3a7c45..3487328 100644 --- a/include/asm-sparc64/tlbflush.h +++ b/include/asm-sparc64/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _SPARC64_TLBFLUSH_H #define _SPARC64_TLBFLUSH_H -#include #include #include diff --git a/include/asm-sparc64/topology.h b/include/asm-sparc64/topology.h index 0e234e2..98a6c61 100644 --- a/include/asm-sparc64/topology.h +++ b/include/asm-sparc64/topology.h @@ -1,6 +1,9 @@ #ifndef _ASM_SPARC64_TOPOLOGY_H #define _ASM_SPARC64_TOPOLOGY_H +#include +#define smt_capable() (tlb_type == hypervisor) + #include #endif /* _ASM_SPARC64_TOPOLOGY_H */ diff --git a/include/asm-sparc64/ttable.h b/include/asm-sparc64/ttable.h index 2d5e3c4..f235260 100644 --- a/include/asm-sparc64/ttable.h +++ b/include/asm-sparc64/ttable.h @@ -2,7 +2,6 @@ #ifndef _SPARC64_TTABLE_H #define _SPARC64_TTABLE_H -#include #include #ifdef __ASSEMBLY__ diff --git a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h index 998ef4a..badc73f 100644 --- a/include/asm-sparc64/unistd.h +++ b/include/asm-sparc64/unistd.h @@ -321,6 +321,7 @@ #define __NR_unshare 299 #define __NR_set_robust_list 300 #define __NR_get_robust_list 301 +#ifdef __KERNEL__ /* WARNING: You MAY NOT add syscall numbers larger than 301, since * all of the syscall tables in the Sparc kernel are * sized to have 301 entries (starting at zero). Therefore @@ -487,7 +488,6 @@ asmlinkage long sys_rt_sigaction(int sig #endif /* __KERNEL_SYSCALLS__ */ -#ifdef __KERNEL__ /* sysconf options, for SunOS compatibility */ #define _SC_ARG_MAX 1 #define _SC_CHILD_MAX 2 @@ -521,7 +521,6 @@ #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGSUSPEND #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND -#endif /* * "Conditional" syscalls @@ -531,4 +530,5 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif /* _SPARC64_UNISTD_H */ diff --git a/include/asm-sparc64/vdev.h b/include/asm-sparc64/vdev.h deleted file mode 100644 index 996e6be..0000000 --- a/include/asm-sparc64/vdev.h +++ /dev/null @@ -1,16 +0,0 @@ -/* vdev.h: SUN4V virtual device interfaces and defines. - * - * Copyright (C) 2006 David S. Miller - */ - -#ifndef _SPARC64_VDEV_H -#define _SPARC64_VDEV_H - -#include - -extern u32 sun4v_vdev_devhandle; -extern int sun4v_vdev_root; - -extern unsigned int sun4v_vdev_device_interrupt(unsigned int); - -#endif /* !(_SPARC64_VDEV_H) */ diff --git a/include/asm-sparc64/vga.h b/include/asm-sparc64/vga.h index 9c57eb3..c69d5b2 100644 --- a/include/asm-sparc64/vga.h +++ b/include/asm-sparc64/vga.h @@ -28,6 +28,6 @@ static inline u16 scr_readw(const u16 *a return *addr; } -#define VGA_MAP_MEM(x) (x) +#define VGA_MAP_MEM(x,s) (x) #endif diff --git a/include/asm-um/Kbuild b/include/asm-um/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-um/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-um/a.out.h b/include/asm-um/a.out.h index 7c26265..50cee7b 100644 --- a/include/asm-um/a.out.h +++ b/include/asm-um/a.out.h @@ -1,7 +1,6 @@ #ifndef __UM_A_OUT_H #define __UM_A_OUT_H -#include "linux/config.h" #include "asm/arch/a.out.h" #include "choose-mode.h" diff --git a/include/asm-um/cache.h b/include/asm-um/cache.h index 3d05870..19e1bdd 100644 --- a/include/asm-um/cache.h +++ b/include/asm-um/cache.h @@ -1,7 +1,6 @@ #ifndef __UM_CACHE_H #define __UM_CACHE_H -#include #if defined(CONFIG_UML_X86) && !defined(CONFIG_64BIT) # define L1_CACHE_SHIFT (CONFIG_X86_L1_CACHE_SHIFT) diff --git a/include/asm-um/elf-ppc.h b/include/asm-um/elf-ppc.h index 2998cf9..9971113 100644 --- a/include/asm-um/elf-ppc.h +++ b/include/asm-um/elf-ppc.h @@ -1,7 +1,6 @@ #ifndef __UM_ELF_PPC_H #define __UM_ELF_PPC_H -#include "linux/config.h" extern long elf_aux_hwcap; #define ELF_HWCAP (elf_aux_hwcap) diff --git a/include/asm-um/fixmap.h b/include/asm-um/fixmap.h index ae0ca39..d352a35 100644 --- a/include/asm-um/fixmap.h +++ b/include/asm-um/fixmap.h @@ -1,7 +1,6 @@ #ifndef __UM_FIXMAP_H #define __UM_FIXMAP_H -#include #include #include #include diff --git a/include/asm-um/hardirq.h b/include/asm-um/hardirq.h index 1224b26..313ebb8 100644 --- a/include/asm-um/hardirq.h +++ b/include/asm-um/hardirq.h @@ -3,7 +3,6 @@ #ifndef __ASM_UM_HARDIRQ_H #define __ASM_UM_HARDIRQ_H -#include #include #include diff --git a/include/asm-um/hw_irq.h b/include/asm-um/hw_irq.h index 4ee38c0..1cf84cf 100644 --- a/include/asm-um/hw_irq.h +++ b/include/asm-um/hw_irq.h @@ -4,7 +4,4 @@ #define _ASM_UM_HW_IRQ_H #include "asm/irq.h" #include "asm/archparam.h" -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) -{} - #endif diff --git a/include/asm-um/io.h b/include/asm-um/io.h index 1934d93..44e8b8c 100644 --- a/include/asm-um/io.h +++ b/include/asm-um/io.h @@ -45,8 +45,13 @@ static inline void writel(unsigned int b { *(volatile unsigned int __force *) addr = b; } +static inline void writeq(unsigned int b, volatile void __iomem *addr) +{ + *(volatile unsigned long long __force *) addr = b; +} #define __raw_writeb writeb #define __raw_writew writew #define __raw_writel writel +#define __raw_writeq writeq #endif diff --git a/include/asm-um/kmap_types.h b/include/asm-um/kmap_types.h index 0b22ad7..6c03acd 100644 --- a/include/asm-um/kmap_types.h +++ b/include/asm-um/kmap_types.h @@ -6,6 +6,24 @@ #ifndef __UM_KMAP_TYPES_H #define __UM_KMAP_TYPES_H -#include "asm/arch/kmap_types.h" +/* No more #include "asm/arch/kmap_types.h" ! */ + +enum km_type { + KM_BOUNCE_READ, + KM_SKB_SUNRPC_DATA, + KM_SKB_DATA_SOFTIRQ, + KM_USER0, + KM_USER1, + KM_UML_USERCOPY, /* UML specific, for copy_*_user - used in do_op_one_page */ + KM_BIO_SRC_IRQ, + KM_BIO_DST_IRQ, + KM_PTE0, + KM_PTE1, + KM_IRQ0, + KM_IRQ1, + KM_SOFTIRQ0, + KM_SOFTIRQ1, + KM_TYPE_NR +}; #endif diff --git a/include/asm-um/linkage.h b/include/asm-um/linkage.h index e3d62dc..78b8624 100644 --- a/include/asm-um/linkage.h +++ b/include/asm-um/linkage.h @@ -3,7 +3,6 @@ #define __ASM_UM_LINKAGE_H #include "asm/arch/linkage.h" -#include /* will pick sane defaults */ #ifdef CONFIG_GPROF diff --git a/include/asm-um/mmu_context.h b/include/asm-um/mmu_context.h index 9a0e48e..f709c78 100644 --- a/include/asm-um/mmu_context.h +++ b/include/asm-um/mmu_context.h @@ -7,7 +7,6 @@ #ifndef __UM_MMU_CONTEXT_H #define __UM_MMU_CONTEXT_H #include "linux/sched.h" -#include "linux/config.h" #include "choose-mode.h" #include "um_mmu.h" diff --git a/include/asm-um/page.h b/include/asm-um/page.h index 4136433..4296d31 100644 --- a/include/asm-um/page.h +++ b/include/asm-um/page.h @@ -9,7 +9,6 @@ #define __UM_PAGE_H struct page; -#include #include /* PAGE_SHIFT determines the page size */ diff --git a/include/asm-um/pgalloc.h b/include/asm-um/pgalloc.h index ea49411..34ab268 100644 --- a/include/asm-um/pgalloc.h +++ b/include/asm-um/pgalloc.h @@ -8,7 +8,6 @@ #ifndef __UM_PGALLOC_H #define __UM_PGALLOC_H -#include "linux/config.h" #include "linux/mm.h" #include "asm/fixmap.h" diff --git a/include/asm-um/processor-generic.h b/include/asm-um/processor-generic.h index da07a69..824c288 100644 --- a/include/asm-um/processor-generic.h +++ b/include/asm-um/processor-generic.h @@ -10,7 +10,6 @@ struct pt_regs; struct task_struct; -#include "linux/config.h" #include "asm/ptrace.h" #include "choose-mode.h" #include "registers.h" diff --git a/include/asm-um/ptrace-generic.h b/include/asm-um/ptrace-generic.h index 5034843..a36f537 100644 --- a/include/asm-um/ptrace-generic.h +++ b/include/asm-um/ptrace-generic.h @@ -8,7 +8,6 @@ #define __UM_PTRACE_GENERIC_H #ifndef __ASSEMBLY__ -#include "linux/config.h" #define pt_regs pt_regs_subarch #define show_regs show_regs_subarch diff --git a/include/asm-um/smp.h b/include/asm-um/smp.h index aeda665..ca55226 100644 --- a/include/asm-um/smp.h +++ b/include/asm-um/smp.h @@ -3,7 +3,6 @@ #define __UM_SMP_H #ifdef CONFIG_SMP -#include "linux/config.h" #include "linux/bitops.h" #include "asm/current.h" #include "linux/cpumask.h" diff --git a/include/asm-um/thread_info.h b/include/asm-um/thread_info.h index f166b98..261e2f4 100644 --- a/include/asm-um/thread_info.h +++ b/include/asm-um/thread_info.h @@ -8,7 +8,6 @@ #define __UM_THREAD_INFO_H #ifndef __ASSEMBLY__ -#include #include #include diff --git a/include/asm-v850/Kbuild b/include/asm-v850/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-v850/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-v850/atomic.h b/include/asm-v850/atomic.h index 166df00..e4e57de 100644 --- a/include/asm-v850/atomic.h +++ b/include/asm-v850/atomic.h @@ -14,7 +14,6 @@ #ifndef __V850_ATOMIC_H__ #define __V850_ATOMIC_H__ -#include #include diff --git a/include/asm-v850/bitops.h b/include/asm-v850/bitops.h index 1f6fd5a..1fa99ba 100644 --- a/include/asm-v850/bitops.h +++ b/include/asm-v850/bitops.h @@ -14,7 +14,6 @@ #ifndef __V850_BITOPS_H__ #define __V850_BITOPS_H__ -#include #include /* unlikely */ #include /* swab32 */ #include /* interrupt enable/disable */ diff --git a/include/asm-v850/dma-mapping.h b/include/asm-v850/dma-mapping.h index c63fb50..1cc42c6 100644 --- a/include/asm-v850/dma-mapping.h +++ b/include/asm-v850/dma-mapping.h @@ -1,7 +1,6 @@ #ifndef __V850_DMA_MAPPING_H__ #define __V850_DMA_MAPPING_H__ -#include #ifdef CONFIG_PCI #include diff --git a/include/asm-v850/hardirq.h b/include/asm-v850/hardirq.h index d98488c..04e2012 100644 --- a/include/asm-v850/hardirq.h +++ b/include/asm-v850/hardirq.h @@ -1,7 +1,6 @@ #ifndef __V850_HARDIRQ_H__ #define __V850_HARDIRQ_H__ -#include #include #include diff --git a/include/asm-v850/hw_irq.h b/include/asm-v850/hw_irq.h index a8aab43..043e94b 100644 --- a/include/asm-v850/hw_irq.h +++ b/include/asm-v850/hw_irq.h @@ -1,8 +1,4 @@ #ifndef __V850_HW_IRQ_H__ #define __V850_HW_IRQ_H__ -static inline void hw_resend_irq (struct hw_interrupt_type *h, unsigned int i) -{ -} - #endif /* __V850_HW_IRQ_H__ */ diff --git a/include/asm-v850/irq.h b/include/asm-v850/irq.h index 4443115..1bf096d 100644 --- a/include/asm-v850/irq.h +++ b/include/asm-v850/irq.h @@ -62,8 +62,6 @@ extern void disable_irq (unsigned int ir /* Disable an irq without waiting. */ extern void disable_irq_nosync (unsigned int irq); -extern int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); - #endif /* !__ASSEMBLY__ */ #endif /* __V850_IRQ_H__ */ diff --git a/include/asm-v850/machdep.h b/include/asm-v850/machdep.h index 98d8bf6..f1e3b8b 100644 --- a/include/asm-v850/machdep.h +++ b/include/asm-v850/machdep.h @@ -14,7 +14,6 @@ #ifndef __V850_MACHDEP_H__ #define __V850_MACHDEP_H__ -#include /* chips */ #ifdef CONFIG_V850E_MA1 diff --git a/include/asm-v850/pgtable.h b/include/asm-v850/pgtable.h index 3cf8775..1ea2a90 100644 --- a/include/asm-v850/pgtable.h +++ b/include/asm-v850/pgtable.h @@ -3,7 +3,6 @@ #define __V850_PGTABLE_H__ #include -#include #include diff --git a/include/asm-v850/processor.h b/include/asm-v850/processor.h index 2d31308..6965b66 100644 --- a/include/asm-v850/processor.h +++ b/include/asm-v850/processor.h @@ -14,7 +14,6 @@ #ifndef __V850_PROCESSOR_H__ #define __V850_PROCESSOR_H__ -#include #ifndef __ASSEMBLY__ /* is not asm-safe. */ #include #endif diff --git a/include/asm-v850/serial.h b/include/asm-v850/serial.h index 8c2a609..36d8f4c 100644 --- a/include/asm-v850/serial.h +++ b/include/asm-v850/serial.h @@ -6,7 +6,6 @@ * Copyright (C) 1999 by Ralf Baechle * Copyright (C) 1999, 2000 Silicon Graphics, Inc. */ -#include #ifdef CONFIG_RTE_CB_ME2 diff --git a/include/asm-v850/signal.h b/include/asm-v850/signal.h index cb52caa..a38df08 100644 --- a/include/asm-v850/signal.h +++ b/include/asm-v850/signal.h @@ -77,7 +77,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -97,7 +96,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-v850/socket.h b/include/asm-v850/socket.h index 0240d36..0dfe55a 100644 --- a/include/asm-v850/socket.h +++ b/include/asm-v850/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* __V850_SOCKET_H__ */ diff --git a/include/asm-v850/unistd.h b/include/asm-v850/unistd.h index 82460a7..bcb44bf 100644 --- a/include/asm-v850/unistd.h +++ b/include/asm-v850/unistd.h @@ -14,8 +14,6 @@ #ifndef __V850_UNISTD_H__ #define __V850_UNISTD_H__ -#include - #define __NR_restart_syscall 0 #define __NR_exit 1 #define __NR_fork 2 @@ -237,10 +235,9 @@ #define SYSCALL_CLOBBERS "r1", "r5", "r1 except the syscall number (r12). */ #define SYSCALL_SHORT_CLOBBERS SYSCALL_CLOBBERS, "r13", "r14" +#ifdef __KERNEL__ -/* User programs sometimes end up including this header file - (indirectly, via uClibc header files), so I'm a bit nervous just - including . */ +#include #define __syscall_return(type, res) \ do { \ @@ -368,7 +365,6 @@ type name (atype a, btype b, ctype c, dt } -#ifdef __KERNEL__ #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_STAT64 @@ -389,7 +385,6 @@ #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_SIGPENDING #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif #ifdef __KERNEL_SYSCALLS__ @@ -440,7 +435,7 @@ asmlinkage long sys_rt_sigaction(int sig struct sigaction __user *oact, size_t sigsetsize); -#endif +#endif /* __KERNEL_SYSCALLS__ */ /* * "Conditional" syscalls @@ -455,4 +450,5 @@ #define cond_syscall(name) \ void name (void) __attribute__ ((weak, alias ("sys_ni_syscall"))); #endif +#endif /* __KERNEL__ */ #endif /* __V850_UNISTD_H__ */ diff --git a/include/asm-v850/v850e_uart.h b/include/asm-v850/v850e_uart.h index 5930d59..5182fb4 100644 --- a/include/asm-v850/v850e_uart.h +++ b/include/asm-v850/v850e_uart.h @@ -19,7 +19,6 @@ #ifndef __V850_V850E_UART_H__ #define __V850_V850E_UART_H__ -#include #include #include diff --git a/include/asm-x86_64/Kbuild b/include/asm-x86_64/Kbuild new file mode 100644 index 0000000..dc4d101 --- /dev/null +++ b/include/asm-x86_64/Kbuild @@ -0,0 +1,11 @@ +include include/asm-generic/Kbuild.asm + +ALTARCH := i386 +ARCHDEF := defined __x86_64__ +ALTARCHDEF := defined __i386__ + +header-y += boot.h bootsetup.h cpufeature.h debugreg.h ldt.h \ + msr.h prctl.h setup.h sigcontext32.h ucontext.h \ + vsyscall32.h + +unifdef-y += mce.h mtrr.h vsyscall.h diff --git a/include/asm-x86_64/acpi.h b/include/asm-x86_64/acpi.h index aa1c7b2..2c95a31 100644 --- a/include/asm-x86_64/acpi.h +++ b/include/asm-x86_64/acpi.h @@ -162,6 +162,8 @@ extern int acpi_pci_disabled; extern u8 x86_acpiid_to_apicid[]; +#define ARCH_HAS_POWER_INIT 1 + extern int acpi_skip_timer_override; #endif /*__KERNEL__*/ diff --git a/include/asm-x86_64/alternative.h b/include/asm-x86_64/alternative.h new file mode 100644 index 0000000..aa67bfd --- /dev/null +++ b/include/asm-x86_64/alternative.h @@ -0,0 +1,155 @@ +#ifndef _X86_64_ALTERNATIVE_H +#define _X86_64_ALTERNATIVE_H + +#ifdef __KERNEL__ + +#include + +struct alt_instr { + u8 *instr; /* original instruction */ + u8 *replacement; + u8 cpuid; /* cpuid bit set for replacement */ + u8 instrlen; /* length of original instruction */ + u8 replacementlen; /* length of new instruction, <= instrlen */ + u8 pad[5]; +}; + +extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); + +struct module; + +#ifdef CONFIG_SMP +extern void alternatives_smp_module_add(struct module *mod, char *name, + void *locks, void *locks_end, + void *text, void *text_end); +extern void alternatives_smp_module_del(struct module *mod); +extern void alternatives_smp_switch(int smp); +#else +static inline void alternatives_smp_module_add(struct module *mod, char *name, + void *locks, void *locks_end, + void *text, void *text_end) {} +static inline void alternatives_smp_module_del(struct module *mod) {} +static inline void alternatives_smp_switch(int smp) {} +#endif + +#endif + +/* + * Alternative instructions for different CPU types or capabilities. + * + * This allows to use optimized instructions even on generic binary + * kernels. + * + * length of oldinstr must be longer or equal the length of newinstr + * It can be padded with nops as needed. + * + * For non barrier like inlines please define new variants + * without volatile and memory clobber. + */ +#define alternative(oldinstr, newinstr, feature) \ + asm volatile ("661:\n\t" oldinstr "\n662:\n" \ + ".section .altinstructions,\"a\"\n" \ + " .align 8\n" \ + " .quad 661b\n" /* label */ \ + " .quad 663f\n" /* new instruction */ \ + " .byte %c0\n" /* feature bit */ \ + " .byte 662b-661b\n" /* sourcelen */ \ + " .byte 664f-663f\n" /* replacementlen */ \ + ".previous\n" \ + ".section .altinstr_replacement,\"ax\"\n" \ + "663:\n\t" newinstr "\n664:\n" /* replacement */ \ + ".previous" :: "i" (feature) : "memory") + +/* + * Alternative inline assembly with input. + * + * Pecularities: + * No memory clobber here. + * Argument numbers start with 1. + * Best is to use constraints that are fixed size (like (%1) ... "r") + * If you use variable sized constraints like "m" or "g" in the + * replacement make sure to pad to the worst case length. + */ +#define alternative_input(oldinstr, newinstr, feature, input...) \ + asm volatile ("661:\n\t" oldinstr "\n662:\n" \ + ".section .altinstructions,\"a\"\n" \ + " .align 8\n" \ + " .quad 661b\n" /* label */ \ + " .quad 663f\n" /* new instruction */ \ + " .byte %c0\n" /* feature bit */ \ + " .byte 662b-661b\n" /* sourcelen */ \ + " .byte 664f-663f\n" /* replacementlen */ \ + ".previous\n" \ + ".section .altinstr_replacement,\"ax\"\n" \ + "663:\n\t" newinstr "\n664:\n" /* replacement */ \ + ".previous" :: "i" (feature), ##input) + +/* Like alternative_input, but with a single output argument */ +#define alternative_io(oldinstr, newinstr, feature, output, input...) \ + asm volatile ("661:\n\t" oldinstr "\n662:\n" \ + ".section .altinstructions,\"a\"\n" \ + " .align 8\n" \ + " .quad 661b\n" /* label */ \ + " .quad 663f\n" /* new instruction */ \ + " .byte %c[feat]\n" /* feature bit */ \ + " .byte 662b-661b\n" /* sourcelen */ \ + " .byte 664f-663f\n" /* replacementlen */ \ + ".previous\n" \ + ".section .altinstr_replacement,\"ax\"\n" \ + "663:\n\t" newinstr "\n664:\n" /* replacement */ \ + ".previous" : output : [feat] "i" (feature), ##input) + +/* + * Alternative inline assembly for SMP. + * + * alternative_smp() takes two versions (SMP first, UP second) and is + * for more complex stuff such as spinlocks. + * + * The LOCK_PREFIX macro defined here replaces the LOCK and + * LOCK_PREFIX macros used everywhere in the source tree. + * + * SMP alternatives use the same data structures as the other + * alternatives and the X86_FEATURE_UP flag to indicate the case of a + * UP system running a SMP kernel. The existing apply_alternatives() + * works fine for patching a SMP kernel for UP. + * + * The SMP alternative tables can be kept after boot and contain both + * UP and SMP versions of the instructions to allow switching back to + * SMP at runtime, when hotplugging in a new CPU, which is especially + * useful in virtualized environments. + * + * The very common lock prefix is handled as special case in a + * separate table which is a pure address list without replacement ptr + * and size information. That keeps the table sizes small. + */ + +#ifdef CONFIG_SMP +#define alternative_smp(smpinstr, upinstr, args...) \ + asm volatile ("661:\n\t" smpinstr "\n662:\n" \ + ".section .smp_altinstructions,\"a\"\n" \ + " .align 8\n" \ + " .quad 661b\n" /* label */ \ + " .quad 663f\n" /* new instruction */ \ + " .byte 0x66\n" /* X86_FEATURE_UP */ \ + " .byte 662b-661b\n" /* sourcelen */ \ + " .byte 664f-663f\n" /* replacementlen */ \ + ".previous\n" \ + ".section .smp_altinstr_replacement,\"awx\"\n" \ + "663:\n\t" upinstr "\n" /* replacement */ \ + "664:\n\t.fill 662b-661b,1,0x42\n" /* space for original */ \ + ".previous" : args) + +#define LOCK_PREFIX \ + ".section .smp_locks,\"a\"\n" \ + " .align 8\n" \ + " .quad 661f\n" /* address */ \ + ".previous\n" \ + "661:\n\tlock; " + +#else /* ! CONFIG_SMP */ +#define alternative_smp(smpinstr, upinstr, args...) \ + asm volatile (upinstr : args) +#define LOCK_PREFIX "" +#endif + +#endif /* _X86_64_ALTERNATIVE_H */ diff --git a/include/asm-x86_64/apic.h b/include/asm-x86_64/apic.h index bdbd893..9c96a0a 100644 --- a/include/asm-x86_64/apic.h +++ b/include/asm-x86_64/apic.h @@ -1,7 +1,6 @@ #ifndef __ASM_APIC_H #define __ASM_APIC_H -#include #include #include #include @@ -50,7 +49,8 @@ static __inline unsigned int apic_read(u static __inline__ void apic_wait_icr_idle(void) { - while ( apic_read( APIC_ICR ) & APIC_ICR_BUSY ); + while (apic_read( APIC_ICR ) & APIC_ICR_BUSY) + cpu_relax(); } static inline void ack_APIC_irq(void) @@ -80,30 +80,23 @@ extern void init_apic_mappings (void); extern void smp_local_timer_interrupt (struct pt_regs * regs); extern void setup_boot_APIC_clock (void); extern void setup_secondary_APIC_clock (void); -extern void setup_apic_nmi_watchdog (void); -extern int reserve_lapic_nmi(void); -extern void release_lapic_nmi(void); -extern void disable_timer_nmi_watchdog(void); -extern void enable_timer_nmi_watchdog(void); -extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); extern int APIC_init_uniprocessor (void); extern void disable_APIC_timer(void); extern void enable_APIC_timer(void); extern void clustered_apic_check(void); -extern void nmi_watchdog_default(void); -extern int setup_nmi_watchdog(char *); +extern void setup_APIC_extened_lvt(unsigned char lvt_off, unsigned char vector, + unsigned char msg_type, unsigned char mask); -extern unsigned int nmi_watchdog; -#define NMI_DEFAULT -1 -#define NMI_NONE 0 -#define NMI_IO_APIC 1 -#define NMI_LOCAL_APIC 2 -#define NMI_INVALID 3 +#define K8_APIC_EXT_LVT_BASE 0x500 +#define K8_APIC_EXT_INT_MSG_FIX 0x0 +#define K8_APIC_EXT_INT_MSG_SMI 0x2 +#define K8_APIC_EXT_INT_MSG_NMI 0x4 +#define K8_APIC_EXT_INT_MSG_EXT 0x7 +#define K8_APIC_EXT_LVT_ENTRY_THRESHOLD 0 extern int disable_timer_pin_1; -extern void setup_threshold_lvt(unsigned long lvt_off); void smp_send_timer_broadcast_ipi(void); void switch_APIC_timer_to_ipi(void *cpumask); diff --git a/include/asm-x86_64/apicdef.h b/include/asm-x86_64/apicdef.h index 5a48e9b..1dd4006 100644 --- a/include/asm-x86_64/apicdef.h +++ b/include/asm-x86_64/apicdef.h @@ -137,8 +137,6 @@ #define NUM_APIC_CLUSTERS ((BAD_APICID + */ #define u32 unsigned int -#define lapic ((volatile struct local_apic *)APIC_BASE) - struct local_apic { /*000*/ struct { u32 __reserved[4]; } __reserved_01; diff --git a/include/asm-x86_64/atomic.h b/include/asm-x86_64/atomic.h index cecbf7b..007e88d 100644 --- a/include/asm-x86_64/atomic.h +++ b/include/asm-x86_64/atomic.h @@ -1,8 +1,7 @@ #ifndef __ARCH_X86_64_ATOMIC__ #define __ARCH_X86_64_ATOMIC__ -#include -#include +#include /* atomic_t should be 32 bit signed type */ @@ -53,7 +52,7 @@ #define atomic_set(v,i) (((v)->counter) static __inline__ void atomic_add(int i, atomic_t *v) { __asm__ __volatile__( - LOCK "addl %1,%0" + LOCK_PREFIX "addl %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } @@ -68,7 +67,7 @@ static __inline__ void atomic_add(int i, static __inline__ void atomic_sub(int i, atomic_t *v) { __asm__ __volatile__( - LOCK "subl %1,%0" + LOCK_PREFIX "subl %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } @@ -87,7 +86,7 @@ static __inline__ int atomic_sub_and_tes unsigned char c; __asm__ __volatile__( - LOCK "subl %2,%0; sete %1" + LOCK_PREFIX "subl %2,%0; sete %1" :"=m" (v->counter), "=qm" (c) :"ir" (i), "m" (v->counter) : "memory"); return c; @@ -102,7 +101,7 @@ static __inline__ int atomic_sub_and_tes static __inline__ void atomic_inc(atomic_t *v) { __asm__ __volatile__( - LOCK "incl %0" + LOCK_PREFIX "incl %0" :"=m" (v->counter) :"m" (v->counter)); } @@ -116,7 +115,7 @@ static __inline__ void atomic_inc(atomic static __inline__ void atomic_dec(atomic_t *v) { __asm__ __volatile__( - LOCK "decl %0" + LOCK_PREFIX "decl %0" :"=m" (v->counter) :"m" (v->counter)); } @@ -134,7 +133,7 @@ static __inline__ int atomic_dec_and_tes unsigned char c; __asm__ __volatile__( - LOCK "decl %0; sete %1" + LOCK_PREFIX "decl %0; sete %1" :"=m" (v->counter), "=qm" (c) :"m" (v->counter) : "memory"); return c != 0; @@ -153,7 +152,7 @@ static __inline__ int atomic_inc_and_tes unsigned char c; __asm__ __volatile__( - LOCK "incl %0; sete %1" + LOCK_PREFIX "incl %0; sete %1" :"=m" (v->counter), "=qm" (c) :"m" (v->counter) : "memory"); return c != 0; @@ -173,7 +172,7 @@ static __inline__ int atomic_add_negativ unsigned char c; __asm__ __volatile__( - LOCK "addl %2,%0; sets %1" + LOCK_PREFIX "addl %2,%0; sets %1" :"=m" (v->counter), "=qm" (c) :"ir" (i), "m" (v->counter) : "memory"); return c; @@ -190,7 +189,7 @@ static __inline__ int atomic_add_return( { int __i = i; __asm__ __volatile__( - LOCK "xaddl %0, %1;" + LOCK_PREFIX "xaddl %0, %1;" :"=r"(i) :"m"(v->counter), "0"(i)); return i + __i; @@ -238,7 +237,7 @@ #define atomic64_set(v,i) (((v)->counte static __inline__ void atomic64_add(long i, atomic64_t *v) { __asm__ __volatile__( - LOCK "addq %1,%0" + LOCK_PREFIX "addq %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } @@ -253,7 +252,7 @@ static __inline__ void atomic64_add(long static __inline__ void atomic64_sub(long i, atomic64_t *v) { __asm__ __volatile__( - LOCK "subq %1,%0" + LOCK_PREFIX "subq %1,%0" :"=m" (v->counter) :"ir" (i), "m" (v->counter)); } @@ -272,7 +271,7 @@ static __inline__ int atomic64_sub_and_t unsigned char c; __asm__ __volatile__( - LOCK "subq %2,%0; sete %1" + LOCK_PREFIX "subq %2,%0; sete %1" :"=m" (v->counter), "=qm" (c) :"ir" (i), "m" (v->counter) : "memory"); return c; @@ -287,7 +286,7 @@ static __inline__ int atomic64_sub_and_t static __inline__ void atomic64_inc(atomic64_t *v) { __asm__ __volatile__( - LOCK "incq %0" + LOCK_PREFIX "incq %0" :"=m" (v->counter) :"m" (v->counter)); } @@ -301,7 +300,7 @@ static __inline__ void atomic64_inc(atom static __inline__ void atomic64_dec(atomic64_t *v) { __asm__ __volatile__( - LOCK "decq %0" + LOCK_PREFIX "decq %0" :"=m" (v->counter) :"m" (v->counter)); } @@ -319,7 +318,7 @@ static __inline__ int atomic64_dec_and_t unsigned char c; __asm__ __volatile__( - LOCK "decq %0; sete %1" + LOCK_PREFIX "decq %0; sete %1" :"=m" (v->counter), "=qm" (c) :"m" (v->counter) : "memory"); return c != 0; @@ -338,7 +337,7 @@ static __inline__ int atomic64_inc_and_t unsigned char c; __asm__ __volatile__( - LOCK "incq %0; sete %1" + LOCK_PREFIX "incq %0; sete %1" :"=m" (v->counter), "=qm" (c) :"m" (v->counter) : "memory"); return c != 0; @@ -358,7 +357,7 @@ static __inline__ int atomic64_add_negat unsigned char c; __asm__ __volatile__( - LOCK "addq %2,%0; sets %1" + LOCK_PREFIX "addq %2,%0; sets %1" :"=m" (v->counter), "=qm" (c) :"ir" (i), "m" (v->counter) : "memory"); return c; @@ -375,7 +374,7 @@ static __inline__ long atomic64_add_retu { long __i = i; __asm__ __volatile__( - LOCK "xaddq %0, %1;" + LOCK_PREFIX "xaddq %0, %1;" :"=r"(i) :"m"(v->counter), "0"(i)); return i + __i; @@ -419,11 +418,11 @@ #define atomic_inc_not_zero(v) atomic_ad /* These are x86-specific, used by some header files */ #define atomic_clear_mask(mask, addr) \ -__asm__ __volatile__(LOCK "andl %0,%1" \ +__asm__ __volatile__(LOCK_PREFIX "andl %0,%1" \ : : "r" (~(mask)),"m" (*addr) : "memory") #define atomic_set_mask(mask, addr) \ -__asm__ __volatile__(LOCK "orl %0,%1" \ +__asm__ __volatile__(LOCK_PREFIX "orl %0,%1" \ : : "r" ((unsigned)mask),"m" (*(addr)) : "memory") /* Atomic operations are already serializing on x86 */ diff --git a/include/asm-x86_64/bitops.h b/include/asm-x86_64/bitops.h index 7921212..f7ba57b 100644 --- a/include/asm-x86_64/bitops.h +++ b/include/asm-x86_64/bitops.h @@ -5,13 +5,7 @@ #define _X86_64_BITOPS_H * Copyright 1992, Linus Torvalds. */ -#include - -#ifdef CONFIG_SMP -#define LOCK_PREFIX "lock ; " -#else -#define LOCK_PREFIX "" -#endif +#include #define ADDR (*(volatile long *) addr) diff --git a/include/asm-x86_64/bugs.h b/include/asm-x86_64/bugs.h index 59bc689..d86c5dd 100644 --- a/include/asm-x86_64/bugs.h +++ b/include/asm-x86_64/bugs.h @@ -10,7 +10,6 @@ * void check_bugs(void); */ -#include #include #include #include diff --git a/include/asm-x86_64/cache.h b/include/asm-x86_64/cache.h index f8dff1c..ed8a9d2 100644 --- a/include/asm-x86_64/cache.h +++ b/include/asm-x86_64/cache.h @@ -4,7 +4,6 @@ #ifndef __ARCH_X8664_CACHE_H #define __ARCH_X8664_CACHE_H -#include /* L1 cache line size */ #define L1_CACHE_SHIFT (CONFIG_X86_L1_CACHE_SHIFT) diff --git a/include/asm-x86_64/calgary.h b/include/asm-x86_64/calgary.h new file mode 100644 index 0000000..6e1654f --- /dev/null +++ b/include/asm-x86_64/calgary.h @@ -0,0 +1,66 @@ +/* + * Derived from include/asm-powerpc/iommu.h + * + * Copyright (C) 2006 Jon Mason , IBM Corporation + * Copyright (C) 2006 Muli Ben-Yehuda , IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ASM_X86_64_CALGARY_H +#define _ASM_X86_64_CALGARY_H + +#include +#include +#include +#include +#include + +struct iommu_table { + unsigned long it_base; /* mapped address of tce table */ + unsigned long it_hint; /* Hint for next alloc */ + unsigned long *it_map; /* A simple allocation bitmap for now */ + spinlock_t it_lock; /* Protects it_map */ + unsigned int it_size; /* Size of iommu table in entries */ + unsigned char it_busno; /* Bus number this table belongs to */ + void __iomem *bbar; + u64 tar_val; + struct timer_list watchdog_timer; +}; + +#define TCE_TABLE_SIZE_UNSPECIFIED ~0 +#define TCE_TABLE_SIZE_64K 0 +#define TCE_TABLE_SIZE_128K 1 +#define TCE_TABLE_SIZE_256K 2 +#define TCE_TABLE_SIZE_512K 3 +#define TCE_TABLE_SIZE_1M 4 +#define TCE_TABLE_SIZE_2M 5 +#define TCE_TABLE_SIZE_4M 6 +#define TCE_TABLE_SIZE_8M 7 + +#ifdef CONFIG_CALGARY_IOMMU +extern int calgary_iommu_init(void); +extern void detect_calgary(void); +#else +static inline int calgary_iommu_init(void) { return 1; } +static inline void detect_calgary(void) { return; } +#endif + +static inline unsigned int bus_to_phb(unsigned char busno) +{ + return ((busno % 15 == 0) ? 0 : busno / 2 + 1); +} + +#endif /* _ASM_X86_64_CALGARY_H */ diff --git a/include/asm-x86_64/calling.h b/include/asm-x86_64/calling.h index fc2c5a6..6f4f63a 100644 --- a/include/asm-x86_64/calling.h +++ b/include/asm-x86_64/calling.h @@ -2,7 +2,6 @@ * Some macros to handle stack frames in assembly. */ -#include #define R15 0 #define R14 8 diff --git a/include/asm-x86_64/cpufeature.h b/include/asm-x86_64/cpufeature.h index 662964b..ee792fa 100644 --- a/include/asm-x86_64/cpufeature.h +++ b/include/asm-x86_64/cpufeature.h @@ -46,6 +46,7 @@ #define X86_FEATURE_IA64 (0*32+30) /* IA #define X86_FEATURE_SYSCALL (1*32+11) /* SYSCALL/SYSRET */ #define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ #define X86_FEATURE_FXSR_OPT (1*32+25) /* FXSR optimizations */ +#define X86_FEATURE_RDTSCP (1*32+27) /* RDTSCP */ #define X86_FEATURE_LM (1*32+29) /* Long Mode (x86-64) */ #define X86_FEATURE_3DNOWEXT (1*32+30) /* AMD 3DNow! extensions */ #define X86_FEATURE_3DNOW (1*32+31) /* 3DNow! */ @@ -65,6 +66,8 @@ #define X86_FEATURE_REP_GOOD (3*32+ 4) / #define X86_FEATURE_CONSTANT_TSC (3*32+5) /* TSC runs at constant rate */ #define X86_FEATURE_SYNC_RDTSC (3*32+6) /* RDTSC syncs CPU core */ #define X86_FEATURE_FXSAVE_LEAK (3*32+7) /* FIP/FOP/FDP leaks through FXSAVE */ +#define X86_FEATURE_UP (3*32+8) /* SMP kernel running on UP */ +#define X86_FEATURE_ARCH_PERFMON (3*32+9) /* Intel Architectural PerfMon */ /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ #define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */ diff --git a/include/asm-x86_64/dma-mapping.h b/include/asm-x86_64/dma-mapping.h index 49a81a6..b6da83d 100644 --- a/include/asm-x86_64/dma-mapping.h +++ b/include/asm-x86_64/dma-mapping.h @@ -6,7 +6,6 @@ #define _X8664_DMA_MAPPING_H 1 * documentation. */ -#include #include #include @@ -56,6 +55,13 @@ extern dma_addr_t bad_dma_address; extern struct dma_mapping_ops* dma_ops; extern int iommu_merge; +static inline int valid_dma_direction(int dma_direction) +{ + return ((dma_direction == DMA_BIDIRECTIONAL) || + (dma_direction == DMA_TO_DEVICE) || + (dma_direction == DMA_FROM_DEVICE)); +} + static inline int dma_mapping_error(dma_addr_t dma_addr) { if (dma_ops->mapping_error) @@ -73,6 +79,7 @@ static inline dma_addr_t dma_map_single(struct device *hwdev, void *ptr, size_t size, int direction) { + BUG_ON(!valid_dma_direction(direction)); return dma_ops->map_single(hwdev, ptr, size, direction); } @@ -80,6 +87,7 @@ static inline void dma_unmap_single(struct device *dev, dma_addr_t addr,size_t size, int direction) { + BUG_ON(!valid_dma_direction(direction)); dma_ops->unmap_single(dev, addr, size, direction); } @@ -92,6 +100,7 @@ static inline void dma_sync_single_for_cpu(struct device *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + BUG_ON(!valid_dma_direction(direction)); if (dma_ops->sync_single_for_cpu) dma_ops->sync_single_for_cpu(hwdev, dma_handle, size, direction); @@ -102,6 +111,7 @@ static inline void dma_sync_single_for_device(struct device *hwdev, dma_addr_t dma_handle, size_t size, int direction) { + BUG_ON(!valid_dma_direction(direction)); if (dma_ops->sync_single_for_device) dma_ops->sync_single_for_device(hwdev, dma_handle, size, direction); @@ -112,6 +122,7 @@ static inline void dma_sync_single_range_for_cpu(struct device *hwdev, dma_addr_t dma_handle, unsigned long offset, size_t size, int direction) { + BUG_ON(!valid_dma_direction(direction)); if (dma_ops->sync_single_range_for_cpu) { dma_ops->sync_single_range_for_cpu(hwdev, dma_handle, offset, size, direction); } @@ -123,6 +134,7 @@ static inline void dma_sync_single_range_for_device(struct device *hwdev, dma_addr_t dma_handle, unsigned long offset, size_t size, int direction) { + BUG_ON(!valid_dma_direction(direction)); if (dma_ops->sync_single_range_for_device) dma_ops->sync_single_range_for_device(hwdev, dma_handle, offset, size, direction); @@ -134,6 +146,7 @@ static inline void dma_sync_sg_for_cpu(struct device *hwdev, struct scatterlist *sg, int nelems, int direction) { + BUG_ON(!valid_dma_direction(direction)); if (dma_ops->sync_sg_for_cpu) dma_ops->sync_sg_for_cpu(hwdev, sg, nelems, direction); flush_write_buffers(); @@ -143,6 +156,7 @@ static inline void dma_sync_sg_for_device(struct device *hwdev, struct scatterlist *sg, int nelems, int direction) { + BUG_ON(!valid_dma_direction(direction)); if (dma_ops->sync_sg_for_device) { dma_ops->sync_sg_for_device(hwdev, sg, nelems, direction); } @@ -153,6 +167,7 @@ dma_sync_sg_for_device(struct device *hw static inline int dma_map_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction) { + BUG_ON(!valid_dma_direction(direction)); return dma_ops->map_sg(hwdev, sg, nents, direction); } @@ -160,6 +175,7 @@ static inline void dma_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents, int direction) { + BUG_ON(!valid_dma_direction(direction)); dma_ops->unmap_sg(hwdev, sg, nents, direction); } diff --git a/include/asm-x86_64/dma.h b/include/asm-x86_64/dma.h index 6f2a817..a37c16f 100644 --- a/include/asm-x86_64/dma.h +++ b/include/asm-x86_64/dma.h @@ -1,4 +1,4 @@ -/* $Id: dma.h,v 1.1.1.1 2001/04/19 20:00:38 ak Exp $ +/* * linux/include/asm/dma.h: Defines for using and allocating dma channels. * Written by Hennus Bergman, 1992. * High DMA channel support & info by Hannu Savolainen @@ -8,7 +8,6 @@ #ifndef _ASM_DMA_H #define _ASM_DMA_H -#include #include /* And spinlocks */ #include /* need byte IO */ #include diff --git a/include/asm-x86_64/dwarf2.h b/include/asm-x86_64/dwarf2.h index 07654bd..0744db7 100644 --- a/include/asm-x86_64/dwarf2.h +++ b/include/asm-x86_64/dwarf2.h @@ -1,7 +1,6 @@ #ifndef _DWARF2_H #define _DWARF2_H 1 -#include #ifndef __ASSEMBLY__ #warning "asm/dwarf2.h should be only included in pure assembly files" diff --git a/include/asm-x86_64/fixmap.h b/include/asm-x86_64/fixmap.h index 7b286bd..0b4ffbd 100644 --- a/include/asm-x86_64/fixmap.h +++ b/include/asm-x86_64/fixmap.h @@ -11,7 +11,6 @@ #ifndef _ASM_FIXMAP_H #define _ASM_FIXMAP_H -#include #include #include #include diff --git a/include/asm-x86_64/floppy.h b/include/asm-x86_64/floppy.h index 52825ce..32ff5d1 100644 --- a/include/asm-x86_64/floppy.h +++ b/include/asm-x86_64/floppy.h @@ -144,13 +144,11 @@ static int vdma_get_dma_residue(unsigned static int fd_request_irq(void) { if(can_use_virtual_dma) - return request_irq(FLOPPY_IRQ, floppy_hardint,SA_INTERRUPT, - "floppy", NULL); + return request_irq(FLOPPY_IRQ, floppy_hardint, + IRQF_DISABLED, "floppy", NULL); else return request_irq(FLOPPY_IRQ, floppy_interrupt, - SA_INTERRUPT|SA_SAMPLE_RANDOM, - "floppy", NULL); - + IRQF_DISABLED, "floppy", NULL); } static unsigned long dma_mem_alloc(unsigned long size) diff --git a/include/asm-x86_64/gart-mapping.h b/include/asm-x86_64/gart-mapping.h deleted file mode 100644 index ada497b..0000000 --- a/include/asm-x86_64/gart-mapping.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _X8664_GART_MAPPING_H -#define _X8664_GART_MAPPING_H 1 - -#include -#include - -struct device; - -extern void* -gart_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *dma_handle, gfp_t gfp); - -extern int -gart_dma_supported(struct device *hwdev, u64 mask); - -#endif /* _X8664_GART_MAPPING_H */ diff --git a/include/asm-x86_64/hardirq.h b/include/asm-x86_64/hardirq.h index 8689951..64a65ce 100644 --- a/include/asm-x86_64/hardirq.h +++ b/include/asm-x86_64/hardirq.h @@ -1,7 +1,6 @@ #ifndef __ASM_HARDIRQ_H #define __ASM_HARDIRQ_H -#include #include #include #include diff --git a/include/asm-x86_64/hpet.h b/include/asm-x86_64/hpet.h index 18ff7ee..b390984 100644 --- a/include/asm-x86_64/hpet.h +++ b/include/asm-x86_64/hpet.h @@ -55,7 +55,7 @@ #define HPET_TICK_RATE (HZ * 100000UL) extern int is_hpet_enabled(void); extern int hpet_rtc_timer_init(void); -extern int oem_force_hpet_timer(void); +extern int apic_is_clustered_box(void); extern int hpet_use_timer; diff --git a/include/asm-x86_64/hw_irq.h b/include/asm-x86_64/hw_irq.h index 0df1715..48a4a53 100644 --- a/include/asm-x86_64/hw_irq.h +++ b/include/asm-x86_64/hw_irq.h @@ -12,12 +12,9 @@ #define _ASM_HW_IRQ_H * * * hacked by Andi Kleen for x86-64. - * - * $Id: hw_irq.h,v 1.24 2001/09/14 20:55:03 vojtech Exp $ */ #ifndef __ASSEMBLY__ -#include #include #include #include @@ -127,18 +124,9 @@ asmlinkage void IRQ_NAME(nr); \ __asm__( \ "\n.p2align\n" \ "IRQ" #nr "_interrupt:\n\t" \ - "push $" #nr "-256 ; " \ + "push $~(" #nr ") ; " \ "jmp common_interrupt"); -#if defined(CONFIG_X86_IO_APIC) -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) { - if (IO_APIC_IRQ(i)) - send_IPI_self(IO_APIC_VECTOR(i)); -} -#else -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) {} -#endif - #define platform_legacy_irq(irq) ((irq) < 16) #endif diff --git a/include/asm-x86_64/ia32.h b/include/asm-x86_64/ia32.h index e6b7f22..0190b7c 100644 --- a/include/asm-x86_64/ia32.h +++ b/include/asm-x86_64/ia32.h @@ -1,7 +1,6 @@ #ifndef _ASM_X86_64_IA32_H #define _ASM_X86_64_IA32_H -#include #ifdef CONFIG_IA32_EMULATION diff --git a/include/asm-x86_64/ia32_unistd.h b/include/asm-x86_64/ia32_unistd.h index b4f4b17..5b52ce5 100644 --- a/include/asm-x86_64/ia32_unistd.h +++ b/include/asm-x86_64/ia32_unistd.h @@ -4,317 +4,15 @@ #define _ASM_X86_64_IA32_UNISTD_H_ /* * This file contains the system call numbers of the ia32 port, * this is for the kernel only. + * Only add syscalls here where some part of the kernel needs to know + * the number. This should be otherwise in sync with asm-i386/unistd.h. -AK */ #define __NR_ia32_restart_syscall 0 #define __NR_ia32_exit 1 -#define __NR_ia32_fork 2 #define __NR_ia32_read 3 #define __NR_ia32_write 4 -#define __NR_ia32_open 5 -#define __NR_ia32_close 6 -#define __NR_ia32_waitpid 7 -#define __NR_ia32_creat 8 -#define __NR_ia32_link 9 -#define __NR_ia32_unlink 10 -#define __NR_ia32_execve 11 -#define __NR_ia32_chdir 12 -#define __NR_ia32_time 13 -#define __NR_ia32_mknod 14 -#define __NR_ia32_chmod 15 -#define __NR_ia32_lchown 16 -#define __NR_ia32_break 17 -#define __NR_ia32_oldstat 18 -#define __NR_ia32_lseek 19 -#define __NR_ia32_getpid 20 -#define __NR_ia32_mount 21 -#define __NR_ia32_umount 22 -#define __NR_ia32_setuid 23 -#define __NR_ia32_getuid 24 -#define __NR_ia32_stime 25 -#define __NR_ia32_ptrace 26 -#define __NR_ia32_alarm 27 -#define __NR_ia32_oldfstat 28 -#define __NR_ia32_pause 29 -#define __NR_ia32_utime 30 -#define __NR_ia32_stty 31 -#define __NR_ia32_gtty 32 -#define __NR_ia32_access 33 -#define __NR_ia32_nice 34 -#define __NR_ia32_ftime 35 -#define __NR_ia32_sync 36 -#define __NR_ia32_kill 37 -#define __NR_ia32_rename 38 -#define __NR_ia32_mkdir 39 -#define __NR_ia32_rmdir 40 -#define __NR_ia32_dup 41 -#define __NR_ia32_pipe 42 -#define __NR_ia32_times 43 -#define __NR_ia32_prof 44 -#define __NR_ia32_brk 45 -#define __NR_ia32_setgid 46 -#define __NR_ia32_getgid 47 -#define __NR_ia32_signal 48 -#define __NR_ia32_geteuid 49 -#define __NR_ia32_getegid 50 -#define __NR_ia32_acct 51 -#define __NR_ia32_umount2 52 -#define __NR_ia32_lock 53 -#define __NR_ia32_ioctl 54 -#define __NR_ia32_fcntl 55 -#define __NR_ia32_mpx 56 -#define __NR_ia32_setpgid 57 -#define __NR_ia32_ulimit 58 -#define __NR_ia32_oldolduname 59 -#define __NR_ia32_umask 60 -#define __NR_ia32_chroot 61 -#define __NR_ia32_ustat 62 -#define __NR_ia32_dup2 63 -#define __NR_ia32_getppid 64 -#define __NR_ia32_getpgrp 65 -#define __NR_ia32_setsid 66 -#define __NR_ia32_sigaction 67 -#define __NR_ia32_sgetmask 68 -#define __NR_ia32_ssetmask 69 -#define __NR_ia32_setreuid 70 -#define __NR_ia32_setregid 71 -#define __NR_ia32_sigsuspend 72 -#define __NR_ia32_sigpending 73 -#define __NR_ia32_sethostname 74 -#define __NR_ia32_setrlimit 75 -#define __NR_ia32_getrlimit 76 /* Back compatible 2Gig limited rlimit */ -#define __NR_ia32_getrusage 77 -#define __NR_ia32_gettimeofday 78 -#define __NR_ia32_settimeofday 79 -#define __NR_ia32_getgroups 80 -#define __NR_ia32_setgroups 81 -#define __NR_ia32_select 82 -#define __NR_ia32_symlink 83 -#define __NR_ia32_oldlstat 84 -#define __NR_ia32_readlink 85 -#define __NR_ia32_uselib 86 -#define __NR_ia32_swapon 87 -#define __NR_ia32_reboot 88 -#define __NR_ia32_readdir 89 -#define __NR_ia32_mmap 90 -#define __NR_ia32_munmap 91 -#define __NR_ia32_truncate 92 -#define __NR_ia32_ftruncate 93 -#define __NR_ia32_fchmod 94 -#define __NR_ia32_fchown 95 -#define __NR_ia32_getpriority 96 -#define __NR_ia32_setpriority 97 -#define __NR_ia32_profil 98 -#define __NR_ia32_statfs 99 -#define __NR_ia32_fstatfs 100 -#define __NR_ia32_ioperm 101 -#define __NR_ia32_socketcall 102 -#define __NR_ia32_syslog 103 -#define __NR_ia32_setitimer 104 -#define __NR_ia32_getitimer 105 -#define __NR_ia32_stat 106 -#define __NR_ia32_lstat 107 -#define __NR_ia32_fstat 108 -#define __NR_ia32_olduname 109 -#define __NR_ia32_iopl 110 -#define __NR_ia32_vhangup 111 -#define __NR_ia32_idle 112 -#define __NR_ia32_vm86old 113 -#define __NR_ia32_wait4 114 -#define __NR_ia32_swapoff 115 -#define __NR_ia32_sysinfo 116 -#define __NR_ia32_ipc 117 -#define __NR_ia32_fsync 118 -#define __NR_ia32_sigreturn 119 -#define __NR_ia32_clone 120 -#define __NR_ia32_setdomainname 121 -#define __NR_ia32_uname 122 -#define __NR_ia32_modify_ldt 123 -#define __NR_ia32_adjtimex 124 -#define __NR_ia32_mprotect 125 -#define __NR_ia32_sigprocmask 126 -#define __NR_ia32_create_module 127 -#define __NR_ia32_init_module 128 -#define __NR_ia32_delete_module 129 -#define __NR_ia32_get_kernel_syms 130 -#define __NR_ia32_quotactl 131 -#define __NR_ia32_getpgid 132 -#define __NR_ia32_fchdir 133 -#define __NR_ia32_bdflush 134 -#define __NR_ia32_sysfs 135 -#define __NR_ia32_personality 136 -#define __NR_ia32_afs_syscall 137 /* Syscall for Andrew File System */ -#define __NR_ia32_setfsuid 138 -#define __NR_ia32_setfsgid 139 -#define __NR_ia32__llseek 140 -#define __NR_ia32_getdents 141 -#define __NR_ia32__newselect 142 -#define __NR_ia32_flock 143 -#define __NR_ia32_msync 144 -#define __NR_ia32_readv 145 -#define __NR_ia32_writev 146 -#define __NR_ia32_getsid 147 -#define __NR_ia32_fdatasync 148 -#define __NR_ia32__sysctl 149 -#define __NR_ia32_mlock 150 -#define __NR_ia32_munlock 151 -#define __NR_ia32_mlockall 152 -#define __NR_ia32_munlockall 153 -#define __NR_ia32_sched_setparam 154 -#define __NR_ia32_sched_getparam 155 -#define __NR_ia32_sched_setscheduler 156 -#define __NR_ia32_sched_getscheduler 157 -#define __NR_ia32_sched_yield 158 -#define __NR_ia32_sched_get_priority_max 159 -#define __NR_ia32_sched_get_priority_min 160 -#define __NR_ia32_sched_rr_get_interval 161 -#define __NR_ia32_nanosleep 162 -#define __NR_ia32_mremap 163 -#define __NR_ia32_setresuid 164 -#define __NR_ia32_getresuid 165 -#define __NR_ia32_vm86 166 -#define __NR_ia32_query_module 167 -#define __NR_ia32_poll 168 -#define __NR_ia32_nfsservctl 169 -#define __NR_ia32_setresgid 170 -#define __NR_ia32_getresgid 171 -#define __NR_ia32_prctl 172 +#define __NR_ia32_sigreturn 119 #define __NR_ia32_rt_sigreturn 173 -#define __NR_ia32_rt_sigaction 174 -#define __NR_ia32_rt_sigprocmask 175 -#define __NR_ia32_rt_sigpending 176 -#define __NR_ia32_rt_sigtimedwait 177 -#define __NR_ia32_rt_sigqueueinfo 178 -#define __NR_ia32_rt_sigsuspend 179 -#define __NR_ia32_pread 180 -#define __NR_ia32_pwrite 181 -#define __NR_ia32_chown 182 -#define __NR_ia32_getcwd 183 -#define __NR_ia32_capget 184 -#define __NR_ia32_capset 185 -#define __NR_ia32_sigaltstack 186 -#define __NR_ia32_sendfile 187 -#define __NR_ia32_getpmsg 188 /* some people actually want streams */ -#define __NR_ia32_putpmsg 189 /* some people actually want streams */ -#define __NR_ia32_vfork 190 -#define __NR_ia32_ugetrlimit 191 /* SuS compliant getrlimit */ -#define __NR_ia32_mmap2 192 -#define __NR_ia32_truncate64 193 -#define __NR_ia32_ftruncate64 194 -#define __NR_ia32_stat64 195 -#define __NR_ia32_lstat64 196 -#define __NR_ia32_fstat64 197 -#define __NR_ia32_lchown32 198 -#define __NR_ia32_getuid32 199 -#define __NR_ia32_getgid32 200 -#define __NR_ia32_geteuid32 201 -#define __NR_ia32_getegid32 202 -#define __NR_ia32_setreuid32 203 -#define __NR_ia32_setregid32 204 -#define __NR_ia32_getgroups32 205 -#define __NR_ia32_setgroups32 206 -#define __NR_ia32_fchown32 207 -#define __NR_ia32_setresuid32 208 -#define __NR_ia32_getresuid32 209 -#define __NR_ia32_setresgid32 210 -#define __NR_ia32_getresgid32 211 -#define __NR_ia32_chown32 212 -#define __NR_ia32_setuid32 213 -#define __NR_ia32_setgid32 214 -#define __NR_ia32_setfsuid32 215 -#define __NR_ia32_setfsgid32 216 -#define __NR_ia32_pivot_root 217 -#define __NR_ia32_mincore 218 -#define __NR_ia32_madvise 219 -#define __NR_ia32_madvise1 219 /* delete when C lib stub is removed */ -#define __NR_ia32_getdents64 220 -#define __NR_ia32_fcntl64 221 -#define __NR_ia32_tuxcall 222 -#define __NR_ia32_security 223 -#define __NR_ia32_gettid 224 -#define __NR_ia32_readahead 225 -#define __NR_ia32_setxattr 226 -#define __NR_ia32_lsetxattr 227 -#define __NR_ia32_fsetxattr 228 -#define __NR_ia32_getxattr 229 -#define __NR_ia32_lgetxattr 230 -#define __NR_ia32_fgetxattr 231 -#define __NR_ia32_listxattr 232 -#define __NR_ia32_llistxattr 233 -#define __NR_ia32_flistxattr 234 -#define __NR_ia32_removexattr 235 -#define __NR_ia32_lremovexattr 236 -#define __NR_ia32_fremovexattr 237 -#define __NR_ia32_tkill 238 -#define __NR_ia32_sendfile64 239 -#define __NR_ia32_futex 240 -#define __NR_ia32_sched_setaffinity 241 -#define __NR_ia32_sched_getaffinity 242 -#define __NR_ia32_set_thread_area 243 -#define __NR_ia32_get_thread_area 244 -#define __NR_ia32_io_setup 245 -#define __NR_ia32_io_destroy 246 -#define __NR_ia32_io_getevents 247 -#define __NR_ia32_io_submit 248 -#define __NR_ia32_io_cancel 249 -#define __NR_ia32_exit_group 252 -#define __NR_ia32_lookup_dcookie 253 -#define __NR_ia32_sys_epoll_create 254 -#define __NR_ia32_sys_epoll_ctl 255 -#define __NR_ia32_sys_epoll_wait 256 -#define __NR_ia32_remap_file_pages 257 -#define __NR_ia32_set_tid_address 258 -#define __NR_ia32_timer_create 259 -#define __NR_ia32_timer_settime (__NR_ia32_timer_create+1) -#define __NR_ia32_timer_gettime (__NR_ia32_timer_create+2) -#define __NR_ia32_timer_getoverrun (__NR_ia32_timer_create+3) -#define __NR_ia32_timer_delete (__NR_ia32_timer_create+4) -#define __NR_ia32_clock_settime (__NR_ia32_timer_create+5) -#define __NR_ia32_clock_gettime (__NR_ia32_timer_create+6) -#define __NR_ia32_clock_getres (__NR_ia32_timer_create+7) -#define __NR_ia32_clock_nanosleep (__NR_ia32_timer_create+8) -#define __NR_ia32_statfs64 268 -#define __NR_ia32_fstatfs64 269 -#define __NR_ia32_tgkill 270 -#define __NR_ia32_utimes 271 -#define __NR_ia32_fadvise64_64 272 -#define __NR_ia32_vserver 273 -#define __NR_ia32_mbind 274 -#define __NR_ia32_get_mempolicy 275 -#define __NR_ia32_set_mempolicy 276 -#define __NR_ia32_mq_open 277 -#define __NR_ia32_mq_unlink (__NR_ia32_mq_open+1) -#define __NR_ia32_mq_timedsend (__NR_ia32_mq_open+2) -#define __NR_ia32_mq_timedreceive (__NR_ia32_mq_open+3) -#define __NR_ia32_mq_notify (__NR_ia32_mq_open+4) -#define __NR_ia32_mq_getsetattr (__NR_ia32_mq_open+5) -#define __NR_ia32_kexec 283 -#define __NR_ia32_waitid 284 -/* #define __NR_sys_setaltroot 285 */ -#define __NR_ia32_add_key 286 -#define __NR_ia32_request_key 287 -#define __NR_ia32_keyctl 288 -#define __NR_ia32_ioprio_set 289 -#define __NR_ia32_ioprio_get 290 -#define __NR_ia32_inotify_init 291 -#define __NR_ia32_inotify_add_watch 292 -#define __NR_ia32_inotify_rm_watch 293 -#define __NR_ia32_migrate_pages 294 -#define __NR_ia32_openat 295 -#define __NR_ia32_mkdirat 296 -#define __NR_ia32_mknodat 297 -#define __NR_ia32_fchownat 298 -#define __NR_ia32_futimesat 299 -#define __NR_ia32_fstatat64 300 -#define __NR_ia32_unlinkat 301 -#define __NR_ia32_renameat 302 -#define __NR_ia32_linkat 303 -#define __NR_ia32_symlinkat 304 -#define __NR_ia32_readlinkat 305 -#define __NR_ia32_fchmodat 306 -#define __NR_ia32_faccessat 307 -#define __NR_ia32_pselect6 308 -#define __NR_ia32_ppoll 309 -#define __NR_ia32_unshare 310 #endif /* _ASM_X86_64_IA32_UNISTD_H_ */ diff --git a/include/asm-x86_64/intel_arch_perfmon.h b/include/asm-x86_64/intel_arch_perfmon.h new file mode 100644 index 0000000..59c3964 --- /dev/null +++ b/include/asm-x86_64/intel_arch_perfmon.h @@ -0,0 +1,19 @@ +#ifndef X86_64_INTEL_ARCH_PERFMON_H +#define X86_64_INTEL_ARCH_PERFMON_H 1 + +#define MSR_ARCH_PERFMON_PERFCTR0 0xc1 +#define MSR_ARCH_PERFMON_PERFCTR1 0xc2 + +#define MSR_ARCH_PERFMON_EVENTSEL0 0x186 +#define MSR_ARCH_PERFMON_EVENTSEL1 0x187 + +#define ARCH_PERFMON_EVENTSEL0_ENABLE (1 << 22) +#define ARCH_PERFMON_EVENTSEL_INT (1 << 20) +#define ARCH_PERFMON_EVENTSEL_OS (1 << 17) +#define ARCH_PERFMON_EVENTSEL_USR (1 << 16) + +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL (0x3c) +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT (1 << 0) + +#endif /* X86_64_INTEL_ARCH_PERFMON_H */ diff --git a/include/asm-x86_64/io.h b/include/asm-x86_64/io.h index a05da8a..70e91fe 100644 --- a/include/asm-x86_64/io.h +++ b/include/asm-x86_64/io.h @@ -1,7 +1,6 @@ #ifndef _ASM_IO_H #define _ASM_IO_H -#include /* * This file contains the definitions for the x86 IO instructions diff --git a/include/asm-x86_64/io_apic.h b/include/asm-x86_64/io_apic.h index 52484e8..fb7a090 100644 --- a/include/asm-x86_64/io_apic.h +++ b/include/asm-x86_64/io_apic.h @@ -1,7 +1,6 @@ #ifndef __ASM_IO_APIC_H #define __ASM_IO_APIC_H -#include #include #include diff --git a/include/asm-x86_64/irqflags.h b/include/asm-x86_64/irqflags.h new file mode 100644 index 0000000..cce6937 --- /dev/null +++ b/include/asm-x86_64/irqflags.h @@ -0,0 +1,141 @@ +/* + * include/asm-x86_64/irqflags.h + * + * IRQ flags handling + * + * This file gets included from lowlevel asm headers too, to provide + * wrapped versions of the local_irq_*() APIs, based on the + * raw_local_irq_*() functions from the lowlevel headers. + */ +#ifndef _ASM_IRQFLAGS_H +#define _ASM_IRQFLAGS_H + +#ifndef __ASSEMBLY__ +/* + * Interrupt control: + */ + +static inline unsigned long __raw_local_save_flags(void) +{ + unsigned long flags; + + __asm__ __volatile__( + "# __raw_save_flags\n\t" + "pushfq ; popq %q0" + : "=g" (flags) + : /* no input */ + : "memory" + ); + + return flags; +} + +#define raw_local_save_flags(flags) \ + do { (flags) = __raw_local_save_flags(); } while (0) + +static inline void raw_local_irq_restore(unsigned long flags) +{ + __asm__ __volatile__( + "pushq %0 ; popfq" + : /* no output */ + :"g" (flags) + :"memory", "cc" + ); +} + +#ifdef CONFIG_X86_VSMP + +/* + * Interrupt control for the VSMP architecture: + */ + +static inline void raw_local_irq_disable(void) +{ + unsigned long flags = __raw_local_save_flags(); + + raw_local_irq_restore((flags & ~(1 << 9)) | (1 << 18)); +} + +static inline void raw_local_irq_enable(void) +{ + unsigned long flags = __raw_local_save_flags(); + + raw_local_irq_restore((flags | (1 << 9)) & ~(1 << 18)); +} + +static inline int raw_irqs_disabled_flags(unsigned long flags) +{ + return !(flags & (1<<9)) || (flags & (1 << 18)); +} + +#else /* CONFIG_X86_VSMP */ + +static inline void raw_local_irq_disable(void) +{ + __asm__ __volatile__("cli" : : : "memory"); +} + +static inline void raw_local_irq_enable(void) +{ + __asm__ __volatile__("sti" : : : "memory"); +} + +static inline int raw_irqs_disabled_flags(unsigned long flags) +{ + return !(flags & (1 << 9)); +} + +#endif + +/* + * For spinlocks, etc.: + */ + +static inline unsigned long __raw_local_irq_save(void) +{ + unsigned long flags = __raw_local_save_flags(); + + raw_local_irq_disable(); + + return flags; +} + +#define raw_local_irq_save(flags) \ + do { (flags) = __raw_local_irq_save(); } while (0) + +static inline int raw_irqs_disabled(void) +{ + unsigned long flags = __raw_local_save_flags(); + + return raw_irqs_disabled_flags(flags); +} + +/* + * Used in the idle loop; sti takes one instruction cycle + * to complete: + */ +static inline void raw_safe_halt(void) +{ + __asm__ __volatile__("sti; hlt" : : : "memory"); +} + +/* + * Used when interrupts are already enabled or to + * shutdown the processor: + */ +static inline void halt(void) +{ + __asm__ __volatile__("hlt": : :"memory"); +} + +#else /* __ASSEMBLY__: */ +# ifdef CONFIG_TRACE_IRQFLAGS +# define TRACE_IRQS_ON call trace_hardirqs_on_thunk +# define TRACE_IRQS_OFF call trace_hardirqs_off_thunk +# else +# define TRACE_IRQS_ON +# define TRACE_IRQS_OFF +# endif +#endif + +#endif diff --git a/include/asm-x86_64/k8.h b/include/asm-x86_64/k8.h new file mode 100644 index 0000000..699dd69 --- /dev/null +++ b/include/asm-x86_64/k8.h @@ -0,0 +1,14 @@ +#ifndef _ASM_K8_H +#define _ASM_K8_H 1 + +#include + +extern struct pci_device_id k8_nb_ids[]; + +extern int early_is_k8_nb(u32 value); +extern struct pci_dev **k8_northbridges; +extern int num_k8_northbridges; +extern int cache_k8_northbridges(void); +extern void k8_flush_garts(void); + +#endif diff --git a/include/asm-x86_64/kdebug.h b/include/asm-x86_64/kdebug.h index cf79563..2b0c088 100644 --- a/include/asm-x86_64/kdebug.h +++ b/include/asm-x86_64/kdebug.h @@ -15,6 +15,8 @@ struct die_args { extern int register_die_notifier(struct notifier_block *); extern int unregister_die_notifier(struct notifier_block *); +extern int register_page_fault_notifier(struct notifier_block *); +extern int unregister_page_fault_notifier(struct notifier_block *); extern struct atomic_notifier_head die_chain; /* Grossly misnamed. */ @@ -47,7 +49,7 @@ static inline int notify_die(enum die_va return atomic_notifier_call_chain(&die_chain, val, &args); } -extern int printk_address(unsigned long address); +extern void printk_address(unsigned long address); extern void die(const char *,struct pt_regs *,long); extern void __die(const char *,struct pt_regs *,long); extern void show_registers(struct pt_regs *regs); diff --git a/include/asm-x86_64/kprobes.h b/include/asm-x86_64/kprobes.h index 98a1e95..d36febd 100644 --- a/include/asm-x86_64/kprobes.h +++ b/include/asm-x86_64/kprobes.h @@ -43,6 +43,7 @@ #define MIN_STACK_SIZE(ADDR) (((MAX_STAC #define JPROBE_ENTRY(pentry) (kprobe_opcode_t *)pentry #define ARCH_SUPPORTS_KRETPROBES +#define ARCH_INACTIVE_KPROBE_COUNT 1 void kretprobe_trampoline(void); extern void arch_remove_kprobe(struct kprobe *p); diff --git a/include/asm-x86_64/local.h b/include/asm-x86_64/local.h index cd17945..e769e62 100644 --- a/include/asm-x86_64/local.h +++ b/include/asm-x86_64/local.h @@ -59,12 +59,26 @@ #define __local_sub(i,l) local_sub((i),( * This could be done better if we moved the per cpu data directly * after GS. */ -#define cpu_local_read(v) local_read(&__get_cpu_var(v)) -#define cpu_local_set(v, i) local_set(&__get_cpu_var(v), (i)) -#define cpu_local_inc(v) local_inc(&__get_cpu_var(v)) -#define cpu_local_dec(v) local_dec(&__get_cpu_var(v)) -#define cpu_local_add(i, v) local_add((i), &__get_cpu_var(v)) -#define cpu_local_sub(i, v) local_sub((i), &__get_cpu_var(v)) + +/* Need to disable preemption for the cpu local counters otherwise we could + still access a variable of a previous CPU in a non atomic way. */ +#define cpu_local_wrap_v(v) \ + ({ local_t res__; \ + preempt_disable(); \ + res__ = (v); \ + preempt_enable(); \ + res__; }) +#define cpu_local_wrap(v) \ + ({ preempt_disable(); \ + v; \ + preempt_enable(); }) \ + +#define cpu_local_read(v) cpu_local_wrap_v(local_read(&__get_cpu_var(v))) +#define cpu_local_set(v, i) cpu_local_wrap(local_set(&__get_cpu_var(v), (i))) +#define cpu_local_inc(v) cpu_local_wrap(local_inc(&__get_cpu_var(v))) +#define cpu_local_dec(v) cpu_local_wrap(local_dec(&__get_cpu_var(v))) +#define cpu_local_add(i, v) cpu_local_wrap(local_add((i), &__get_cpu_var(v))) +#define cpu_local_sub(i, v) cpu_local_wrap(local_sub((i), &__get_cpu_var(v))) #define __cpu_local_inc(v) cpu_local_inc(v) #define __cpu_local_dec(v) cpu_local_dec(v) diff --git a/include/asm-x86_64/mce.h b/include/asm-x86_64/mce.h index 7229785..d13687d 100644 --- a/include/asm-x86_64/mce.h +++ b/include/asm-x86_64/mce.h @@ -67,13 +67,22 @@ #define MCE_GETCLEAR_FLAGS _IOR('M', 3 /* Software defined banks */ #define MCE_EXTENDED_BANK 128 #define MCE_THERMAL_BANK MCE_EXTENDED_BANK + 0 -#define MCE_THRESHOLD_BASE MCE_EXTENDED_BANK + 1 /* MCE_AMD */ -#define MCE_THRESHOLD_DRAM_ECC MCE_THRESHOLD_BASE + 4 + +#define K8_MCE_THRESHOLD_BASE (MCE_EXTENDED_BANK + 1) /* MCE_AMD */ +#define K8_MCE_THRESHOLD_BANK_0 (MCE_THRESHOLD_BASE + 0 * 9) +#define K8_MCE_THRESHOLD_BANK_1 (MCE_THRESHOLD_BASE + 1 * 9) +#define K8_MCE_THRESHOLD_BANK_2 (MCE_THRESHOLD_BASE + 2 * 9) +#define K8_MCE_THRESHOLD_BANK_3 (MCE_THRESHOLD_BASE + 3 * 9) +#define K8_MCE_THRESHOLD_BANK_4 (MCE_THRESHOLD_BASE + 4 * 9) +#define K8_MCE_THRESHOLD_BANK_5 (MCE_THRESHOLD_BASE + 5 * 9) +#define K8_MCE_THRESHOLD_DRAM_ECC (MCE_THRESHOLD_BANK_4 + 0) #ifdef __KERNEL__ #include void mce_log(struct mce *m); +DECLARE_PER_CPU(struct sys_device, device_mce); + #ifdef CONFIG_X86_MCE_INTEL void mce_intel_feature_init(struct cpuinfo_x86 *c); #else diff --git a/include/asm-x86_64/mmu_context.h b/include/asm-x86_64/mmu_context.h index 19f0c83..af03b9f 100644 --- a/include/asm-x86_64/mmu_context.h +++ b/include/asm-x86_64/mmu_context.h @@ -1,7 +1,6 @@ #ifndef __X86_64_MMU_CONTEXT_H #define __X86_64_MMU_CONTEXT_H -#include #include #include #include diff --git a/include/asm-x86_64/mmzone.h b/include/asm-x86_64/mmzone.h index 6944e71..c38ebdf 100644 --- a/include/asm-x86_64/mmzone.h +++ b/include/asm-x86_64/mmzone.h @@ -4,7 +4,6 @@ #ifndef _ASM_X86_64_MMZONE_H #define _ASM_X86_64_MMZONE_H 1 -#include #ifdef CONFIG_NUMA @@ -43,7 +42,6 @@ #define node_end_pfn(nid) (NODE_DA #ifdef CONFIG_DISCONTIGMEM #define pfn_to_nid(pfn) phys_to_nid((unsigned long)(pfn) << PAGE_SHIFT) -#define kvaddr_to_nid(kaddr) phys_to_nid(__pa(kaddr)) extern int pfn_valid(unsigned long pfn); #endif diff --git a/include/asm-x86_64/msi.h b/include/asm-x86_64/msi.h index 356e0e8..3ad2346 100644 --- a/include/asm-x86_64/msi.h +++ b/include/asm-x86_64/msi.h @@ -10,7 +10,15 @@ #include #include #include -#define LAST_DEVICE_VECTOR 232 +#define LAST_DEVICE_VECTOR (FIRST_SYSTEM_VECTOR - 1) #define MSI_TARGET_CPU_SHIFT 12 +extern struct msi_ops msi_apic_ops; + +static inline int msi_arch_init(void) +{ + msi_register(&msi_apic_ops); + return 0; +} + #endif /* ASM_MSI_H */ diff --git a/include/asm-x86_64/mtrr.h b/include/asm-x86_64/mtrr.h index 66ac1c0..d6135b2 100644 --- a/include/asm-x86_64/mtrr.h +++ b/include/asm-x86_64/mtrr.h @@ -23,9 +23,7 @@ #ifndef _LINUX_MTRR_H #define _LINUX_MTRR_H -#include #include -#include #define MTRR_IOCTL_BASE 'M' @@ -102,11 +100,10 @@ static __inline__ int mtrr_del_page (int return -ENODEV; } -# endif - -#endif +#endif /* CONFIG_MTRR */ #ifdef CONFIG_COMPAT +#include struct mtrr_sentry32 { @@ -138,4 +135,6 @@ #define MTRRIOC32_KILL_PAGE_ENTRY _IOW( #endif /* CONFIG_COMPAT */ +#endif /* __KERNEL__ */ + #endif /* _LINUX_MTRR_H */ diff --git a/include/asm-x86_64/mutex.h b/include/asm-x86_64/mutex.h index 11fbee2..06fab6d 100644 --- a/include/asm-x86_64/mutex.h +++ b/include/asm-x86_64/mutex.h @@ -24,7 +24,7 @@ do { \ typecheck_fn(fastcall void (*)(atomic_t *), fail_fn); \ \ __asm__ __volatile__( \ - LOCK " decl (%%rdi) \n" \ + LOCK_PREFIX " decl (%%rdi) \n" \ " js 2f \n" \ "1: \n" \ \ @@ -74,7 +74,7 @@ do { \ typecheck_fn(fastcall void (*)(atomic_t *), fail_fn); \ \ __asm__ __volatile__( \ - LOCK " incl (%%rdi) \n" \ + LOCK_PREFIX " incl (%%rdi) \n" \ " jle 2f \n" \ "1: \n" \ \ diff --git a/include/asm-x86_64/nmi.h b/include/asm-x86_64/nmi.h index d3abfc6..efb45c8 100644 --- a/include/asm-x86_64/nmi.h +++ b/include/asm-x86_64/nmi.h @@ -5,26 +5,27 @@ #ifndef ASM_NMI_H #define ASM_NMI_H #include +#include struct pt_regs; - + typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu); - -/** + +/** * set_nmi_callback * * Set a handler for an NMI. Only one handler may be * set. Return 1 if the NMI was handled. */ void set_nmi_callback(nmi_callback_t callback); - -/** + +/** * unset_nmi_callback * * Remove the handler previously set. */ void unset_nmi_callback(void); - + #ifdef CONFIG_PM /** Replace the PM callback routine for NMI. */ @@ -56,4 +57,21 @@ extern int unknown_nmi_panic; extern int check_nmi_watchdog(void); +extern void setup_apic_nmi_watchdog (void); +extern int reserve_lapic_nmi(void); +extern void release_lapic_nmi(void); +extern void disable_timer_nmi_watchdog(void); +extern void enable_timer_nmi_watchdog(void); +extern void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason); + +extern void nmi_watchdog_default(void); +extern int setup_nmi_watchdog(char *); + +extern unsigned int nmi_watchdog; +#define NMI_DEFAULT -1 +#define NMI_NONE 0 +#define NMI_IO_APIC 1 +#define NMI_LOCAL_APIC 2 +#define NMI_INVALID 3 + #endif /* ASM_NMI_H */ diff --git a/include/asm-x86_64/numa.h b/include/asm-x86_64/numa.h index 1cc92fe..933ff11 100644 --- a/include/asm-x86_64/numa.h +++ b/include/asm-x86_64/numa.h @@ -8,7 +8,6 @@ struct bootnode { }; extern int compute_hash_shift(struct bootnode *nodes, int numnodes); -extern int pxm_to_node(int nid); #define ZONE_ALIGN (1UL << (MAX_ORDER+PAGE_SHIFT)) diff --git a/include/asm-x86_64/page.h b/include/asm-x86_64/page.h index 408185b..f7bf875 100644 --- a/include/asm-x86_64/page.h +++ b/include/asm-x86_64/page.h @@ -1,7 +1,6 @@ #ifndef _X86_64_PAGE_H #define _X86_64_PAGE_H -#include /* PAGE_SHIFT determines the page size */ #define PAGE_SHIFT 12 @@ -136,9 +135,9 @@ #define VM_DATA_DEFAULT_FLAGS \ #define __HAVE_ARCH_GATE_AREA 1 -#endif /* __KERNEL__ */ - #include #include +#endif /* __KERNEL__ */ + #endif /* _X86_64_PAGE_H */ diff --git a/include/asm-x86_64/param.h b/include/asm-x86_64/param.h index 5956b23..a728786 100644 --- a/include/asm-x86_64/param.h +++ b/include/asm-x86_64/param.h @@ -2,7 +2,6 @@ #ifndef _ASMx86_64_PARAM_H #define _ASMx86_64_PARAM_H #ifdef __KERNEL__ -# include # define HZ CONFIG_HZ /* Internal kernel timer frequency */ # define USER_HZ 100 /* .. some user interfaces are in "ticks */ #define CLOCKS_PER_SEC (USER_HZ) /* like times() */ diff --git a/include/asm-x86_64/pci.h b/include/asm-x86_64/pci.h index 8a05af2..49c5e92 100644 --- a/include/asm-x86_64/pci.h +++ b/include/asm-x86_64/pci.h @@ -1,7 +1,6 @@ #ifndef __x8664_PCI_H #define __x8664_PCI_H -#include #include #ifdef __KERNEL__ @@ -40,8 +39,8 @@ #include #include #include #include -#include /* for have_iommu */ +extern void pci_iommu_alloc(void); extern int iommu_setup(char *opt); /* The PCI address space does equal the physical memory @@ -53,7 +52,7 @@ extern int iommu_setup(char *opt); */ #define PCI_DMA_BUS_IS_PHYS (dma_ops->is_phys) -#ifdef CONFIG_GART_IOMMU +#if defined(CONFIG_IOMMU) || defined(CONFIG_CALGARY_IOMMU) /* * x86-64 always supports DAC, but sometimes it is useful to force diff --git a/include/asm-x86_64/percpu.h b/include/asm-x86_64/percpu.h index 7f33aaf..08dd9f9 100644 --- a/include/asm-x86_64/percpu.h +++ b/include/asm-x86_64/percpu.h @@ -14,6 +14,8 @@ #include #define __per_cpu_offset(cpu) (cpu_pda(cpu)->data_offset) #define __my_cpu_offset() read_pda(data_offset) +#define per_cpu_offset(x) (__per_cpu_offset(x)) + /* Separate out the type, so (int[3], foo) works. */ #define DEFINE_PER_CPU(type, name) \ __attribute__((__section__(".data.percpu"))) __typeof__(type) per_cpu__##name @@ -21,6 +23,7 @@ #define DEFINE_PER_CPU(type, name) \ /* var is in discarded region: offset to particular copy we want */ #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset(cpu))) #define __get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __my_cpu_offset())) +#define __raw_get_cpu_var(var) (*RELOC_HIDE(&per_cpu__##var, __my_cpu_offset())) /* A macro to avoid #include hell... */ #define percpu_modcopy(pcpudst, src, size) \ @@ -40,6 +43,7 @@ #define DEFINE_PER_CPU(type, name) \ #define per_cpu(var, cpu) (*((void)(cpu), &per_cpu__##var)) #define __get_cpu_var(var) per_cpu__##var +#define __raw_get_cpu_var(var) per_cpu__##var #endif /* SMP */ diff --git a/include/asm-x86_64/pgtable.h b/include/asm-x86_64/pgtable.h index 31e83c3..a31ab4e 100644 --- a/include/asm-x86_64/pgtable.h +++ b/include/asm-x86_64/pgtable.h @@ -337,14 +337,8 @@ #define mk_kernel_pgd(address) ((pgd_t){ /* to find an entry in a page-table-directory. */ #define pud_index(address) (((address) >> PUD_SHIFT) & (PTRS_PER_PUD-1)) #define pud_offset(pgd, address) ((pud_t *) pgd_page(*(pgd)) + pud_index(address)) -#define pud_offset_k(pgd, addr) pud_offset(pgd, addr) #define pud_present(pud) (pud_val(pud) & _PAGE_PRESENT) -static inline pud_t *__pud_offset_k(pud_t *pud, unsigned long address) -{ - return pud + pud_index(address); -} - /* PMD - Level 2 access */ #define pmd_page_kernel(pmd) ((unsigned long) __va(pmd_val(pmd) & PTE_MASK)) #define pmd_page(pmd) (pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)) diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h index 37a3ec4..3b3c121 100644 --- a/include/asm-x86_64/processor.h +++ b/include/asm-x86_64/processor.h @@ -12,7 +12,6 @@ #include #include #include #include -#include #include #include #include @@ -70,7 +69,11 @@ #ifdef CONFIG_SMP cpumask_t llc_shared_map; /* cpus sharing the last level cache */ #endif __u8 apicid; +#ifdef CONFIG_SMP __u8 booted_cores; /* number of cores as seen by OS */ + __u8 phys_proc_id; /* Physical Processor id. */ + __u8 cpu_core_id; /* Core id. */ +#endif } ____cacheline_aligned; #define X86_VENDOR_INTEL 0 @@ -97,6 +100,7 @@ extern char ignore_irq13; extern void identify_cpu(struct cpuinfo_x86 *); extern void print_cpu_info(struct cpuinfo_x86 *); extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c); +extern unsigned short num_cache_leaves; /* * EFLAGS bits diff --git a/include/asm-x86_64/proto.h b/include/asm-x86_64/proto.h index 8abf2a4..038fe1f 100644 --- a/include/asm-x86_64/proto.h +++ b/include/asm-x86_64/proto.h @@ -37,7 +37,6 @@ extern void ia32_sysenter_target(void); extern void config_acpi_tables(void); extern void ia32_syscall(void); -extern void iommu_hole_init(void); extern int pmtimer_mark_offset(void); extern void pmtimer_resume(void); @@ -75,7 +74,7 @@ extern void main_timer_handler(struct pt extern unsigned long end_pfn_map; -extern void show_trace(unsigned long * rsp); +extern void show_trace(struct task_struct *, struct pt_regs *, unsigned long * rsp); extern void show_registers(struct pt_regs *regs); extern void exception_table_check(void); @@ -101,13 +100,9 @@ extern int unsynchronized_tsc(void); extern void select_idle_routine(const struct cpuinfo_x86 *c); -extern void gart_parse_options(char *); -extern void __init no_iommu_init(void); - extern unsigned long table_start, table_end; extern int exception_trace; -extern int force_iommu, no_iommu; extern int using_apic_timer; extern int disable_apic; extern unsigned cpu_khz; @@ -116,7 +111,13 @@ extern int skip_ioapic_setup; extern int acpi_ht; extern int acpi_disabled; -#ifdef CONFIG_GART_IOMMU +extern void no_iommu_init(void); +extern int force_iommu, no_iommu; +extern int iommu_detected; +#ifdef CONFIG_IOMMU +extern void gart_iommu_init(void); +extern void gart_parse_options(char *); +extern void iommu_hole_init(void); extern int fallback_aper_order; extern int fallback_aper_force; extern int iommu_aperture; diff --git a/include/asm-x86_64/rwlock.h b/include/asm-x86_64/rwlock.h index 9942cc3..dea0e94 100644 --- a/include/asm-x86_64/rwlock.h +++ b/include/asm-x86_64/rwlock.h @@ -24,7 +24,7 @@ #define RW_LOCK_BIAS 0x01000000 #define RW_LOCK_BIAS_STR "0x01000000" #define __build_read_lock_ptr(rw, helper) \ - asm volatile(LOCK "subl $1,(%0)\n\t" \ + asm volatile(LOCK_PREFIX "subl $1,(%0)\n\t" \ "js 2f\n" \ "1:\n" \ LOCK_SECTION_START("") \ @@ -34,7 +34,7 @@ #define __build_read_lock_ptr(rw, helper ::"a" (rw) : "memory") #define __build_read_lock_const(rw, helper) \ - asm volatile(LOCK "subl $1,%0\n\t" \ + asm volatile(LOCK_PREFIX "subl $1,%0\n\t" \ "js 2f\n" \ "1:\n" \ LOCK_SECTION_START("") \ @@ -54,7 +54,7 @@ #define __build_read_lock(rw, helper) do } while (0) #define __build_write_lock_ptr(rw, helper) \ - asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ + asm volatile(LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \ "jnz 2f\n" \ "1:\n" \ LOCK_SECTION_START("") \ @@ -64,7 +64,7 @@ #define __build_write_lock_ptr(rw, helpe ::"a" (rw) : "memory") #define __build_write_lock_const(rw, helper) \ - asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ + asm volatile(LOCK_PREFIX "subl $" RW_LOCK_BIAS_STR ",%0\n\t" \ "jnz 2f\n" \ "1:\n" \ LOCK_SECTION_START("") \ diff --git a/include/asm-x86_64/semaphore.h b/include/asm-x86_64/semaphore.h index a389aa6..064df08 100644 --- a/include/asm-x86_64/semaphore.h +++ b/include/asm-x86_64/semaphore.h @@ -106,7 +106,7 @@ static inline void down(struct semaphore __asm__ __volatile__( "# atomic down operation\n\t" - LOCK "decl %0\n\t" /* --sem->count */ + LOCK_PREFIX "decl %0\n\t" /* --sem->count */ "js 2f\n" "1:\n" LOCK_SECTION_START("") @@ -130,7 +130,7 @@ static inline int down_interruptible(str __asm__ __volatile__( "# atomic interruptible down operation\n\t" - LOCK "decl %1\n\t" /* --sem->count */ + LOCK_PREFIX "decl %1\n\t" /* --sem->count */ "js 2f\n\t" "xorl %0,%0\n" "1:\n" @@ -154,7 +154,7 @@ static inline int down_trylock(struct se __asm__ __volatile__( "# atomic interruptible down operation\n\t" - LOCK "decl %1\n\t" /* --sem->count */ + LOCK_PREFIX "decl %1\n\t" /* --sem->count */ "js 2f\n\t" "xorl %0,%0\n" "1:\n" @@ -178,7 +178,7 @@ static inline void up(struct semaphore * { __asm__ __volatile__( "# atomic up operation\n\t" - LOCK "incl %0\n\t" /* ++sem->count */ + LOCK_PREFIX "incl %0\n\t" /* ++sem->count */ "jle 2f\n" "1:\n" LOCK_SECTION_START("") diff --git a/include/asm-x86_64/serial.h b/include/asm-x86_64/serial.h index dc752ea..b0496e0 100644 --- a/include/asm-x86_64/serial.h +++ b/include/asm-x86_64/serial.h @@ -2,7 +2,6 @@ * include/asm-x86_64/serial.h */ -#include /* * This assumes you have a 1.8432 MHz clock for your UART. diff --git a/include/asm-x86_64/signal.h b/include/asm-x86_64/signal.h index f8d5579..cef7a7d 100644 --- a/include/asm-x86_64/signal.h +++ b/include/asm-x86_64/signal.h @@ -83,7 +83,6 @@ #define SIGRTMAX _NSIG * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -103,7 +102,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 diff --git a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h index a4fdaeb..6805e1f 100644 --- a/include/asm-x86_64/smp.h +++ b/include/asm-x86_64/smp.h @@ -5,7 +5,6 @@ #define __ASM_SMP_H * We need the APIC definitions automatically as part of 'smp.h' */ #ifndef __ASSEMBLY__ -#include #include #include #include @@ -54,8 +53,6 @@ extern int smp_call_function_single(int extern cpumask_t cpu_sibling_map[NR_CPUS]; extern cpumask_t cpu_core_map[NR_CPUS]; -extern u8 phys_proc_id[NR_CPUS]; -extern u8 cpu_core_id[NR_CPUS]; extern u8 cpu_llc_id[NR_CPUS]; #define SMP_TRAMPOLINE_BASE 0x6000 diff --git a/include/asm-x86_64/socket.h b/include/asm-x86_64/socket.h index f2cdbea..b467026 100644 --- a/include/asm-x86_64/socket.h +++ b/include/asm-x86_64/socket.h @@ -48,5 +48,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _ASM_SOCKET_H */ diff --git a/include/asm-x86_64/spinlock.h b/include/asm-x86_64/spinlock.h index fe484a6..8d34219 100644 --- a/include/asm-x86_64/spinlock.h +++ b/include/asm-x86_64/spinlock.h @@ -4,7 +4,6 @@ #define __ASM_SPINLOCK_H #include #include #include -#include /* * Your basic SMP spinlocks, allowing only a single CPU anywhere @@ -32,15 +31,19 @@ #define __raw_spin_lock_string \ "jmp 1b\n" \ LOCK_SECTION_END +#define __raw_spin_lock_string_up \ + "\n\tdecl %0" + #define __raw_spin_unlock_string \ "movl $1,%0" \ :"=m" (lock->slock) : : "memory" static inline void __raw_spin_lock(raw_spinlock_t *lock) { - __asm__ __volatile__( - __raw_spin_lock_string - :"=m" (lock->slock) : : "memory"); + alternative_smp( + __raw_spin_lock_string, + __raw_spin_lock_string_up, + "=m" (lock->slock) : : "memory"); } #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) diff --git a/include/asm-x86_64/string.h b/include/asm-x86_64/string.h index ee6bf27..9505d9f 100644 --- a/include/asm-x86_64/string.h +++ b/include/asm-x86_64/string.h @@ -6,7 +6,8 @@ #ifdef __KERNEL__ /* Written 2002 by Andi Kleen */ /* Only used for special circumstances. Stolen from i386/string.h */ -static inline void * __inline_memcpy(void * to, const void * from, size_t n) +static __always_inline void * +__inline_memcpy(void * to, const void * from, size_t n) { unsigned long d0, d1, d2; __asm__ __volatile__( diff --git a/include/asm-x86_64/swiotlb.h b/include/asm-x86_64/swiotlb.h index 60757ef..5f9a018 100644 --- a/include/asm-x86_64/swiotlb.h +++ b/include/asm-x86_64/swiotlb.h @@ -1,7 +1,6 @@ #ifndef _ASM_SWIOTLB_H #define _ASM_SWTIOLB_H 1 -#include #include diff --git a/include/asm-x86_64/system.h b/include/asm-x86_64/system.h index 3975989..f67f287 100644 --- a/include/asm-x86_64/system.h +++ b/include/asm-x86_64/system.h @@ -1,18 +1,12 @@ #ifndef __ASM_SYSTEM_H #define __ASM_SYSTEM_H -#include #include #include +#include #ifdef __KERNEL__ -#ifdef CONFIG_SMP -#define LOCK_PREFIX "lock ; " -#else -#define LOCK_PREFIX "" -#endif - #define __STR(x) #x #define STR(x) __STR(x) @@ -35,7 +29,7 @@ #define switch_to(prev,next,last) \ "thread_return:\n\t" \ "movq %%gs:%P[pda_pcurrent],%%rsi\n\t" \ "movq %P[thread_info](%%rsi),%%r8\n\t" \ - LOCK "btr %[tif_fork],%P[ti_flags](%%r8)\n\t" \ + LOCK_PREFIX "btr %[tif_fork],%P[ti_flags](%%r8)\n\t" \ "movq %%rax,%%rdi\n\t" \ "jc ret_from_fork\n\t" \ RESTORE_CONTEXT \ @@ -70,82 +64,6 @@ #define loadsegment(seg,value) \ ".previous" \ : :"r" (value), "r" (0)) -#ifdef __KERNEL__ -struct alt_instr { - __u8 *instr; /* original instruction */ - __u8 *replacement; - __u8 cpuid; /* cpuid bit set for replacement */ - __u8 instrlen; /* length of original instruction */ - __u8 replacementlen; /* length of new instruction, <= instrlen */ - __u8 pad[5]; -}; -#endif - -/* - * Alternative instructions for different CPU types or capabilities. - * - * This allows to use optimized instructions even on generic binary - * kernels. - * - * length of oldinstr must be longer or equal the length of newinstr - * It can be padded with nops as needed. - * - * For non barrier like inlines please define new variants - * without volatile and memory clobber. - */ -#define alternative(oldinstr, newinstr, feature) \ - asm volatile ("661:\n\t" oldinstr "\n662:\n" \ - ".section .altinstructions,\"a\"\n" \ - " .align 8\n" \ - " .quad 661b\n" /* label */ \ - " .quad 663f\n" /* new instruction */ \ - " .byte %c0\n" /* feature bit */ \ - " .byte 662b-661b\n" /* sourcelen */ \ - " .byte 664f-663f\n" /* replacementlen */ \ - ".previous\n" \ - ".section .altinstr_replacement,\"ax\"\n" \ - "663:\n\t" newinstr "\n664:\n" /* replacement */ \ - ".previous" :: "i" (feature) : "memory") - -/* - * Alternative inline assembly with input. - * - * Peculiarities: - * No memory clobber here. - * Argument numbers start with 1. - * Best is to use constraints that are fixed size (like (%1) ... "r") - * If you use variable sized constraints like "m" or "g" in the - * replacement make sure to pad to the worst case length. - */ -#define alternative_input(oldinstr, newinstr, feature, input...) \ - asm volatile ("661:\n\t" oldinstr "\n662:\n" \ - ".section .altinstructions,\"a\"\n" \ - " .align 8\n" \ - " .quad 661b\n" /* label */ \ - " .quad 663f\n" /* new instruction */ \ - " .byte %c0\n" /* feature bit */ \ - " .byte 662b-661b\n" /* sourcelen */ \ - " .byte 664f-663f\n" /* replacementlen */ \ - ".previous\n" \ - ".section .altinstr_replacement,\"ax\"\n" \ - "663:\n\t" newinstr "\n664:\n" /* replacement */ \ - ".previous" :: "i" (feature), ##input) - -/* Like alternative_input, but with a single output argument */ -#define alternative_io(oldinstr, newinstr, feature, output, input...) \ - asm volatile ("661:\n\t" oldinstr "\n662:\n" \ - ".section .altinstructions,\"a\"\n" \ - " .align 8\n" \ - " .quad 661b\n" /* label */ \ - " .quad 663f\n" /* new instruction */ \ - " .byte %c[feat]\n" /* feature bit */ \ - " .byte 662b-661b\n" /* sourcelen */ \ - " .byte 664f-663f\n" /* replacementlen */ \ - ".previous\n" \ - ".section .altinstr_replacement,\"ax\"\n" \ - "663:\n\t" newinstr "\n664:\n" /* replacement */ \ - ".previous" : output : [feat] "i" (feature), ##input) - /* * Clear and set 'TS' bit respectively */ @@ -326,46 +244,11 @@ #define set_wmb(var, value) do { var = v #define warn_if_not_ulong(x) do { unsigned long foo; (void) (&(x) == &foo); } while (0) -/* interrupt control.. */ -#define local_save_flags(x) do { warn_if_not_ulong(x); __asm__ __volatile__("# save_flags \n\t pushfq ; popq %q0":"=g" (x): /* no input */ :"memory"); } while (0) -#define local_irq_restore(x) __asm__ __volatile__("# restore_flags \n\t pushq %0 ; popfq": /* no output */ :"g" (x):"memory", "cc") - -#ifdef CONFIG_X86_VSMP -/* Interrupt control for VSMP architecture */ -#define local_irq_disable() do { unsigned long flags; local_save_flags(flags); local_irq_restore((flags & ~(1 << 9)) | (1 << 18)); } while (0) -#define local_irq_enable() do { unsigned long flags; local_save_flags(flags); local_irq_restore((flags | (1 << 9)) & ~(1 << 18)); } while (0) - -#define irqs_disabled() \ -({ \ - unsigned long flags; \ - local_save_flags(flags); \ - (flags & (1<<18)) || !(flags & (1<<9)); \ -}) - -/* For spinlocks etc */ -#define local_irq_save(x) do { local_save_flags(x); local_irq_restore((x & ~(1 << 9)) | (1 << 18)); } while (0) -#else /* CONFIG_X86_VSMP */ -#define local_irq_disable() __asm__ __volatile__("cli": : :"memory") -#define local_irq_enable() __asm__ __volatile__("sti": : :"memory") - -#define irqs_disabled() \ -({ \ - unsigned long flags; \ - local_save_flags(flags); \ - !(flags & (1<<9)); \ -}) - -/* For spinlocks etc */ -#define local_irq_save(x) do { warn_if_not_ulong(x); __asm__ __volatile__("# local_irq_save \n\t pushfq ; popq %0 ; cli":"=g" (x): /* no input */ :"memory"); } while (0) -#endif - -/* used in the idle loop; sti takes one instruction cycle to complete */ -#define safe_halt() __asm__ __volatile__("sti; hlt": : :"memory") -/* used when interrupts are already enabled or to shutdown the processor */ -#define halt() __asm__ __volatile__("hlt": : :"memory") +#include void cpu_idle_wait(void); extern unsigned long arch_align_stack(unsigned long sp); +extern void free_init_pages(char *what, unsigned long begin, unsigned long end); #endif diff --git a/include/asm-x86_64/tce.h b/include/asm-x86_64/tce.h new file mode 100644 index 0000000..ee51d31 --- /dev/null +++ b/include/asm-x86_64/tce.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006 Muli Ben-Yehuda , IBM Corporation + * Copyright (C) 2006 Jon Mason , IBM Corporation + * + * This file is derived from asm-powerpc/tce.h. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ASM_X86_64_TCE_H +#define _ASM_X86_64_TCE_H + +extern void* tce_table_kva[]; +extern unsigned int specified_table_size; +struct iommu_table; + +#define TCE_ENTRY_SIZE 8 /* in bytes */ + +#define TCE_READ_SHIFT 0 +#define TCE_WRITE_SHIFT 1 +#define TCE_HUBID_SHIFT 2 /* unused */ +#define TCE_RSVD_SHIFT 8 /* unused */ +#define TCE_RPN_SHIFT 12 +#define TCE_UNUSED_SHIFT 48 /* unused */ + +#define TCE_RPN_MASK 0x0000fffffffff000ULL + +extern void tce_build(struct iommu_table *tbl, unsigned long index, + unsigned int npages, unsigned long uaddr, int direction); +extern void tce_free(struct iommu_table *tbl, long index, unsigned int npages); +extern void* alloc_tce_table(void); +extern void free_tce_table(void *tbl); +extern int build_tce_table(struct pci_dev *dev, void __iomem *bbar); + +#endif /* _ASM_X86_64_TCE_H */ diff --git a/include/asm-x86_64/thread_info.h b/include/asm-x86_64/thread_info.h index 4ac0e0a..2029b00 100644 --- a/include/asm-x86_64/thread_info.h +++ b/include/asm-x86_64/thread_info.h @@ -73,8 +73,21 @@ static inline struct thread_info *stack_ } /* thread information allocation */ +#ifdef CONFIG_DEBUG_STACK_USAGE +#define alloc_thread_info(tsk) \ + ({ \ + struct thread_info *ret; \ + \ + ret = ((struct thread_info *) __get_free_pages(GFP_KERNEL,THREAD_ORDER)); \ + if (ret) \ + memset(ret, 0, THREAD_SIZE); \ + ret; \ + }) +#else #define alloc_thread_info(tsk) \ ((struct thread_info *) __get_free_pages(GFP_KERNEL,THREAD_ORDER)) +#endif + #define free_thread_info(ti) free_pages((unsigned long) (ti), THREAD_ORDER) #else /* !__ASSEMBLY__ */ @@ -101,7 +114,7 @@ #define TIF_SINGLESTEP 4 /* reenable si #define TIF_IRET 5 /* force IRET */ #define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */ #define TIF_SECCOMP 8 /* secure computing */ -#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */ +/* 16 free */ #define TIF_IA32 17 /* 32bit process */ #define TIF_FORK 18 /* ret_from_fork */ #define TIF_ABI_PENDING 19 @@ -115,7 +128,6 @@ #define _TIF_NEED_RESCHED (1<thread_info->status & TS_POLLING) #endif /* __KERNEL__ */ diff --git a/include/asm-x86_64/tlbflush.h b/include/asm-x86_64/tlbflush.h index 4a9c20e..d16d5b6 100644 --- a/include/asm-x86_64/tlbflush.h +++ b/include/asm-x86_64/tlbflush.h @@ -1,7 +1,6 @@ #ifndef _X8664_TLBFLUSH_H #define _X8664_TLBFLUSH_H -#include #include #include diff --git a/include/asm-x86_64/topology.h b/include/asm-x86_64/topology.h index 9db54e9..6e7a2e9 100644 --- a/include/asm-x86_64/topology.h +++ b/include/asm-x86_64/topology.h @@ -1,15 +1,12 @@ #ifndef _ASM_X86_64_TOPOLOGY_H #define _ASM_X86_64_TOPOLOGY_H -#include #ifdef CONFIG_NUMA #include #include -/* Map the K8 CPU local memory controllers to a simple 1:1 CPU:NODE topology */ - extern cpumask_t cpu_online_map; extern unsigned char cpu_to_node[]; @@ -58,12 +55,12 @@ #define SD_NODE_INIT (struct sched_domai #endif #ifdef CONFIG_SMP -#define topology_physical_package_id(cpu) \ - (phys_proc_id[cpu] == BAD_APICID ? -1 : phys_proc_id[cpu]) -#define topology_core_id(cpu) \ - (cpu_core_id[cpu] == BAD_APICID ? 0 : cpu_core_id[cpu]) +#define topology_physical_package_id(cpu) (cpu_data[cpu].phys_proc_id) +#define topology_core_id(cpu) (cpu_data[cpu].cpu_core_id) #define topology_core_siblings(cpu) (cpu_core_map[cpu]) #define topology_thread_siblings(cpu) (cpu_sibling_map[cpu]) +#define mc_capable() (boot_cpu_data.x86_max_cores > 1) +#define smt_capable() (smp_num_siblings > 1) #endif #include diff --git a/include/asm-x86_64/uaccess.h b/include/asm-x86_64/uaccess.h index bddffcb..1e1fa00 100644 --- a/include/asm-x86_64/uaccess.h +++ b/include/asm-x86_64/uaccess.h @@ -4,7 +4,6 @@ #define __X86_64_UACCESS_H /* * User space memory access functions */ -#include #include #include #include diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index feb77cb..94387c9 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -617,8 +617,12 @@ #define __NR_sync_file_range 277 __SYSCALL(__NR_sync_file_range, sys_sync_file_range) #define __NR_vmsplice 278 __SYSCALL(__NR_vmsplice, sys_vmsplice) +#define __NR_move_pages 279 +__SYSCALL(__NR_move_pages, sys_move_pages) -#define __NR_syscall_max __NR_vmsplice +#ifdef __KERNEL__ + +#define __NR_syscall_max __NR_move_pages #ifndef __NO_STUBS @@ -635,7 +639,6 @@ do { \ return (type) (res); \ } while (0) -#ifdef __KERNEL__ #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_STAT #define __ARCH_WANT_SYS_ALARM @@ -657,7 +660,6 @@ #define __ARCH_WANT_SYS_SIGPROCMASK #define __ARCH_WANT_SYS_RT_SIGACTION #define __ARCH_WANT_SYS_TIME #define __ARCH_WANT_COMPAT_SYS_TIME -#endif #ifndef __KERNEL_SYSCALLS__ @@ -821,7 +823,7 @@ asmlinkage long sys_pipe(int *fildes); #endif /* __KERNEL_SYSCALLS__ */ -#if !defined(__ASSEMBLY__) && defined(__KERNEL__) +#ifndef __ASSEMBLY__ #include #include @@ -848,4 +850,5 @@ #endif /* __NO_STUBS */ */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall") +#endif /* __KERNEL__ */ #endif diff --git a/include/asm-x86_64/unwind.h b/include/asm-x86_64/unwind.h new file mode 100644 index 0000000..f3e7124 --- /dev/null +++ b/include/asm-x86_64/unwind.h @@ -0,0 +1,106 @@ +#ifndef _ASM_X86_64_UNWIND_H +#define _ASM_X86_64_UNWIND_H + +/* + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich + * This code is released under version 2 of the GNU GPL. + */ + +#ifdef CONFIG_STACK_UNWIND + +#include +#include +#include +#include + +struct unwind_frame_info +{ + struct pt_regs regs; + struct task_struct *task; +}; + +#define UNW_PC(frame) (frame)->regs.rip +#define UNW_SP(frame) (frame)->regs.rsp +#ifdef CONFIG_FRAME_POINTER +#define UNW_FP(frame) (frame)->regs.rbp +#define FRAME_RETADDR_OFFSET 8 +#define FRAME_LINK_OFFSET 0 +#define STACK_BOTTOM(tsk) (((tsk)->thread.rsp0 - 1) & ~(THREAD_SIZE - 1)) +#define STACK_TOP(tsk) ((tsk)->thread.rsp0) +#endif +/* Might need to account for the special exception and interrupt handling + stacks here, since normally + EXCEPTION_STACK_ORDER < THREAD_ORDER < IRQSTACK_ORDER, + but the construct is needed only for getting across the stack switch to + the interrupt stack - thus considering the IRQ stack itself is unnecessary, + and the overhead of comparing against all exception handling stacks seems + not desirable. */ +#define STACK_LIMIT(ptr) (((ptr) - 1) & ~(THREAD_SIZE - 1)) + +#define UNW_REGISTER_INFO \ + PTREGS_INFO(rax), \ + PTREGS_INFO(rdx), \ + PTREGS_INFO(rcx), \ + PTREGS_INFO(rbx), \ + PTREGS_INFO(rsi), \ + PTREGS_INFO(rdi), \ + PTREGS_INFO(rbp), \ + PTREGS_INFO(rsp), \ + PTREGS_INFO(r8), \ + PTREGS_INFO(r9), \ + PTREGS_INFO(r10), \ + PTREGS_INFO(r11), \ + PTREGS_INFO(r12), \ + PTREGS_INFO(r13), \ + PTREGS_INFO(r14), \ + PTREGS_INFO(r15), \ + PTREGS_INFO(rip) + +static inline void arch_unw_init_frame_info(struct unwind_frame_info *info, + /*const*/ struct pt_regs *regs) +{ + info->regs = *regs; +} + +static inline void arch_unw_init_blocked(struct unwind_frame_info *info) +{ + extern const char thread_return[]; + + memset(&info->regs, 0, sizeof(info->regs)); + info->regs.rip = (unsigned long)thread_return; + info->regs.cs = __KERNEL_CS; + __get_user(info->regs.rbp, (unsigned long *)info->task->thread.rsp); + info->regs.rsp = info->task->thread.rsp; + info->regs.ss = __KERNEL_DS; +} + +extern int arch_unwind_init_running(struct unwind_frame_info *, + int (*callback)(struct unwind_frame_info *, + void *arg), + void *arg); + +static inline int arch_unw_user_mode(const struct unwind_frame_info *info) +{ +#if 0 /* This can only work when selector register saves/restores + are properly annotated (and tracked in UNW_REGISTER_INFO). */ + return user_mode(&info->regs); +#else + return (long)info->regs.rip >= 0 + || (info->regs.rip >= VSYSCALL_START && info->regs.rip < VSYSCALL_END) + || (long)info->regs.rsp >= 0; +#endif +} + +#else + +#define UNW_PC(frame) ((void)(frame), 0) + +static inline int arch_unw_user_mode(const void *info) +{ + return 0; +} + +#endif + +#endif /* _ASM_X86_64_UNWIND_H */ diff --git a/include/asm-x86_64/vga.h b/include/asm-x86_64/vga.h index ef0c0e5..0ecf68a 100644 --- a/include/asm-x86_64/vga.h +++ b/include/asm-x86_64/vga.h @@ -12,7 +12,7 @@ #define _LINUX_ASM_VGA_H_ * access the videoram directly without any black magic. */ -#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) +#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x) #define vga_readb(x) (*(x)) #define vga_writeb(x,y) (*(y) = (x)) diff --git a/include/asm-xtensa/Kbuild b/include/asm-xtensa/Kbuild new file mode 100644 index 0000000..c68e168 --- /dev/null +++ b/include/asm-xtensa/Kbuild @@ -0,0 +1 @@ +include include/asm-generic/Kbuild.asm diff --git a/include/asm-xtensa/atomic.h b/include/asm-xtensa/atomic.h index fe105a1..5c26720 100644 --- a/include/asm-xtensa/atomic.h +++ b/include/asm-xtensa/atomic.h @@ -13,7 +13,6 @@ #ifndef _XTENSA_ATOMIC_H #define _XTENSA_ATOMIC_H -#include #include typedef struct { volatile int counter; } atomic_t; diff --git a/include/asm-xtensa/checksum.h b/include/asm-xtensa/checksum.h index 81a797a..03114f8 100644 --- a/include/asm-xtensa/checksum.h +++ b/include/asm-xtensa/checksum.h @@ -11,7 +11,6 @@ #ifndef _XTENSA_CHECKSUM_H #define _XTENSA_CHECKSUM_H -#include #include #include @@ -44,8 +43,7 @@ asmlinkage unsigned int csum_partial_cop * Note: when you get a NULL pointer exception here this means someone * passed in an incorrect kernel address to one of these functions. * - * If you use these functions directly please don't forget the - * verify_area(). + * If you use these functions directly please don't forget the access_ok(). */ static inline unsigned int csum_partial_copy_nocheck ( const char *src, char *dst, diff --git a/include/asm-xtensa/delay.h b/include/asm-xtensa/delay.h index 1bc601e..e1d8c9e 100644 --- a/include/asm-xtensa/delay.h +++ b/include/asm-xtensa/delay.h @@ -12,7 +12,6 @@ #ifndef _XTENSA_DELAY_H #define _XTENSA_DELAY_H -#include #include #include diff --git a/include/asm-xtensa/dma.h b/include/asm-xtensa/dma.h index 1c22b02..db2633f 100644 --- a/include/asm-xtensa/dma.h +++ b/include/asm-xtensa/dma.h @@ -11,7 +11,6 @@ #ifndef _XTENSA_DMA_H #define _XTENSA_DMA_H -#include #include /* need byte IO */ #include diff --git a/include/asm-xtensa/hardirq.h b/include/asm-xtensa/hardirq.h index aa9c1ad..87cb19d 100644 --- a/include/asm-xtensa/hardirq.h +++ b/include/asm-xtensa/hardirq.h @@ -11,7 +11,6 @@ #ifndef _XTENSA_HARDIRQ_H #define _XTENSA_HARDIRQ_H -#include #include #include diff --git a/include/asm-xtensa/hw_irq.h b/include/asm-xtensa/hw_irq.h index ccf4362..3ddbea7 100644 --- a/include/asm-xtensa/hw_irq.h +++ b/include/asm-xtensa/hw_irq.h @@ -11,8 +11,4 @@ #ifndef _XTENSA_HW_IRQ_H #define _XTENSA_HW_IRQ_H -static inline void hw_resend_irq(struct hw_interrupt_type *h, unsigned int i) -{ -} - #endif diff --git a/include/asm-xtensa/ide.h b/include/asm-xtensa/ide.h index b523cd4..6b91274 100644 --- a/include/asm-xtensa/ide.h +++ b/include/asm-xtensa/ide.h @@ -14,7 +14,6 @@ #define _XTENSA_IDE_H #ifdef __KERNEL__ -#include #ifndef MAX_HWIFS # define MAX_HWIFS 1 diff --git a/include/asm-xtensa/io.h b/include/asm-xtensa/io.h index c5c1398..556e5ee 100644 --- a/include/asm-xtensa/io.h +++ b/include/asm-xtensa/io.h @@ -12,7 +12,6 @@ #ifndef _XTENSA_IO_H #define _XTENSA_IO_H #ifdef __KERNEL__ -#include #include #include diff --git a/include/asm-xtensa/irq.h b/include/asm-xtensa/irq.h index d984e95..049fde7 100644 --- a/include/asm-xtensa/irq.h +++ b/include/asm-xtensa/irq.h @@ -11,7 +11,6 @@ #ifndef _XTENSA_IRQ_H #define _XTENSA_IRQ_H -#include #include #include diff --git a/include/asm-xtensa/mmu_context.h b/include/asm-xtensa/mmu_context.h index 364a7b0..af683a7 100644 --- a/include/asm-xtensa/mmu_context.h +++ b/include/asm-xtensa/mmu_context.h @@ -13,7 +13,6 @@ #ifndef _XTENSA_MMU_CONTEXT_H #define _XTENSA_MMU_CONTEXT_H -#include #include #include diff --git a/include/asm-xtensa/page.h b/include/asm-xtensa/page.h index 992bac5..40f4c6c 100644 --- a/include/asm-xtensa/page.h +++ b/include/asm-xtensa/page.h @@ -14,7 +14,6 @@ #define _XTENSA_PAGE_H #ifdef __KERNEL__ #include -#include /* * PAGE_SHIFT determines the page size diff --git a/include/asm-xtensa/pgalloc.h b/include/asm-xtensa/pgalloc.h index 734a8d0..d56ddf2 100644 --- a/include/asm-xtensa/pgalloc.h +++ b/include/asm-xtensa/pgalloc.h @@ -13,7 +13,6 @@ #define _XTENSA_PGALLOC_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/asm-xtensa/platform.h b/include/asm-xtensa/platform.h index 3616389..48135a9 100644 --- a/include/asm-xtensa/platform.h +++ b/include/asm-xtensa/platform.h @@ -13,7 +13,6 @@ #ifndef _XTENSA_PLATFORM_H #define _XTENSA_PLATFORM_H -#include #include #include diff --git a/include/asm-xtensa/rwsem.h b/include/asm-xtensa/rwsem.h index 3c02b0e..0aad3a5 100644 --- a/include/asm-xtensa/rwsem.h +++ b/include/asm-xtensa/rwsem.h @@ -31,24 +31,11 @@ #define RWSEM_ACTIVE_READ_BIAS RWSEM_AC #define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS) spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; -#endif }; -/* - * initialisation - */ -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 -#else -#define __RWSEM_DEBUG_INIT /* */ -#endif - #define __RWSEM_INITIALIZER(name) \ { RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, \ - LIST_HEAD_INIT((name).wait_list) \ - __RWSEM_DEBUG_INIT } + LIST_HEAD_INIT((name).wait_list) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) @@ -63,9 +50,6 @@ static inline void init_rwsem(struct rw_ sem->count = RWSEM_UNLOCKED_VALUE; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif } /* @@ -172,4 +156,9 @@ static inline int rwsem_atomic_update(in return atomic_add_return(delta, (atomic_t *)(&sem->count)); } -#endif /* _XTENSA_RWSEM_XADD_H */ +static inline int rwsem_is_locked(struct rw_semaphore *sem) +{ + return (sem->count != 0); +} + +#endif /* _XTENSA_RWSEM_H */ diff --git a/include/asm-xtensa/signal.h b/include/asm-xtensa/signal.h index a99c9ae..633ba73 100644 --- a/include/asm-xtensa/signal.h +++ b/include/asm-xtensa/signal.h @@ -75,7 +75,6 @@ #define SIGRTMAX (_NSIG-1) * SA_FLAGS values: * * SA_ONSTACK indicates that a registered stack_t will be used. - * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the * SA_RESTART flag to get restarting signals (which were the default long ago) * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop. * SA_RESETHAND clears the handler when the signal is delivered. @@ -95,7 +94,6 @@ #define SA_RESETHAND 0x80000000 #define SA_NOMASK SA_NODEFER #define SA_ONESHOT SA_RESETHAND -#define SA_INTERRUPT 0x20000000 /* dummy -- ignored */ #define SA_RESTORER 0x04000000 @@ -109,19 +107,6 @@ #define MINSIGSTKSZ 2048 #define SIGSTKSZ 8192 #ifndef __ASSEMBLY__ -#ifdef __KERNEL__ - -/* - * These values of sa_flags are used only by the kernel as part of the - * irq handling routines. - * - * SA_INTERRUPT is also used by the irq handling routines. - * SA_SHIRQ is for shared interrupt support on PCI and EISA. - */ -#define SA_SAMPLE_RANDOM SA_RESTART -#define SA_SHIRQ 0x04000000 -#define SA_PROBEIRQ 0x08000000 -#endif #define SIG_BLOCK 0 /* for blocking signals */ #define SIG_UNBLOCK 1 /* for unblocking signals */ diff --git a/include/asm-xtensa/socket.h b/include/asm-xtensa/socket.h index 00f83f3..971d231 100644 --- a/include/asm-xtensa/socket.h +++ b/include/asm-xtensa/socket.h @@ -59,5 +59,6 @@ #define SCM_TIMESTAMP SO_TIMESTAMP #define SO_ACCEPTCONN 30 #define SO_PEERSEC 31 +#define SO_PASSSEC 34 #endif /* _XTENSA_SOCKET_H */ diff --git a/include/asm-xtensa/system.h b/include/asm-xtensa/system.h index b29f7ae..f986170 100644 --- a/include/asm-xtensa/system.h +++ b/include/asm-xtensa/system.h @@ -11,7 +11,6 @@ #ifndef _XTENSA_SYSTEM_H #define _XTENSA_SYSTEM_H -#include #include #include diff --git a/include/asm-xtensa/uaccess.h b/include/asm-xtensa/uaccess.h index 06a22b8..88a64e1 100644 --- a/include/asm-xtensa/uaccess.h +++ b/include/asm-xtensa/uaccess.h @@ -154,35 +154,6 @@ #endif .Laccess_ok_\@: .endm -/* - * verify_area determines whether a memory access is allowed. It's - * mostly an unnecessary wrapper for access_ok, but we provide it as a - * duplicate of the verify_area() C inline function below. See the - * equivalent C version below for clarity. - * - * On error, verify_area branches to a label indicated by parameter - * . This implies that the macro falls through to the next - * instruction on success. - * - * Note that we assume success is the common case, and we optimize the - * branch fall-through case on success. - * - * On Entry: - * register containing memory address - * register containing memory size - * temp register - * label to branch to on error; implies fall-through - * macro on success - * On Exit: - * preserved - * preserved - * destroyed - */ - .macro verify_area aa, as, at, sp, error - access_ok \at, \aa, \as, \sp, \error - .endm - - #else /* __ASSEMBLY__ not defined */ #include @@ -211,11 +182,6 @@ #define __user_ok(addr,size) (((size) <= #define __access_ok(addr,size) (__kernel_ok || __user_ok((addr),(size))) #define access_ok(type,addr,size) __access_ok((unsigned long)(addr),(size)) -static inline int verify_area(int type, const void * addr, unsigned long size) -{ - return access_ok(type,addr,size) ? 0 : -EFAULT; -} - /* * These are the main single-value transfer routines. They * automatically use the right size if we just have the right pointer diff --git a/include/asm-xtensa/unistd.h b/include/asm-xtensa/unistd.h index 6b39d66..5e1b99d 100644 --- a/include/asm-xtensa/unistd.h +++ b/include/asm-xtensa/unistd.h @@ -11,8 +11,6 @@ #ifndef _XTENSA_UNISTD_H #define _XTENSA_UNISTD_H -#include - #define __NR_spill 0 #define __NR_exit 1 #define __NR_read 3 @@ -221,21 +219,9 @@ #define SYSXTENSA_ATOMIC_CMP_SWP 4 /* #define SYSXTENSA_COUNT 5 /* count of syscall0 functions*/ #ifdef __KERNEL__ -#define __syscall_return(type, res) return ((type)(res)) -#else -#define __syscall_return(type, res) \ -do { \ - if ((unsigned long)(res) >= (unsigned long)(-125)) { \ - /* Avoid using "res" which is declared to be in register r2; \ - * errno might expand to a function call and clobber it. */ \ - int __err = -(res); \ - errno = __err; \ - res = -1; \ - } \ - return (type) (res); \ -} while (0) -#endif +#include +#define __syscall_return(type, res) return ((type)(res)) /* Tensilica's xt-xcc compiler is much more agressive at code * optimization than gcc. Multiple __asm__ statements are @@ -429,11 +415,10 @@ #endif */ #define cond_syscall(x) asm(".weak\t" #x "\n\t.set\t" #x ",sys_ni_syscall"); -#ifdef __KERNEL__ #define __ARCH_WANT_STAT64 #define __ARCH_WANT_SYS_UTIME #define __ARCH_WANT_SYS_LLSEEK #define __ARCH_WANT_SYS_RT_SIGACTION -#endif +#endif /* __KERNEL__ */ #endif /* _XTENSA_UNISTD_H */ diff --git a/include/asm-xtensa/vga.h b/include/asm-xtensa/vga.h index 23d82f6..1fd8cab 100644 --- a/include/asm-xtensa/vga.h +++ b/include/asm-xtensa/vga.h @@ -11,7 +11,7 @@ #ifndef _XTENSA_VGA_H #define _XTENSA_VGA_H -#define VGA_MAP_MEM(x) (unsigned long)phys_to_virt(x) +#define VGA_MAP_MEM(x,s) (unsigned long)phys_to_virt(x) #define vga_readb(x) (*(x)) #define vga_writeb(x,y) (*(y) = (x)) diff --git a/include/keys/user-type.h b/include/keys/user-type.h index a3dae18..c37c342 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -37,6 +37,7 @@ extern struct key_type key_type_user; extern int user_instantiate(struct key *key, const void *data, size_t datalen); extern int user_update(struct key *key, const void *data, size_t datalen); extern int user_match(const struct key *key, const void *criterion); +extern void user_revoke(struct key *key); extern void user_destroy(struct key *key); extern void user_describe(const struct key *user, struct seq_file *m); extern long user_read(const struct key *key, diff --git a/include/linux/Kbuild b/include/linux/Kbuild new file mode 100644 index 0000000..2b8a7d6 --- /dev/null +++ b/include/linux/Kbuild @@ -0,0 +1,63 @@ +header-y := byteorder/ dvb/ hdlc/ isdn/ nfsd/ raid/ sunrpc/ tc_act/ \ + netfilter/ netfilter_arp/ netfilter_bridge/ netfilter_ipv4/ \ + netfilter_ipv6/ + +header-y += affs_fs.h affs_hardblocks.h aio_abi.h a.out.h arcfb.h \ + atmapi.h atmbr2684.h atmclip.h atm_eni.h atm_he.h \ + atm_idt77105.h atmioc.h atmlec.h atmmpc.h atm_nicstar.h \ + atmppp.h atmsap.h atmsvc.h atm_zatm.h auto_fs4.h auxvec.h \ + awe_voice.h ax25.h b1lli.h baycom.h bfs_fs.h blkpg.h \ + bpqether.h cdk.h chio.h coda_psdev.h coff.h comstats.h \ + consolemap.h cycx_cfm.h dm-ioctl.h dn.h dqblk_v1.h \ + dqblk_v2.h dqblk_xfs.h efs_fs_sb.h elf-fdpic.h elf.h elf-em.h \ + fadvise.h fd.h fdreg.h ftape-header-segment.h ftape-vendors.h \ + fuse.h futex.h genetlink.h gen_stats.h gigaset_dev.h hdsmart.h \ + hpfs_fs.h hysdn_if.h i2c-dev.h i8k.h icmp.h \ + if_arcnet.h if_arp.h if_bonding.h if_cablemodem.h if_fc.h \ + if_fddi.h if.h if_hippi.h if_infiniband.h if_packet.h \ + if_plip.h if_ppp.h if_slip.h if_strip.h if_tunnel.h in6.h \ + in_route.h ioctl.h ip.h ipmi_msgdefs.h ip_mp_alg.h ipsec.h \ + ipx.h irda.h isdn_divertif.h iso_fs.h ite_gpio.h ixjuser.h \ + jffs2.h keyctl.h limits.h major.h matroxfb.h meye.h minix_fs.h \ + mmtimer.h mqueue.h mtio.h ncp_no.h netfilter_arp.h netrom.h \ + nfs2.h nfs4_mount.h nfs_mount.h openprom_fs.h param.h \ + pci_ids.h pci_regs.h personality.h pfkeyv2.h pg.h pkt_cls.h \ + pkt_sched.h posix_types.h ppdev.h prctl.h ps2esdi.h qic117.h \ + qnxtypes.h quotaio_v1.h quotaio_v2.h radeonfb.h raw.h \ + resource.h rose.h sctp.h smbno.h snmp.h sockios.h som.h \ + sound.h stddef.h synclink.h telephony.h termios.h ticable.h \ + times.h tiocl.h tipc.h toshiba.h ultrasound.h un.h utime.h \ + utsname.h video_decoder.h video_encoder.h videotext.h vt.h \ + wavefront.h wireless.h xattr.h x25.h zorro_ids.h + +unifdef-y += acct.h adb.h adfs_fs.h agpgart.h apm_bios.h atalk.h \ + atmarp.h atmdev.h atm.h atm_tcp.h audit.h auto_fs.h binfmts.h \ + capability.h capi.h cciss_ioctl.h cdrom.h cm4000_cs.h \ + cn_proc.h coda.h connector.h cramfs_fs.h cuda.h cyclades.h \ + dccp.h dirent.h divert.h elfcore.h errno.h errqueue.h \ + ethtool.h eventpoll.h ext2_fs.h ext3_fs.h fb.h fcntl.h \ + filter.h flat.h fs.h ftape.h gameport.h generic_serial.h \ + genhd.h hayesesp.h hdlcdrv.h hdlc.h hdreg.h hiddev.h hpet.h \ + i2c.h i2o-dev.h icmpv6.h if_bridge.h if_ec.h \ + if_eql.h if_ether.h if_frad.h if_ltalk.h if_pppox.h \ + if_shaper.h if_tr.h if_tun.h if_vlan.h if_wanpipe.h igmp.h \ + inet_diag.h in.h inotify.h input.h ipc.h ipmi.h ipv6.h \ + ipv6_route.h isdn.h isdnif.h isdn_ppp.h isicom.h jbd.h \ + joystick.h kdev_t.h kd.h kernelcapi.h kernel.h keyboard.h \ + llc.h loop.h lp.h mempolicy.h mii.h mman.h mroute.h msdos_fs.h \ + msg.h nbd.h ncp_fs.h ncp.h ncp_mount.h netdevice.h \ + netfilter_bridge.h netfilter_decnet.h netfilter.h \ + netfilter_ipv4.h netfilter_ipv6.h netfilter_logging.h net.h \ + netlink.h nfs3.h nfs4.h nfsacl.h nfs_fs.h nfs.h nfs_idmap.h \ + n_r3964.h nubus.h nvram.h parport.h patchkey.h pci.h pktcdvd.h \ + pmu.h poll.h ppp_defs.h ppp-comp.h ptrace.h qnx4_fs.h quota.h \ + random.h reboot.h reiserfs_fs.h reiserfs_xattr.h romfs_fs.h \ + route.h rtc.h rtnetlink.h scc.h sched.h sdla.h \ + selinux_netlink.h sem.h serial_core.h serial.h serio.h shm.h \ + signal.h smb_fs.h smb.h smb_mount.h socket.h sonet.h sonypi.h \ + soundcard.h stat.h sysctl.h tcp.h time.h timex.h tty.h types.h \ + udf_fs_i.h udp.h uinput.h uio.h unistd.h usb_ch9.h \ + usbdevice_fs.h user.h videodev2.h videodev.h wait.h \ + wanrouter.h watchdog.h xfrm.h zftape.h + +objhdr-y := version.h diff --git a/include/linux/ac97_codec.h b/include/linux/ac97_codec.h index c358338..2ed2fd8 100644 --- a/include/linux/ac97_codec.h +++ b/include/linux/ac97_codec.h @@ -259,7 +259,7 @@ struct ac97_codec { int type; u32 model; - int modem:1; + unsigned int modem:1; struct ac97_ops *codec_ops; diff --git a/include/linux/acct.h b/include/linux/acct.h index 9a66401..e86bae7 100644 --- a/include/linux/acct.h +++ b/include/linux/acct.h @@ -16,7 +16,6 @@ #ifndef _LINUX_ACCT_H #define _LINUX_ACCT_H #include -#include #include #include @@ -116,20 +115,23 @@ #endif #ifdef __KERNEL__ -#include #ifdef CONFIG_BSD_PROCESS_ACCT struct vfsmount; struct super_block; extern void acct_auto_close_mnt(struct vfsmount *m); extern void acct_auto_close(struct super_block *sb); -extern void acct_process(long exitcode); +extern void acct_init_pacct(struct pacct_struct *pacct); +extern void acct_collect(long exitcode, int group_dead); +extern void acct_process(void); extern void acct_update_integrals(struct task_struct *tsk); extern void acct_clear_integrals(struct task_struct *tsk); #else #define acct_auto_close_mnt(x) do { } while (0) #define acct_auto_close(x) do { } while (0) -#define acct_process(x) do { } while (0) +#define acct_init_pacct(x) do { } while (0) +#define acct_collect(x,y) do { } while (0) +#define acct_process() do { } while (0) #define acct_update_integrals(x) do { } while (0) #define acct_clear_integrals(task) do { } while (0) #endif @@ -165,6 +167,7 @@ #define AHZ (HZ) #endif /* __KERNEL */ #ifdef __KERNEL__ +#include /* * Yet another set of HZ to *HZ helper functions. * See for the original. diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d3bc25e..88b5dfd 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -25,7 +25,6 @@ #ifndef _LINUX_ACPI_H #define _LINUX_ACPI_H -#include #ifdef CONFIG_ACPI @@ -38,6 +37,7 @@ #include #include #include #include +#include #include @@ -408,10 +408,18 @@ void acpi_table_print_madt_entry (acpi_t void acpi_table_print_srat_entry (acpi_table_entry_header *srat); /* the following four functions are architecture-dependent */ +#ifdef CONFIG_HAVE_ARCH_PARSE_SRAT +#define NR_NODE_MEMBLKS MAX_NUMNODES +#define acpi_numa_slit_init(slit) do {} while (0) +#define acpi_numa_processor_affinity_init(pa) do {} while (0) +#define acpi_numa_memory_affinity_init(ma) do {} while (0) +#define acpi_numa_arch_fixup() do {} while (0) +#else void acpi_numa_slit_init (struct acpi_table_slit *slit); void acpi_numa_processor_affinity_init (struct acpi_table_processor_affinity *pa); void acpi_numa_memory_affinity_init (struct acpi_table_memory_affinity *ma); void acpi_numa_arch_fixup(void); +#endif #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ @@ -520,12 +528,18 @@ #endif #ifdef CONFIG_ACPI_NUMA int acpi_get_pxm(acpi_handle handle); +int acpi_get_node(acpi_handle *handle); #else static inline int acpi_get_pxm(acpi_handle handle) { return 0; } +static inline int acpi_get_node(acpi_handle *handle) +{ + return 0; +} #endif +extern int acpi_paddr_to_node(u64 start_addr, u64 size); extern int pnpacpi_disabled; diff --git a/include/linux/affs_hardblocks.h b/include/linux/affs_hardblocks.h index 3fb8699..f1b948c 100644 --- a/include/linux/affs_hardblocks.h +++ b/include/linux/affs_hardblocks.h @@ -1,45 +1,47 @@ #ifndef AFFS_HARDBLOCKS_H #define AFFS_HARDBLOCKS_H +#include + /* Just the needed definitions for the RDB of an Amiga HD. */ struct RigidDiskBlock { - u32 rdb_ID; + __u32 rdb_ID; __be32 rdb_SummedLongs; - s32 rdb_ChkSum; - u32 rdb_HostID; + __s32 rdb_ChkSum; + __u32 rdb_HostID; __be32 rdb_BlockBytes; - u32 rdb_Flags; - u32 rdb_BadBlockList; + __u32 rdb_Flags; + __u32 rdb_BadBlockList; __be32 rdb_PartitionList; - u32 rdb_FileSysHeaderList; - u32 rdb_DriveInit; - u32 rdb_Reserved1[6]; - u32 rdb_Cylinders; - u32 rdb_Sectors; - u32 rdb_Heads; - u32 rdb_Interleave; - u32 rdb_Park; - u32 rdb_Reserved2[3]; - u32 rdb_WritePreComp; - u32 rdb_ReducedWrite; - u32 rdb_StepRate; - u32 rdb_Reserved3[5]; - u32 rdb_RDBBlocksLo; - u32 rdb_RDBBlocksHi; - u32 rdb_LoCylinder; - u32 rdb_HiCylinder; - u32 rdb_CylBlocks; - u32 rdb_AutoParkSeconds; - u32 rdb_HighRDSKBlock; - u32 rdb_Reserved4; + __u32 rdb_FileSysHeaderList; + __u32 rdb_DriveInit; + __u32 rdb_Reserved1[6]; + __u32 rdb_Cylinders; + __u32 rdb_Sectors; + __u32 rdb_Heads; + __u32 rdb_Interleave; + __u32 rdb_Park; + __u32 rdb_Reserved2[3]; + __u32 rdb_WritePreComp; + __u32 rdb_ReducedWrite; + __u32 rdb_StepRate; + __u32 rdb_Reserved3[5]; + __u32 rdb_RDBBlocksLo; + __u32 rdb_RDBBlocksHi; + __u32 rdb_LoCylinder; + __u32 rdb_HiCylinder; + __u32 rdb_CylBlocks; + __u32 rdb_AutoParkSeconds; + __u32 rdb_HighRDSKBlock; + __u32 rdb_Reserved4; char rdb_DiskVendor[8]; char rdb_DiskProduct[16]; char rdb_DiskRevision[4]; char rdb_ControllerVendor[8]; char rdb_ControllerProduct[16]; char rdb_ControllerRevision[4]; - u32 rdb_Reserved5[10]; + __u32 rdb_Reserved5[10]; }; #define IDNAME_RIGIDDISK 0x5244534B /* "RDSK" */ @@ -47,16 +49,16 @@ #define IDNAME_RIGIDDISK 0x5244534B /* " struct PartitionBlock { __be32 pb_ID; __be32 pb_SummedLongs; - s32 pb_ChkSum; - u32 pb_HostID; + __s32 pb_ChkSum; + __u32 pb_HostID; __be32 pb_Next; - u32 pb_Flags; - u32 pb_Reserved1[2]; - u32 pb_DevFlags; - u8 pb_DriveName[32]; - u32 pb_Reserved2[15]; + __u32 pb_Flags; + __u32 pb_Reserved1[2]; + __u32 pb_DevFlags; + __u8 pb_DriveName[32]; + __u32 pb_Reserved2[15]; __be32 pb_Environment[17]; - u32 pb_EReserved[15]; + __u32 pb_EReserved[15]; }; #define IDNAME_PARTITION 0x50415254 /* "PART" */ diff --git a/include/linux/agpgart.h b/include/linux/agpgart.h index 6d59c8e..bfb8ec7 100644 --- a/include/linux/agpgart.h +++ b/include/linux/agpgart.h @@ -27,8 +27,6 @@ #ifndef _AGP_H #define _AGP_H 1 -#include - #define AGPIOC_BASE 'A' #define AGPIOC_INFO _IOR (AGPIOC_BASE, 0, struct agp_info*) #define AGPIOC_ACQUIRE _IO (AGPIOC_BASE, 1) @@ -112,6 +110,7 @@ typedef struct _agp_unbind { #else /* __KERNEL__ */ #include +#include #define AGPGART_MINOR 175 diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h index 9cf64b1..29c0448 100644 --- a/include/linux/amba/clcd.h +++ b/include/linux/amba/clcd.h @@ -9,7 +9,6 @@ * License. See the file COPYING in the main directory of this archive * for more details. */ -#include #include /* diff --git a/include/linux/ata.h b/include/linux/ata.h index 312a2c0..3671af8 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -97,6 +97,9 @@ enum { ATA_DRQ = (1 << 3), /* data request i/o */ ATA_ERR = (1 << 0), /* have an error */ ATA_SRST = (1 << 2), /* software reset */ + ATA_ICRC = (1 << 7), /* interface CRC error */ + ATA_UNC = (1 << 6), /* uncorrectable media error */ + ATA_IDNF = (1 << 4), /* ID not found */ ATA_ABORTED = (1 << 2), /* command aborted */ /* ATA command block registers */ @@ -130,6 +133,8 @@ enum { ATA_CMD_WRITE = 0xCA, ATA_CMD_WRITE_EXT = 0x35, ATA_CMD_WRITE_FUA_EXT = 0x3D, + ATA_CMD_FPDMA_READ = 0x60, + ATA_CMD_FPDMA_WRITE = 0x61, ATA_CMD_PIO_READ = 0x20, ATA_CMD_PIO_READ_EXT = 0x24, ATA_CMD_PIO_WRITE = 0x30, @@ -148,6 +153,10 @@ enum { ATA_CMD_INIT_DEV_PARAMS = 0x91, ATA_CMD_READ_NATIVE_MAX = 0xF8, ATA_CMD_READ_NATIVE_MAX_EXT = 0x27, + ATA_CMD_READ_LOG_EXT = 0x2f, + + /* READ_LOG_EXT pages */ + ATA_LOG_SATA_NCQ = 0x10, /* SETFEATURES stuff */ SETFEATURES_XFER = 0x03, @@ -172,6 +181,9 @@ enum { XFER_PIO_0 = 0x08, XFER_PIO_SLOW = 0x00, + SETFEATURES_WC_ON = 0x02, /* Enable write cache */ + SETFEATURES_WC_OFF = 0x82, /* Disable write cache */ + /* ATAPI stuff */ ATAPI_PKT_DMA = (1 << 0), ATAPI_DMADIR = (1 << 2), /* ATAPI data dir: @@ -192,6 +204,16 @@ enum { SCR_ACTIVE = 3, SCR_NOTIFICATION = 4, + /* SError bits */ + SERR_DATA_RECOVERED = (1 << 0), /* recovered data error */ + SERR_COMM_RECOVERED = (1 << 1), /* recovered comm failure */ + SERR_DATA = (1 << 8), /* unrecovered data error */ + SERR_PERSISTENT = (1 << 9), /* persistent data/comm error */ + SERR_PROTOCOL = (1 << 10), /* protocol violation */ + SERR_INTERNAL = (1 << 11), /* host internal error */ + SERR_PHYRDY_CHG = (1 << 16), /* PHY RDY changed */ + SERR_DEV_XCHG = (1 << 26), /* device exchanged */ + /* struct ata_taskfile flags */ ATA_TFLAG_LBA48 = (1 << 0), /* enable 48-bit LBA and "HOB" */ ATA_TFLAG_ISADDR = (1 << 1), /* enable r/w to nsect/lba regs */ @@ -199,6 +221,7 @@ enum { ATA_TFLAG_WRITE = (1 << 3), /* data dir: host->dev==1 (write) */ ATA_TFLAG_LBA = (1 << 4), /* enable LBA */ ATA_TFLAG_FUA = (1 << 5), /* enable FUA */ + ATA_TFLAG_POLLING = (1 << 6), /* set nIEN to 1 and use polling */ }; enum ata_tf_protocols { @@ -207,6 +230,7 @@ enum ata_tf_protocols { ATA_PROT_NODATA, /* no data */ ATA_PROT_PIO, /* PIO single sector */ ATA_PROT_DMA, /* DMA */ + ATA_PROT_NCQ, /* NCQ */ ATA_PROT_ATAPI, /* packet command, PIO data xfer*/ ATA_PROT_ATAPI_NODATA, /* packet command, no data */ ATA_PROT_ATAPI_DMA, /* packet command with special DMA sauce */ @@ -262,6 +286,8 @@ #define ata_id_has_wcache(id) ((id)[82] #define ata_id_has_pm(id) ((id)[82] & (1 << 3)) #define ata_id_has_lba(id) ((id)[49] & (1 << 9)) #define ata_id_has_dma(id) ((id)[49] & (1 << 8)) +#define ata_id_has_ncq(id) ((id)[76] & (1 << 8)) +#define ata_id_queue_depth(id) (((id)[75] & 0x1f) + 1) #define ata_id_removeable(id) ((id)[0] & (1 << 7)) #define ata_id_has_dword_io(id) ((id)[50] & (1 << 0)) #define ata_id_u32(id,n) \ @@ -272,6 +298,8 @@ #define ata_id_u64(id,n) \ ((u64) (id)[(n) + 1] << 16) | \ ((u64) (id)[(n) + 0]) ) +#define ata_id_cdb_intr(id) (((id)[0] & 0x60) == 0x20) + static inline unsigned int ata_id_major_version(const u16 *id) { unsigned int mver; @@ -311,6 +339,15 @@ static inline int is_atapi_taskfile(cons (tf->protocol == ATA_PROT_ATAPI_DMA); } +static inline int is_multi_taskfile(struct ata_taskfile *tf) +{ + return (tf->command == ATA_CMD_READ_MULTI) || + (tf->command == ATA_CMD_WRITE_MULTI) || + (tf->command == ATA_CMD_READ_MULTI_EXT) || + (tf->command == ATA_CMD_WRITE_MULTI_EXT) || + (tf->command == ATA_CMD_WRITE_MULTI_FUA_EXT); +} + static inline int ata_ok(u8 status) { return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR)) diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h index b203ea8..41788a3 100644 --- a/include/linux/atmdev.h +++ b/include/linux/atmdev.h @@ -7,6 +7,7 @@ #ifndef LINUX_ATMDEV_H #define LINUX_ATMDEV_H +#include #include #include #include @@ -209,7 +210,6 @@ #define ATM_VF2TXT_MAP \ #ifdef __KERNEL__ -#include #include /* wait_queue_head_t */ #include /* struct timeval */ #include @@ -359,6 +359,7 @@ #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_entry; /* proc entry */ char *proc_name; /* proc entry name */ #endif + struct class_device class_dev; /* sysfs class device */ struct list_head dev_list; /* linkage */ }; @@ -460,7 +461,7 @@ static inline void atm_dev_put(struct at BUG_ON(!test_bit(ATM_DF_REMOVED, &dev->flags)); if (dev->ops->dev_close) dev->ops->dev_close(dev); - kfree(dev); + class_device_put(&dev->class_dev); } } diff --git a/include/linux/audit.h b/include/linux/audit.h index b74c148..b27d7de 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -24,8 +24,7 @@ #ifndef _LINUX_AUDIT_H_ #define _LINUX_AUDIT_H_ -#include -#include +#include /* The netlink messages for the audit system is divided into blocks: * 1000 - 1099 are for commanding the audit system @@ -83,7 +82,12 @@ #define AUDIT_SOCKETCALL 1304 /* sys_soc #define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */ #define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */ #define AUDIT_CWD 1307 /* Current working directory */ +#define AUDIT_EXECVE 1309 /* execve arguments */ #define AUDIT_IPC_SET_PERM 1311 /* IPC new permissions record type */ +#define AUDIT_MQ_OPEN 1312 /* POSIX MQ open record type */ +#define AUDIT_MQ_SENDRECV 1313 /* POSIX MQ send/receive record type */ +#define AUDIT_MQ_NOTIFY 1314 /* POSIX MQ notify record type */ +#define AUDIT_MQ_GETSETATTR 1315 /* POSIX MQ get/set attribute record type */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ @@ -118,10 +122,17 @@ #define AUDIT_ALWAYS 2 /* Generate aud /* Rule structure sizes -- if these change, different AUDIT_ADD and * AUDIT_LIST commands must be implemented. */ #define AUDIT_MAX_FIELDS 64 +#define AUDIT_MAX_KEY_LEN 32 #define AUDIT_BITMASK_SIZE 64 #define AUDIT_WORD(nr) ((__u32)((nr)/32)) #define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32)) +#define AUDIT_SYSCALL_CLASSES 16 +#define AUDIT_CLASS_DIR_WRITE 0 +#define AUDIT_CLASS_DIR_WRITE_32 1 +#define AUDIT_CLASS_CHATTR 2 +#define AUDIT_CLASS_CHATTR_32 3 + /* This bitmask is used to validate user input. It represents all bits that * are currently used in an audit field constant understood by the kernel. * If you are adding a new #define AUDIT_, please ensure that @@ -146,11 +157,17 @@ #define AUDIT_LOGINUID 9 #define AUDIT_PERS 10 #define AUDIT_ARCH 11 #define AUDIT_MSGTYPE 12 -#define AUDIT_SE_USER 13 /* security label user */ -#define AUDIT_SE_ROLE 14 /* security label role */ -#define AUDIT_SE_TYPE 15 /* security label type */ -#define AUDIT_SE_SEN 16 /* security label sensitivity label */ -#define AUDIT_SE_CLR 17 /* security label clearance label */ +#define AUDIT_SUBJ_USER 13 /* security label user */ +#define AUDIT_SUBJ_ROLE 14 /* security label role */ +#define AUDIT_SUBJ_TYPE 15 /* security label type */ +#define AUDIT_SUBJ_SEN 16 /* security label sensitivity label */ +#define AUDIT_SUBJ_CLR 17 /* security label clearance label */ +#define AUDIT_PPID 18 +#define AUDIT_OBJ_USER 19 +#define AUDIT_OBJ_ROLE 20 +#define AUDIT_OBJ_TYPE 21 +#define AUDIT_OBJ_LEV_LOW 22 +#define AUDIT_OBJ_LEV_HIGH 23 /* These are ONLY useful when checking * at syscall exit time (AUDIT_AT_EXIT). */ @@ -159,12 +176,15 @@ #define AUDIT_DEVMINOR 101 #define AUDIT_INODE 102 #define AUDIT_EXIT 103 #define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */ +#define AUDIT_WATCH 105 #define AUDIT_ARG0 200 #define AUDIT_ARG1 (AUDIT_ARG0+1) #define AUDIT_ARG2 (AUDIT_ARG0+2) #define AUDIT_ARG3 (AUDIT_ARG0+3) +#define AUDIT_FILTERKEY 210 + #define AUDIT_NEGATE 0x80000000 /* These are the supported operators. @@ -273,21 +293,27 @@ struct audit_rule { /* for AUDIT_LIST, }; #ifdef __KERNEL__ +#include struct audit_sig_info { uid_t uid; pid_t pid; + char ctx[0]; }; struct audit_buffer; struct audit_context; struct inode; struct netlink_skb_parms; +struct linux_binprm; +struct mq_attr; +struct mqstat; #define AUDITSC_INVALID 0 #define AUDITSC_SUCCESS 1 #define AUDITSC_FAILURE 2 #define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS ) +extern int __init audit_register_class(int class, unsigned *list); #ifdef CONFIG_AUDITSYSCALL /* These are defined in auditsc.c */ /* Public API */ @@ -297,15 +323,19 @@ extern void audit_syscall_entry(int arch int major, unsigned long a0, unsigned long a1, unsigned long a2, unsigned long a3); extern void audit_syscall_exit(int failed, long return_code); -extern void audit_getname(const char *name); +extern void __audit_getname(const char *name); extern void audit_putname(const char *name); -extern void __audit_inode(const char *name, const struct inode *inode, unsigned flags); +extern void __audit_inode(const char *name, const struct inode *inode); extern void __audit_inode_child(const char *dname, const struct inode *inode, unsigned long pino); -static inline void audit_inode(const char *name, const struct inode *inode, - unsigned flags) { +static inline void audit_getname(const char *name) +{ + if (unlikely(current->audit_context)) + __audit_getname(name); +} +static inline void audit_inode(const char *name, const struct inode *inode) { if (unlikely(current->audit_context)) - __audit_inode(name, inode, flags); + __audit_inode(name, inode); } static inline void audit_inode_child(const char *dname, const struct inode *inode, @@ -320,13 +350,61 @@ extern void auditsc_get_stamp(struct aud struct timespec *t, unsigned int *serial); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern uid_t audit_get_loginuid(struct audit_context *ctx); -extern int audit_ipc_obj(struct kern_ipc_perm *ipcp); -extern int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp); +extern int __audit_ipc_obj(struct kern_ipc_perm *ipcp); +extern int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); +extern int audit_bprm(struct linux_binprm *bprm); extern int audit_socketcall(int nargs, unsigned long *args); extern int audit_sockaddr(int len, void *addr); extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); -extern void audit_signal_info(int sig, struct task_struct *t); extern int audit_set_macxattr(const char *name); +extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr); +extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout); +extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout); +extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification); +extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); + +static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp) +{ + if (unlikely(current->audit_context)) + return __audit_ipc_obj(ipcp); + return 0; +} +static inline int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) +{ + if (unlikely(current->audit_context)) + return __audit_ipc_set_perm(qbytes, uid, gid, mode); + return 0; +} +static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr) +{ + if (unlikely(current->audit_context)) + return __audit_mq_open(oflag, mode, u_attr); + return 0; +} +static inline int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout) +{ + if (unlikely(current->audit_context)) + return __audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout); + return 0; +} +static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout) +{ + if (unlikely(current->audit_context)) + return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout); + return 0; +} +static inline int audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification) +{ + if (unlikely(current->audit_context)) + return __audit_mq_notify(mqdes, u_notification); + return 0; +} +static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) +{ + if (unlikely(current->audit_context)) + return __audit_mq_getsetattr(mqdes, mqstat); + return 0; +} #else #define audit_alloc(t) ({ 0; }) #define audit_free(t) do { ; } while (0) @@ -334,19 +412,24 @@ #define audit_syscall_entry(ta,a,b,c,d,e #define audit_syscall_exit(f,r) do { ; } while (0) #define audit_getname(n) do { ; } while (0) #define audit_putname(n) do { ; } while (0) -#define __audit_inode(n,i,f) do { ; } while (0) +#define __audit_inode(n,i) do { ; } while (0) #define __audit_inode_child(d,i,p) do { ; } while (0) -#define audit_inode(n,i,f) do { ; } while (0) +#define audit_inode(n,i) do { ; } while (0) #define audit_inode_child(d,i,p) do { ; } while (0) #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) #define audit_get_loginuid(c) ({ -1; }) #define audit_ipc_obj(i) ({ 0; }) -#define audit_ipc_set_perm(q,u,g,m,i) ({ 0; }) +#define audit_ipc_set_perm(q,u,g,m) ({ 0; }) +#define audit_bprm(p) ({ 0; }) #define audit_socketcall(n,a) ({ 0; }) #define audit_sockaddr(len, addr) ({ 0; }) #define audit_avc_path(dentry, mnt) ({ 0; }) -#define audit_signal_info(s,t) do { ; } while (0) #define audit_set_macxattr(n) do { ; } while (0) +#define audit_mq_open(o,m,a) ({ 0; }) +#define audit_mq_timedsend(d,l,p,t) ({ 0; }) +#define audit_mq_timedreceive(d,l,p,t) ({ 0; }) +#define audit_mq_notify(d,n) ({ 0; }) +#define audit_mq_getsetattr(d,s) ({ 0; }) #endif #ifdef CONFIG_AUDIT @@ -364,8 +447,11 @@ extern void audit_log_end(struct au extern void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len); -extern void audit_log_untrustedstring(struct audit_buffer *ab, +extern const char * audit_log_untrustedstring(struct audit_buffer *ab, const char *string); +extern const char * audit_log_n_untrustedstring(struct audit_buffer *ab, + size_t n, + const char *string); extern void audit_log_d_path(struct audit_buffer *ab, const char *prefix, struct dentry *dentry, @@ -383,8 +469,8 @@ #define audit_log_format(b,f,...) do { ; #define audit_log_end(b) do { ; } while (0) #define audit_log_hex(a,b,l) do { ; } while (0) #define audit_log_untrustedstring(a,s) do { ; } while (0) +#define audit_log_n_untrustedstring(a,n,s) do { ; } while (0) #define audit_log_d_path(b,p,d,v) do { ; } while (0) -#define audit_panic(m) do { ; } while (0) #endif #endif #endif diff --git a/include/linux/bio.h b/include/linux/bio.h index b60ffe3..76bdaea 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -47,7 +47,7 @@ #else #define BIO_BUG_ON #endif -#define BIO_MAX_PAGES (256) +#define BIO_MAX_PAGES 256 #define BIO_MAX_SIZE (BIO_MAX_PAGES << PAGE_CACHE_SHIFT) #define BIO_MAX_SECTORS (BIO_MAX_SIZE >> 9) diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index d9ed279..dcc5de7 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -24,6 +24,9 @@ #include * The available bitmap operations and their rough meaning in the * case that the bitmap is a single unsigned long are thus: * + * Note that nbits should be always a compile time evaluable constant. + * Otherwise many inlines will generate horrible code. + * * bitmap_zero(dst, nbits) *dst = 0UL * bitmap_fill(dst, nbits) *dst = ~0UL * bitmap_copy(dst, src, nbits) *dst = *src @@ -244,6 +247,8 @@ static inline int bitmap_full(const unsi static inline int bitmap_weight(const unsigned long *src, int nbits) { + if (nbits <= BITS_PER_LONG) + return hweight_long(*src & BITMAP_LAST_WORD_MASK(nbits)); return __bitmap_weight(src, nbits); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 59e1259..aafe827 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1,7 +1,6 @@ #ifndef _LINUX_BLKDEV_H #define _LINUX_BLKDEV_H -#include #include #include #include @@ -152,11 +151,9 @@ struct request { void *elevator_private; void *completion_data; - unsigned short ioprio; - int rq_status; /* should split this into a few status bits */ - struct gendisk *rq_disk; int errors; + struct gendisk *rq_disk; unsigned long start_time; /* Number of scatter-gather DMA addr+len pairs after @@ -171,8 +168,9 @@ struct request { */ unsigned short nr_hw_segments; + unsigned short ioprio; + int tag; - char *buffer; int ref_count; request_queue_t *q; @@ -180,6 +178,7 @@ struct request { struct completion *waiting; void *special; + char *buffer; /* * when request is used as a packet command carrier @@ -188,20 +187,14 @@ struct request { unsigned char cmd[BLK_MAX_CDB]; unsigned int data_len; - void *data; - unsigned int sense_len; + void *data; void *sense; unsigned int timeout; int retries; /* - * For Power Management requests - */ - struct request_pm_state *pm; - - /* * completion callback. end_io_data should be folded in with waiting */ rq_end_io_fn *end_io; @@ -242,6 +235,7 @@ enum rq_flag_bits { __REQ_PM_RESUME, /* resume request */ __REQ_PM_SHUTDOWN, /* shutdown request */ __REQ_ORDERED_COLOR, /* is before or after barrier */ + __REQ_RW_SYNC, /* request is sync (O_DIRECT) */ __REQ_NR_BITS, /* stops here */ }; @@ -271,6 +265,7 @@ #define REQ_PM_SUSPEND (1 << __REQ_PM_SU #define REQ_PM_RESUME (1 << __REQ_PM_RESUME) #define REQ_PM_SHUTDOWN (1 << __REQ_PM_SHUTDOWN) #define REQ_ORDERED_COLOR (1 << __REQ_ORDERED_COLOR) +#define REQ_RW_SYNC (1 << __REQ_RW_SYNC) /* * State information carried for REQ_PM_SUSPEND and REQ_PM_RESUME @@ -439,9 +434,6 @@ struct request_queue #define RQ_INACTIVE (-1) #define RQ_ACTIVE 1 -#define RQ_SCSI_BUSY 0xffff -#define RQ_SCSI_DONE 0xfffe -#define RQ_SCSI_DISCONNECTING 0xffe0 #define QUEUE_FLAG_CLUSTER 0 /* cluster several segments into 1 */ #define QUEUE_FLAG_QUEUED 1 /* uses generic tag queueing */ diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h index be5d0f4..faf8a45 100644 --- a/include/linux/blkpg.h +++ b/include/linux/blkpg.h @@ -24,6 +24,7 @@ #define _LINUX_BLKPG_H * * For today, only the partition stuff - aeb, 990515 */ +#include #include #define BLKPG _IO(0x12,105) diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index b34d3e7..a7e8cef 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -1,7 +1,6 @@ #ifndef BLKTRACE_H #define BLKTRACE_H -#include #include #include @@ -91,9 +90,9 @@ struct blk_io_trace { * The remap event */ struct blk_io_trace_remap { - u32 device; + __be32 device; u32 __pad; - u64 sector; + __be64 sector; }; enum { @@ -225,7 +224,7 @@ static inline void blk_add_trace_pdu_int struct bio *bio, unsigned int pdu) { struct blk_trace *bt = q->blk_trace; - u64 rpdu = cpu_to_be64(pdu); + __be64 rpdu = cpu_to_be64(pdu); if (likely(!bt)) return; diff --git a/include/linux/blockgroup_lock.h b/include/linux/blockgroup_lock.h index 0137ee5..8607312 100644 --- a/include/linux/blockgroup_lock.h +++ b/include/linux/blockgroup_lock.h @@ -6,7 +6,6 @@ #define _LINUX_BLOCKGROUP_LOCK_H * Simple hashed spinlocking. */ -#include #include #include diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index da2d107..22866fa 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -91,8 +91,8 @@ static inline void *alloc_remap(int nid, } #endif -extern unsigned long __initdata nr_kernel_pages; -extern unsigned long __initdata nr_all_pages; +extern unsigned long nr_kernel_pages; +extern unsigned long nr_all_pages; extern void *__init alloc_large_system_hash(const char *tablename, unsigned long bucketsize, diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index fb7e9b7..737e407 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -149,7 +149,6 @@ void create_empty_buffers(struct page *, unsigned long b_state); void end_buffer_read_sync(struct buffer_head *bh, int uptodate); void end_buffer_write_sync(struct buffer_head *bh, int uptodate); -void end_buffer_async_write(struct buffer_head *bh, int uptodate); /* Things to do with buffers at mapping->private_list */ void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode); @@ -214,6 +213,7 @@ int nobh_truncate_page(struct address_sp int nobh_writepage(struct page *page, get_block_t *get_block, struct writeback_control *wbc); +void buffer_init(void); /* * inline definitions diff --git a/include/linux/byteorder/Kbuild b/include/linux/byteorder/Kbuild new file mode 100644 index 0000000..84a57d4 --- /dev/null +++ b/include/linux/byteorder/Kbuild @@ -0,0 +1,2 @@ +unifdef-y += generic.h swabb.h swab.h +header-y += big_endian.h little_endian.h pdp_endian.h diff --git a/include/linux/cache.h b/include/linux/cache.h index cc4b3aa..4552504 100644 --- a/include/linux/cache.h +++ b/include/linux/cache.h @@ -2,7 +2,6 @@ #ifndef __LINUX_CACHE_H #define __LINUX_CACHE_H #include -#include #include #ifndef L1_CACHE_ALIGN diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h new file mode 100644 index 0000000..d852024 --- /dev/null +++ b/include/linux/clocksource.h @@ -0,0 +1,185 @@ +/* linux/include/linux/clocksource.h + * + * This file contains the structure definitions for clocksources. + * + * If you are not a clocksource, or timekeeping code, you should + * not be including this file! + */ +#ifndef _LINUX_CLOCKSOURCE_H +#define _LINUX_CLOCKSOURCE_H + +#include +#include +#include +#include +#include +#include + +/* clocksource cycle base type */ +typedef u64 cycle_t; + +/** + * struct clocksource - hardware abstraction for a free running counter + * Provides mostly state-free accessors to the underlying hardware. + * + * @name: ptr to clocksource name + * @list: list head for registration + * @rating: rating value for selection (higher is better) + * To avoid rating inflation the following + * list should give you a guide as to how + * to assign your clocksource a rating + * 1-99: Unfit for real use + * Only available for bootup and testing purposes. + * 100-199: Base level usability. + * Functional for real use, but not desired. + * 200-299: Good. + * A correct and usable clocksource. + * 300-399: Desired. + * A reasonably fast and accurate clocksource. + * 400-499: Perfect + * The ideal clocksource. A must-use where + * available. + * @read: returns a cycle value + * @mask: bitmask for two's complement + * subtraction of non 64 bit counters + * @mult: cycle to nanosecond multiplier + * @shift: cycle to nanosecond divisor (power of two) + * @update_callback: called when safe to alter clocksource values + * @is_continuous: defines if clocksource is free-running. + * @cycle_interval: Used internally by timekeeping core, please ignore. + * @xtime_interval: Used internally by timekeeping core, please ignore. + */ +struct clocksource { + char *name; + struct list_head list; + int rating; + cycle_t (*read)(void); + cycle_t mask; + u32 mult; + u32 shift; + int (*update_callback)(void); + int is_continuous; + + /* timekeeping specific data, ignore */ + cycle_t cycle_last, cycle_interval; + u64 xtime_nsec, xtime_interval; + s64 error; +}; + +/* simplify initialization of mask field */ +#define CLOCKSOURCE_MASK(bits) (cycle_t)(bits<64 ? ((1ULL<read(); +} + +/** + * cyc2ns - converts clocksource cycles to nanoseconds + * @cs: Pointer to clocksource + * @cycles: Cycles + * + * Uses the clocksource and ntp ajdustment to convert cycle_ts to nanoseconds. + * + * XXX - This could use some mult_lxl_ll() asm optimization + */ +static inline s64 cyc2ns(struct clocksource *cs, cycle_t cycles) +{ + u64 ret = (u64)cycles; + ret = (ret * cs->mult) >> cs->shift; + return ret; +} + +/** + * clocksource_calculate_interval - Calculates a clocksource interval struct + * + * @c: Pointer to clocksource. + * @length_nsec: Desired interval length in nanoseconds. + * + * Calculates a fixed cycle/nsec interval for a given clocksource/adjustment + * pair and interval request. + * + * Unless you're the timekeeping code, you should not be using this! + */ +static inline void clocksource_calculate_interval(struct clocksource *c, + unsigned long length_nsec) +{ + u64 tmp; + + /* XXX - All of this could use a whole lot of optimization */ + tmp = length_nsec; + tmp <<= c->shift; + tmp += c->mult/2; + do_div(tmp, c->mult); + + c->cycle_interval = (cycle_t)tmp; + if (c->cycle_interval == 0) + c->cycle_interval = 1; + + c->xtime_interval = (u64)c->cycle_interval * c->mult; +} + + +/* used to install a new clocksource */ +int clocksource_register(struct clocksource*); +void clocksource_reselect(void); +struct clocksource* clocksource_get_next(void); + +#endif /* _LINUX_CLOCKSOURCE_H */ diff --git a/include/linux/cn_proc.h b/include/linux/cn_proc.h index 1417de9..dbb7769 100644 --- a/include/linux/cn_proc.h +++ b/include/linux/cn_proc.h @@ -3,31 +3,22 @@ * * Copyright (C) Matt Helsley, IBM Corp. 2005 * Based on cn_fork.h by Nguyen Anh Quynh and Guillaume Thouvenin - * Original copyright notice follows: * Copyright (C) 2005 Nguyen Anh Quynh * Copyright (C) 2005 Guillaume Thouvenin * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef CN_PROC_H #define CN_PROC_H #include -#include -#include /* * Userspace sends this enum to register with the kernel that it is listening diff --git a/include/linux/coda.h b/include/linux/coda.h index bbc5afc..b5cf078 100644 --- a/include/linux/coda.h +++ b/include/linux/coda.h @@ -59,7 +59,6 @@ Mellon the rights to redistribute these #ifndef _CODA_HEADER_ #define _CODA_HEADER_ -#include /* Catch new _KERNEL defn for NetBSD and DJGPP/__CYGWIN32__ */ #if defined(__NetBSD__) || \ diff --git a/include/linux/coda_linux.h b/include/linux/coda_linux.h index b3ecf8f..be512cc 100644 --- a/include/linux/coda_linux.h +++ b/include/linux/coda_linux.h @@ -27,8 +27,8 @@ extern struct inode_operations coda_dir_ extern struct inode_operations coda_file_inode_operations; extern struct inode_operations coda_ioctl_inode_operations; -extern struct address_space_operations coda_file_aops; -extern struct address_space_operations coda_symlink_aops; +extern const struct address_space_operations coda_file_aops; +extern const struct address_space_operations coda_symlink_aops; extern const struct file_operations coda_dir_operations; extern const struct file_operations coda_file_operations; @@ -36,7 +36,7 @@ extern const struct file_operations coda /* operations shared over more than one file */ int coda_open(struct inode *i, struct file *f); -int coda_flush(struct file *f); +int coda_flush(struct file *f, fl_owner_t id); int coda_release(struct inode *i, struct file *f); int coda_permission(struct inode *inode, int mask, struct nameidata *nd); int coda_revalidate_inode(struct dentry *); diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index d539262..98f6c52 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -70,7 +70,7 @@ int venus_pioctl(struct super_block *sb, unsigned int cmd, struct PioctlData *data); int coda_downcall(int opcode, union outputArgs *out, struct super_block *sb); int venus_fsync(struct super_block *sb, struct CodaFid *fid); -int venus_statfs(struct super_block *sb, struct kstatfs *sfs); +int venus_statfs(struct dentry *dentry, struct kstatfs *sfs); /* messages between coda filesystem in kernel and Venus */ diff --git a/include/linux/compat.h b/include/linux/compat.h index 6d3a654..9760753 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -4,7 +4,6 @@ #define _LINUX_COMPAT_H * These are the type definitions for the architecture specific * syscall compatibility layer. */ -#include #ifdef CONFIG_COMPAT @@ -227,5 +226,7 @@ static inline int compat_timespec_compar asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp); +extern int compat_printk(const char *fmt, ...); + #endif /* CONFIG_COMPAT */ #endif /* _LINUX_COMPAT_H */ diff --git a/include/linux/compat_ioctl.h b/include/linux/compat_ioctl.h index 89ab677..269d000 100644 --- a/include/linux/compat_ioctl.h +++ b/include/linux/compat_ioctl.h @@ -567,11 +567,6 @@ COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) COMPATIBLE_IOCTL(AUTOFS_IOC_ASKREGHOST) COMPATIBLE_IOCTL(AUTOFS_IOC_TOGGLEREGHOST) COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) -/* DEVFS */ -COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV) -COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK) -COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE) -COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK) /* Raw devices */ COMPATIBLE_IOCTL(RAW_SETBIND) COMPATIBLE_IOCTL(RAW_GETBIND) @@ -673,6 +668,11 @@ COMPATIBLE_IOCTL(CAPI_SET_FLAGS) COMPATIBLE_IOCTL(CAPI_CLR_FLAGS) COMPATIBLE_IOCTL(CAPI_NCCI_OPENCOUNT) COMPATIBLE_IOCTL(CAPI_NCCI_GETUNIT) +/* Siemens Gigaset */ +COMPATIBLE_IOCTL(GIGASET_REDIR) +COMPATIBLE_IOCTL(GIGASET_CONFIG) +COMPATIBLE_IOCTL(GIGASET_BRKCHARS) +COMPATIBLE_IOCTL(GIGASET_VERSION) /* Misc. */ COMPATIBLE_IOCTL(0x41545900) /* ATYIO_CLKR */ COMPATIBLE_IOCTL(0x41545901) /* ATYIO_CLKW */ diff --git a/include/linux/compiler.h b/include/linux/compiler.h index f23d3c6..9b4f110 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -78,6 +78,7 @@ #endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */ +#ifdef __KERNEL__ /* * Allow us to mark functions as 'deprecated' and have gcc emit a nice * warning for each use, in hopes of speeding the functions removal. @@ -127,6 +128,16 @@ #ifndef __attribute_pure__ # define __attribute_pure__ /* unimplemented */ #endif +#ifndef noinline +#define noinline +#endif + +#ifndef __always_inline +#define __always_inline inline +#endif + +#endif /* __KERNEL__ */ + /* * From the GCC manual: * @@ -145,12 +156,4 @@ #ifndef __attribute_const__ # define __attribute_const__ /* unimplemented */ #endif -#ifndef noinline -#define noinline -#endif - -#ifndef __always_inline -#define __always_inline inline -#endif - #endif /* __LINUX_COMPILER_H */ diff --git a/include/linux/completion.h b/include/linux/completion.h index 90663ad..251c41e 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -21,6 +21,18 @@ #define COMPLETION_INITIALIZER(work) \ #define DECLARE_COMPLETION(work) \ struct completion work = COMPLETION_INITIALIZER(work) +/* + * Lockdep needs to run a non-constant initializer for on-stack + * completions - so we use the _ONSTACK() variant for those that + * are on the kernel stack: + */ +#ifdef CONFIG_LOCKDEP +# define DECLARE_COMPLETION_ONSTACK(work) \ + struct completion work = ({ init_completion(&work); work; }) +#else +# define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work) +#endif + static inline void init_completion(struct completion *x) { x->done = 0; diff --git a/include/linux/connector.h b/include/linux/connector.h index ad1a22c..4c02119 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -34,8 +34,11 @@ #define CN_IDX_PROC 0x1 #define CN_VAL_PROC 0x1 #define CN_IDX_CIFS 0x2 #define CN_VAL_CIFS 0x1 +#define CN_W1_IDX 0x3 /* w1 communication */ +#define CN_W1_VAL 0x1 -#define CN_NETLINK_USERS 1 + +#define CN_NETLINK_USERS 4 /* * Maximum connector's message size. diff --git a/include/linux/console.h b/include/linux/console.h index 7213713..3bdf215 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -63,9 +63,11 @@ extern const struct consw vga_con; /* VG extern const struct consw newport_con; /* SGI Newport console */ extern const struct consw prom_con; /* SPARC PROM console */ +int con_is_bound(const struct consw *csw); +int register_con_driver(const struct consw *csw, int first, int last); +int unregister_con_driver(const struct consw *csw); int take_over_console(const struct consw *sw, int first, int last, int deflt); void give_up_console(const struct consw *sw); - /* scroll */ #define SM_UP (1) #define SM_DOWN (2) @@ -87,6 +89,7 @@ #define CON_PRINTBUFFER (1) #define CON_CONSDEV (2) /* Last on the command line */ #define CON_ENABLED (4) #define CON_BOOT (8) +#define CON_ANYTIME (16) /* Safe to call when cpu is offline */ struct console { @@ -117,6 +120,10 @@ extern void console_stop(struct console extern void console_start(struct console *); extern int is_console_locked(void); +/* Suspend and resume console messages over PM events */ +extern void suspend_console(void); +extern void resume_console(void); + /* Some debug stub to catch some of the obvious races in the VT code */ #if 1 #define WARN_CONSOLE_UNLOCKED() WARN_ON(!is_console_locked() && !oops_in_progress) diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 08d50c5..44a11f1 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -31,17 +31,23 @@ struct cpu { struct sys_device sysdev; }; -extern int register_cpu(struct cpu *, int, struct node *); +extern int register_cpu(struct cpu *cpu, int num); extern struct sys_device *get_cpu_sysdev(unsigned cpu); #ifdef CONFIG_HOTPLUG_CPU -extern void unregister_cpu(struct cpu *, struct node *); +extern void unregister_cpu(struct cpu *cpu); #endif struct notifier_block; #ifdef CONFIG_SMP /* Need to know about CPUs going up/down? */ extern int register_cpu_notifier(struct notifier_block *nb); +#ifdef CONFIG_HOTPLUG_CPU extern void unregister_cpu_notifier(struct notifier_block *nb); +#else +static inline void unregister_cpu_notifier(struct notifier_block *nb) +{ +} +#endif extern int current_in_cpu_hotplug(void); int cpu_up(unsigned int cpu); @@ -73,13 +79,17 @@ #define hotcpu_notifier(fn, pri) { \ { .notifier_call = fn, .priority = pri }; \ register_cpu_notifier(&fn##_nb); \ } +#define register_hotcpu_notifier(nb) register_cpu_notifier(nb) +#define unregister_hotcpu_notifier(nb) unregister_cpu_notifier(nb) int cpu_down(unsigned int cpu); #define cpu_is_offline(cpu) unlikely(!cpu_online(cpu)) #else #define lock_cpu_hotplug() do { } while (0) #define unlock_cpu_hotplug() do { } while (0) #define lock_cpu_hotplug_interruptible() 0 -#define hotcpu_notifier(fn, pri) +#define hotcpu_notifier(fn, pri) do { } while (0) +#define register_hotcpu_notifier(nb) do { } while (0) +#define unregister_hotcpu_notifier(nb) do { } while (0) /* CPUs don't go offline once they're online w/o CONFIG_HOTPLUG_CPU */ static inline int cpu_is_offline(int cpu) { return 0; } diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 17866d7..35e1376 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -15,7 +15,6 @@ #ifndef _LINUX_CPUFREQ_H #define _LINUX_CPUFREQ_H #include -#include #include #include #include @@ -73,6 +72,8 @@ struct cpufreq_real_policy { struct cpufreq_policy { cpumask_t cpus; /* affected CPUs */ + unsigned int shared_type; /* ANY or ALL affected CPUs + should set cpufreq */ unsigned int cpu; /* cpu nr of registered CPU */ struct cpufreq_cpuinfo cpuinfo;/* see above */ @@ -99,6 +100,10 @@ #define CPUFREQ_ADJUST (0) #define CPUFREQ_INCOMPATIBLE (1) #define CPUFREQ_NOTIFY (2) +#define CPUFREQ_SHARED_TYPE_NONE (0) /* None */ +#define CPUFREQ_SHARED_TYPE_HW (1) /* HW does needed coordination */ +#define CPUFREQ_SHARED_TYPE_ALL (2) /* All dependent CPUs should set freq */ +#define CPUFREQ_SHARED_TYPE_ANY (3) /* Freq can be set from any dependent CPU*/ /******************** cpufreq transition notifiers *******************/ diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index 9cbb781..b268a3c 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -317,7 +317,8 @@ #define for_each_cpu_mask(cpu, mask) \ (cpu) < NR_CPUS; \ (cpu) = next_cpu((cpu), (mask))) #else /* NR_CPUS == 1 */ -#define for_each_cpu_mask(cpu, mask) for ((cpu) = 0; (cpu) < 1; (cpu)++) +#define for_each_cpu_mask(cpu, mask) \ + for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask) #endif /* NR_CPUS */ /* @@ -405,7 +406,6 @@ #define highest_possible_processor_id() #define any_online_cpu(mask) 0 #endif -#define for_each_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map) #define for_each_possible_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map) #define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map) #define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map) diff --git a/include/linux/cramfs_fs.h b/include/linux/cramfs_fs.h index a8948f3..a41f384 100644 --- a/include/linux/cramfs_fs.h +++ b/include/linux/cramfs_fs.h @@ -1,13 +1,7 @@ #ifndef __CRAMFS_H #define __CRAMFS_H -#ifndef __KERNEL__ - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#endif +#include #define CRAMFS_MAGIC 0x28cd3d45 /* some random number */ #define CRAMFS_SIGNATURE "Compressed ROMFS" @@ -33,9 +27,9 @@ #define CRAMFS_MAXPATHLEN (((1 << CRAMFS * Reasonably terse representation of the inode data. */ struct cramfs_inode { - u32 mode:CRAMFS_MODE_WIDTH, uid:CRAMFS_UID_WIDTH; + __u32 mode:CRAMFS_MODE_WIDTH, uid:CRAMFS_UID_WIDTH; /* SIZE for device files is i_rdev */ - u32 size:CRAMFS_SIZE_WIDTH, gid:CRAMFS_GID_WIDTH; + __u32 size:CRAMFS_SIZE_WIDTH, gid:CRAMFS_GID_WIDTH; /* NAMELEN is the length of the file name, divided by 4 and rounded up. (cramfs doesn't support hard links.) */ /* OFFSET: For symlinks and non-empty regular files, this @@ -44,27 +38,27 @@ struct cramfs_inode { see README). For non-empty directories it is the offset (divided by 4) of the inode of the first file in that directory. For anything else, offset is zero. */ - u32 namelen:CRAMFS_NAMELEN_WIDTH, offset:CRAMFS_OFFSET_WIDTH; + __u32 namelen:CRAMFS_NAMELEN_WIDTH, offset:CRAMFS_OFFSET_WIDTH; }; struct cramfs_info { - u32 crc; - u32 edition; - u32 blocks; - u32 files; + __u32 crc; + __u32 edition; + __u32 blocks; + __u32 files; }; /* * Superblock information at the beginning of the FS. */ struct cramfs_super { - u32 magic; /* 0x28cd3d45 - random number */ - u32 size; /* length in bytes */ - u32 flags; /* feature flags */ - u32 future; /* reserved for future use */ - u8 signature[16]; /* "Compressed ROMFS" */ + __u32 magic; /* 0x28cd3d45 - random number */ + __u32 size; /* length in bytes */ + __u32 flags; /* feature flags */ + __u32 future; /* reserved for future use */ + __u8 signature[16]; /* "Compressed ROMFS" */ struct cramfs_info fsid; /* unique filesystem info */ - u8 name[16]; /* user-defined name */ + __u8 name[16]; /* user-defined name */ struct cramfs_inode root; /* root inode data */ }; diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 0ab1bc1..7f94624 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -17,7 +17,6 @@ #ifndef _LINUX_CRYPTO_H #define _LINUX_CRYPTO_H -#include #include #include #include @@ -67,7 +66,7 @@ struct crypto_tfm; struct cipher_desc { struct crypto_tfm *tfm; - void (*crfn)(void *ctx, u8 *dst, const u8 *src); + void (*crfn)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); unsigned int (*prfn)(const struct cipher_desc *desc, u8 *dst, const u8 *src, unsigned int nbytes); void *info; @@ -80,10 +79,10 @@ struct cipher_desc { struct cipher_alg { unsigned int cia_min_keysize; unsigned int cia_max_keysize; - int (*cia_setkey)(void *ctx, const u8 *key, + int (*cia_setkey)(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen, u32 *flags); - void (*cia_encrypt)(void *ctx, u8 *dst, const u8 *src); - void (*cia_decrypt)(void *ctx, u8 *dst, const u8 *src); + void (*cia_encrypt)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); + void (*cia_decrypt)(struct crypto_tfm *tfm, u8 *dst, const u8 *src); unsigned int (*cia_encrypt_ecb)(const struct cipher_desc *desc, u8 *dst, const u8 *src, @@ -101,20 +100,19 @@ struct cipher_alg { struct digest_alg { unsigned int dia_digestsize; - void (*dia_init)(void *ctx); - void (*dia_update)(void *ctx, const u8 *data, unsigned int len); - void (*dia_final)(void *ctx, u8 *out); - int (*dia_setkey)(void *ctx, const u8 *key, + void (*dia_init)(struct crypto_tfm *tfm); + void (*dia_update)(struct crypto_tfm *tfm, const u8 *data, + unsigned int len); + void (*dia_final)(struct crypto_tfm *tfm, u8 *out); + int (*dia_setkey)(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen, u32 *flags); }; struct compress_alg { - int (*coa_init)(void *ctx); - void (*coa_exit)(void *ctx); - int (*coa_compress)(void *ctx, const u8 *src, unsigned int slen, - u8 *dst, unsigned int *dlen); - int (*coa_decompress)(void *ctx, const u8 *src, unsigned int slen, - u8 *dst, unsigned int *dlen); + int (*coa_compress)(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen); + int (*coa_decompress)(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen); }; #define cra_cipher cra_u.cipher @@ -130,14 +128,17 @@ struct crypto_alg { int cra_priority; - const char cra_name[CRYPTO_MAX_ALG_NAME]; - const char cra_driver_name[CRYPTO_MAX_ALG_NAME]; + char cra_name[CRYPTO_MAX_ALG_NAME]; + char cra_driver_name[CRYPTO_MAX_ALG_NAME]; union { struct cipher_alg cipher; struct digest_alg digest; struct compress_alg compress; } cra_u; + + int (*cra_init)(struct crypto_tfm *tfm); + void (*cra_exit)(struct crypto_tfm *tfm); struct module *cra_module; }; diff --git a/include/linux/cyclomx.h b/include/linux/cyclomx.h index 300d704..b88f7f4 100644 --- a/include/linux/cyclomx.h +++ b/include/linux/cyclomx.h @@ -24,7 +24,6 @@ #define _CYCLOMX_H * 1998/08/08 acme Version 0.0.1 */ -#include #include #include diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 836325e..471781f 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -114,6 +114,18 @@ #endif unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ }; +/* + * dentry->d_lock spinlock nesting subclasses: + * + * 0: normal + * 1: nested + */ +enum dentry_d_lock_class +{ + DENTRY_D_LOCK_NORMAL, /* implicitly used by plain spin_lock() APIs. */ + DENTRY_D_LOCK_NESTED +}; + struct dentry_operations { int (*d_revalidate)(struct dentry *, struct nameidata *); int (*d_hash) (struct dentry *, struct qstr *); @@ -217,7 +229,6 @@ extern struct dentry * d_alloc_anon(stru extern struct dentry * d_splice_alias(struct inode *, struct dentry *); extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); -extern void shrink_dcache_anon(struct hlist_head *); extern int d_invalidate(struct dentry *); /* only used at mount-time */ diff --git a/include/linux/dcookies.h b/include/linux/dcookies.h index 1d68428..0fe7cdf 100644 --- a/include/linux/dcookies.h +++ b/include/linux/dcookies.h @@ -9,7 +9,6 @@ #ifndef DCOOKIES_H #define DCOOKIES_H -#include #ifdef CONFIG_PROFILING diff --git a/include/linux/debug_locks.h b/include/linux/debug_locks.h new file mode 100644 index 0000000..6a70478 --- /dev/null +++ b/include/linux/debug_locks.h @@ -0,0 +1,69 @@ +#ifndef __LINUX_DEBUG_LOCKING_H +#define __LINUX_DEBUG_LOCKING_H + +extern int debug_locks; +extern int debug_locks_silent; + +/* + * Generic 'turn off all lock debugging' function: + */ +extern int debug_locks_off(void); + +/* + * In the debug case we carry the caller's instruction pointer into + * other functions, but we dont want the function argument overhead + * in the nondebug case - hence these macros: + */ +#define _RET_IP_ (unsigned long)__builtin_return_address(0) +#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) + +#define DEBUG_LOCKS_WARN_ON(c) \ +({ \ + int __ret = 0; \ + \ + if (unlikely(c)) { \ + if (debug_locks_off()) \ + WARN_ON(1); \ + __ret = 1; \ + } \ + __ret; \ +}) + +#ifdef CONFIG_SMP +# define SMP_DEBUG_LOCKS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c) +#else +# define SMP_DEBUG_LOCKS_WARN_ON(c) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_LOCKING_API_SELFTESTS + extern void locking_selftest(void); +#else +# define locking_selftest() do { } while (0) +#endif + +#ifdef CONFIG_LOCKDEP +extern void debug_show_all_locks(void); +extern void debug_show_held_locks(struct task_struct *task); +extern void debug_check_no_locks_freed(const void *from, unsigned long len); +extern void debug_check_no_locks_held(struct task_struct *task); +#else +static inline void debug_show_all_locks(void) +{ +} + +static inline void debug_show_held_locks(struct task_struct *task) +{ +} + +static inline void +debug_check_no_locks_freed(const void *from, unsigned long len) +{ +} + +static inline void +debug_check_no_locks_held(struct task_struct *task) +{ +} +#endif + +#endif diff --git a/include/linux/delay.h b/include/linux/delay.h index acb7486..17ddb55 100644 --- a/include/linux/delay.h +++ b/include/linux/delay.h @@ -25,10 +25,7 @@ #ifndef MAX_UDELAY_MS #define MAX_UDELAY_MS 5 #endif -#ifdef notdef -#define mdelay(n) (\ - {unsigned long __ms=(n); while (__ms--) udelay(1000);}) -#else +#ifndef mdelay #define mdelay(n) (\ (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \ ({unsigned long __ms=(n); while (__ms--) udelay(1000);})) diff --git a/include/linux/devfs_fs.h b/include/linux/devfs_fs.h deleted file mode 100644 index de236f4..0000000 --- a/include/linux/devfs_fs.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _LINUX_DEVFS_FS_H -#define _LINUX_DEVFS_FS_H - -#include - -#define DEVFSD_PROTOCOL_REVISION_KERNEL 5 - -#define DEVFSD_IOCTL_BASE 'd' - -/* These are the various ioctls */ -#define DEVFSDIOC_GET_PROTO_REV _IOR(DEVFSD_IOCTL_BASE, 0, int) -#define DEVFSDIOC_SET_EVENT_MASK _IOW(DEVFSD_IOCTL_BASE, 2, int) -#define DEVFSDIOC_RELEASE_EVENT_QUEUE _IOW(DEVFSD_IOCTL_BASE, 3, int) -#define DEVFSDIOC_SET_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int) - -#define DEVFSD_NOTIFY_REGISTERED 0 -#define DEVFSD_NOTIFY_UNREGISTERED 1 -#define DEVFSD_NOTIFY_ASYNC_OPEN 2 -#define DEVFSD_NOTIFY_CLOSE 3 -#define DEVFSD_NOTIFY_LOOKUP 4 -#define DEVFSD_NOTIFY_CHANGE 5 -#define DEVFSD_NOTIFY_CREATE 6 -#define DEVFSD_NOTIFY_DELETE 7 - -#define DEVFS_PATHLEN 1024 /* Never change this otherwise the - binary interface will change */ - -struct devfsd_notify_struct { /* Use native C types to ensure same types in kernel and user space */ - unsigned int type; /* DEVFSD_NOTIFY_* value */ - unsigned int mode; /* Mode of the inode or device entry */ - unsigned int major; /* Major number of device entry */ - unsigned int minor; /* Minor number of device entry */ - unsigned int uid; /* Uid of process, inode or device entry */ - unsigned int gid; /* Gid of process, inode or device entry */ - unsigned int overrun_count; /* Number of lost events */ - unsigned int namelen; /* Number of characters not including '\0' */ - /* The device name MUST come last */ - char devname[DEVFS_PATHLEN]; /* This will be '\0' terminated */ -}; - -#endif /* _LINUX_DEVFS_FS_H */ diff --git a/include/linux/devfs_fs_kernel.h b/include/linux/devfs_fs_kernel.h deleted file mode 100644 index 89810e7..0000000 --- a/include/linux/devfs_fs_kernel.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _LINUX_DEVFS_FS_KERNEL_H -#define _LINUX_DEVFS_FS_KERNEL_H - -#include -#include -#include -#include - -#include - -#define DEVFS_SUPER_MAGIC 0x1373 - -#ifdef CONFIG_DEVFS_FS -extern int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...) - __attribute__ ((format(printf, 3, 4))); -extern int devfs_mk_cdev(dev_t dev, umode_t mode, const char *fmt, ...) - __attribute__ ((format(printf, 3, 4))); -extern int devfs_mk_symlink(const char *name, const char *link); -extern int devfs_mk_dir(const char *fmt, ...) - __attribute__ ((format(printf, 1, 2))); -extern void devfs_remove(const char *fmt, ...) - __attribute__ ((format(printf, 1, 2))); -extern int devfs_register_tape(const char *name); -extern void devfs_unregister_tape(int num); -extern void mount_devfs_fs(void); -#else /* CONFIG_DEVFS_FS */ -static inline int devfs_mk_bdev(dev_t dev, umode_t mode, const char *fmt, ...) -{ - return 0; -} -static inline int devfs_mk_cdev(dev_t dev, umode_t mode, const char *fmt, ...) -{ - return 0; -} -static inline int devfs_mk_symlink(const char *name, const char *link) -{ - return 0; -} -static inline int devfs_mk_dir(const char *fmt, ...) -{ - return 0; -} -static inline void devfs_remove(const char *fmt, ...) -{ -} -static inline int devfs_register_tape(const char *name) -{ - return -1; -} -static inline void devfs_unregister_tape(int num) -{ -} -static inline void mount_devfs_fs(void) -{ - return; -} -#endif /* CONFIG_DEVFS_FS */ -#endif /* _LINUX_DEVFS_FS_KERNEL_H */ diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h index aee10b2..e3d1c33 100644 --- a/include/linux/device-mapper.h +++ b/include/linux/device-mapper.h @@ -8,9 +8,12 @@ #ifndef _LINUX_DEVICE_MAPPER_H #define _LINUX_DEVICE_MAPPER_H +#ifdef __KERNEL__ + struct dm_target; struct dm_table; struct dm_dev; +struct mapped_device; typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t; @@ -78,7 +81,7 @@ void dm_put_device(struct dm_target *ti, struct target_type { const char *name; struct module *module; - unsigned version[3]; + unsigned version[3]; dm_ctr_fn ctr; dm_dtr_fn dtr; dm_map_fn map; @@ -128,4 +131,108 @@ struct dm_target { int dm_register_target(struct target_type *t); int dm_unregister_target(struct target_type *t); -#endif /* _LINUX_DEVICE_MAPPER_H */ + +/*----------------------------------------------------------------- + * Functions for creating and manipulating mapped devices. + * Drop the reference with dm_put when you finish with the object. + *---------------------------------------------------------------*/ + +/* + * DM_ANY_MINOR chooses the next available minor number. + */ +#define DM_ANY_MINOR (-1) +int dm_create(int minor, struct mapped_device **md); + +/* + * Reference counting for md. + */ +struct mapped_device *dm_get_md(dev_t dev); +void dm_get(struct mapped_device *md); +void dm_put(struct mapped_device *md); + +/* + * An arbitrary pointer may be stored alongside a mapped device. + */ +void dm_set_mdptr(struct mapped_device *md, void *ptr); +void *dm_get_mdptr(struct mapped_device *md); + +/* + * A device can still be used while suspended, but I/O is deferred. + */ +int dm_suspend(struct mapped_device *md, int with_lockfs); +int dm_resume(struct mapped_device *md); + +/* + * Event functions. + */ +uint32_t dm_get_event_nr(struct mapped_device *md); +int dm_wait_event(struct mapped_device *md, int event_nr); + +/* + * Info functions. + */ +const char *dm_device_name(struct mapped_device *md); +struct gendisk *dm_disk(struct mapped_device *md); +int dm_suspended(struct mapped_device *md); + +/* + * Geometry functions. + */ +int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo); +int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo); + + +/*----------------------------------------------------------------- + * Functions for manipulating device-mapper tables. + *---------------------------------------------------------------*/ + +/* + * First create an empty table. + */ +int dm_table_create(struct dm_table **result, int mode, + unsigned num_targets, struct mapped_device *md); + +/* + * Then call this once for each target. + */ +int dm_table_add_target(struct dm_table *t, const char *type, + sector_t start, sector_t len, char *params); + +/* + * Finally call this to make the table ready for use. + */ +int dm_table_complete(struct dm_table *t); + +/* + * Table reference counting. + */ +struct dm_table *dm_get_table(struct mapped_device *md); +void dm_table_get(struct dm_table *t); +void dm_table_put(struct dm_table *t); + +/* + * Queries + */ +sector_t dm_table_get_size(struct dm_table *t); +unsigned int dm_table_get_num_targets(struct dm_table *t); +int dm_table_get_mode(struct dm_table *t); +struct mapped_device *dm_table_get_md(struct dm_table *t); + +/* + * Trigger an event. + */ +void dm_table_event(struct dm_table *t); + +/* + * The device must be suspended before calling this method. + */ +int dm_swap_table(struct mapped_device *md, struct dm_table *t); + +/* + * Prepare a table for a device that will error all I/O. + * To make it active, call dm_suspend(), dm_swap_table() then dm_resume(). + */ +int dm_create_error_table(struct dm_table **result, struct mapped_device *md); + +#endif /* __KERNEL__ */ +#endif /* _LINUX_DEVICE_MAPPER_H */ diff --git a/include/linux/device.h b/include/linux/device.h index e8e53b9..1e5f30d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -11,7 +11,6 @@ #ifndef _DEVICE_H_ #define _DEVICE_H_ -#include #include #include #include @@ -61,11 +60,6 @@ extern void bus_unregister(struct bus_ty extern void bus_rescan_devices(struct bus_type * bus); -extern struct bus_type * get_bus(struct bus_type * bus); -extern void put_bus(struct bus_type * bus); - -extern struct bus_type * find_bus(char * name); - /* iterator helpers for buses */ int bus_for_each_dev(struct bus_type * bus, struct device * start, void * data, @@ -148,6 +142,7 @@ struct class { struct subsystem subsys; struct list_head children; + struct list_head devices; struct list_head interfaces; struct semaphore sem; /* locks both the children and interfaces lists */ @@ -164,9 +159,6 @@ struct class { extern int class_register(struct class *); extern void class_unregister(struct class *); -extern struct class * class_get(struct class *); -extern void class_put(struct class *); - struct class_attribute { struct attribute attr; @@ -314,6 +306,7 @@ struct device { struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ struct device_attribute uevent_attr; + struct device_attribute *devt_attr; struct semaphore sem; /* semaphore to synchronize calls to * its driver. @@ -341,6 +334,11 @@ struct device { struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ + /* class_device migration path */ + struct list_head node; + struct class *class; /* optional*/ + dev_t devt; /* dev_t, creates the sysfs "dev" */ + void (*release)(struct device * dev); }; @@ -382,6 +380,13 @@ extern int device_attach(struct device extern void driver_attach(struct device_driver * drv); extern void device_reprobe(struct device *dev); +/* + * Easy functions for dynamically creating devices on the fly + */ +extern struct device *device_create(struct class *cls, struct device *parent, + dev_t devt, char *fmt, ...) + __attribute__((format(printf,4,5))); +extern void device_destroy(struct class *cls, dev_t devt); /* * Platform "fixup" functions - allow the platform to have their say @@ -411,8 +416,9 @@ extern int firmware_register(struct subs extern void firmware_unregister(struct subsystem *); /* debugging and troubleshooting/diagnostic helpers. */ +extern const char *dev_driver_string(struct device *dev); #define dev_printk(level, dev, format, arg...) \ - printk(level "%s %s: " format , (dev)->driver ? (dev)->driver->name : "" , (dev)->bus_id , ## arg) + printk(level "%s %s: " format , dev_driver_string(dev) , (dev)->bus_id , ## arg) #ifdef DEBUG #define dev_dbg(dev, format, arg...) \ diff --git a/include/linux/divert.h b/include/linux/divert.h index 6919b09..8fb4e9d 100644 --- a/include/linux/divert.h +++ b/include/linux/divert.h @@ -27,10 +27,10 @@ struct divert_blk { int divert; /* are we active */ unsigned int protos; /* protocols */ - u16 tcp_dst[MAX_DIVERT_PORTS]; /* specific tcp dst ports to divert */ - u16 tcp_src[MAX_DIVERT_PORTS]; /* specific tcp src ports to divert */ - u16 udp_dst[MAX_DIVERT_PORTS]; /* specific udp dst ports to divert */ - u16 udp_src[MAX_DIVERT_PORTS]; /* specific udp src ports to divert */ + __u16 tcp_dst[MAX_DIVERT_PORTS]; /* specific tcp dst ports to divert */ + __u16 tcp_src[MAX_DIVERT_PORTS]; /* specific tcp src ports to divert */ + __u16 udp_dst[MAX_DIVERT_PORTS]; /* specific udp dst ports to divert */ + __u16 udp_src[MAX_DIVERT_PORTS]; /* specific udp src ports to divert */ }; /* @@ -40,12 +40,12 @@ struct divert_blk typedef union _divert_cf_arg { - s16 int16; - u16 uint16; - s32 int32; - u32 uint32; - s64 int64; - u64 uint64; + __s16 int16; + __u16 uint16; + __s32 int32; + __u32 uint32; + __s64 int64; + __u64 uint64; void __user *ptr; } divert_cf_arg; diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h index c67c678..9623bb6 100644 --- a/include/linux/dm-ioctl.h +++ b/include/linux/dm-ioctl.h @@ -285,9 +285,9 @@ #define DM_TARGET_MSG _IOWR(DM_IOCTL, D #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) #define DM_VERSION_MAJOR 4 -#define DM_VERSION_MINOR 6 +#define DM_VERSION_MINOR 7 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2006-02-17)" +#define DM_VERSION_EXTRA "-ioctl (2006-06-24)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ @@ -314,7 +314,7 @@ #define DM_INACTIVE_PRESENT_FLAG (1 << 6 #define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ /* - * Set this to improve performance when you aren't going to use open_count. + * This flag is now ignored. */ #define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h new file mode 100644 index 0000000..c94d8f1 --- /dev/null +++ b/include/linux/dmaengine.h @@ -0,0 +1,366 @@ +/* + * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + */ +#ifndef DMAENGINE_H +#define DMAENGINE_H + +#ifdef CONFIG_DMA_ENGINE + +#include +#include +#include +#include +#include + +/** + * enum dma_event - resource PNP/power managment events + * @DMA_RESOURCE_SUSPEND: DMA device going into low power state + * @DMA_RESOURCE_RESUME: DMA device returning to full power + * @DMA_RESOURCE_ADDED: DMA device added to the system + * @DMA_RESOURCE_REMOVED: DMA device removed from the system + */ +enum dma_event { + DMA_RESOURCE_SUSPEND, + DMA_RESOURCE_RESUME, + DMA_RESOURCE_ADDED, + DMA_RESOURCE_REMOVED, +}; + +/** + * typedef dma_cookie_t - an opaque DMA cookie + * + * if dma_cookie_t is >0 it's a DMA request cookie, <0 it's an error code + */ +typedef s32 dma_cookie_t; + +#define dma_submit_error(cookie) ((cookie) < 0 ? 1 : 0) + +/** + * enum dma_status - DMA transaction status + * @DMA_SUCCESS: transaction completed successfully + * @DMA_IN_PROGRESS: transaction not yet processed + * @DMA_ERROR: transaction failed + */ +enum dma_status { + DMA_SUCCESS, + DMA_IN_PROGRESS, + DMA_ERROR, +}; + +/** + * struct dma_chan_percpu - the per-CPU part of struct dma_chan + * @refcount: local_t used for open-coded "bigref" counting + * @memcpy_count: transaction counter + * @bytes_transferred: byte counter + */ + +struct dma_chan_percpu { + local_t refcount; + /* stats */ + unsigned long memcpy_count; + unsigned long bytes_transferred; +}; + +/** + * struct dma_chan - devices supply DMA channels, clients use them + * @client: ptr to the client user of this chan, will be %NULL when unused + * @device: ptr to the dma device who supplies this channel, always !%NULL + * @cookie: last cookie value returned to client + * @chan_id: channel ID for sysfs + * @class_dev: class device for sysfs + * @refcount: kref, used in "bigref" slow-mode + * @slow_ref: indicates that the DMA channel is free + * @rcu: the DMA channel's RCU head + * @client_node: used to add this to the client chan list + * @device_node: used to add this to the device chan list + * @local: per-cpu pointer to a struct dma_chan_percpu + */ +struct dma_chan { + struct dma_client *client; + struct dma_device *device; + dma_cookie_t cookie; + + /* sysfs */ + int chan_id; + struct class_device class_dev; + + struct kref refcount; + int slow_ref; + struct rcu_head rcu; + + struct list_head client_node; + struct list_head device_node; + struct dma_chan_percpu *local; +}; + +void dma_chan_cleanup(struct kref *kref); + +static inline void dma_chan_get(struct dma_chan *chan) +{ + if (unlikely(chan->slow_ref)) + kref_get(&chan->refcount); + else { + local_inc(&(per_cpu_ptr(chan->local, get_cpu())->refcount)); + put_cpu(); + } +} + +static inline void dma_chan_put(struct dma_chan *chan) +{ + if (unlikely(chan->slow_ref)) + kref_put(&chan->refcount, dma_chan_cleanup); + else { + local_dec(&(per_cpu_ptr(chan->local, get_cpu())->refcount)); + put_cpu(); + } +} + +/* + * typedef dma_event_callback - function pointer to a DMA event callback + */ +typedef void (*dma_event_callback) (struct dma_client *client, + struct dma_chan *chan, enum dma_event event); + +/** + * struct dma_client - info on the entity making use of DMA services + * @event_callback: func ptr to call when something happens + * @chan_count: number of chans allocated + * @chans_desired: number of chans requested. Can be +/- chan_count + * @lock: protects access to the channels list + * @channels: the list of DMA channels allocated + * @global_node: list_head for global dma_client_list + */ +struct dma_client { + dma_event_callback event_callback; + unsigned int chan_count; + unsigned int chans_desired; + + spinlock_t lock; + struct list_head channels; + struct list_head global_node; +}; + +/** + * struct dma_device - info on the entity supplying DMA services + * @chancnt: how many DMA channels are supported + * @channels: the list of struct dma_chan + * @global_node: list_head for global dma_device_list + * @refcount: reference count + * @done: IO completion struct + * @dev_id: unique device ID + * @device_alloc_chan_resources: allocate resources and return the + * number of allocated descriptors + * @device_free_chan_resources: release DMA channel's resources + * @device_memcpy_buf_to_buf: memcpy buf pointer to buf pointer + * @device_memcpy_buf_to_pg: memcpy buf pointer to struct page + * @device_memcpy_pg_to_pg: memcpy struct page/offset to struct page/offset + * @device_memcpy_complete: poll the status of an IOAT DMA transaction + * @device_memcpy_issue_pending: push appended descriptors to hardware + */ +struct dma_device { + + unsigned int chancnt; + struct list_head channels; + struct list_head global_node; + + struct kref refcount; + struct completion done; + + int dev_id; + + int (*device_alloc_chan_resources)(struct dma_chan *chan); + void (*device_free_chan_resources)(struct dma_chan *chan); + dma_cookie_t (*device_memcpy_buf_to_buf)(struct dma_chan *chan, + void *dest, void *src, size_t len); + dma_cookie_t (*device_memcpy_buf_to_pg)(struct dma_chan *chan, + struct page *page, unsigned int offset, void *kdata, + size_t len); + dma_cookie_t (*device_memcpy_pg_to_pg)(struct dma_chan *chan, + struct page *dest_pg, unsigned int dest_off, + struct page *src_pg, unsigned int src_off, size_t len); + enum dma_status (*device_memcpy_complete)(struct dma_chan *chan, + dma_cookie_t cookie, dma_cookie_t *last, + dma_cookie_t *used); + void (*device_memcpy_issue_pending)(struct dma_chan *chan); +}; + +/* --- public DMA engine API --- */ + +struct dma_client *dma_async_client_register(dma_event_callback event_callback); +void dma_async_client_unregister(struct dma_client *client); +void dma_async_client_chan_request(struct dma_client *client, + unsigned int number); + +/** + * dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses + * @chan: DMA channel to offload copy to + * @dest: destination address (virtual) + * @src: source address (virtual) + * @len: length + * + * Both @dest and @src must be mappable to a bus address according to the + * DMA mapping API rules for streaming mappings. + * Both @dest and @src must stay memory resident (kernel memory or locked + * user space pages). + */ +static inline dma_cookie_t dma_async_memcpy_buf_to_buf(struct dma_chan *chan, + void *dest, void *src, size_t len) +{ + int cpu = get_cpu(); + per_cpu_ptr(chan->local, cpu)->bytes_transferred += len; + per_cpu_ptr(chan->local, cpu)->memcpy_count++; + put_cpu(); + + return chan->device->device_memcpy_buf_to_buf(chan, dest, src, len); +} + +/** + * dma_async_memcpy_buf_to_pg - offloaded copy from address to page + * @chan: DMA channel to offload copy to + * @page: destination page + * @offset: offset in page to copy to + * @kdata: source address (virtual) + * @len: length + * + * Both @page/@offset and @kdata must be mappable to a bus address according + * to the DMA mapping API rules for streaming mappings. + * Both @page/@offset and @kdata must stay memory resident (kernel memory or + * locked user space pages) + */ +static inline dma_cookie_t dma_async_memcpy_buf_to_pg(struct dma_chan *chan, + struct page *page, unsigned int offset, void *kdata, size_t len) +{ + int cpu = get_cpu(); + per_cpu_ptr(chan->local, cpu)->bytes_transferred += len; + per_cpu_ptr(chan->local, cpu)->memcpy_count++; + put_cpu(); + + return chan->device->device_memcpy_buf_to_pg(chan, page, offset, + kdata, len); +} + +/** + * dma_async_memcpy_pg_to_pg - offloaded copy from page to page + * @chan: DMA channel to offload copy to + * @dest_pg: destination page + * @dest_off: offset in page to copy to + * @src_pg: source page + * @src_off: offset in page to copy from + * @len: length + * + * Both @dest_page/@dest_off and @src_page/@src_off must be mappable to a bus + * address according to the DMA mapping API rules for streaming mappings. + * Both @dest_page/@dest_off and @src_page/@src_off must stay memory resident + * (kernel memory or locked user space pages). + */ +static inline dma_cookie_t dma_async_memcpy_pg_to_pg(struct dma_chan *chan, + struct page *dest_pg, unsigned int dest_off, struct page *src_pg, + unsigned int src_off, size_t len) +{ + int cpu = get_cpu(); + per_cpu_ptr(chan->local, cpu)->bytes_transferred += len; + per_cpu_ptr(chan->local, cpu)->memcpy_count++; + put_cpu(); + + return chan->device->device_memcpy_pg_to_pg(chan, dest_pg, dest_off, + src_pg, src_off, len); +} + +/** + * dma_async_memcpy_issue_pending - flush pending copies to HW + * @chan: target DMA channel + * + * This allows drivers to push copies to HW in batches, + * reducing MMIO writes where possible. + */ +static inline void dma_async_memcpy_issue_pending(struct dma_chan *chan) +{ + return chan->device->device_memcpy_issue_pending(chan); +} + +/** + * dma_async_memcpy_complete - poll for transaction completion + * @chan: DMA channel + * @cookie: transaction identifier to check status of + * @last: returns last completed cookie, can be NULL + * @used: returns last issued cookie, can be NULL + * + * If @last and @used are passed in, upon return they reflect the driver + * internal state and can be used with dma_async_is_complete() to check + * the status of multiple cookies without re-checking hardware state. + */ +static inline enum dma_status dma_async_memcpy_complete(struct dma_chan *chan, + dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used) +{ + return chan->device->device_memcpy_complete(chan, cookie, last, used); +} + +/** + * dma_async_is_complete - test a cookie against chan state + * @cookie: transaction identifier to test status of + * @last_complete: last know completed transaction + * @last_used: last cookie value handed out + * + * dma_async_is_complete() is used in dma_async_memcpy_complete() + * the test logic is seperated for lightweight testing of multiple cookies + */ +static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie, + dma_cookie_t last_complete, dma_cookie_t last_used) +{ + if (last_complete <= last_used) { + if ((cookie <= last_complete) || (cookie > last_used)) + return DMA_SUCCESS; + } else { + if ((cookie <= last_complete) && (cookie > last_used)) + return DMA_SUCCESS; + } + return DMA_IN_PROGRESS; +} + + +/* --- DMA device --- */ + +int dma_async_device_register(struct dma_device *device); +void dma_async_device_unregister(struct dma_device *device); + +/* --- Helper iov-locking functions --- */ + +struct dma_page_list { + char *base_address; + int nr_pages; + struct page **pages; +}; + +struct dma_pinned_list { + int nr_iovecs; + struct dma_page_list page_list[0]; +}; + +struct dma_pinned_list *dma_pin_iovec_pages(struct iovec *iov, size_t len); +void dma_unpin_iovec_pages(struct dma_pinned_list* pinned_list); + +dma_cookie_t dma_memcpy_to_iovec(struct dma_chan *chan, struct iovec *iov, + struct dma_pinned_list *pinned_list, unsigned char *kdata, size_t len); +dma_cookie_t dma_memcpy_pg_to_iovec(struct dma_chan *chan, struct iovec *iov, + struct dma_pinned_list *pinned_list, struct page *page, + unsigned int offset, size_t len); + +#endif /* CONFIG_DMA_ENGINE */ +#endif /* DMAENGINE_H */ diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 64fd6c3..b2cd207 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -2,7 +2,6 @@ #ifndef __DMI_H__ #define __DMI_H__ #include -#include enum dmi_field { DMI_NONE, diff --git a/include/linux/dnotify.h b/include/linux/dnotify.h index f134a01..102a902 100644 --- a/include/linux/dnotify.h +++ b/include/linux/dnotify.h @@ -18,7 +18,6 @@ struct dnotify_struct { #ifdef __KERNEL__ -#include #ifdef CONFIG_DNOTIFY diff --git a/include/linux/dqblk_xfs.h b/include/linux/dqblk_xfs.h index 2fda1b2..527504c 100644 --- a/include/linux/dqblk_xfs.h +++ b/include/linux/dqblk_xfs.h @@ -125,14 +125,14 @@ #define XFS_GROUP_QUOTA (1<<2) /* group /* * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system. - * Provides a centralized way to get meta infomation about the quota subsystem. + * Provides a centralized way to get meta information about the quota subsystem. * eg. space taken up for user and group quotas, number of dquots currently * incore. */ #define FS_QSTAT_VERSION 1 /* fs_quota_stat.qs_version */ /* - * Some basic infomation about 'quota files'. + * Some basic information about 'quota files'. */ typedef struct fs_qfilestat { __u64 qfs_ino; /* inode number */ diff --git a/include/linux/dvb/Kbuild b/include/linux/dvb/Kbuild new file mode 100644 index 0000000..63973af --- /dev/null +++ b/include/linux/dvb/Kbuild @@ -0,0 +1,2 @@ +header-y += ca.h frontend.h net.h osd.h version.h +unifdef-y := audio.h dmx.h video.h diff --git a/include/linux/dvb/dmx.h b/include/linux/dvb/dmx.h index 2787b8a..c6a2353 100644 --- a/include/linux/dvb/dmx.h +++ b/include/linux/dvb/dmx.h @@ -88,20 +88,6 @@ #define DMX_PES_SUBTITLE DMX_PES_SUBTITL #define DMX_PES_PCR DMX_PES_PCR0 -typedef enum -{ - DMX_SCRAMBLING_EV, - DMX_FRONTEND_EV -} dmx_event_t; - - -typedef enum -{ - DMX_SCRAMBLING_OFF, - DMX_SCRAMBLING_ON -} dmx_scrambling_status_t; - - typedef struct dmx_filter { __u8 filter[DMX_FILTER_SIZE]; @@ -132,17 +118,6 @@ struct dmx_pes_filter_params __u32 flags; }; - -struct dmx_event -{ - dmx_event_t event; - time_t timeStamp; - union - { - dmx_scrambling_status_t scrambling; - } u; -}; - typedef struct dmx_caps { __u32 caps; int num_decoders; @@ -171,7 +146,6 @@ #define DMX_STOP _IO('o' #define DMX_SET_FILTER _IOW('o', 43, struct dmx_sct_filter_params) #define DMX_SET_PES_FILTER _IOW('o', 44, struct dmx_pes_filter_params) #define DMX_SET_BUFFER_SIZE _IO('o', 45) -#define DMX_GET_EVENT _IOR('o', 46, struct dmx_event) #define DMX_GET_PES_PIDS _IOR('o', 47, __u16[5]) #define DMX_GET_CAPS _IOR('o', 48, dmx_caps_t) #define DMX_SET_SOURCE _IOW('o', 49, dmx_source_t) diff --git a/include/linux/efi.h b/include/linux/efi.h index e203613..66d621d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -294,6 +294,7 @@ extern void efi_enter_virtual_mode (void extern u64 efi_get_iobase (void); extern u32 efi_mem_type (unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); +extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size); extern int efi_mem_attribute_range (unsigned long phys_addr, unsigned long size, u64 attr); extern int __init efi_uart_console_only (void); diff --git a/include/linux/efs_fs.h b/include/linux/efs_fs.h index fbfa6b5..278ef44 100644 --- a/include/linux/efs_fs.h +++ b/include/linux/efs_fs.h @@ -38,7 +38,7 @@ struct statfs; extern struct inode_operations efs_dir_inode_operations; extern const struct file_operations efs_dir_operations; -extern struct address_space_operations efs_symlink_aops; +extern const struct address_space_operations efs_symlink_aops; extern void efs_read_inode(struct inode *); extern efs_block_t efs_map_block(struct inode *, efs_block_t); diff --git a/include/linux/elf-em.h b/include/linux/elf-em.h new file mode 100644 index 0000000..6a5796c --- /dev/null +++ b/include/linux/elf-em.h @@ -0,0 +1,49 @@ +#ifndef _LINUX_ELF_EM_H +#define _LINUX_ELF_EM_H + +/* These constants define the various ELF target machines */ +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_486 6 /* Perhaps disused */ +#define EM_860 7 +#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ + /* Next two are historical and binaries and + modules of these types will be rejected by + Linux. */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC64 */ +#define EM_SH 42 /* SuperH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_IA_64 50 /* HP/Intel IA-64 */ +#define EM_X86_64 62 /* AMD x86-64 */ +#define EM_S390 22 /* IBM S/390 */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Renesas M32R */ +#define EM_H8_300 46 /* Renesas H8/300,300H,H8S */ +#define EM_FRV 0x5441 /* Fujitsu FR-V */ + +/* + * This is an interim value that we will use until the committee comes + * up with a final number. + */ +#define EM_ALPHA 0x9026 + +/* Bogus old v850 magic number, used by old tools. */ +#define EM_CYGNUS_V850 0x9080 +/* Bogus old m32r magic number, used by old tools. */ +#define EM_CYGNUS_M32R 0x9041 +/* This is the old interim value for S/390 architecture */ +#define EM_S390_OLD 0xA390 + + +#endif /* _LINUX_ELF_EM_H */ diff --git a/include/linux/elf.h b/include/linux/elf.h index d3bfacb..b70d1d2 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -3,6 +3,7 @@ #define _LINUX_ELF_H #include #include +#include #include #ifndef elf_read_implies_exec @@ -55,64 +56,6 @@ #define ET_CORE 4 #define ET_LOPROC 0xff00 #define ET_HIPROC 0xffff -/* These constants define the various ELF target machines */ -#define EM_NONE 0 -#define EM_M32 1 -#define EM_SPARC 2 -#define EM_386 3 -#define EM_68K 4 -#define EM_88K 5 -#define EM_486 6 /* Perhaps disused */ -#define EM_860 7 - -#define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ - -#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ - -#define EM_PARISC 15 /* HPPA */ - -#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ - -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* PowerPC64 */ - -#define EM_SH 42 /* SuperH */ - -#define EM_SPARCV9 43 /* SPARC v9 64-bit */ - -#define EM_IA_64 50 /* HP/Intel IA-64 */ - -#define EM_X86_64 62 /* AMD x86-64 */ - -#define EM_S390 22 /* IBM S/390 */ - -#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ - -#define EM_V850 87 /* NEC v850 */ - -#define EM_M32R 88 /* Renesas M32R */ - -#define EM_H8_300 46 /* Renesas H8/300,300H,H8S */ - -/* - * This is an interim value that we will use until the committee comes - * up with a final number. - */ -#define EM_ALPHA 0x9026 - -/* Bogus old v850 magic number, used by old tools. */ -#define EM_CYGNUS_V850 0x9080 - -/* Bogus old m32r magic number, used by old tools. */ -#define EM_CYGNUS_M32R 0x9041 - -/* - * This is the old interim value for S/390 architecture - */ -#define EM_S390_OLD 0xA390 - -#define EM_FRV 0x5441 /* Fujitsu FR-V */ - /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 #define DT_NEEDED 1 diff --git a/include/linux/err.h b/include/linux/err.h index ff71d2a..cd3b367 100644 --- a/include/linux/err.h +++ b/include/linux/err.h @@ -13,7 +13,9 @@ #include * This should be a per-architecture thing, to allow different * error and pointer decisions. */ -#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L) +#define MAX_ERRNO 4095 + +#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO) static inline void *ERR_PTR(long error) { diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h index 174582f..408118a 100644 --- a/include/linux/errqueue.h +++ b/include/linux/errqueue.h @@ -21,7 +21,6 @@ #define SO_EE_OFFENDER(ee) ((struct sock #ifdef __KERNEL__ -#include #include #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 93535f0..c6310ae 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -15,24 +15,24 @@ #define _LINUX_ETHTOOL_H /* This should work for both 32 and 64 bit userland. */ struct ethtool_cmd { - u32 cmd; - u32 supported; /* Features this interface supports */ - u32 advertising; /* Features this interface advertises */ - u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ - u8 duplex; /* Duplex, half or full */ - u8 port; /* Which connector port */ - u8 phy_address; - u8 transceiver; /* Which transceiver to use */ - u8 autoneg; /* Enable or disable autonegotiation */ - u32 maxtxpkt; /* Tx pkts before generating tx int */ - u32 maxrxpkt; /* Rx pkts before generating rx int */ - u32 reserved[4]; + __u32 cmd; + __u32 supported; /* Features this interface supports */ + __u32 advertising; /* Features this interface advertises */ + __u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ + __u8 duplex; /* Duplex, half or full */ + __u8 port; /* Which connector port */ + __u8 phy_address; + __u8 transceiver; /* Which transceiver to use */ + __u8 autoneg; /* Enable or disable autonegotiation */ + __u32 maxtxpkt; /* Tx pkts before generating tx int */ + __u32 maxrxpkt; /* Rx pkts before generating rx int */ + __u32 reserved[4]; }; #define ETHTOOL_BUSINFO_LEN 32 /* these strings are set to whatever the driver author decides... */ struct ethtool_drvinfo { - u32 cmd; + __u32 cmd; char driver[32]; /* driver short name, "tulip", "eepro100" */ char version[32]; /* driver version string */ char fw_version[32]; /* firmware version string, if applicable */ @@ -40,53 +40,53 @@ struct ethtool_drvinfo { /* For PCI devices, use pci_name(pci_dev). */ char reserved1[32]; char reserved2[16]; - u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ - u32 testinfo_len; - u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ - u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ + __u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ + __u32 testinfo_len; + __u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ + __u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ }; #define SOPASS_MAX 6 /* wake-on-lan settings */ struct ethtool_wolinfo { - u32 cmd; - u32 supported; - u32 wolopts; - u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ + __u32 cmd; + __u32 supported; + __u32 wolopts; + __u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ }; /* for passing single values */ struct ethtool_value { - u32 cmd; - u32 data; + __u32 cmd; + __u32 data; }; /* for passing big chunks of data */ struct ethtool_regs { - u32 cmd; - u32 version; /* driver-specific, indicates different chips/revs */ - u32 len; /* bytes */ - u8 data[0]; + __u32 cmd; + __u32 version; /* driver-specific, indicates different chips/revs */ + __u32 len; /* bytes */ + __u8 data[0]; }; /* for passing EEPROM chunks */ struct ethtool_eeprom { - u32 cmd; - u32 magic; - u32 offset; /* in bytes */ - u32 len; /* in bytes */ - u8 data[0]; + __u32 cmd; + __u32 magic; + __u32 offset; /* in bytes */ + __u32 len; /* in bytes */ + __u8 data[0]; }; /* for configuring coalescing parameters of chip */ struct ethtool_coalesce { - u32 cmd; /* ETHTOOL_{G,S}COALESCE */ + __u32 cmd; /* ETHTOOL_{G,S}COALESCE */ /* How many usecs to delay an RX interrupt after * a packet arrives. If 0, only rx_max_coalesced_frames * is used. */ - u32 rx_coalesce_usecs; + __u32 rx_coalesce_usecs; /* How many packets to delay an RX interrupt after * a packet arrives. If 0, only rx_coalesce_usecs is @@ -94,21 +94,21 @@ struct ethtool_coalesce { * to zero as this would cause RX interrupts to never be * generated. */ - u32 rx_max_coalesced_frames; + __u32 rx_max_coalesced_frames; /* Same as above two parameters, except that these values * apply while an IRQ is being serviced by the host. Not * all cards support this feature and the values are ignored * in that case. */ - u32 rx_coalesce_usecs_irq; - u32 rx_max_coalesced_frames_irq; + __u32 rx_coalesce_usecs_irq; + __u32 rx_max_coalesced_frames_irq; /* How many usecs to delay a TX interrupt after * a packet is sent. If 0, only tx_max_coalesced_frames * is used. */ - u32 tx_coalesce_usecs; + __u32 tx_coalesce_usecs; /* How many packets to delay a TX interrupt after * a packet is sent. If 0, only tx_coalesce_usecs is @@ -116,22 +116,22 @@ struct ethtool_coalesce { * to zero as this would cause TX interrupts to never be * generated. */ - u32 tx_max_coalesced_frames; + __u32 tx_max_coalesced_frames; /* Same as above two parameters, except that these values * apply while an IRQ is being serviced by the host. Not * all cards support this feature and the values are ignored * in that case. */ - u32 tx_coalesce_usecs_irq; - u32 tx_max_coalesced_frames_irq; + __u32 tx_coalesce_usecs_irq; + __u32 tx_max_coalesced_frames_irq; /* How many usecs to delay in-memory statistics * block updates. Some drivers do not have an in-memory * statistic block, and in such cases this value is ignored. * This value must not be zero. */ - u32 stats_block_coalesce_usecs; + __u32 stats_block_coalesce_usecs; /* Adaptive RX/TX coalescing is an algorithm implemented by * some drivers to improve latency under low packet rates and @@ -140,18 +140,18 @@ struct ethtool_coalesce { * not implemented by the driver causes these values to be * silently ignored. */ - u32 use_adaptive_rx_coalesce; - u32 use_adaptive_tx_coalesce; + __u32 use_adaptive_rx_coalesce; + __u32 use_adaptive_tx_coalesce; /* When the packet rate (measured in packets per second) * is below pkt_rate_low, the {rx,tx}_*_low parameters are * used. */ - u32 pkt_rate_low; - u32 rx_coalesce_usecs_low; - u32 rx_max_coalesced_frames_low; - u32 tx_coalesce_usecs_low; - u32 tx_max_coalesced_frames_low; + __u32 pkt_rate_low; + __u32 rx_coalesce_usecs_low; + __u32 rx_max_coalesced_frames_low; + __u32 tx_coalesce_usecs_low; + __u32 tx_max_coalesced_frames_low; /* When the packet rate is below pkt_rate_high but above * pkt_rate_low (both measured in packets per second) the @@ -162,43 +162,43 @@ struct ethtool_coalesce { * is above pkt_rate_high, the {rx,tx}_*_high parameters are * used. */ - u32 pkt_rate_high; - u32 rx_coalesce_usecs_high; - u32 rx_max_coalesced_frames_high; - u32 tx_coalesce_usecs_high; - u32 tx_max_coalesced_frames_high; + __u32 pkt_rate_high; + __u32 rx_coalesce_usecs_high; + __u32 rx_max_coalesced_frames_high; + __u32 tx_coalesce_usecs_high; + __u32 tx_max_coalesced_frames_high; /* How often to do adaptive coalescing packet rate sampling, * measured in seconds. Must not be zero. */ - u32 rate_sample_interval; + __u32 rate_sample_interval; }; /* for configuring RX/TX ring parameters */ struct ethtool_ringparam { - u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ + __u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ /* Read only attributes. These indicate the maximum number * of pending RX/TX ring entries the driver will allow the * user to set. */ - u32 rx_max_pending; - u32 rx_mini_max_pending; - u32 rx_jumbo_max_pending; - u32 tx_max_pending; + __u32 rx_max_pending; + __u32 rx_mini_max_pending; + __u32 rx_jumbo_max_pending; + __u32 tx_max_pending; /* Values changeable by the user. The valid values are * in the range 1 to the "*_max_pending" counterpart above. */ - u32 rx_pending; - u32 rx_mini_pending; - u32 rx_jumbo_pending; - u32 tx_pending; + __u32 rx_pending; + __u32 rx_mini_pending; + __u32 rx_jumbo_pending; + __u32 tx_pending; }; /* for configuring link flow control parameters */ struct ethtool_pauseparam { - u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ + __u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ /* If the link is being auto-negotiated (via ethtool_cmd.autoneg * being true) the user may set 'autonet' here non-zero to have the @@ -210,9 +210,9 @@ struct ethtool_pauseparam { * then {rx,tx}_pause force the driver to use/not-use pause * flow control. */ - u32 autoneg; - u32 rx_pause; - u32 tx_pause; + __u32 autoneg; + __u32 rx_pause; + __u32 tx_pause; }; #define ETH_GSTRING_LEN 32 @@ -223,10 +223,10 @@ enum ethtool_stringset { /* for passing string sets for data tagging */ struct ethtool_gstrings { - u32 cmd; /* ETHTOOL_GSTRINGS */ - u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ - u32 len; /* number of strings in the string set */ - u8 data[0]; + __u32 cmd; /* ETHTOOL_GSTRINGS */ + __u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ + __u32 len; /* number of strings in the string set */ + __u8 data[0]; }; enum ethtool_test_flags { @@ -236,26 +236,28 @@ enum ethtool_test_flags { /* for requesting NIC test and getting results*/ struct ethtool_test { - u32 cmd; /* ETHTOOL_TEST */ - u32 flags; /* ETH_TEST_FL_xxx */ - u32 reserved; - u32 len; /* result length, in number of u64 elements */ - u64 data[0]; + __u32 cmd; /* ETHTOOL_TEST */ + __u32 flags; /* ETH_TEST_FL_xxx */ + __u32 reserved; + __u32 len; /* result length, in number of u64 elements */ + __u64 data[0]; }; /* for dumping NIC-specific statistics */ struct ethtool_stats { - u32 cmd; /* ETHTOOL_GSTATS */ - u32 n_stats; /* number of u64's being returned */ - u64 data[0]; + __u32 cmd; /* ETHTOOL_GSTATS */ + __u32 n_stats; /* number of u64's being returned */ + __u64 data[0]; }; struct ethtool_perm_addr { - u32 cmd; /* ETHTOOL_GPERMADDR */ - u32 size; - u8 data[0]; + __u32 cmd; /* ETHTOOL_GPERMADDR */ + __u32 size; + __u8 data[0]; }; +#ifdef __KERNEL__ + struct net_device; /* Some generic methods drivers may use in their ethtool_ops */ @@ -371,6 +373,7 @@ struct ethtool_ops { u32 (*get_ufo)(struct net_device *); int (*set_ufo)(struct net_device *, u32); }; +#endif /* __KERNEL__ */ /* CMDs currently supported */ #define ETHTOOL_GSET 0x00000001 /* Get settings. */ @@ -408,6 +411,8 @@ #define ETHTOOL_STSO 0x0000001f /* Set #define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ #define ETHTOOL_GUFO 0x00000021 /* Get UFO enable (ethtool_value) */ #define ETHTOOL_SUFO 0x00000022 /* Set UFO enable (ethtool_value) */ +#define ETHTOOL_GGSO 0x00000023 /* Get GSO enable (ethtool_value) */ +#define ETHTOOL_SGSO 0x00000024 /* Set GSO enable (ethtool_value) */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 1e4bdfc..84cfa8b 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -1,6 +1,6 @@ /* * include/linux/eventpoll.h ( Efficent event polling implementation ) - * Copyright (C) 2001,...,2003 Davide Libenzi + * Copyright (C) 2001,...,2006 Davide Libenzi * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/include/linux/ext2_fs.h b/include/linux/ext2_fs.h index f7bd1c7..facf34e 100644 --- a/include/linux/ext2_fs.h +++ b/include/linux/ext2_fs.h @@ -17,7 +17,6 @@ #ifndef _LINUX_EXT2_FS_H #define _LINUX_EXT2_FS_H #include -#include /* * The second extended filesystem constants/structures @@ -70,6 +69,7 @@ #define EXT2_GOOD_OLD_FIRST_INO 11 #define EXT2_SUPER_MAGIC 0xEF53 #ifdef __KERNEL__ +#include static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb) { return sb->s_fs_info; diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 3ade6a4..5607e64 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -17,11 +17,6 @@ #ifndef _LINUX_EXT3_FS_H #define _LINUX_EXT3_FS_H #include -#include -#include - - -struct statfs; /* * The second extended filesystem constants/structures @@ -487,6 +482,8 @@ struct ext3_super_block { }; #ifdef __KERNEL__ +#include +#include static inline struct ext3_sb_info * EXT3_SB(struct super_block *sb) { return sb->s_fs_info; @@ -664,6 +661,8 @@ #define DX_HASH_LEGACY 0 #define DX_HASH_HALF_MD4 1 #define DX_HASH_TEA 2 +#ifdef __KERNEL__ + /* hash info structure used by the directory hash */ struct dx_hash_info { @@ -675,7 +674,6 @@ struct dx_hash_info #define EXT3_HTREE_EOF 0x7fffffff -#ifdef __KERNEL__ /* * Control parameters used by ext3_htree_next_block */ @@ -712,6 +710,14 @@ struct dir_private_info { __u32 next_hash; }; +/* calculate the first block number of the group */ +static inline ext3_fsblk_t +ext3_group_first_block_no(struct super_block *sb, unsigned long group_no) +{ + return group_no * (ext3_fsblk_t)EXT3_BLOCKS_PER_GROUP(sb) + + le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block); +} + /* * Special error return code only used by dx_probe() and its callers. */ @@ -732,14 +738,16 @@ # define NORET_AND noreturn, /* balloc.c */ extern int ext3_bg_has_super(struct super_block *sb, int group); extern unsigned long ext3_bg_num_gdb(struct super_block *sb, int group); -extern int ext3_new_block (handle_t *, struct inode *, unsigned long, int *); -extern int ext3_new_blocks (handle_t *, struct inode *, unsigned long, - unsigned long *, int *); -extern void ext3_free_blocks (handle_t *, struct inode *, unsigned long, - unsigned long); -extern void ext3_free_blocks_sb (handle_t *, struct super_block *, - unsigned long, unsigned long, int *); -extern unsigned long ext3_count_free_blocks (struct super_block *); +extern ext3_fsblk_t ext3_new_block (handle_t *handle, struct inode *inode, + ext3_fsblk_t goal, int *errp); +extern ext3_fsblk_t ext3_new_blocks (handle_t *handle, struct inode *inode, + ext3_fsblk_t goal, unsigned long *count, int *errp); +extern void ext3_free_blocks (handle_t *handle, struct inode *inode, + ext3_fsblk_t block, unsigned long count); +extern void ext3_free_blocks_sb (handle_t *handle, struct super_block *sb, + ext3_fsblk_t block, unsigned long count, + unsigned long *pdquot_freed_blocks); +extern ext3_fsblk_t ext3_count_free_blocks (struct super_block *); extern void ext3_check_blocks_bitmap (struct super_block *); extern struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb, unsigned int block_group, @@ -775,7 +783,8 @@ extern unsigned long ext3_count_free (st /* inode.c */ -int ext3_forget(handle_t *, int, struct inode *, struct buffer_head *, int); +int ext3_forget(handle_t *handle, int is_metadata, struct inode *inode, + struct buffer_head *bh, ext3_fsblk_t blocknr); struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *); struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *); int ext3_get_blocks_handle(handle_t *handle, struct inode *inode, @@ -810,7 +819,7 @@ extern int ext3_group_add(struct super_b struct ext3_new_group_data *input); extern int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, - unsigned long n_blocks_count); + ext3_fsblk_t n_blocks_count); /* super.c */ extern void ext3_error (struct super_block *, const char *, const char *, ...) diff --git a/include/linux/ext3_fs_i.h b/include/linux/ext3_fs_i.h index 7abf901..2f18b95 100644 --- a/include/linux/ext3_fs_i.h +++ b/include/linux/ext3_fs_i.h @@ -21,9 +21,17 @@ #include #include #include +/* data type for block offset of block group */ +typedef int ext3_grpblk_t; + +/* data type for filesystem-wide blocks number */ +typedef unsigned long ext3_fsblk_t; + +#define E3FSBLK "%lu" + struct ext3_reserve_window { - __u32 _rsv_start; /* First byte reserved */ - __u32 _rsv_end; /* Last byte reserved or 0 */ + ext3_fsblk_t _rsv_start; /* First byte reserved */ + ext3_fsblk_t _rsv_end; /* Last byte reserved or 0 */ }; struct ext3_reserve_window_node { @@ -50,7 +58,7 @@ struct ext3_block_alloc_info { * allocated to this file. This give us the goal (target) for the next * allocation when we detect linearly ascending requests. */ - __u32 last_alloc_physical_block; + ext3_fsblk_t last_alloc_physical_block; }; #define rsv_start rsv_window._rsv_start @@ -67,7 +75,7 @@ #ifdef EXT3_FRAGMENTS __u8 i_frag_no; __u8 i_frag_size; #endif - __u32 i_file_acl; + ext3_fsblk_t i_file_acl; __u32 i_dir_acl; __u32 i_dtime; diff --git a/include/linux/fb.h b/include/linux/fb.h index 315d897..ffefeee 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -1,6 +1,7 @@ #ifndef _LINUX_FB_H #define _LINUX_FB_H +#include #include /* Definitions of frame buffers */ @@ -366,6 +367,12 @@ struct fb_cursor { struct fb_image image; /* Cursor image */ }; +#ifdef CONFIG_FB_BACKLIGHT +/* Settings for the generic backlight code */ +#define FB_BACKLIGHT_LEVELS 128 +#define FB_BACKLIGHT_MAX 0xFF +#endif + #ifdef __KERNEL__ #include @@ -373,7 +380,6 @@ #include #include #include #include -#include #include #include #include @@ -497,23 +503,19 @@ #define FB_EVENT_RESUME 0x03 #define FB_EVENT_MODE_DELETE 0x04 /* A driver registered itself */ #define FB_EVENT_FB_REGISTERED 0x05 +/* A driver unregistered itself */ +#define FB_EVENT_FB_UNREGISTERED 0x06 /* CONSOLE-SPECIFIC: get console to framebuffer mapping */ -#define FB_EVENT_GET_CONSOLE_MAP 0x06 +#define FB_EVENT_GET_CONSOLE_MAP 0x07 /* CONSOLE-SPECIFIC: set console to framebuffer mapping */ -#define FB_EVENT_SET_CONSOLE_MAP 0x07 +#define FB_EVENT_SET_CONSOLE_MAP 0x08 /* A display blank is requested */ -#define FB_EVENT_BLANK 0x08 +#define FB_EVENT_BLANK 0x09 /* Private modelist is to be replaced */ -#define FB_EVENT_NEW_MODELIST 0x09 +#define FB_EVENT_NEW_MODELIST 0x0A /* The resolution of the passed in fb_info about to change and all vc's should be changed */ -#define FB_EVENT_MODE_CHANGE_ALL 0x0A -/* CONSOLE-SPECIFIC: set console rotation */ -#define FB_EVENT_SET_CON_ROTATE 0x0B -/* CONSOLE-SPECIFIC: get console rotation */ -#define FB_EVENT_GET_CON_ROTATE 0x0C -/* CONSOLE-SPECIFIC: rotate all consoles */ -#define FB_EVENT_SET_CON_ROTATE_ALL 0x0D +#define FB_EVENT_MODE_CHANGE_ALL 0x0B struct fb_event { struct fb_info *info; @@ -555,7 +557,7 @@ struct fb_pixmap { * Frame buffer operations * * LOCKING NOTE: those functions must _ALL_ be called with the console - * semaphore held, this is the only suitable locking mecanism we have + * semaphore held, this is the only suitable locking mechanism we have * in 2.6. Some may be called at interrupt time at this point though. */ @@ -756,6 +758,21 @@ struct fb_info { struct fb_cmap cmap; /* Current cmap */ struct list_head modelist; /* mode list */ struct fb_videomode *mode; /* current mode */ + +#ifdef CONFIG_FB_BACKLIGHT + /* Lock ordering: + * bl_mutex (protects bl_dev and bl_curve) + * bl_dev->sem (backlight class) + */ + struct mutex bl_mutex; + + /* assigned backlight device */ + struct backlight_device *bl_dev; + + /* Backlight level curve */ + u8 bl_curve[FB_BACKLIGHT_LEVELS]; +#endif + struct fb_ops *fbops; struct device *device; struct class_device *class_device; /* sysfs per device attrs */ @@ -870,7 +887,6 @@ extern int fb_get_color_depth(struct fb_ struct fb_fix_screeninfo *fix); extern int fb_get_options(char *name, char **option); extern int fb_new_modelist(struct fb_info *info); -extern int fb_con_duit(struct fb_info *info, int event, void *data); extern struct fb_info *registered_fb[FB_MAX]; extern int num_registered_fb; @@ -895,6 +911,7 @@ extern struct fb_info *framebuffer_alloc extern void framebuffer_release(struct fb_info *info); extern int fb_init_class_device(struct fb_info *fb_info); extern void fb_cleanup_class_device(struct fb_info *head); +extern void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max); /* drivers/video/fbmon.c */ #define FB_MAXTIMINGS 0 diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h index c52a637..996f561 100644 --- a/include/linux/fcntl.h +++ b/include/linux/fcntl.h @@ -29,6 +29,7 @@ #define AT_FDCWD -100 /* Special val #define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */ #define AT_REMOVEDIR 0x200 /* Remove directory instead of unlinking file. */ +#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */ #ifdef __KERNEL__ diff --git a/include/linux/fs.h b/include/linux/fs.h index f813bc8..43aef9b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -6,7 +6,6 @@ #define _LINUX_FS_H * structures etc. */ -#include #include #include @@ -377,7 +376,8 @@ struct address_space_operations { struct page* (*get_xip_page)(struct address_space *, sector_t, int); /* migrate the contents of a page to the specified target */ - int (*migratepage) (struct page *, struct page *); + int (*migratepage) (struct address_space *, + struct page *, struct page *); }; struct backing_dev_info; @@ -392,7 +392,7 @@ struct address_space { unsigned int truncate_count; /* Cover race condition with truncate */ unsigned long nrpages; /* number of total pages */ pgoff_t writeback_index;/* writeback starts here */ - struct address_space_operations *a_ops; /* methods */ + const struct address_space_operations *a_ops; /* methods */ unsigned long flags; /* error bits/gfp mask */ struct backing_dev_info *backing_dev_info; /* device readahead, etc */ spinlock_t private_lock; /* for use by the address_space */ @@ -436,6 +436,21 @@ #endif }; /* + * bdev->bd_mutex nesting subclasses for the lock validator: + * + * 0: normal + * 1: 'whole' + * 2: 'partition' + */ +enum bdev_bd_mutex_lock_class +{ + BD_MUTEX_NORMAL, + BD_MUTEX_WHOLE, + BD_MUTEX_PARTITION +}; + + +/* * Radix-tree tags, for tagging dirty and writeback pages within the pagecache * radix trees */ @@ -543,6 +558,25 @@ #endif }; /* + * inode->i_mutex nesting subclasses for the lock validator: + * + * 0: the object of the current VFS operation + * 1: parent + * 2: child/target + * 3: quota file + * + * The locking order between these classes is + * parent -> child -> normal -> quota + */ +enum inode_i_mutex_lock_class +{ + I_MUTEX_NORMAL, + I_MUTEX_PARENT, + I_MUTEX_CHILD, + I_MUTEX_QUOTA +}; + +/* * NOTE: in a 32bit arch with a preemptable kernel and * an UP compile the i_size_read/write must be atomic * with respect to the local cpu (unlike with preempt disabled), @@ -682,7 +716,9 @@ #endif #define FL_POSIX 1 #define FL_FLOCK 2 #define FL_ACCESS 8 /* not trying to lock, just looking */ +#define FL_EXISTS 16 /* when unlocking, test for existence */ #define FL_LEASE 32 /* lease held on this file */ +#define FL_CLOSE 64 /* unlock on close */ #define FL_SLEEP 128 /* A blocking lock */ /* @@ -775,7 +811,6 @@ extern int posix_lock_file_conf(struct f extern int posix_lock_file(struct file *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); extern int posix_unblock_lock(struct file *, struct file_lock *); -extern int posix_locks_deadlock(struct file_lock *, struct file_lock *); extern int flock_lock_file_wait(struct file *filp, struct file_lock *fl); extern int __break_lease(struct inode *inode, unsigned int flags); extern void lease_get_mtime(struct inode *, struct timespec *time); @@ -783,7 +818,6 @@ extern int setlease(struct file *, long, extern int lease_modify(struct file_lock **, int); extern int lock_may_read(struct inode *, loff_t start, unsigned long count); extern int lock_may_write(struct inode *, loff_t start, unsigned long count); -extern void steal_locks(fl_owner_t from); struct fasync_struct { int magic; @@ -1026,7 +1060,7 @@ struct file_operations { long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); - int (*flush) (struct file *); + int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); @@ -1098,10 +1132,10 @@ struct super_operations { int (*sync_fs)(struct super_block *sb, int wait); void (*write_super_lockfs) (struct super_block *); void (*unlockfs) (struct super_block *); - int (*statfs) (struct super_block *, struct kstatfs *); + int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); void (*clear_inode) (struct inode *); - void (*umount_begin) (struct super_block *); + void (*umount_begin) (struct vfsmount *, int); int (*show_options)(struct seq_file *, struct vfsmount *); int (*show_stats)(struct seq_file *, struct vfsmount *); @@ -1271,23 +1305,28 @@ find_exported_dentry(struct super_block struct file_system_type { const char *name; int fs_flags; - struct super_block *(*get_sb) (struct file_system_type *, int, - const char *, void *); + int (*get_sb) (struct file_system_type *, int, + const char *, void *, struct vfsmount *); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; struct list_head fs_supers; + struct lock_class_key s_lock_key; + struct lock_class_key s_umount_key; }; -struct super_block *get_sb_bdev(struct file_system_type *fs_type, +extern int get_sb_bdev(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, - int (*fill_super)(struct super_block *, void *, int)); -struct super_block *get_sb_single(struct file_system_type *fs_type, + int (*fill_super)(struct super_block *, void *, int), + struct vfsmount *mnt); +extern int get_sb_single(struct file_system_type *fs_type, int flags, void *data, - int (*fill_super)(struct super_block *, void *, int)); -struct super_block *get_sb_nodev(struct file_system_type *fs_type, + int (*fill_super)(struct super_block *, void *, int), + struct vfsmount *mnt); +extern int get_sb_nodev(struct file_system_type *fs_type, int flags, void *data, - int (*fill_super)(struct super_block *, void *, int)); + int (*fill_super)(struct super_block *, void *, int), + struct vfsmount *mnt); void generic_shutdown_super(struct super_block *sb); void kill_block_super(struct super_block *sb); void kill_anon_super(struct super_block *sb); @@ -1298,8 +1337,10 @@ struct super_block *sget(struct file_sys int (*test)(struct super_block *,void *), int (*set)(struct super_block *,void *), void *data); -struct super_block *get_sb_pseudo(struct file_system_type *, char *, - struct super_operations *ops, unsigned long); +extern int get_sb_pseudo(struct file_system_type *, char *, + struct super_operations *ops, unsigned long, + struct vfsmount *mnt); +extern int simple_set_mnt(struct vfsmount *mnt, struct super_block *sb); int __put_super(struct super_block *sb); int __put_super_and_need_restart(struct super_block *sb); void unnamed_dev_init(void); @@ -1322,7 +1363,7 @@ extern struct vfsmount *copy_tree(struct extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, struct vfsmount *); -extern int vfs_statfs(struct super_block *, struct kstatfs *); +extern int vfs_statfs(struct dentry *, struct kstatfs *); /* /sys/fs */ extern struct subsystem fs_subsys; @@ -1400,8 +1441,9 @@ extern void bd_set_size(struct block_dev extern void bd_forget(struct inode *inode); extern void bdput(struct block_device *); extern struct block_device *open_by_devnum(dev_t, unsigned); +extern struct block_device *open_partition_by_devnum(dev_t, unsigned); extern const struct file_operations def_blk_fops; -extern struct address_space_operations def_blk_aops; +extern const struct address_space_operations def_blk_aops; extern const struct file_operations def_chr_fops; extern const struct file_operations bad_sock_fops; extern const struct file_operations def_fifo_fops; @@ -1410,6 +1452,7 @@ extern int blkdev_ioctl(struct inode *, extern long compat_blkdev_ioctl(struct file *, unsigned, unsigned long); extern int blkdev_get(struct block_device *, mode_t, unsigned); extern int blkdev_put(struct block_device *); +extern int blkdev_put_partition(struct block_device *); extern int bd_claim(struct block_device *, void *); extern void bd_release(struct block_device *); #ifdef CONFIG_SYSFS @@ -1743,7 +1786,7 @@ extern int dcache_dir_close(struct inode extern loff_t dcache_dir_lseek(struct file *, loff_t, int); extern int dcache_readdir(struct file *, void *, filldir_t); extern int simple_getattr(struct vfsmount *, struct dentry *, struct kstat *); -extern int simple_statfs(struct super_block *, struct kstatfs *); +extern int simple_statfs(struct dentry *, struct kstatfs *); extern int simple_link(struct dentry *, struct inode *, struct dentry *); extern int simple_unlink(struct inode *, struct dentry *); extern int simple_rmdir(struct inode *, struct dentry *); @@ -1763,13 +1806,14 @@ extern struct inode_operations simple_di struct tree_descr { char *name; const struct file_operations *ops; int mode; }; struct dentry *d_alloc_name(struct dentry *, const char *); extern int simple_fill_super(struct super_block *, int, struct tree_descr *); -extern int simple_pin_fs(char *name, struct vfsmount **mount, int *count); +extern int simple_pin_fs(struct file_system_type *, struct vfsmount **mount, int *count); extern void simple_release_fs(struct vfsmount **mount, int *count); extern ssize_t simple_read_from_buffer(void __user *, size_t, loff_t *, const void *, size_t); #ifdef CONFIG_MIGRATION -extern int buffer_migrate_page(struct page *, struct page *); +extern int buffer_migrate_page(struct address_space *, + struct page *, struct page *); #else #define buffer_migrate_page NULL #endif diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 11438ef..cc5dec7 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -54,19 +54,20 @@ static inline void fsnotify_move(struct if (isdir) isdir = IN_ISDIR; - inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name); - inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name); + inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name, + source); + inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name, + source); if (target) { - inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL); + inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL); inotify_inode_is_dead(target); } if (source) { - inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL); + inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL); } - audit_inode_child(old_name, source, old_dir->i_ino); - audit_inode_child(new_name, target, new_dir->i_ino); + audit_inode_child(new_name, source, new_dir->i_ino); } /* @@ -85,7 +86,7 @@ static inline void fsnotify_nameremove(s */ static inline void fsnotify_inoderemove(struct inode *inode) { - inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL); + inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL); inotify_inode_is_dead(inode); } @@ -95,7 +96,8 @@ static inline void fsnotify_inoderemove( static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { inode_dir_notify(inode, DN_CREATE); - inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name); + inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name, + dentry->d_inode); audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino); } @@ -106,7 +108,7 @@ static inline void fsnotify_mkdir(struct { inode_dir_notify(inode, DN_CREATE); inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, - dentry->d_name.name); + dentry->d_name.name, dentry->d_inode); audit_inode_child(dentry->d_name.name, dentry->d_inode, inode->i_ino); } @@ -123,7 +125,7 @@ static inline void fsnotify_access(struc dnotify_parent(dentry, DN_ACCESS); inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); - inotify_inode_queue_event(inode, mask, 0, NULL); + inotify_inode_queue_event(inode, mask, 0, NULL, NULL); } /* @@ -139,7 +141,7 @@ static inline void fsnotify_modify(struc dnotify_parent(dentry, DN_MODIFY); inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); - inotify_inode_queue_event(inode, mask, 0, NULL); + inotify_inode_queue_event(inode, mask, 0, NULL, NULL); } /* @@ -154,7 +156,7 @@ static inline void fsnotify_open(struct mask |= IN_ISDIR; inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); - inotify_inode_queue_event(inode, mask, 0, NULL); + inotify_inode_queue_event(inode, mask, 0, NULL, NULL); } /* @@ -172,7 +174,7 @@ static inline void fsnotify_close(struct mask |= IN_ISDIR; inotify_dentry_parent_queue_event(dentry, mask, 0, name); - inotify_inode_queue_event(inode, mask, 0, NULL); + inotify_inode_queue_event(inode, mask, 0, NULL, NULL); } /* @@ -187,7 +189,7 @@ static inline void fsnotify_xattr(struct mask |= IN_ISDIR; inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); - inotify_inode_queue_event(inode, mask, 0, NULL); + inotify_inode_queue_event(inode, mask, 0, NULL, NULL); } /* @@ -234,7 +236,7 @@ static inline void fsnotify_change(struc if (in_mask) { if (S_ISDIR(inode->i_mode)) in_mask |= IN_ISDIR; - inotify_inode_queue_event(inode, in_mask, 0, NULL); + inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL); inotify_dentry_parent_queue_event(dentry, in_mask, 0, dentry->d_name.name); } diff --git a/include/linux/ftape.h b/include/linux/ftape.h index 72faeec..7e7038c 100644 --- a/include/linux/ftape.h +++ b/include/linux/ftape.h @@ -35,7 +35,6 @@ #include #include #endif #include -#include #include #define FT_SECTOR(x) (x+1) /* sector offset into real sector */ diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 5425b60..9fc48a6 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -1,6 +1,6 @@ /* FUSE: Filesystem in Userspace - Copyright (C) 2001-2005 Miklos Szeredi + Copyright (C) 2001-2006 Miklos Szeredi This program can be distributed under the terms of the GNU GPL. See the file COPYING. @@ -9,18 +9,19 @@ /* This file defines the kernel interface of FUSE */ #include +#include /** Version number of this interface */ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 6 +#define FUSE_KERNEL_MINOR_VERSION 7 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 /** The major number of the fuse character device */ -#define FUSE_MAJOR 10 +#define FUSE_MAJOR MISC_MAJOR /** The minor number of the fuse character device */ #define FUSE_MINOR 229 @@ -58,6 +59,13 @@ struct fuse_kstatfs { __u32 spare[6]; }; +struct fuse_file_lock { + __u64 start; + __u64 end; + __u32 type; + __u32 pid; /* tgid */ +}; + /** * Bitmasks for fuse_setattr_in.valid */ @@ -82,6 +90,7 @@ #define FOPEN_KEEP_CACHE (1 << 1) * INIT request/reply flags */ #define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) enum fuse_opcode { FUSE_LOOKUP = 1, @@ -112,8 +121,12 @@ enum fuse_opcode { FUSE_READDIR = 28, FUSE_RELEASEDIR = 29, FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, FUSE_ACCESS = 34, - FUSE_CREATE = 35 + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, }; /* The read buffer is required to be at least 8k, but may be much larger */ @@ -199,6 +212,7 @@ struct fuse_flush_in { __u64 fh; __u32 flush_flags; __u32 padding; + __u64 lock_owner; }; struct fuse_read_in { @@ -247,6 +261,16 @@ struct fuse_getxattr_out { __u32 padding; }; +struct fuse_lk_in { + __u64 fh; + __u64 owner; + struct fuse_file_lock lk; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + struct fuse_access_in { __u32 mask; __u32 padding; @@ -268,6 +292,10 @@ struct fuse_init_out { __u32 max_write; }; +struct fuse_interrupt_in { + __u64 unique; +}; + struct fuse_in_header { __u32 len; __u32 opcode; diff --git a/include/linux/futex.h b/include/linux/futex.h index 966a5b3..34c3a21 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h @@ -12,6 +12,9 @@ #define FUTEX_FD 2 #define FUTEX_REQUEUE 3 #define FUTEX_CMP_REQUEUE 4 #define FUTEX_WAKE_OP 5 +#define FUTEX_LOCK_PI 6 +#define FUTEX_UNLOCK_PI 7 +#define FUTEX_TRYLOCK_PI 8 /* * Support for robust futexes: the kernel cleans up held futexes at @@ -90,18 +93,21 @@ #define FUTEX_TID_MASK 0x3fffffff */ #define ROBUST_LIST_LIMIT 2048 -long do_futex(unsigned long uaddr, int op, int val, - unsigned long timeout, unsigned long uaddr2, int val2, - int val3); +long do_futex(u32 __user *uaddr, int op, u32 val, unsigned long timeout, + u32 __user *uaddr2, u32 val2, u32 val3); extern int handle_futex_death(u32 __user *uaddr, struct task_struct *curr); #ifdef CONFIG_FUTEX extern void exit_robust_list(struct task_struct *curr); +extern void exit_pi_state_list(struct task_struct *curr); #else static inline void exit_robust_list(struct task_struct *curr) { } +static inline void exit_pi_state_list(struct task_struct *curr) +{ +} #endif #define FUTEX_OP_SET 0 /* *(int *)UADDR2 = OPARG; */ diff --git a/include/linux/gameport.h b/include/linux/gameport.h index 71e7b28..2cdba0c 100644 --- a/include/linux/gameport.h +++ b/include/linux/gameport.h @@ -9,6 +9,7 @@ #define _GAMEPORT_H * the Free Software Foundation. */ +#ifdef __KERNEL__ #include #include #include @@ -154,6 +155,8 @@ static inline void gameport_register_dri void gameport_unregister_driver(struct gameport_driver *drv); +#endif /* __KERNEL__ */ + #define GAMEPORT_MODE_DISABLED 0 #define GAMEPORT_MODE_RAW 1 #define GAMEPORT_MODE_COOKED 2 @@ -169,6 +172,8 @@ #define GAMEPORT_ID_VENDOR_THRUSTMASTER #define GAMEPORT_ID_VENDOR_GRAVIS 0x0009 #define GAMEPORT_ID_VENDOR_GUILLEMOT 0x000a +#ifdef __KERNEL__ + static inline void gameport_trigger(struct gameport *gameport) { if (gameport->trigger) @@ -219,4 +224,5 @@ static inline void gameport_set_poll_int void gameport_start_polling(struct gameport *gameport); void gameport_stop_polling(struct gameport *gameport); +#endif /* __KERNEL__ */ #endif diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h index 7fd0576..690c428 100644 --- a/include/linux/genalloc.h +++ b/include/linux/genalloc.h @@ -4,37 +4,32 @@ * Uses for this includes on-device special memory, uncached memory * etc. * - * This code is based on the buddy allocator found in the sym53c8xx_2 - * driver, adapted for general purpose use. - * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ -#include -#define ALLOC_MIN_SHIFT 5 /* 32 bytes minimum */ /* - * Link between free memory chunks of a given size. + * General purpose special memory pool descriptor. */ -struct gen_pool_link { - struct gen_pool_link *next; +struct gen_pool { + rwlock_t lock; + struct list_head chunks; /* list of chunks in this pool */ + int min_alloc_order; /* minimum allocation order */ }; /* - * Memory pool descriptor. + * General purpose special memory pool chunk descriptor. */ -struct gen_pool { +struct gen_pool_chunk { spinlock_t lock; - unsigned long (*get_new_chunk)(struct gen_pool *); - struct gen_pool *next; - struct gen_pool_link *h; - unsigned long private; - int max_chunk_shift; + struct list_head next_chunk; /* next chunk in pool */ + unsigned long start_addr; /* starting address of memory chunk */ + unsigned long end_addr; /* ending address of memory chunk */ + unsigned long bits[0]; /* bitmap for allocating memory chunk */ }; -unsigned long gen_pool_alloc(struct gen_pool *poolp, int size); -void gen_pool_free(struct gen_pool *mp, unsigned long ptr, int size); -struct gen_pool *gen_pool_create(int nr_chunks, int max_chunk_shift, - unsigned long (*fp)(struct gen_pool *), - unsigned long data); +extern struct gen_pool *gen_pool_create(int, int); +extern int gen_pool_add(struct gen_pool *, unsigned long, size_t, int); +extern unsigned long gen_pool_alloc(struct gen_pool *, size_t); +extern void gen_pool_free(struct gen_pool *, unsigned long, size_t); diff --git a/include/linux/generic_serial.h b/include/linux/generic_serial.h index 652611a..e253845 100644 --- a/include/linux/generic_serial.h +++ b/include/linux/generic_serial.h @@ -12,6 +12,7 @@ #ifndef GENERIC_SERIAL_H #define GENERIC_SERIAL_H +#ifdef __KERNEL__ #include struct real_driver { @@ -54,6 +55,7 @@ struct gs_port { spinlock_t driver_lock; }; +#endif /* __KERNEL__ */ /* Flags */ /* Warning: serial.h defines some ASYNC_ flags, they say they are "only" @@ -75,7 +77,7 @@ #define GS_DEBUG_CLOSE 0x00000010 #define GS_DEBUG_FLOW 0x00000020 #define GS_DEBUG_WRITE 0x00000040 - +#ifdef __KERNEL__ void gs_put_char(struct tty_struct *tty, unsigned char ch); int gs_write(struct tty_struct *tty, const unsigned char *buf, int count); @@ -94,5 +96,5 @@ int gs_init_port(struct gs_port *port); int gs_setserial(struct gs_port *port, struct serial_struct __user *sp); int gs_getserial(struct gs_port *port, struct serial_struct __user *sp); void gs_got_break(struct gs_port *port); - +#endif /* __KERNEL__ */ #endif diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 2ef845b..e4af57e 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -9,13 +9,7 @@ #define _LINUX_GENHD_H * */ -#include #include -#include -#include -#include -#include -#include enum { /* These three have identical behaviour; use the second one if DOS FDISK gets @@ -61,6 +55,12 @@ struct partition { #endif #ifdef __KERNEL__ +#include +#include +#include +#include +#include + struct partition { unsigned char boot_ind; /* 0x80 - active */ unsigned char head; /* starting head */ @@ -112,8 +112,6 @@ struct gendisk { sector_t capacity; int flags; - char devfs_name[64]; /* devfs crap */ - int number; /* more of the same */ struct device *driverfs_dev; struct kobject kobj; struct kobject *holder_dir; diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 3ac4529..cc9e608 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -4,7 +4,6 @@ #define __LINUX_GFP_H #include #include #include -#include struct vm_area_struct; diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index eab5370..50d8b57 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h @@ -1,9 +1,9 @@ #ifndef LINUX_HARDIRQ_H #define LINUX_HARDIRQ_H -#include #include #include +#include #include #include @@ -87,9 +87,6 @@ #else # define synchronize_irq(irq) barrier() #endif -#define nmi_enter() irq_enter() -#define nmi_exit() sub_preempt_count(HARDIRQ_OFFSET) - struct task_struct; #ifndef CONFIG_VIRT_CPU_ACCOUNTING @@ -98,12 +95,35 @@ static inline void account_system_vtime( } #endif +/* + * It is safe to do non-atomic ops on ->hardirq_context, + * because NMI handlers may not preempt and the ops are + * always balanced, so the interrupted value of ->hardirq_context + * will always be restored. + */ #define irq_enter() \ do { \ account_system_vtime(current); \ add_preempt_count(HARDIRQ_OFFSET); \ + trace_hardirq_enter(); \ + } while (0) + +/* + * Exit irq context without processing softirqs: + */ +#define __irq_exit() \ + do { \ + trace_hardirq_exit(); \ + account_system_vtime(current); \ + sub_preempt_count(HARDIRQ_OFFSET); \ } while (0) +/* + * Exit irq context and process softirqs if needed: + */ extern void irq_exit(void); +#define nmi_enter() do { lockdep_off(); irq_enter(); } while (0) +#define nmi_exit() do { __irq_exit(); lockdep_on(); } while (0) + #endif /* LINUX_HARDIRQ_H */ diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index df695e9..4513f9e 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -188,7 +188,7 @@ int hdlc_x25_ioctl(struct net_device *de int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); /* Must be used by hardware driver on module startup/exit */ -int register_hdlc_device(struct net_device *dev); +#define register_hdlc_device(dev) register_netdev(dev) void unregister_hdlc_device(struct net_device *dev); struct net_device *alloc_hdlcdev(void *priv); diff --git a/include/linux/hdlc/Kbuild b/include/linux/hdlc/Kbuild new file mode 100644 index 0000000..1fb2644 --- /dev/null +++ b/include/linux/hdlc/Kbuild @@ -0,0 +1 @@ +header-y += ioctl.h diff --git a/include/linux/highmem.h b/include/linux/highmem.h index 892c4ea..85ce7ef 100644 --- a/include/linux/highmem.h +++ b/include/linux/highmem.h @@ -1,7 +1,6 @@ #ifndef _LINUX_HIGHMEM_H #define _LINUX_HIGHMEM_H -#include #include #include diff --git a/include/linux/highuid.h b/include/linux/highuid.h index 53ecac3..434e562 100644 --- a/include/linux/highuid.h +++ b/include/linux/highuid.h @@ -1,7 +1,6 @@ #ifndef _LINUX_HIGHUID_H #define _LINUX_HIGHUID_H -#include #include /* diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 306acf1..e4bccbc 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -40,7 +40,6 @@ struct hrtimer_base; /** * struct hrtimer - the basic hrtimer structure - * * @node: red black tree node for time ordered insertion * @expires: the absolute expiry time in the hrtimers internal * representation. The time is related to the clock on @@ -59,7 +58,6 @@ struct hrtimer { /** * struct hrtimer_sleeper - simple sleeper structure - * * @timer: embedded timer structure * @task: task to wake up * @@ -72,7 +70,6 @@ struct hrtimer_sleeper { /** * struct hrtimer_base - the timer base for a specific clock - * * @index: clock type index for per_cpu support when moving a timer * to a base on another cpu. * @lock: lock protecting the base and associated timers @@ -94,6 +91,7 @@ struct hrtimer_base { ktime_t (*get_softirq_time)(void); struct hrtimer *curr_timer; ktime_t softirq_time; + struct lock_class_key lock_key; }; /* @@ -127,7 +125,7 @@ #endif static inline int hrtimer_active(const struct hrtimer *timer) { - return timer->node.rb_parent != HRTIMER_INACTIVE; + return rb_parent(&timer->node) != &timer->node; } /* Forward a hrtimer so it expires after now: */ diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 4c5e610..c25a38d 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -23,6 +23,8 @@ int hugetlb_report_node_meminfo(int, cha unsigned long hugetlb_total_pages(void); int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, int write_access); +int hugetlb_reserve_pages(struct inode *inode, long from, long to); +void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed); extern unsigned long max_huge_pages; extern const unsigned long hugetlb_zero, hugetlb_infinity; @@ -139,8 +141,6 @@ struct hugetlbfs_sb_info { struct hugetlbfs_inode_info { struct shared_policy policy; - /* Protected by the (global) hugetlb_lock */ - unsigned long prereserved_hpages; struct inode vfs_inode; }; @@ -157,10 +157,6 @@ static inline struct hugetlbfs_sb_info * extern const struct file_operations hugetlbfs_file_operations; extern struct vm_operations_struct hugetlb_vm_ops; struct file *hugetlb_zero_setup(size_t); -int hugetlb_extend_reservation(struct hugetlbfs_inode_info *info, - unsigned long atleast_hpages); -void hugetlb_truncate_reservation(struct hugetlbfs_inode_info *info, - unsigned long atmost_hpages); int hugetlb_get_quota(struct address_space *mapping); void hugetlb_put_quota(struct address_space *mapping); diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h new file mode 100644 index 0000000..21ea761 --- /dev/null +++ b/include/linux/hw_random.h @@ -0,0 +1,50 @@ +/* + Hardware Random Number Generator + + Please read Documentation/hw_random.txt for details on use. + + ---------------------------------------------------------- + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. + + */ + +#ifndef LINUX_HWRANDOM_H_ +#define LINUX_HWRANDOM_H_ +#ifdef __KERNEL__ + +#include +#include + +/** + * struct hwrng - Hardware Random Number Generator driver + * @name: Unique RNG name. + * @init: Initialization callback (can be NULL). + * @cleanup: Cleanup callback (can be NULL). + * @data_present: Callback to determine if data is available + * on the RNG. If NULL, it is assumed that + * there is always data available. + * @data_read: Read data from the RNG device. + * Returns the number of lower random bytes in "data". + * Must not be NULL. + * @priv: Private data, for use by the RNG driver. + */ +struct hwrng { + const char *name; + int (*init)(struct hwrng *rng); + void (*cleanup)(struct hwrng *rng); + int (*data_present)(struct hwrng *rng); + int (*data_read)(struct hwrng *rng, u32 *data); + unsigned long priv; + + /* internal. */ + struct list_head list; +}; + +/** Register a new Hardware Random Number Generator driver. */ +extern int hwrng_register(struct hwrng *rng); +/** Unregister a Hardware Random Number Generator driver. */ +extern void hwrng_unregister(struct hwrng *rng); + +#endif /* __KERNEL__ */ +#endif /* LINUX_HWRANDOM_H_ */ diff --git a/include/linux/i2c-algo-ite.h b/include/linux/i2c-algo-ite.h index 26a8b89..0073fe9 100644 --- a/include/linux/i2c-algo-ite.h +++ b/include/linux/i2c-algo-ite.h @@ -29,7 +29,7 @@ #ifndef I2C_ALGO_ITE_H #define I2C_ALGO_ITE_H 1 -#include +#include /* Example of a sequential read request: struct i2c_iic_msg s_msg; @@ -49,6 +49,9 @@ struct i2c_iic_msg { char *buf; /* pointer to msg data */ }; +#ifdef __KERNEL__ +struct i2c_adapter; + struct i2c_algo_iic_data { void *data; /* private data for lolevel routines */ void (*setiic) (void *data, int ctl, int val); @@ -65,5 +68,5 @@ struct i2c_algo_iic_data { int i2c_iic_add_bus(struct i2c_adapter *); int i2c_iic_del_bus(struct i2c_adapter *); - +#endif /* __KERNEL__ */ #endif /* I2C_ALGO_ITE_H */ diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index c8b81f4..21338bb 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -112,6 +112,9 @@ #define I2C_DRIVERID_DS1672 81 /* Dallas #define I2C_DRIVERID_X1205 82 /* Xicor/Intersil X1205 RTC */ #define I2C_DRIVERID_PCF8563 83 /* Philips PCF8563 RTC */ #define I2C_DRIVERID_RS5C372 84 /* Ricoh RS5C372 RTC */ +#define I2C_DRIVERID_BT866 85 /* Conexant bt866 video encoder */ +#define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */ +#define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */ #define I2C_DRIVERID_I2CDEV 900 #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ diff --git a/include/linux/i2c-ocores.h b/include/linux/i2c-ocores.h new file mode 100644 index 0000000..8ed591b --- /dev/null +++ b/include/linux/i2c-ocores.h @@ -0,0 +1,19 @@ +/* + * i2c-ocores.h - definitions for the i2c-ocores interface + * + * Peter Korsgaard + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef _LINUX_I2C_OCORES_H +#define _LINUX_I2C_OCORES_H + +struct ocores_i2c_platform_data { + u32 regstep; /* distance between registers */ + u32 clock_khz; /* input clock in kHz */ +}; + +#endif /* _LINUX_I2C_OCORES_H */ diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 1635ee2..526ddc8 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -20,14 +20,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ------------------------------------------------------------------------- */ -/* With some changes from Kyösti Mälkki and +/* With some changes from Kyösti MÀlkki and Frodo Looijaard */ #ifndef _LINUX_I2C_H #define _LINUX_I2C_H -#include #include +#ifdef __KERNEL__ +#include #include #include #include /* for struct device */ @@ -96,13 +97,13 @@ extern s32 i2c_smbus_write_word_data(str u8 command, u16 value); extern s32 i2c_smbus_write_block_data(struct i2c_client * client, u8 command, u8 length, - u8 *values); + const u8 *values); /* Returns the number of read bytes */ extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, u8 command, u8 *values); extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, u8 command, u8 length, - u8 *values); + const u8 *values); /* * A driver is capable of handling one or more physical devices present on @@ -354,6 +355,7 @@ static inline int i2c_adapter_id(struct { return adap->nr; } +#endif /* __KERNEL__ */ /* * I2C Message - used for pure i2c transaction, also from /dev interface @@ -469,6 +471,7 @@ #define I2C_PEC 0x0708 /* != 0 for SMBu #define I2C_SMBUS 0x0720 /* SMBus-level access */ /* ----- I2C-DEV: char device interface stuff ------------------------- */ +#ifdef __KERNEL__ #define I2C_MAJOR 89 /* Device major number */ @@ -646,5 +649,5 @@ static unsigned short *forces[] = { forc force_##chip6, force_##chip7, \ force_##chip8, NULL }; \ I2C_CLIENT_INSMOD_COMMON - +#endif /* __KERNEL__ */ #endif /* _LINUX_I2C_H */ diff --git a/include/linux/i2o-dev.h b/include/linux/i2o-dev.h index 36fd18c..c2519df 100644 --- a/include/linux/i2o-dev.h +++ b/include/linux/i2o-dev.h @@ -13,7 +13,7 @@ * This header file defines the I2O APIs that are available to both * the kernel and user level applications. Kernel specific structures * are defined in i2o_osm. OSMs should include _only_ i2o_osm.h which - * automatically includs this file. + * automatically includes this file. * */ @@ -23,14 +23,7 @@ #define _I2O_DEV_H /* How many controllers are we allowing */ #define MAX_I2O_CONTROLLERS 32 -//#include -#ifndef __KERNEL__ - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; - -#endif /* __KERNEL__ */ +#include /* * I2O Control IOCTLs and structures @@ -53,7 +46,7 @@ #define I2OPASSTHRU32 _IOR(I2O_MAGIC_NU struct i2o_cmd_passthru32 { unsigned int iop; /* IOP unit number */ - u32 msg; /* message */ + __u32 msg; /* message */ }; struct i2o_cmd_passthru { @@ -138,53 +131,53 @@ #define I2O_BUS_CARDBUS 7 #define I2O_BUS_UNKNOWN 0x80 typedef struct _i2o_pci_bus { - u8 PciFunctionNumber; - u8 PciDeviceNumber; - u8 PciBusNumber; - u8 reserved; - u16 PciVendorID; - u16 PciDeviceID; + __u8 PciFunctionNumber; + __u8 PciDeviceNumber; + __u8 PciBusNumber; + __u8 reserved; + __u16 PciVendorID; + __u16 PciDeviceID; } i2o_pci_bus; typedef struct _i2o_local_bus { - u16 LbBaseIOPort; - u16 reserved; - u32 LbBaseMemoryAddress; + __u16 LbBaseIOPort; + __u16 reserved; + __u32 LbBaseMemoryAddress; } i2o_local_bus; typedef struct _i2o_isa_bus { - u16 IsaBaseIOPort; - u8 CSN; - u8 reserved; - u32 IsaBaseMemoryAddress; + __u16 IsaBaseIOPort; + __u8 CSN; + __u8 reserved; + __u32 IsaBaseMemoryAddress; } i2o_isa_bus; typedef struct _i2o_eisa_bus_info { - u16 EisaBaseIOPort; - u8 reserved; - u8 EisaSlotNumber; - u32 EisaBaseMemoryAddress; + __u16 EisaBaseIOPort; + __u8 reserved; + __u8 EisaSlotNumber; + __u32 EisaBaseMemoryAddress; } i2o_eisa_bus; typedef struct _i2o_mca_bus { - u16 McaBaseIOPort; - u8 reserved; - u8 McaSlotNumber; - u32 McaBaseMemoryAddress; + __u16 McaBaseIOPort; + __u8 reserved; + __u8 McaSlotNumber; + __u32 McaBaseMemoryAddress; } i2o_mca_bus; typedef struct _i2o_other_bus { - u16 BaseIOPort; - u16 reserved; - u32 BaseMemoryAddress; + __u16 BaseIOPort; + __u16 reserved; + __u32 BaseMemoryAddress; } i2o_other_bus; typedef struct _i2o_hrt_entry { - u32 adapter_id; - u32 parent_tid:12; - u32 state:4; - u32 bus_num:8; - u32 bus_type:8; + __u32 adapter_id; + __u32 parent_tid:12; + __u32 state:4; + __u32 bus_num:8; + __u32 bus_type:8; union { i2o_pci_bus pci_bus; i2o_local_bus local_bus; @@ -196,66 +189,66 @@ typedef struct _i2o_hrt_entry { } i2o_hrt_entry; typedef struct _i2o_hrt { - u16 num_entries; - u8 entry_len; - u8 hrt_version; - u32 change_ind; + __u16 num_entries; + __u8 entry_len; + __u8 hrt_version; + __u32 change_ind; i2o_hrt_entry hrt_entry[1]; } i2o_hrt; typedef struct _i2o_lct_entry { - u32 entry_size:16; - u32 tid:12; - u32 reserved:4; - u32 change_ind; - u32 device_flags; - u32 class_id:12; - u32 version:4; - u32 vendor_id:16; - u32 sub_class; - u32 user_tid:12; - u32 parent_tid:12; - u32 bios_info:8; - u8 identity_tag[8]; - u32 event_capabilities; + __u32 entry_size:16; + __u32 tid:12; + __u32 reserved:4; + __u32 change_ind; + __u32 device_flags; + __u32 class_id:12; + __u32 version:4; + __u32 vendor_id:16; + __u32 sub_class; + __u32 user_tid:12; + __u32 parent_tid:12; + __u32 bios_info:8; + __u8 identity_tag[8]; + __u32 event_capabilities; } i2o_lct_entry; typedef struct _i2o_lct { - u32 table_size:16; - u32 boot_tid:12; - u32 lct_ver:4; - u32 iop_flags; - u32 change_ind; + __u32 table_size:16; + __u32 boot_tid:12; + __u32 lct_ver:4; + __u32 iop_flags; + __u32 change_ind; i2o_lct_entry lct_entry[1]; } i2o_lct; typedef struct _i2o_status_block { - u16 org_id; - u16 reserved; - u16 iop_id:12; - u16 reserved1:4; - u16 host_unit_id; - u16 segment_number:12; - u16 i2o_version:4; - u8 iop_state; - u8 msg_type; - u16 inbound_frame_size; - u8 init_code; - u8 reserved2; - u32 max_inbound_frames; - u32 cur_inbound_frames; - u32 max_outbound_frames; + __u16 org_id; + __u16 reserved; + __u16 iop_id:12; + __u16 reserved1:4; + __u16 host_unit_id; + __u16 segment_number:12; + __u16 i2o_version:4; + __u8 iop_state; + __u8 msg_type; + __u16 inbound_frame_size; + __u8 init_code; + __u8 reserved2; + __u32 max_inbound_frames; + __u32 cur_inbound_frames; + __u32 max_outbound_frames; char product_id[24]; - u32 expected_lct_size; - u32 iop_capabilities; - u32 desired_mem_size; - u32 current_mem_size; - u32 current_mem_base; - u32 desired_io_size; - u32 current_io_size; - u32 current_io_base; - u32 reserved3:24; - u32 cmd_status:8; + __u32 expected_lct_size; + __u32 iop_capabilities; + __u32 desired_mem_size; + __u32 current_mem_size; + __u32 current_mem_base; + __u32 desired_io_size; + __u32 current_io_size; + __u32 current_io_base; + __u32 reserved3:24; + __u32 cmd_status:8; } i2o_status_block; /* Event indicator mask flags */ diff --git a/include/linux/ide.h b/include/linux/ide.h index a8bef1d..dc7abef 100644 --- a/include/linux/ide.h +++ b/include/linux/ide.h @@ -6,7 +6,6 @@ #define _IDE_H * Copyright (C) 1994-2002 Linus Torvalds & authors */ -#include #include #include #include @@ -553,7 +552,6 @@ typedef struct ide_drive_s { struct hd_driveid *id; /* drive model identification info */ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */ struct ide_settings_s *settings;/* /proc/ide/ drive settings */ - char devfs_name[64]; /* devfs crap */ struct hwif_s *hwif; /* actually (ide_hwif_t *) */ @@ -631,6 +629,7 @@ typedef struct ide_drive_s { unsigned int usage; /* current "open()" count for drive */ unsigned int failures; /* current failure count */ unsigned int max_failures; /* maximum allowed failure count */ + u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */ u64 capacity64; /* total number of sectors */ @@ -793,6 +792,7 @@ #endif unsigned auto_poll : 1; /* supports nop auto-poll */ unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */ unsigned no_io_32bit : 1; /* 1 = can not do 32-bit IO ops */ + unsigned err_stops_fifo : 1; /* 1=data FIFO is cleared by an error */ struct device gendev; struct completion gendev_rel_comp; /* To deal with device release() */ @@ -1006,6 +1006,8 @@ #endif extern int noautodma; extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs); +int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq, + int uptodate, int nr_sectors); /* * This is used on exit from the driver to designate the next irq handler @@ -1357,7 +1359,7 @@ extern struct semaphore ide_cfg_sem; * ide_drive_t->hwif: constant, no locking */ -#define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable(); } while (0) +#define local_irq_set(flags) do { local_save_flags((flags)); local_irq_enable_in_hardirq(); } while (0) extern struct bus_type ide_bus_type; diff --git a/include/linux/idr.h b/include/linux/idr.h index d37c8d8..8268034 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -66,7 +66,7 @@ #define IDR_INIT(name) \ .id_free = NULL, \ .layers = 0, \ .id_free_cnt = 0, \ - .lock = SPIN_LOCK_UNLOCKED, \ + .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ } #define DEFINE_IDR(name) struct idr name = IDR_INIT(name) @@ -78,6 +78,7 @@ void *idr_find(struct idr *idp, int id); int idr_pre_get(struct idr *idp, gfp_t gfp_mask); int idr_get_new(struct idr *idp, void *ptr, int *id); int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id); +void *idr_replace(struct idr *idp, void *ptr, int id); void idr_remove(struct idr *idp, int id); void idr_destroy(struct idr *idp); void idr_init(struct idr *idp); diff --git a/include/linux/if_fddi.h b/include/linux/if_fddi.h index 1288a16..e0a1500 100644 --- a/include/linux/if_fddi.h +++ b/include/linux/if_fddi.h @@ -102,6 +102,7 @@ struct fddihdr } hdr; } __attribute__ ((packed)); +#ifdef __KERNEL__ /* Define FDDI statistics structure */ struct fddi_statistics { @@ -193,5 +194,6 @@ struct fddi_statistics { __u32 port_ler_flag[2]; __u32 port_hardware_present[2]; }; +#endif /* __KERNEL__ */ #endif /* _LINUX_IF_FDDI_H */ diff --git a/include/linux/if_frad.h b/include/linux/if_frad.h index 395f0aa..f272a80 100644 --- a/include/linux/if_frad.h +++ b/include/linux/if_frad.h @@ -24,7 +24,6 @@ #ifndef _FRAD_H_ #define _FRAD_H_ -#include #include #if defined(CONFIG_DLCI) || defined(CONFIG_DLCI_MODULE) diff --git a/include/linux/if_tr.h b/include/linux/if_tr.h index 5502f59..2f94cf2 100644 --- a/include/linux/if_tr.h +++ b/include/linux/if_tr.h @@ -43,7 +43,6 @@ struct trh_hdr { }; #ifdef __KERNEL__ -#include #include static inline struct trh_hdr *tr_hdr(const struct sk_buff *skb) diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 28f4f3b..899c3d4 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -169,7 +169,7 @@ struct ip_sf_list struct ip_mc_list { struct in_device *interface; - unsigned long multiaddr; + __be32 multiaddr; struct ip_sf_list *sources; struct ip_sf_list *tomb; unsigned int sfmode; diff --git a/include/linux/init.h b/include/linux/init.h index 93dcbe1..6667785 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -1,7 +1,6 @@ #ifndef _LINUX_INIT_H #define _LINUX_INIT_H -#include #include /* These macros are used to mark some functions or diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 41ecbb8..60aac2c 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -3,6 +3,8 @@ #define _LINUX__INIT_TASK_H #include #include +#include +#include #define INIT_FDTABLE \ { \ @@ -21,7 +23,7 @@ #define INIT_FILES \ .count = ATOMIC_INIT(1), \ .fdt = &init_files.fdtab, \ .fdtab = INIT_FDTABLE, \ - .file_lock = SPIN_LOCK_UNLOCKED, \ + .file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock), \ .next_fd = 0, \ .close_on_exec_init = { { 0, } }, \ .open_fds_init = { { 0, } }, \ @@ -36,7 +38,7 @@ #define INIT_KIOCTX(name, which_mm) \ .user_id = 0, \ .next = NULL, \ .wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.wait), \ - .ctx_lock = SPIN_LOCK_UNLOCKED, \ + .ctx_lock = __SPIN_LOCK_UNLOCKED(name.ctx_lock), \ .reqs_active = 0U, \ .max_reqs = ~0U, \ } @@ -48,7 +50,7 @@ #define INIT_MM(name) \ .mm_users = ATOMIC_INIT(2), \ .mm_count = ATOMIC_INIT(1), \ .mmap_sem = __RWSEM_INITIALIZER(name.mmap_sem), \ - .page_table_lock = SPIN_LOCK_UNLOCKED, \ + .page_table_lock = __SPIN_LOCK_UNLOCKED(name.page_table_lock), \ .mmlist = LIST_HEAD_INIT(name.mmlist), \ .cpu_vm_mask = CPU_MASK_ALL, \ } @@ -69,7 +71,7 @@ #define INIT_SIGNALS(sig) { \ #define INIT_SIGHAND(sighand) { \ .count = ATOMIC_INIT(1), \ .action = { { { .sa_handler = NULL, } }, }, \ - .siglock = SPIN_LOCK_UNLOCKED, \ + .siglock = __SPIN_LOCK_UNLOCKED(sighand.siglock), \ } extern struct group_info init_groups; @@ -87,6 +89,7 @@ #define INIT_TASK(tsk) \ .lock_depth = -1, \ .prio = MAX_PRIO-20, \ .static_prio = MAX_PRIO-20, \ + .normal_prio = MAX_PRIO-20, \ .policy = SCHED_NORMAL, \ .cpus_allowed = CPU_MASK_ALL, \ .mm = NULL, \ @@ -118,11 +121,13 @@ #define INIT_TASK(tsk) \ .list = LIST_HEAD_INIT(tsk.pending.list), \ .signal = {{0}}}, \ .blocked = {{0}}, \ - .alloc_lock = SPIN_LOCK_UNLOCKED, \ - .proc_lock = SPIN_LOCK_UNLOCKED, \ + .alloc_lock = __SPIN_LOCK_UNLOCKED(tsk.alloc_lock), \ .journal_info = NULL, \ .cpu_timers = INIT_CPU_TIMERS(tsk.cpu_timers), \ .fs_excl = ATOMIC_INIT(0), \ + .pi_lock = SPIN_LOCK_UNLOCKED, \ + INIT_TRACE_IRQFLAGS \ + INIT_LOCKDEP \ } diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 09e0043..d4f48c6 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -67,20 +67,66 @@ #ifdef __KERNEL__ #include #include -#include + +/* + * struct inotify_watch - represents a watch request on a specific inode + * + * h_list is protected by ih->mutex of the associated inotify_handle. + * i_list, mask are protected by inode->inotify_mutex of the associated inode. + * ih, inode, and wd are never written to once the watch is created. + * + * Callers must use the established inotify interfaces to access inotify_watch + * contents. The content of this structure is private to the inotify + * implementation. + */ +struct inotify_watch { + struct list_head h_list; /* entry in inotify_handle's list */ + struct list_head i_list; /* entry in inode's list */ + atomic_t count; /* reference count */ + struct inotify_handle *ih; /* associated inotify handle */ + struct inode *inode; /* associated inode */ + __s32 wd; /* watch descriptor */ + __u32 mask; /* event mask for this watch */ +}; + +struct inotify_operations { + void (*handle_event)(struct inotify_watch *, u32, u32, u32, + const char *, struct inode *); + void (*destroy_watch)(struct inotify_watch *); +}; #ifdef CONFIG_INOTIFY +/* Kernel API for producing events */ + extern void inotify_d_instantiate(struct dentry *, struct inode *); extern void inotify_d_move(struct dentry *); extern void inotify_inode_queue_event(struct inode *, __u32, __u32, - const char *); + const char *, struct inode *); extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32, const char *); extern void inotify_unmount_inodes(struct list_head *); extern void inotify_inode_is_dead(struct inode *); extern u32 inotify_get_cookie(void); +/* Kernel Consumer API */ + +extern struct inotify_handle *inotify_init(const struct inotify_operations *); +extern void inotify_init_watch(struct inotify_watch *); +extern void inotify_destroy(struct inotify_handle *); +extern __s32 inotify_find_watch(struct inotify_handle *, struct inode *, + struct inotify_watch **); +extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *, + u32); +extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *, + struct inode *, __u32); +extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *); +extern int inotify_rm_wd(struct inotify_handle *, __u32); +extern void inotify_remove_watch_locked(struct inotify_handle *, + struct inotify_watch *); +extern void get_inotify_watch(struct inotify_watch *); +extern void put_inotify_watch(struct inotify_watch *); + #else static inline void inotify_d_instantiate(struct dentry *dentry, @@ -94,7 +140,8 @@ static inline void inotify_d_move(struct static inline void inotify_inode_queue_event(struct inode *inode, __u32 mask, __u32 cookie, - const char *filename) + const char *filename, + struct inode *n_inode) { } @@ -117,6 +164,62 @@ static inline u32 inotify_get_cookie(voi return 0; } +static inline struct inotify_handle *inotify_init(const struct inotify_operations *ops) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline void inotify_init_watch(struct inotify_watch *watch) +{ +} + +static inline void inotify_destroy(struct inotify_handle *ih) +{ +} + +static inline __s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, + struct inotify_watch **watchp) +{ + return -EOPNOTSUPP; +} + +static inline __s32 inotify_find_update_watch(struct inotify_handle *ih, + struct inode *inode, u32 mask) +{ + return -EOPNOTSUPP; +} + +static inline __s32 inotify_add_watch(struct inotify_handle *ih, + struct inotify_watch *watch, + struct inode *inode, __u32 mask) +{ + return -EOPNOTSUPP; +} + +static inline int inotify_rm_watch(struct inotify_handle *ih, + struct inotify_watch *watch) +{ + return -EOPNOTSUPP; +} + +static inline int inotify_rm_wd(struct inotify_handle *ih, __u32 wd) +{ + return -EOPNOTSUPP; +} + +static inline void inotify_remove_watch_locked(struct inotify_handle *ih, + struct inotify_watch *watch) +{ +} + +static inline void get_inotify_watch(struct inotify_watch *watch) +{ +} + +static inline void put_inotify_watch(struct inotify_watch *watch) +{ +} + #endif /* CONFIG_INOTIFY */ #endif /* __KERNEL __ */ diff --git a/include/linux/input.h b/include/linux/input.h index ce1a756..56f1e0e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -15,6 +15,7 @@ #include #else #include #include +#include #include #endif @@ -231,7 +232,8 @@ #define KEY_KPPLUSMINUS 118 #define KEY_PAUSE 119 #define KEY_KPCOMMA 121 -#define KEY_HANGUEL 122 +#define KEY_HANGEUL 122 +#define KEY_HANGUEL KEY_HANGEUL #define KEY_HANJA 123 #define KEY_YEN 124 #define KEY_LEFTMETA 125 @@ -1004,6 +1006,7 @@ static inline void init_input_dev(struct } struct input_dev *input_allocate_device(void); +void input_free_device(struct input_dev *dev); static inline struct input_dev *input_get_device(struct input_dev *dev) { @@ -1015,12 +1018,6 @@ static inline void input_put_device(stru class_device_put(&dev->cdev); } -static inline void input_free_device(struct input_dev *dev) -{ - if (dev) - input_put_device(dev); -} - int input_register_device(struct input_dev *); void input_unregister_device(struct input_dev *); diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 2c08fdc..d5afee9 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -2,37 +2,67 @@ #ifndef _LINUX_INTERRUPT_H #define _LINUX_INTERRUPT_H -#include #include #include #include #include #include +#include #include #include +#include #include #include #include /* - * For 2.4.x compatibility, 2.4.x can use - * - * typedef void irqreturn_t; - * #define IRQ_NONE - * #define IRQ_HANDLED - * #define IRQ_RETVAL(x) - * - * To mix old-style and new-style irq handler returns. + * These correspond to the IORESOURCE_IRQ_* defines in + * linux/ioport.h to select the interrupt line behaviour. When + * requesting an interrupt without specifying a IRQF_TRIGGER, the + * setting should be assumed to be "as already configured", which + * may be as per machine or firmware initialisation. + */ +#define IRQF_TRIGGER_NONE 0x00000000 +#define IRQF_TRIGGER_RISING 0x00000001 +#define IRQF_TRIGGER_FALLING 0x00000002 +#define IRQF_TRIGGER_HIGH 0x00000004 +#define IRQF_TRIGGER_LOW 0x00000008 +#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \ + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING) +#define IRQF_TRIGGER_PROBE 0x00000010 + +/* + * These flags used only by the kernel as part of the + * irq handling routines. * - * IRQ_NONE means we didn't handle it. - * IRQ_HANDLED means that we did have a valid interrupt and handled it. - * IRQ_RETVAL(x) selects on the two depending on x being non-zero (for handled) + * IRQF_DISABLED - keep irqs disabled when calling the action handler + * IRQF_SAMPLE_RANDOM - irq is used to feed the random generator + * IRQF_SHARED - allow sharing the irq among several devices + * IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur + * IRQF_TIMER - Flag to mark this interrupt as timer interrupt */ -typedef int irqreturn_t; +#define IRQF_DISABLED 0x00000020 +#define IRQF_SAMPLE_RANDOM 0x00000040 +#define IRQF_SHARED 0x00000080 +#define IRQF_PROBE_SHARED 0x00000100 +#define IRQF_TIMER 0x00000200 +#define IRQF_PERCPU 0x00000400 -#define IRQ_NONE (0) -#define IRQ_HANDLED (1) -#define IRQ_RETVAL(x) ((x) != 0) +/* + * Migration helpers. Scheduled for removal in 1/2007 + * Do not use for new code ! + */ +#define SA_INTERRUPT IRQF_DISABLED +#define SA_SAMPLE_RANDOM IRQF_SAMPLE_RANDOM +#define SA_SHIRQ IRQF_SHARED +#define SA_PROBEIRQ IRQF_PROBE_SHARED +#define SA_PERCPU IRQF_PERCPU + +#define SA_TRIGGER_LOW IRQF_TRIGGER_LOW +#define SA_TRIGGER_HIGH IRQF_TRIGGER_HIGH +#define SA_TRIGGER_FALLING IRQF_TRIGGER_FALLING +#define SA_TRIGGER_RISING IRQF_TRIGGER_RISING +#define SA_TRIGGER_MASK IRQF_TRIGGER_MASK struct irqaction { irqreturn_t (*handler)(int, void *, struct pt_regs *); @@ -51,12 +81,90 @@ extern int request_irq(unsigned int, unsigned long, const char *, void *); extern void free_irq(unsigned int, void *); +/* + * On lockdep we dont want to enable hardirqs in hardirq + * context. Use local_irq_enable_in_hardirq() to annotate + * kernel code that has to do this nevertheless (pretty much + * the only valid case is for old/broken hardware that is + * insanely slow). + * + * NOTE: in theory this might break fragile code that relies + * on hardirq delivery - in practice we dont seem to have such + * places left. So the only effect should be slightly increased + * irqs-off latencies. + */ +#ifdef CONFIG_LOCKDEP +# define local_irq_enable_in_hardirq() do { } while (0) +#else +# define local_irq_enable_in_hardirq() local_irq_enable() +#endif #ifdef CONFIG_GENERIC_HARDIRQS extern void disable_irq_nosync(unsigned int irq); extern void disable_irq(unsigned int irq); extern void enable_irq(unsigned int irq); + +/* + * Special lockdep variants of irq disabling/enabling. + * These should be used for locking constructs that + * know that a particular irq context which is disabled, + * and which is the only irq-context user of a lock, + * that it's safe to take the lock in the irq-disabled + * section without disabling hardirqs. + * + * On !CONFIG_LOCKDEP they are equivalent to the normal + * irq disable/enable methods. + */ +static inline void disable_irq_nosync_lockdep(unsigned int irq) +{ + disable_irq_nosync(irq); +#ifdef CONFIG_LOCKDEP + local_irq_disable(); #endif +} + +static inline void disable_irq_lockdep(unsigned int irq) +{ + disable_irq(irq); +#ifdef CONFIG_LOCKDEP + local_irq_disable(); +#endif +} + +static inline void enable_irq_lockdep(unsigned int irq) +{ +#ifdef CONFIG_LOCKDEP + local_irq_enable(); +#endif + enable_irq(irq); +} + +/* IRQ wakeup (PM) control: */ +extern int set_irq_wake(unsigned int irq, unsigned int on); + +static inline int enable_irq_wake(unsigned int irq) +{ + return set_irq_wake(irq, 1); +} + +static inline int disable_irq_wake(unsigned int irq) +{ + return set_irq_wake(irq, 0); +} + +#else /* !CONFIG_GENERIC_HARDIRQS */ +/* + * NOTE: non-genirq architectures, if they want to support the lock + * validator need to define the methods below in their asm/irq.h + * files, under an #ifdef CONFIG_LOCKDEP section. + */ +# ifndef CONFIG_LOCKDEP +# define disable_irq_nosync_lockdep(irq) disable_irq_nosync(irq) +# define disable_irq_lockdep(irq) disable_irq(irq) +# define enable_irq_lockdep(irq) enable_irq(irq) +# endif + +#endif /* CONFIG_GENERIC_HARDIRQS */ #ifndef __ARCH_SET_SOFTIRQ_PENDING #define set_softirq_pending(x) (local_softirq_pending() = (x)) @@ -92,13 +200,11 @@ static inline void __deprecated save_and #define save_and_cli(x) save_and_cli(&x) #endif /* CONFIG_SMP */ -/* SoftIRQ primitives. */ -#define local_bh_disable() \ - do { add_preempt_count(SOFTIRQ_OFFSET); barrier(); } while (0) -#define __local_bh_enable() \ - do { barrier(); sub_preempt_count(SOFTIRQ_OFFSET); } while (0) - +extern void local_bh_disable(void); +extern void __local_bh_enable(void); +extern void _local_bh_enable(void); extern void local_bh_enable(void); +extern void local_bh_enable_ip(unsigned long ip); /* PLEASE, avoid to allocate new softirqs, if you need not _really_ high frequency threaded job scheduling. For almost all the purposes diff --git a/include/linux/io.h b/include/linux/io.h index 85533ec..420e2fd 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -21,5 +21,6 @@ #define _LINUX_IO_H #include void __iowrite32_copy(void __iomem *to, const void *from, size_t count); +void __iowrite64_copy(void __iomem *to, const void *from, size_t count); #endif /* _LINUX_IO_H */ diff --git a/include/linux/ioc4.h b/include/linux/ioc4.h index 3dd18b7..de73a32 100644 --- a/include/linux/ioc4.h +++ b/include/linux/ioc4.h @@ -147,6 +147,10 @@ #define IOC4_GPCR_EDGE_5 0x20 #define IOC4_GPCR_EDGE_6 0x40 #define IOC4_GPCR_EDGE_7 0x80 +#define IOC4_VARIANT_IO9 0x0900 +#define IOC4_VARIANT_PCI_RT 0x0901 +#define IOC4_VARIANT_IO10 0x1000 + /* One of these per IOC4 */ struct ioc4_driver_data { struct list_head idd_list; @@ -156,6 +160,7 @@ struct ioc4_driver_data { struct __iomem ioc4_misc_regs *idd_misc_regs; unsigned long count_period; void *idd_serial_data; + unsigned int idd_variant; }; /* One per submodule */ diff --git a/include/linux/ioport.h b/include/linux/ioport.h index cd6bd00..5612dfe 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -9,13 +9,15 @@ #ifndef _LINUX_IOPORT_H #define _LINUX_IOPORT_H #include +#include /* * Resources are tree-like, allowing * nesting etc.. */ struct resource { + resource_size_t start; + resource_size_t end; const char *name; - unsigned long start, end; unsigned long flags; struct resource *parent, *sibling, *child; }; @@ -53,6 +55,7 @@ #define IORESOURCE_IRQ_HIGHEDGE (1<<0) #define IORESOURCE_IRQ_LOWEDGE (1<<1) #define IORESOURCE_IRQ_HIGHLEVEL (1<<2) #define IORESOURCE_IRQ_LOWLEVEL (1<<3) +#define IORESOURCE_IRQ_SHAREABLE (1<<4) /* ISA PnP DMA specific bits (IORESOURCE_BITS) */ #define IORESOURCE_DMA_TYPE_MASK (3<<0) @@ -96,31 +99,37 @@ extern struct resource * ____request_res extern int release_resource(struct resource *new); extern __deprecated_for_modules int insert_resource(struct resource *parent, struct resource *new); extern int allocate_resource(struct resource *root, struct resource *new, - unsigned long size, - unsigned long min, unsigned long max, - unsigned long align, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, void (*alignf)(void *, struct resource *, - unsigned long, unsigned long), + resource_size_t, resource_size_t), void *alignf_data); -int adjust_resource(struct resource *res, unsigned long start, - unsigned long size); +int adjust_resource(struct resource *res, resource_size_t start, + resource_size_t size); + +/* get registered SYSTEM_RAM resources in specified area */ +extern int find_next_system_ram(struct resource *res); /* Convenience shorthand with allocation */ #define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name)) #define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name)) #define rename_region(region, newname) do { (region)->name = (newname); } while (0) -extern struct resource * __request_region(struct resource *, unsigned long start, unsigned long n, const char *name); +extern struct resource * __request_region(struct resource *, + resource_size_t start, + resource_size_t n, const char *name); /* Compatibility cruft */ #define release_region(start,n) __release_region(&ioport_resource, (start), (n)) #define check_mem_region(start,n) __check_region(&iomem_resource, (start), (n)) #define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n)) -extern int __check_region(struct resource *, unsigned long, unsigned long); -extern void __release_region(struct resource *, unsigned long, unsigned long); +extern int __check_region(struct resource *, resource_size_t, resource_size_t); +extern void __release_region(struct resource *, resource_size_t, + resource_size_t); -static inline int __deprecated check_region(unsigned long s, unsigned long n) +static inline int __deprecated check_region(resource_size_t s, + resource_size_t n) { return __check_region(&ioport_resource, s, n); } diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index 0a84b56..d09fbea 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -36,7 +36,6 @@ #define __LINUX_IPMI_H #include #include -#include /* * This file describes an interface to an IPMI driver. You have to @@ -210,11 +209,8 @@ #ifdef __KERNEL__ */ #include #include - -#ifdef CONFIG_PROC_FS +#include #include -extern struct proc_dir_entry *proc_ipmi_root; -#endif /* CONFIG_PROC_FS */ /* Opaque type for a IPMI message user. One of these is needed to send and receive messages. */ diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 1263d8c..297853c 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -1,7 +1,6 @@ #ifndef _IPV6_H #define _IPV6_H -#include #include #include diff --git a/include/linux/irq.h b/include/linux/irq.h index ee2a82a..b48eae3 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -1,5 +1,5 @@ -#ifndef __irq_h -#define __irq_h +#ifndef _LINUX_IRQ_H +#define _LINUX_IRQ_H /* * Please do not include this file in generic code. There is currently @@ -9,99 +9,187 @@ #define __irq_h * Thanks. --rmk */ -#include #include -#if !defined(CONFIG_S390) +#ifndef CONFIG_S390 #include #include #include #include +#include #include #include /* * IRQ line status. + * + * Bits 0-16 are reserved for the IRQF_* bits in linux/interrupt.h + * + * IRQ types */ -#define IRQ_INPROGRESS 1 /* IRQ handler active - do not enter! */ -#define IRQ_DISABLED 2 /* IRQ disabled - do not enter! */ -#define IRQ_PENDING 4 /* IRQ pending - replay on enable */ -#define IRQ_REPLAY 8 /* IRQ has been replayed but not acked yet */ -#define IRQ_AUTODETECT 16 /* IRQ is being autodetected */ -#define IRQ_WAITING 32 /* IRQ not yet seen - for autodetection */ -#define IRQ_LEVEL 64 /* IRQ level triggered */ -#define IRQ_MASKED 128 /* IRQ masked - shouldn't be seen again */ -#if defined(ARCH_HAS_IRQ_PER_CPU) -# define IRQ_PER_CPU 256 /* IRQ is per CPU */ +#define IRQ_TYPE_NONE 0x00000000 /* Default, unspecified type */ +#define IRQ_TYPE_EDGE_RISING 0x00000001 /* Edge rising type */ +#define IRQ_TYPE_EDGE_FALLING 0x00000002 /* Edge falling type */ +#define IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING) +#define IRQ_TYPE_LEVEL_HIGH 0x00000004 /* Level high type */ +#define IRQ_TYPE_LEVEL_LOW 0x00000008 /* Level low type */ +#define IRQ_TYPE_SENSE_MASK 0x0000000f /* Mask of the above */ +#define IRQ_TYPE_PROBE 0x00000010 /* Probing in progress */ + +/* Internal flags */ +#define IRQ_INPROGRESS 0x00010000 /* IRQ handler active - do not enter! */ +#define IRQ_DISABLED 0x00020000 /* IRQ disabled - do not enter! */ +#define IRQ_PENDING 0x00040000 /* IRQ pending - replay on enable */ +#define IRQ_REPLAY 0x00080000 /* IRQ has been replayed but not acked yet */ +#define IRQ_AUTODETECT 0x00100000 /* IRQ is being autodetected */ +#define IRQ_WAITING 0x00200000 /* IRQ not yet seen - for autodetection */ +#define IRQ_LEVEL 0x00400000 /* IRQ level triggered */ +#define IRQ_MASKED 0x00800000 /* IRQ masked - shouldn't be seen again */ +#ifdef CONFIG_IRQ_PER_CPU +# define IRQ_PER_CPU 0x01000000 /* IRQ is per CPU */ # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) #else # define CHECK_IRQ_PER_CPU(var) 0 #endif -/* - * Interrupt controller descriptor. This is all we need - * to describe about the low-level hardware. +#define IRQ_NOPROBE 0x02000000 /* IRQ is not valid for probing */ +#define IRQ_NOREQUEST 0x04000000 /* IRQ cannot be requested */ +#define IRQ_NOAUTOEN 0x08000000 /* IRQ will not be enabled on request irq */ +#define IRQ_DELAYED_DISABLE 0x10000000 /* IRQ disable (masking) happens delayed. */ + +struct proc_dir_entry; + +/** + * struct irq_chip - hardware interrupt chip descriptor + * + * @name: name for /proc/interrupts + * @startup: start up the interrupt (defaults to ->enable if NULL) + * @shutdown: shut down the interrupt (defaults to ->disable if NULL) + * @enable: enable the interrupt (defaults to chip->unmask if NULL) + * @disable: disable the interrupt (defaults to chip->mask if NULL) + * @ack: start of a new interrupt + * @mask: mask an interrupt source + * @mask_ack: ack and mask an interrupt source + * @unmask: unmask an interrupt source + * @eoi: end of interrupt - chip level + * @end: end of interrupt - flow level + * @set_affinity: set the CPU affinity on SMP machines + * @retrigger: resend an IRQ to the CPU + * @set_type: set the flow type (IRQ_TYPE_LEVEL/etc.) of an IRQ + * @set_wake: enable/disable power-management wake-on of an IRQ + * + * @release: release function solely used by UML + * @typename: obsoleted by name, kept as migration helper */ -struct hw_interrupt_type { - const char * typename; - unsigned int (*startup)(unsigned int irq); - void (*shutdown)(unsigned int irq); - void (*enable)(unsigned int irq); - void (*disable)(unsigned int irq); - void (*ack)(unsigned int irq); - void (*end)(unsigned int irq); - void (*set_affinity)(unsigned int irq, cpumask_t dest); +struct irq_chip { + const char *name; + unsigned int (*startup)(unsigned int irq); + void (*shutdown)(unsigned int irq); + void (*enable)(unsigned int irq); + void (*disable)(unsigned int irq); + + void (*ack)(unsigned int irq); + void (*mask)(unsigned int irq); + void (*mask_ack)(unsigned int irq); + void (*unmask)(unsigned int irq); + void (*eoi)(unsigned int irq); + + void (*end)(unsigned int irq); + void (*set_affinity)(unsigned int irq, cpumask_t dest); + int (*retrigger)(unsigned int irq); + int (*set_type)(unsigned int irq, unsigned int flow_type); + int (*set_wake)(unsigned int irq, unsigned int on); + /* Currently used only by UML, might disappear one day.*/ #ifdef CONFIG_IRQ_RELEASE_METHOD - void (*release)(unsigned int irq, void *dev_id); + void (*release)(unsigned int irq, void *dev_id); #endif + /* + * For compatibility, ->typename is copied into ->name. + * Will disappear. + */ + const char *typename; }; -typedef struct hw_interrupt_type hw_irq_controller; - -/* - * This is the "IRQ descriptor", which contains various information - * about the irq, including what kind of hardware handling it has, - * whether it is disabled etc etc. +/** + * struct irq_desc - interrupt descriptor + * + * @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()] + * @chip: low level interrupt hardware access + * @handler_data: per-IRQ data for the irq_chip methods + * @chip_data: platform-specific per-chip private data for the chip + * methods, to allow shared chip implementations + * @action: the irq action chain + * @status: status information + * @depth: disable-depth, for nested irq_disable() calls + * @irq_count: stats field to detect stalled irqs + * @irqs_unhandled: stats field for spurious unhandled interrupts + * @lock: locking for SMP + * @affinity: IRQ affinity on SMP + * @cpu: cpu index useful for balancing + * @pending_mask: pending rebalanced interrupts + * @move_irq: need to re-target IRQ destination + * @dir: /proc/irq/ procfs entry + * @affinity_entry: /proc/irq/smp_affinity procfs entry on SMP * * Pad this out to 32 bytes for cache and indexing reasons. */ -typedef struct irq_desc { - hw_irq_controller *handler; - void *handler_data; - struct irqaction *action; /* IRQ action list */ - unsigned int status; /* IRQ status */ - unsigned int depth; /* nested irq disables */ - unsigned int irq_count; /* For detecting broken interrupts */ - unsigned int irqs_unhandled; - spinlock_t lock; -#if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE) - unsigned int move_irq; /* Flag need to re-target intr dest*/ +struct irq_desc { + void fastcall (*handle_irq)(unsigned int irq, + struct irq_desc *desc, + struct pt_regs *regs); + struct irq_chip *chip; + void *handler_data; + void *chip_data; + struct irqaction *action; /* IRQ action list */ + unsigned int status; /* IRQ status */ + + unsigned int depth; /* nested irq disables */ + unsigned int irq_count; /* For detecting broken IRQs */ + unsigned int irqs_unhandled; + spinlock_t lock; +#ifdef CONFIG_SMP + cpumask_t affinity; + unsigned int cpu; +#endif +#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) + cpumask_t pending_mask; + unsigned int move_irq; /* need to re-target IRQ dest */ +#endif +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *dir; #endif -} ____cacheline_aligned irq_desc_t; +} ____cacheline_aligned; -extern irq_desc_t irq_desc [NR_IRQS]; +extern struct irq_desc irq_desc[NR_IRQS]; -/* Return a pointer to the irq descriptor for IRQ. */ -static inline irq_desc_t * -irq_descp (int irq) -{ - return irq_desc + irq; -} +/* + * Migration helpers for obsolete names, they will go away: + */ +#define hw_interrupt_type irq_chip +typedef struct irq_chip hw_irq_controller; +#define no_irq_type no_irq_chip +typedef struct irq_desc irq_desc_t; -#include /* the arch dependent stuff */ +/* + * Pick up the arch-dependent methods: + */ +#include -extern int setup_irq(unsigned int irq, struct irqaction * new); +extern int setup_irq(unsigned int irq, struct irqaction *new); #ifdef CONFIG_GENERIC_HARDIRQS -extern cpumask_t irq_affinity[NR_IRQS]; + +#ifndef handle_dynamic_tick +# define handle_dynamic_tick(a) do { } while (0) +#endif #ifdef CONFIG_SMP static inline void set_native_irq_info(int irq, cpumask_t mask) { - irq_affinity[irq] = mask; + irq_desc[irq].affinity = mask; } #else static inline void set_native_irq_info(int irq, cpumask_t mask) @@ -111,8 +199,7 @@ #endif #ifdef CONFIG_SMP -#if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE) -extern cpumask_t pending_irq_cpumask[NR_IRQS]; +#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) void set_pending_irq(unsigned int irq, cpumask_t mask); void move_native_irq(int irq); @@ -133,7 +220,7 @@ static inline void set_irq_info(int irq, { } -#else // CONFIG_PCI_MSI +#else /* CONFIG_PCI_MSI */ static inline void move_irq(int irq) { @@ -144,53 +231,178 @@ static inline void set_irq_info(int irq, { set_native_irq_info(irq, mask); } -#endif // CONFIG_PCI_MSI -#else // CONFIG_GENERIC_PENDING_IRQ || CONFIG_IRQBALANCE +#endif /* CONFIG_PCI_MSI */ + +#else /* CONFIG_GENERIC_PENDING_IRQ || CONFIG_IRQBALANCE */ + +static inline void move_irq(int irq) +{ +} + +static inline void move_native_irq(int irq) +{ +} + +static inline void set_pending_irq(unsigned int irq, cpumask_t mask) +{ +} -#define move_irq(x) -#define move_native_irq(x) -#define set_pending_irq(x,y) static inline void set_irq_info(int irq, cpumask_t mask) { set_native_irq_info(irq, mask); } -#endif // CONFIG_GENERIC_PENDING_IRQ +#endif /* CONFIG_GENERIC_PENDING_IRQ */ -#else // CONFIG_SMP +#else /* CONFIG_SMP */ #define move_irq(x) #define move_native_irq(x) -#endif // CONFIG_SMP - -extern int no_irq_affinity; -extern int noirqdebug_setup(char *str); +#endif /* CONFIG_SMP */ -extern fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, - struct irqaction *action); -extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); -extern void note_interrupt(unsigned int irq, irq_desc_t *desc, - int action_ret, struct pt_regs *regs); -extern int can_request_irq(unsigned int irq, unsigned long irqflags); - -extern void init_irq_proc(void); +#ifdef CONFIG_IRQBALANCE +extern void set_balance_irq_affinity(unsigned int irq, cpumask_t mask); +#else +static inline void set_balance_irq_affinity(unsigned int irq, cpumask_t mask) +{ +} +#endif #ifdef CONFIG_AUTO_IRQ_AFFINITY extern int select_smp_affinity(unsigned int irq); #else -static inline int -select_smp_affinity(unsigned int irq) +static inline int select_smp_affinity(unsigned int irq) { return 1; } #endif -#endif +extern int no_irq_affinity; -extern hw_irq_controller no_irq_type; /* needed in every arch ? */ +/* Handle irq action chains: */ +extern int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, + struct irqaction *action); -#endif +/* + * Built-in IRQ handlers for various IRQ types, + * callable via desc->chip->handle_irq() + */ +extern void fastcall +handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs); +extern void fastcall +handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc, + struct pt_regs *regs); +extern void fastcall +handle_edge_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs); +extern void fastcall +handle_simple_irq(unsigned int irq, struct irq_desc *desc, + struct pt_regs *regs); +extern void fastcall +handle_percpu_irq(unsigned int irq, struct irq_desc *desc, + struct pt_regs *regs); +extern void fastcall +handle_bad_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs); + +/* + * Get a descriptive string for the highlevel handler, for + * /proc/interrupts output: + */ +extern const char * +handle_irq_name(void fastcall (*handle)(unsigned int, struct irq_desc *, + struct pt_regs *)); + +/* + * Monolithic do_IRQ implementation. + * (is an explicit fastcall, because i386 4KSTACKS calls it from assembly) + */ +extern fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs); + +/* + * Architectures call this to let the generic IRQ layer + * handle an interrupt. If the descriptor is attached to an + * irqchip-style controller then we call the ->handle_irq() handler, + * and it calls __do_IRQ() if it's attached to an irqtype-style controller. + */ +static inline void generic_handle_irq(unsigned int irq, struct pt_regs *regs) +{ + struct irq_desc *desc = irq_desc + irq; + + if (likely(desc->handle_irq)) + desc->handle_irq(irq, desc, regs); + else + __do_IRQ(irq, regs); +} + +/* Handling of unhandled and spurious interrupts: */ +extern void note_interrupt(unsigned int irq, struct irq_desc *desc, + int action_ret, struct pt_regs *regs); + +/* Resending of interrupts :*/ +void check_irq_resend(struct irq_desc *desc, unsigned int irq); + +/* Initialize /proc/irq/ */ +extern void init_irq_proc(void); + +/* Enable/disable irq debugging output: */ +extern int noirqdebug_setup(char *str); + +/* Checks whether the interrupt can be requested by request_irq(): */ +extern int can_request_irq(unsigned int irq, unsigned long irqflags); + +/* Dummy irq-chip implementations: */ +extern struct irq_chip no_irq_chip; +extern struct irq_chip dummy_irq_chip; + +extern void +set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip, + void fastcall (*handle)(unsigned int, + struct irq_desc *, + struct pt_regs *)); +extern void +__set_irq_handler(unsigned int irq, + void fastcall (*handle)(unsigned int, struct irq_desc *, + struct pt_regs *), + int is_chained); + +/* + * Set a highlevel flow handler for a given IRQ: + */ +static inline void +set_irq_handler(unsigned int irq, + void fastcall (*handle)(unsigned int, struct irq_desc *, + struct pt_regs *)) +{ + __set_irq_handler(irq, handle, 0); +} + +/* + * Set a highlevel chained flow handler for a given IRQ. + * (a chained handler is automatically enabled and set to + * IRQ_NOREQUEST and IRQ_NOPROBE) + */ +static inline void +set_irq_chained_handler(unsigned int irq, + void fastcall (*handle)(unsigned int, struct irq_desc *, + struct pt_regs *)) +{ + __set_irq_handler(irq, handle, 1); +} + +/* Set/get chip/data for an IRQ: */ + +extern int set_irq_chip(unsigned int irq, struct irq_chip *chip); +extern int set_irq_data(unsigned int irq, void *data); +extern int set_irq_chip_data(unsigned int irq, void *data); +extern int set_irq_type(unsigned int irq, unsigned int type); + +#define get_irq_chip(irq) (irq_desc[irq].chip) +#define get_irq_chip_data(irq) (irq_desc[irq].chip_data) +#define get_irq_data(irq) (irq_desc[irq].handler_data) + +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#endif /* !CONFIG_S390 */ -#endif /* __irq_h */ +#endif /* _LINUX_IRQ_H */ diff --git a/include/linux/irq_cpustat.h b/include/linux/irq_cpustat.h index af93505..77e4bac 100644 --- a/include/linux/irq_cpustat.h +++ b/include/linux/irq_cpustat.h @@ -9,7 +9,6 @@ #define __irq_cpustat_h * Keith Owens July 2000. */ -#include /* * Simple wrappers reducing source bloat. Define all irq_stat fields diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h new file mode 100644 index 0000000..412e025 --- /dev/null +++ b/include/linux/irqflags.h @@ -0,0 +1,96 @@ +/* + * include/linux/irqflags.h + * + * IRQ flags tracing: follow the state of the hardirq and softirq flags and + * provide callbacks for transitions between ON and OFF states. + * + * This file gets included from lowlevel asm headers too, to provide + * wrapped versions of the local_irq_*() APIs, based on the + * raw_local_irq_*() macros from the lowlevel headers. + */ +#ifndef _LINUX_TRACE_IRQFLAGS_H +#define _LINUX_TRACE_IRQFLAGS_H + +#ifdef CONFIG_TRACE_IRQFLAGS + extern void trace_hardirqs_on(void); + extern void trace_hardirqs_off(void); + extern void trace_softirqs_on(unsigned long ip); + extern void trace_softirqs_off(unsigned long ip); +# define trace_hardirq_context(p) ((p)->hardirq_context) +# define trace_softirq_context(p) ((p)->softirq_context) +# define trace_hardirqs_enabled(p) ((p)->hardirqs_enabled) +# define trace_softirqs_enabled(p) ((p)->softirqs_enabled) +# define trace_hardirq_enter() do { current->hardirq_context++; } while (0) +# define trace_hardirq_exit() do { current->hardirq_context--; } while (0) +# define trace_softirq_enter() do { current->softirq_context++; } while (0) +# define trace_softirq_exit() do { current->softirq_context--; } while (0) +# define INIT_TRACE_IRQFLAGS .softirqs_enabled = 1, +#else +# define trace_hardirqs_on() do { } while (0) +# define trace_hardirqs_off() do { } while (0) +# define trace_softirqs_on(ip) do { } while (0) +# define trace_softirqs_off(ip) do { } while (0) +# define trace_hardirq_context(p) 0 +# define trace_softirq_context(p) 0 +# define trace_hardirqs_enabled(p) 0 +# define trace_softirqs_enabled(p) 0 +# define trace_hardirq_enter() do { } while (0) +# define trace_hardirq_exit() do { } while (0) +# define trace_softirq_enter() do { } while (0) +# define trace_softirq_exit() do { } while (0) +# define INIT_TRACE_IRQFLAGS +#endif + +#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT + +#include + +#define local_irq_enable() \ + do { trace_hardirqs_on(); raw_local_irq_enable(); } while (0) +#define local_irq_disable() \ + do { raw_local_irq_disable(); trace_hardirqs_off(); } while (0) +#define local_irq_save(flags) \ + do { raw_local_irq_save(flags); trace_hardirqs_off(); } while (0) + +#define local_irq_restore(flags) \ + do { \ + if (raw_irqs_disabled_flags(flags)) { \ + raw_local_irq_restore(flags); \ + trace_hardirqs_off(); \ + } else { \ + trace_hardirqs_on(); \ + raw_local_irq_restore(flags); \ + } \ + } while (0) +#else /* !CONFIG_TRACE_IRQFLAGS_SUPPORT */ +/* + * The local_irq_*() APIs are equal to the raw_local_irq*() + * if !TRACE_IRQFLAGS. + */ +# define raw_local_irq_disable() local_irq_disable() +# define raw_local_irq_enable() local_irq_enable() +# define raw_local_irq_save(flags) local_irq_save(flags) +# define raw_local_irq_restore(flags) local_irq_restore(flags) +#endif /* CONFIG_TRACE_IRQFLAGS_SUPPORT */ + +#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT +#define safe_halt() \ + do { \ + trace_hardirqs_on(); \ + raw_safe_halt(); \ + } while (0) + +#define local_save_flags(flags) raw_local_save_flags(flags) + +#define irqs_disabled() \ +({ \ + unsigned long flags; \ + \ + raw_local_save_flags(flags); \ + raw_irqs_disabled_flags(flags); \ +}) + +#define irqs_disabled_flags(flags) raw_irqs_disabled_flags(flags) +#endif /* CONFIG_X86 */ + +#endif diff --git a/include/linux/irqreturn.h b/include/linux/irqreturn.h new file mode 100644 index 0000000..881883c --- /dev/null +++ b/include/linux/irqreturn.h @@ -0,0 +1,25 @@ +/* irqreturn.h */ +#ifndef _LINUX_IRQRETURN_H +#define _LINUX_IRQRETURN_H + +/* + * For 2.4.x compatibility, 2.4.x can use + * + * typedef void irqreturn_t; + * #define IRQ_NONE + * #define IRQ_HANDLED + * #define IRQ_RETVAL(x) + * + * To mix old-style and new-style irq handler returns. + * + * IRQ_NONE means we didn't handle it. + * IRQ_HANDLED means that we did have a valid interrupt and handled it. + * IRQ_RETVAL(x) selects on the two depending on x being non-zero (for handled) + */ +typedef int irqreturn_t; + +#define IRQ_NONE (0) +#define IRQ_HANDLED (1) +#define IRQ_RETVAL(x) ((x) != 0) + +#endif diff --git a/include/linux/isa.h b/include/linux/isa.h new file mode 100644 index 0000000..1b85533 --- /dev/null +++ b/include/linux/isa.h @@ -0,0 +1,28 @@ +/* + * ISA bus. + */ + +#ifndef __LINUX_ISA_H +#define __LINUX_ISA_H + +#include +#include + +struct isa_driver { + int (*match)(struct device *, unsigned int); + int (*probe)(struct device *, unsigned int); + int (*remove)(struct device *, unsigned int); + void (*shutdown)(struct device *, unsigned int); + int (*suspend)(struct device *, unsigned int, pm_message_t); + int (*resume)(struct device *, unsigned int); + + struct device_driver driver; + struct device *devices; +}; + +#define to_isa_driver(x) container_of((x), struct isa_driver, driver) + +int isa_register_driver(struct isa_driver *, unsigned int); +void isa_unregister_driver(struct isa_driver *); + +#endif /* __LINUX_ISA_H */ diff --git a/include/linux/isapnp.h b/include/linux/isapnp.h index 26c64c2..1e8728a 100644 --- a/include/linux/isapnp.h +++ b/include/linux/isapnp.h @@ -22,7 +22,6 @@ #ifndef LINUX_ISAPNP_H #define LINUX_ISAPNP_H -#include #include #include diff --git a/include/linux/isdn.h b/include/linux/isdn.h index 53eaee9..6299114 100644 --- a/include/linux/isdn.h +++ b/include/linux/isdn.h @@ -146,7 +146,6 @@ #define ISDN_NET_DIALMODE(x) ((&(x))->fl #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/linux/isdn/Kbuild b/include/linux/isdn/Kbuild new file mode 100644 index 0000000..991cdb2 --- /dev/null +++ b/include/linux/isdn/Kbuild @@ -0,0 +1 @@ +header-y += capicmd.h diff --git a/include/linux/isdn/tpam.h b/include/linux/isdn/tpam.h deleted file mode 100644 index 9f65bea..0000000 --- a/include/linux/isdn/tpam.h +++ /dev/null @@ -1,56 +0,0 @@ -/* $Id: tpam.h,v 1.1.2.1 2001/06/08 08:23:46 kai Exp $ - * - * Turbo PAM ISDN driver for Linux. (Kernel Driver) - * - * Copyright 2001 Stelian Pop , Alcôve - * - * For all support questions please contact: - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#ifndef _TPAM_H_ -#define _TPAM_H_ - -#include -#include - -/* IOCTL commands */ -#define TPAM_CMD_DSPLOAD 0x0001 -#define TPAM_CMD_DSPSAVE 0x0002 -#define TPAM_CMD_DSPRUN 0x0003 -#define TPAM_CMD_LOOPMODEON 0x0004 -#define TPAM_CMD_LOOPMODEOFF 0x0005 - -/* addresses of debug information zones on board */ -#define TPAM_TRAPAUDIT_REGISTER 0x005493e4 -#define TPAM_NCOAUDIT_REGISTER 0x00500000 -#define TPAM_MSGAUDIT_REGISTER 0x008E30F0 - -/* length of debug information zones on board */ -#define TPAM_TRAPAUDIT_LENGTH 10000 -#define TPAM_NCOAUDIT_LENGTH 300000 -#define TPAM_NCOAUDIT_COUNT 30 -#define TPAM_MSGAUDIT_LENGTH 60000 - -/* IOCTL load/save parameter */ -typedef struct tpam_dsp_ioctl { - __u32 address; /* address to load/save data */ - __u32 data_len; /* size of data to be loaded/saved */ - __u8 data[0]; /* data */ -} tpam_dsp_ioctl; - -#endif /* _TPAM_H_ */ diff --git a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h index 26b00a7..8687a7d 100644 --- a/include/linux/isdn_ppp.h +++ b/include/linux/isdn_ppp.h @@ -67,7 +67,6 @@ struct isdn_ppp_comp_data { #ifdef __KERNEL__ -#include #ifdef CONFIG_IPPP_FILTER #include diff --git a/include/linux/isdnif.h b/include/linux/isdnif.h index 04e10f9..b9b5a68 100644 --- a/include/linux/isdnif.h +++ b/include/linux/isdnif.h @@ -54,7 +54,6 @@ #define ISDN_PROTO_L3_MAX 7 /* Max. 8 Pr #ifdef __KERNEL__ -#include #include /***************************************************************************/ diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 6a425e3..20eb344 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h @@ -501,6 +501,12 @@ struct transaction_s struct journal_head *t_checkpoint_list; /* + * Doubly-linked circular list of all buffers submitted for IO while + * checkpointing. [j_list_lock] + */ + struct journal_head *t_checkpoint_io_list; + + /* * Doubly-linked circular list of temporary buffers currently undergoing * IO in the log [j_list_lock] */ @@ -849,7 +855,7 @@ extern void journal_commit_transaction(j /* Checkpoint list management */ int __journal_clean_checkpoint_list(journal_t *journal); -void __journal_remove_checkpoint(struct journal_head *); +int __journal_remove_checkpoint(struct journal_head *); void __journal_insert_checkpoint(struct journal_head *, transaction_t *); /* Buffer IO */ diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index cf792bb..c9c7607 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -65,6 +65,18 @@ #define JFFS2_NODETYPE_PADDING (JFFS2_FE #define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6) +#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8) +#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9) + +/* XATTR Related */ +#define JFFS2_XPREFIX_USER 1 /* for "user." */ +#define JFFS2_XPREFIX_SECURITY 2 /* for "security." */ +#define JFFS2_XPREFIX_ACL_ACCESS 3 /* for "system.posix_acl_access" */ +#define JFFS2_XPREFIX_ACL_DEFAULT 4 /* for "system.posix_acl_default" */ +#define JFFS2_XPREFIX_TRUSTED 5 /* for "trusted.*" */ + +#define JFFS2_ACL_VERSION 0x0001 + // Maybe later... //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) @@ -82,11 +94,11 @@ #define JFFS2_INO_FLAG_USERCOMPR 2 /* U typedef struct { uint32_t v32; -} __attribute__((packed)) jint32_t; +} __attribute__((packed)) jint32_t; typedef struct { uint32_t m; -} __attribute__((packed)) jmode_t; +} __attribute__((packed)) jmode_t; typedef struct { uint16_t v16; @@ -99,7 +111,7 @@ struct jffs2_unknown_node jint16_t nodetype; jint32_t totlen; /* So we can skip over nodes we don't grok */ jint32_t hdr_crc; -} __attribute__((packed)); +}; struct jffs2_raw_dirent { @@ -117,7 +129,7 @@ struct jffs2_raw_dirent jint32_t node_crc; jint32_t name_crc; uint8_t name[0]; -} __attribute__((packed)); +}; /* The JFFS2 raw inode structure: Used for storage on physical media. */ /* The uid, gid, atime, mtime and ctime members could be longer, but @@ -149,6 +161,33 @@ struct jffs2_raw_inode jint32_t data_crc; /* CRC for the (compressed) data. */ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ uint8_t data[0]; +}; + +struct jffs2_raw_xattr { + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XATTR */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t xid; /* XATTR identifier number */ + jint32_t version; + uint8_t xprefix; + uint8_t name_len; + jint16_t value_len; + jint32_t data_crc; + jint32_t node_crc; + uint8_t data[0]; +} __attribute__((packed)); + +struct jffs2_raw_xref +{ + jint16_t magic; + jint16_t nodetype; /* = JFFS2_NODETYPE_XREF */ + jint32_t totlen; + jint32_t hdr_crc; + jint32_t ino; /* inode number */ + jint32_t xid; /* XATTR identifier number */ + jint32_t xseqno; /* xref sequencial number */ + jint32_t node_crc; } __attribute__((packed)); struct jffs2_raw_summary @@ -163,14 +202,22 @@ struct jffs2_raw_summary jint32_t sum_crc; /* summary information crc */ jint32_t node_crc; /* node crc */ jint32_t sum[0]; /* inode summary info */ -} __attribute__((packed)); +}; union jffs2_node_union { struct jffs2_raw_inode i; struct jffs2_raw_dirent d; + struct jffs2_raw_xattr x; + struct jffs2_raw_xref r; struct jffs2_raw_summary s; struct jffs2_unknown_node u; }; +/* Data payload for device nodes. */ +union jffs2_device_node { + jint16_t old; + jint32_t new; +}; + #endif /* __LINUX_JFFS2_H__ */ diff --git a/include/linux/jffs2_fs_i.h b/include/linux/jffs2_fs_i.h deleted file mode 100644 index ad565bf..0000000 --- a/include/linux/jffs2_fs_i.h +++ /dev/null @@ -1,50 +0,0 @@ -/* $Id: jffs2_fs_i.h,v 1.19 2005/11/07 11:14:52 gleixner Exp $ */ - -#ifndef _JFFS2_FS_I -#define _JFFS2_FS_I - -#include -#include -#include - -struct jffs2_inode_info { - /* We need an internal mutex similar to inode->i_mutex. - Unfortunately, we can't used the existing one, because - either the GC would deadlock, or we'd have to release it - before letting GC proceed. Or we'd have to put ugliness - into the GC code so it didn't attempt to obtain the i_mutex - for the inode(s) which are already locked */ - struct semaphore sem; - - /* The highest (datanode) version number used for this ino */ - uint32_t highest_version; - - /* List of data fragments which make up the file */ - struct rb_root fragtree; - - /* There may be one datanode which isn't referenced by any of the - above fragments, if it contains a metadata update but no actual - data - or if this is a directory inode */ - /* This also holds the _only_ dnode for symlinks/device nodes, - etc. */ - struct jffs2_full_dnode *metadata; - - /* Directory entries */ - struct jffs2_full_dirent *dents; - - /* The target path if this is the inode of a symlink */ - unsigned char *target; - - /* Some stuff we just have to keep in-core at all times, for each inode. */ - struct jffs2_inode_cache *inocache; - - uint16_t flags; - uint8_t usercompr; -#if !defined (__ECOS) -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) - struct inode vfs_inode; -#endif -#endif -}; - -#endif /* _JFFS2_FS_I */ diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h deleted file mode 100644 index 4bcfb55..0000000 --- a/include/linux/jffs2_fs_sb.h +++ /dev/null @@ -1,122 +0,0 @@ -/* $Id: jffs2_fs_sb.h,v 1.54 2005/09/21 13:37:34 dedekind Exp $ */ - -#ifndef _JFFS2_FS_SB -#define _JFFS2_FS_SB - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define JFFS2_SB_FLAG_RO 1 -#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */ -#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */ - -struct jffs2_inodirty; - -/* A struct for the overall file system control. Pointers to - jffs2_sb_info structs are named `c' in the source code. - Nee jffs_control -*/ -struct jffs2_sb_info { - struct mtd_info *mtd; - - uint32_t highest_ino; - uint32_t checked_ino; - - unsigned int flags; - - struct task_struct *gc_task; /* GC task struct */ - struct completion gc_thread_start; /* GC thread start completion */ - struct completion gc_thread_exit; /* GC thread exit completion port */ - - struct semaphore alloc_sem; /* Used to protect all the following - fields, and also to protect against - out-of-order writing of nodes. And GC. */ - uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER - (i.e. zero for OOB CLEANMARKER */ - - uint32_t flash_size; - uint32_t used_size; - uint32_t dirty_size; - uint32_t wasted_size; - uint32_t free_size; - uint32_t erasing_size; - uint32_t bad_size; - uint32_t sector_size; - uint32_t unchecked_size; - - uint32_t nr_free_blocks; - uint32_t nr_erasing_blocks; - - /* Number of free blocks there must be before we... */ - uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ - uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ - uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ - uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ - uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ - - uint32_t nospc_dirty_size; - - uint32_t nr_blocks; - struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks - * from the offset (blocks[ofs / sector_size]) */ - struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ - - struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ - - struct list_head clean_list; /* Blocks 100% full of clean data */ - struct list_head very_dirty_list; /* Blocks with lots of dirty space */ - struct list_head dirty_list; /* Blocks with some dirty space */ - struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ - struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ - struct list_head erasing_list; /* Blocks which are currently erasing */ - struct list_head erase_pending_list; /* Blocks which need erasing now */ - struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ - struct list_head free_list; /* Blocks which are free and ready to be used */ - struct list_head bad_list; /* Bad blocks. */ - struct list_head bad_used_list; /* Bad blocks with valid data in. */ - - spinlock_t erase_completion_lock; /* Protect free_list and erasing_list - against erase completion handler */ - wait_queue_head_t erase_wait; /* For waiting for erases to complete */ - - wait_queue_head_t inocache_wq; - struct jffs2_inode_cache **inocache_list; - spinlock_t inocache_lock; - - /* Sem to allow jffs2_garbage_collect_deletion_dirent to - drop the erase_completion_lock while it's holding a pointer - to an obsoleted node. I don't like this. Alternatives welcomed. */ - struct semaphore erase_free_sem; - - uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ - -#ifdef CONFIG_JFFS2_FS_WRITEBUFFER - /* Write-behind buffer for NAND flash */ - unsigned char *wbuf; - uint32_t wbuf_ofs; - uint32_t wbuf_len; - struct jffs2_inodirty *wbuf_inodes; - - struct rw_semaphore wbuf_sem; /* Protects the write buffer */ - - /* Information about out-of-band area usage... */ - struct nand_oobinfo *oobinfo; - uint32_t badblock_pos; - uint32_t fsdata_pos; - uint32_t fsdata_len; -#endif - - struct jffs2_summary *summary; /* Summary information */ - - /* OS-private pointer for getting back to master superblock info */ - void *os_priv; -}; - -#endif /* _JFFS2_FB_SB */ diff --git a/include/linux/joystick.h b/include/linux/joystick.h index 5fd20dd..e2d3a18 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -111,25 +111,25 @@ #define JS_GET_ALL 7 #define JS_SET_ALL 8 struct JS_DATA_TYPE { - int32_t buttons; - int32_t x; - int32_t y; + __s32 buttons; + __s32 x; + __s32 y; }; struct JS_DATA_SAVE_TYPE_32 { - int32_t JS_TIMEOUT; - int32_t BUSY; - int32_t JS_EXPIRETIME; - int32_t JS_TIMELIMIT; + __s32 JS_TIMEOUT; + __s32 BUSY; + __s32 JS_EXPIRETIME; + __s32 JS_TIMELIMIT; struct JS_DATA_TYPE JS_SAVE; struct JS_DATA_TYPE JS_CORR; }; struct JS_DATA_SAVE_TYPE_64 { - int32_t JS_TIMEOUT; - int32_t BUSY; - int64_t JS_EXPIRETIME; - int64_t JS_TIMELIMIT; + __s32 JS_TIMEOUT; + __s32 BUSY; + __s64 JS_EXPIRETIME; + __s64 JS_TIMELIMIT; struct JS_DATA_TYPE JS_SAVE; struct JS_DATA_TYPE JS_CORR; }; diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h index 9bbd040..849043c 100644 --- a/include/linux/kallsyms.h +++ b/include/linux/kallsyms.h @@ -5,7 +5,6 @@ #ifndef _LINUX_KALLSYMS_H #define _LINUX_KALLSYMS_H -#include #define KSYM_NAME_LEN 127 @@ -58,10 +57,25 @@ #else #define print_fn_descriptor_symbol(fmt, addr) print_symbol(fmt, addr) #endif -#define print_symbol(fmt, addr) \ -do { \ - __check_printsym_format(fmt, ""); \ - __print_symbol(fmt, addr); \ +static inline void print_symbol(const char *fmt, unsigned long addr) +{ + __check_printsym_format(fmt, ""); + __print_symbol(fmt, (unsigned long) + __builtin_extract_return_addr((void *)addr)); +} + +#ifndef CONFIG_64BIT +#define print_ip_sym(ip) \ +do { \ + printk("[<%08lx>]", ip); \ + print_symbol(" %s\n", ip); \ } while(0) +#else +#define print_ip_sym(ip) \ +do { \ + printk("[<%016lx>]", ip); \ + print_symbol(" %s\n", ip); \ +} while(0) +#endif #endif /*_LINUX_KALLSYMS_H*/ diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index 4eb851e..efe0ee4 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -155,10 +155,8 @@ static inline void con_schedule_flip(str { unsigned long flags; spin_lock_irqsave(&t->buf.lock, flags); - if (t->buf.tail != NULL) { - t->buf.tail->active = 0; + if (t->buf.tail != NULL) t->buf.tail->commit = t->buf.tail->used; - } spin_unlock_irqrestore(&t->buf.lock, flags); schedule_work(&t->buf.work); } diff --git a/include/linux/kernel.h b/include/linux/kernel.h index f4fc576..5c1ec1f 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -24,11 +24,15 @@ #define UINT_MAX (~0U) #define LONG_MAX ((long)(~0UL>>1)) #define LONG_MIN (-LONG_MAX - 1) #define ULONG_MAX (~0UL) +#define LLONG_MAX ((long long)(~0ULL>>1)) +#define LLONG_MIN (-LLONG_MAX - 1) +#define ULLONG_MAX (~0ULL) #define STACK_MAGIC 0xdeadbeef #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define ALIGN(x,a) (((x)+(a)-1)&~((a)-1)) +#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) #define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ @@ -75,7 +79,7 @@ #else # define might_sleep() do { might_resched(); } while (0) #endif -#define might_sleep_if(cond) do { if (unlikely(cond)) might_sleep(); } while (0) +#define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0) #define abs(x) ({ \ int __x = (x); \ @@ -114,6 +118,8 @@ extern int scnprintf(char * buf, size_t __attribute__ ((format (printf, 3, 4))); extern int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) __attribute__ ((format (printf, 3, 0))); +extern char *kasprintf(gfp_t gfp, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); extern int sscanf(const char *, const char *, ...) __attribute__ ((format (scanf, 2, 3))); @@ -331,6 +337,12 @@ struct sysinfo { /* Force a compilation error if condition is true */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +/* Force a compilation error if condition is true, but also produce a + result (of value 0 and type size_t), so the expression can be used + e.g. in a structure initializer (or where-ever else comma expressions + aren't permitted). */ +#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) + /* Trap pasters of __FUNCTION__ at compile-time */ #define __FUNCTION__ (__func__) diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index b462490..43e895f 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -1,7 +1,6 @@ #ifndef _LINUX_KERNEL_STAT_H #define _LINUX_KERNEL_STAT_H -#include #include #include #include diff --git a/include/linux/kexec.h b/include/linux/kexec.h index cfb3410..6427949 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -106,6 +106,7 @@ extern struct page *kimage_alloc_control extern void crash_kexec(struct pt_regs *); int kexec_should_crash(struct task_struct *); extern struct kimage *kexec_image; +extern struct kimage *kexec_crash_image; #define KEXEC_ON_CRASH 0x00000001 #define KEXEC_ARCH_MASK 0xffff0000 diff --git a/include/linux/key.h b/include/linux/key.h index cbf464a..169f05e 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -177,7 +177,8 @@ #define KEY_FLAG_NEGATIVE 5 /* set if ke /* * kernel managed key type definition */ -typedef int (*request_key_actor_t)(struct key *key, struct key *authkey, const char *op); +typedef int (*request_key_actor_t)(struct key *key, struct key *authkey, + const char *op, void *aux); struct key_type { /* name of the type */ @@ -205,6 +206,11 @@ struct key_type { /* match a key against a description */ int (*match)(const struct key *key, const void *desc); + /* clear some of the data from a key on revokation (optional) + * - the key's semaphore will be write-locked by the caller + */ + void (*revoke)(struct key *key); + /* clear the data from a key (optional) */ void (*destroy)(struct key *key); @@ -241,8 +247,16 @@ extern void unregister_key_type(struct k extern struct key *key_alloc(struct key_type *type, const char *desc, - uid_t uid, gid_t gid, key_perm_t perm, - int not_in_quota); + uid_t uid, gid_t gid, + struct task_struct *ctx, + key_perm_t perm, + unsigned long flags); + + +#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ +#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ +#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ + extern int key_payload_reserve(struct key *key, size_t datalen); extern int key_instantiate_and_link(struct key *key, const void *data, @@ -272,6 +286,11 @@ extern struct key *request_key(struct ke const char *description, const char *callout_info); +extern struct key *request_key_with_auxdata(struct key_type *type, + const char *description, + const char *callout_info, + void *aux); + extern int key_validate(struct key *key); extern key_ref_t key_create_or_update(key_ref_t keyring, @@ -279,7 +298,7 @@ extern key_ref_t key_create_or_update(ke const char *description, const void *payload, size_t plen, - int not_in_quota); + unsigned long flags); extern int key_update(key_ref_t key, const void *payload, @@ -292,7 +311,9 @@ extern int key_unlink(struct key *keyrin struct key *key); extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, - int not_in_quota, struct key *dest); + struct task_struct *ctx, + unsigned long flags, + struct key *dest); extern int keyring_clear(struct key *keyring); @@ -313,7 +334,8 @@ #define key_serial(key) ((key) ? (key)-> * the userspace interface */ extern struct key root_user_keyring, root_session_keyring; -extern int alloc_uid_keyring(struct user_struct *user); +extern int alloc_uid_keyring(struct user_struct *user, + struct task_struct *ctx); extern void switch_uid_keyring(struct user_struct *new_user); extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk); extern int copy_thread_group_keys(struct task_struct *tsk); @@ -342,7 +364,7 @@ #define key_ref_put(k) do { } while(0) #define make_key_ref(k) ({ NULL; }) #define key_ref_to_ptr(k) ({ NULL; }) #define is_key_possessed(k) 0 -#define alloc_uid_keyring(u) 0 +#define alloc_uid_keyring(u,c) 0 #define switch_uid_keyring(u) do { } while(0) #define __install_session_keyring(t, k) ({ NULL; }) #define copy_keys(f,t) 0 @@ -355,6 +377,10 @@ #define key_fsuid_changed(t) do { } whi #define key_fsgid_changed(t) do { } while(0) #define key_init() do { } while(0) +/* Initial keyrings */ +extern struct key root_user_keyring; +extern struct key root_session_keyring; + #endif /* CONFIG_KEYS */ #endif /* __KERNEL__ */ #endif /* _LINUX_KEY_H */ diff --git a/include/linux/kmod.h b/include/linux/kmod.h index e4a2315..0db22a1 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -20,7 +20,6 @@ #define __LINUX_KMOD_H__ */ #include -#include #include #include diff --git a/include/linux/kobject.h b/include/linux/kobject.h index c187c53..0503b2e 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -46,6 +46,8 @@ enum kobject_action { KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */ KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */ KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */ + KOBJ_UNDOCK = (__force kobject_action_t) 0x08, /* undocking */ + KOBJ_DOCK = (__force kobject_action_t) 0x09, /* dock */ }; struct kobject { @@ -190,6 +192,8 @@ struct subsystem _varname##_subsys = { \ /* The global /sys/kernel/ subsystem for people to chain off of */ extern struct subsystem kernel_subsys; +/* The global /sys/hypervisor/ subsystem */ +extern struct subsystem hypervisor_subsys; /** * Helpers for setting the kset of registered objects. diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 778adc0..8bf6702 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h @@ -29,7 +29,6 @@ #define _LINUX_KPROBES_H * and Prasanna S Panchamukhi * added function-return probes. */ -#include #include #include #include diff --git a/include/linux/kthread.h b/include/linux/kthread.h index ebdd41f..7cce5df 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -4,37 +4,19 @@ #define _LINUX_KTHREAD_H #include #include -/** - * kthread_create: create a kthread. - * @threadfn: the function to run until signal_pending(current). - * @data: data ptr for @threadfn. - * @namefmt: printf-style name for the thread. - * - * Description: This helper function creates and names a kernel - * thread. The thread will be stopped: use wake_up_process() to start - * it. See also kthread_run(), kthread_create_on_cpu(). - * - * When woken, the thread will run @threadfn() with @data as its - * argument. @threadfn can either call do_exit() directly if it is a - * standalone thread for which noone will call kthread_stop(), or - * return when 'kthread_should_stop()' is true (which means - * kthread_stop() has been called). The return value should be zero - * or a negative error number: it will be passed to kthread_stop(). - * - * Returns a task_struct or ERR_PTR(-ENOMEM). - */ struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], ...); /** - * kthread_run: create and wake a thread. + * kthread_run - create and wake a thread. * @threadfn: the function to run until signal_pending(current). * @data: data ptr for @threadfn. * @namefmt: printf-style name for the thread. * * Description: Convenient wrapper for kthread_create() followed by - * wake_up_process(). Returns the kthread, or ERR_PTR(-ENOMEM). */ + * wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM). + */ #define kthread_run(threadfn, data, namefmt, ...) \ ({ \ struct task_struct *__k \ @@ -44,50 +26,9 @@ ({ \ __k; \ }) -/** - * kthread_bind: bind a just-created kthread to a cpu. - * @k: thread created by kthread_create(). - * @cpu: cpu (might not be online, must be possible) for @k to run on. - * - * Description: This function is equivalent to set_cpus_allowed(), - * except that @cpu doesn't need to be online, and the thread must be - * stopped (ie. just returned from kthread_create(). - */ void kthread_bind(struct task_struct *k, unsigned int cpu); - -/** - * kthread_stop: stop a thread created by kthread_create(). - * @k: thread created by kthread_create(). - * - * Sets kthread_should_stop() for @k to return true, wakes it, and - * waits for it to exit. Your threadfn() must not call do_exit() - * itself if you use this function! This can also be called after - * kthread_create() instead of calling wake_up_process(): the thread - * will exit without calling threadfn(). - * - * Returns the result of threadfn(), or -EINTR if wake_up_process() - * was never called. */ int kthread_stop(struct task_struct *k); - -/** - * kthread_stop_sem: stop a thread created by kthread_create(). - * @k: thread created by kthread_create(). - * @s: semaphore that @k waits on while idle. - * - * Does essentially the same thing as kthread_stop() above, but wakes - * @k by calling up(@s). - * - * Returns the result of threadfn(), or -EINTR if wake_up_process() - * was never called. */ int kthread_stop_sem(struct task_struct *k, struct semaphore *s); - -/** - * kthread_should_stop: should this kthread return now? - * - * When someone calls kthread_stop on your kthread, it will be woken - * and this will return true. You should then return, and your return - * value will be passed through to kthread_stop(). - */ int kthread_should_stop(void); #endif /* _LINUX_KTHREAD_H */ diff --git a/include/linux/ktime.h b/include/linux/ktime.h index 62bc575..ed3396d 100644 --- a/include/linux/ktime.h +++ b/include/linux/ktime.h @@ -66,7 +66,6 @@ #if (BITS_PER_LONG == 64) || defined(CON /** * ktime_set - Set a ktime_t variable from a seconds/nanoseconds value - * * @secs: seconds to set * @nsecs: nanoseconds to set * @@ -138,7 +137,6 @@ static inline ktime_t ktime_set(const lo /** * ktime_sub - subtract two ktime_t variables - * * @lhs: minuend * @rhs: subtrahend * @@ -157,7 +155,6 @@ static inline ktime_t ktime_sub(const kt /** * ktime_add - add two ktime_t variables - * * @add1: addend1 * @add2: addend2 * @@ -184,7 +181,6 @@ static inline ktime_t ktime_add(const kt /** * ktime_add_ns - Add a scalar nanoseconds value to a ktime_t variable - * * @kt: addend * @nsec: the scalar nsec value to add * @@ -194,7 +190,6 @@ extern ktime_t ktime_add_ns(const ktime_ /** * timespec_to_ktime - convert a timespec to ktime_t format - * * @ts: the timespec variable to convert * * Returns a ktime_t variable with the converted timespec value @@ -207,7 +202,6 @@ static inline ktime_t timespec_to_ktime( /** * timeval_to_ktime - convert a timeval to ktime_t format - * * @tv: the timeval variable to convert * * Returns a ktime_t variable with the converted timeval value @@ -220,7 +214,6 @@ static inline ktime_t timeval_to_ktime(c /** * ktime_to_timespec - convert a ktime_t variable to timespec format - * * @kt: the ktime_t variable to convert * * Returns the timespec representation of the ktime value @@ -233,7 +226,6 @@ static inline struct timespec ktime_to_t /** * ktime_to_timeval - convert a ktime_t variable to timeval format - * * @kt: the ktime_t variable to convert * * Returns the timeval representation of the ktime value diff --git a/include/linux/libata.h b/include/linux/libata.h index b80d2e7..6cc497a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -30,9 +30,11 @@ #include #include #include #include +#include #include #include #include +#include /* * compile-time options: to be removed as soon as all the drivers are @@ -44,7 +46,6 @@ #undef ATA_IRQ_TRAP /* define to ack sc #undef ATA_NDEBUG /* define to disable quick runtime checks */ #undef ATA_ENABLE_PATA /* define to enable PATA support in some * low-level drivers */ -#undef ATAPI_ENABLE_DMADIR /* enables ATAPI DMADIR bridge support */ /* note: prints function name for you */ @@ -108,8 +109,11 @@ enum { LIBATA_MAX_PRD = ATA_MAX_PRD / 2, ATA_MAX_PORTS = 8, ATA_DEF_QUEUE = 1, - ATA_MAX_QUEUE = 1, + /* tag ATA_MAX_QUEUE - 1 is reserved for internal commands */ + ATA_MAX_QUEUE = 32, + ATA_TAG_INTERNAL = ATA_MAX_QUEUE - 1, ATA_MAX_SECTORS = 200, /* FIXME */ + ATA_MAX_SECTORS_LBA48 = 65535, ATA_MAX_BUS = 2, ATA_DEF_BUSY_WAIT = 10000, ATA_SHORT_PAUSE = (HZ >> 6) + 1, @@ -120,9 +124,18 @@ enum { ATA_SHT_USE_CLUSTERING = 1, /* struct ata_device stuff */ - ATA_DFLAG_LBA48 = (1 << 0), /* device supports LBA48 */ - ATA_DFLAG_PIO = (1 << 1), /* device currently in PIO mode */ - ATA_DFLAG_LBA = (1 << 2), /* device supports LBA */ + ATA_DFLAG_LBA = (1 << 0), /* device supports LBA */ + ATA_DFLAG_LBA48 = (1 << 1), /* device supports LBA48 */ + ATA_DFLAG_CDB_INTR = (1 << 2), /* device asserts INTRQ when ready for CDB */ + ATA_DFLAG_NCQ = (1 << 3), /* device supports NCQ */ + ATA_DFLAG_CFG_MASK = (1 << 8) - 1, + + ATA_DFLAG_PIO = (1 << 8), /* device currently in PIO mode */ + ATA_DFLAG_SUSPENDED = (1 << 9), /* device suspended */ + ATA_DFLAG_INIT_MASK = (1 << 16) - 1, + + ATA_DFLAG_DETACH = (1 << 16), + ATA_DFLAG_DETACHED = (1 << 17), ATA_DEV_UNKNOWN = 0, /* unknown device */ ATA_DEV_ATA = 1, /* ATA device */ @@ -132,43 +145,63 @@ enum { ATA_DEV_NONE = 5, /* no device */ /* struct ata_port flags */ - ATA_FLAG_SLAVE_POSS = (1 << 1), /* host supports slave dev */ + ATA_FLAG_SLAVE_POSS = (1 << 0), /* host supports slave dev */ /* (doesn't imply presence) */ - ATA_FLAG_PORT_DISABLED = (1 << 2), /* port is disabled, ignore it */ - ATA_FLAG_SATA = (1 << 3), - ATA_FLAG_NO_LEGACY = (1 << 4), /* no legacy mode check */ - ATA_FLAG_SRST = (1 << 5), /* (obsolete) use ATA SRST, not E.D.D. */ - ATA_FLAG_MMIO = (1 << 6), /* use MMIO, not PIO */ - ATA_FLAG_SATA_RESET = (1 << 7), /* (obsolete) use COMRESET */ - ATA_FLAG_PIO_DMA = (1 << 8), /* PIO cmds via DMA */ - ATA_FLAG_NOINTR = (1 << 9), /* FIXME: Remove this once - * proper HSM is in place. */ - ATA_FLAG_DEBUGMSG = (1 << 10), - ATA_FLAG_NO_ATAPI = (1 << 11), /* No ATAPI support */ - - ATA_FLAG_SUSPENDED = (1 << 12), /* port is suspended */ - - ATA_FLAG_PIO_LBA48 = (1 << 13), /* Host DMA engine is LBA28 only */ - ATA_FLAG_IRQ_MASK = (1 << 14), /* Mask IRQ in PIO xfers */ - - ATA_FLAG_FLUSH_PORT_TASK = (1 << 15), /* Flush port task */ - ATA_FLAG_IN_EH = (1 << 16), /* EH in progress */ - - ATA_QCFLAG_ACTIVE = (1 << 1), /* cmd not yet ack'd to scsi lyer */ - ATA_QCFLAG_SG = (1 << 3), /* have s/g table? */ - ATA_QCFLAG_SINGLE = (1 << 4), /* no s/g, just a single buffer */ + ATA_FLAG_SATA = (1 << 1), + ATA_FLAG_NO_LEGACY = (1 << 2), /* no legacy mode check */ + ATA_FLAG_MMIO = (1 << 3), /* use MMIO, not PIO */ + ATA_FLAG_SRST = (1 << 4), /* (obsolete) use ATA SRST, not E.D.D. */ + ATA_FLAG_SATA_RESET = (1 << 5), /* (obsolete) use COMRESET */ + ATA_FLAG_NO_ATAPI = (1 << 6), /* No ATAPI support */ + ATA_FLAG_PIO_DMA = (1 << 7), /* PIO cmds via DMA */ + ATA_FLAG_PIO_LBA48 = (1 << 8), /* Host DMA engine is LBA28 only */ + ATA_FLAG_PIO_POLLING = (1 << 9), /* use polling PIO if LLD + * doesn't handle PIO interrupts */ + ATA_FLAG_NCQ = (1 << 10), /* host supports NCQ */ + ATA_FLAG_HRST_TO_RESUME = (1 << 11), /* hardreset to resume phy */ + ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H + * Register FIS clearing BSY */ + ATA_FLAG_DEBUGMSG = (1 << 13), + + /* The following flag belongs to ap->pflags but is kept in + * ap->flags because it's referenced in many LLDs and will be + * removed in not-too-distant future. + */ + ATA_FLAG_DISABLED = (1 << 23), /* port is disabled, ignore it */ + + /* bits 24:31 of ap->flags are reserved for LLD specific flags */ + + /* struct ata_port pflags */ + ATA_PFLAG_EH_PENDING = (1 << 0), /* EH pending */ + ATA_PFLAG_EH_IN_PROGRESS = (1 << 1), /* EH in progress */ + ATA_PFLAG_FROZEN = (1 << 2), /* port is frozen */ + ATA_PFLAG_RECOVERED = (1 << 3), /* recovery action performed */ + ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */ + ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */ + ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */ + + ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */ + ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ + ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ + + /* struct ata_queued_cmd flags */ + ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */ + ATA_QCFLAG_SG = (1 << 1), /* have s/g table? */ + ATA_QCFLAG_SINGLE = (1 << 2), /* no s/g, just a single buffer */ ATA_QCFLAG_DMAMAP = ATA_QCFLAG_SG | ATA_QCFLAG_SINGLE, - ATA_QCFLAG_EH_SCHEDULED = (1 << 5), /* EH scheduled */ + ATA_QCFLAG_IO = (1 << 3), /* standard IO command */ + ATA_QCFLAG_RESULT_TF = (1 << 4), /* result TF requested */ + + ATA_QCFLAG_FAILED = (1 << 16), /* cmd failed and is owned by EH */ + ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */ + ATA_QCFLAG_EH_SCHEDULED = (1 << 18), /* EH scheduled (obsolete) */ /* host set flags */ ATA_HOST_SIMPLEX = (1 << 0), /* Host is simplex, one DMA channel per host_set only */ /* various lengths of time */ - ATA_TMOUT_PIO = 30 * HZ, ATA_TMOUT_BOOT = 30 * HZ, /* heuristic */ ATA_TMOUT_BOOT_QUICK = 7 * HZ, /* heuristic */ - ATA_TMOUT_CDB = 30 * HZ, - ATA_TMOUT_CDB_QUICK = 5 * HZ, ATA_TMOUT_INTERNAL = 30 * HZ, ATA_TMOUT_INTERNAL_QUICK = 5 * HZ, @@ -207,21 +240,63 @@ enum { /* size of buffer to pad xfers ending on unaligned boundaries */ ATA_DMA_PAD_SZ = 4, ATA_DMA_PAD_BUF_SZ = ATA_DMA_PAD_SZ * ATA_MAX_QUEUE, - - /* Masks for port functions */ + + /* masks for port functions */ ATA_PORT_PRIMARY = (1 << 0), ATA_PORT_SECONDARY = (1 << 1), + + /* ering size */ + ATA_ERING_SIZE = 32, + + /* desc_len for ata_eh_info and context */ + ATA_EH_DESC_LEN = 80, + + /* reset / recovery action types */ + ATA_EH_REVALIDATE = (1 << 0), + ATA_EH_SOFTRESET = (1 << 1), + ATA_EH_HARDRESET = (1 << 2), + ATA_EH_SUSPEND = (1 << 3), + ATA_EH_RESUME = (1 << 4), + ATA_EH_PM_FREEZE = (1 << 5), + + ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, + ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE | ATA_EH_SUSPEND | + ATA_EH_RESUME | ATA_EH_PM_FREEZE, + + /* ata_eh_info->flags */ + ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */ + ATA_EHI_RESUME_LINK = (1 << 1), /* need to resume link */ + ATA_EHI_NO_AUTOPSY = (1 << 2), /* no autopsy */ + ATA_EHI_QUIET = (1 << 3), /* be quiet */ + + ATA_EHI_DID_RESET = (1 << 16), /* already reset this port */ + + /* max repeat if error condition is still set after ->error_handler */ + ATA_EH_MAX_REPEAT = 5, + + /* how hard are we gonna try to probe/recover devices */ + ATA_PROBE_MAX_TRIES = 3, + ATA_EH_RESET_TRIES = 3, + ATA_EH_DEV_TRIES = 3, + + /* Drive spinup time (time from power-on to the first D2H FIS) + * in msecs - 8s currently. Failing to get ready in this time + * isn't critical. It will result in reset failure for + * controllers which can't wait for the first D2H FIS. libata + * will retry, so it just has to be long enough to spin up + * most devices. + */ + ATA_SPINUP_WAIT = 8000, }; enum hsm_task_states { - HSM_ST_UNKNOWN, - HSM_ST_IDLE, - HSM_ST_POLL, - HSM_ST_TMOUT, - HSM_ST, - HSM_ST_LAST, - HSM_ST_LAST_POLL, - HSM_ST_ERR, + HSM_ST_UNKNOWN, /* state unknown */ + HSM_ST_IDLE, /* no command on going */ + HSM_ST, /* (waiting the device to) transfer data */ + HSM_ST_LAST, /* (waiting the device to) complete command */ + HSM_ST_ERR, /* error */ + HSM_ST_FIRST, /* (waiting the device to) + write CDB or first data block */ }; enum ata_completion_errors { @@ -244,9 +319,9 @@ struct ata_queued_cmd; /* typedefs */ typedef void (*ata_qc_cb_t) (struct ata_queued_cmd *qc); -typedef void (*ata_probeinit_fn_t)(struct ata_port *); -typedef int (*ata_reset_fn_t)(struct ata_port *, int, unsigned int *); -typedef void (*ata_postreset_fn_t)(struct ata_port *ap, unsigned int *); +typedef int (*ata_prereset_fn_t)(struct ata_port *ap); +typedef int (*ata_reset_fn_t)(struct ata_port *ap, unsigned int *classes); +typedef void (*ata_postreset_fn_t)(struct ata_port *ap, unsigned int *classes); struct ata_ioports { unsigned long cmd_addr; @@ -297,7 +372,8 @@ struct ata_host_set { unsigned long flags; int simplex_claimed; /* Keep seperate in case we ever need to do this locked */ - struct ata_port * ports[0]; + struct ata_host_set *next; /* for legacy mode */ + struct ata_port *ports[0]; }; struct ata_queued_cmd { @@ -336,7 +412,7 @@ struct ata_queued_cmd { struct scatterlist *__sg; unsigned int err_mask; - + struct ata_taskfile result_tf; ata_qc_cb_t complete_fn; void *private_data; @@ -348,12 +424,26 @@ struct ata_host_stats { unsigned long rw_reqbuf; }; +struct ata_ering_entry { + int is_io; + unsigned int err_mask; + u64 timestamp; +}; + +struct ata_ering { + int cursor; + struct ata_ering_entry ring[ATA_ERING_SIZE]; +}; + struct ata_device { - u64 n_sectors; /* size of device, if ATA */ + struct ata_port *ap; + unsigned int devno; /* 0 or 1 */ unsigned long flags; /* ATA_DFLAG_xxx */ + struct scsi_device *sdev; /* attached SCSI device */ + /* n_sector is used as CLEAR_OFFSET, read comment above CLEAR_OFFSET */ + u64 n_sectors; /* size of device, if ATA */ unsigned int class; /* ATA_DEV_xxx */ - unsigned int devno; /* 0 or 1 */ - u16 *id; /* IDENTIFY xxx DEVICE data */ + u16 id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */ u8 pio_mode; u8 dma_mode; u8 xfer_mode; @@ -373,12 +463,44 @@ struct ata_device { u16 cylinders; /* Number of cylinders */ u16 heads; /* Number of heads */ u16 sectors; /* Number of sectors per track */ + + /* error history */ + struct ata_ering ering; +}; + +/* Offset into struct ata_device. Fields above it are maintained + * acress device init. Fields below are zeroed. + */ +#define ATA_DEVICE_CLEAR_OFFSET offsetof(struct ata_device, n_sectors) + +struct ata_eh_info { + struct ata_device *dev; /* offending device */ + u32 serror; /* SError from LLDD */ + unsigned int err_mask; /* port-wide err_mask */ + unsigned int action; /* ATA_EH_* action mask */ + unsigned int dev_action[ATA_MAX_DEVICES]; /* dev EH action */ + unsigned int flags; /* ATA_EHI_* flags */ + + unsigned long hotplug_timestamp; + unsigned int probe_mask; + + char desc[ATA_EH_DESC_LEN]; + int desc_len; +}; + +struct ata_eh_context { + struct ata_eh_info i; + int tries[ATA_MAX_DEVICES]; + unsigned int classes[ATA_MAX_DEVICES]; + unsigned int did_probe_mask; }; struct ata_port { struct Scsi_Host *host; /* our co-allocated scsi host */ const struct ata_port_operations *ops; + spinlock_t *lock; unsigned long flags; /* ATA_FLAG_xxx */ + unsigned int pflags; /* ATA_PFLAG_xxx */ unsigned int id; /* unique id req'd by scsi midlyr */ unsigned int port_no; /* unique port #; from zero */ unsigned int hard_port_no; /* hardware port #; from zero */ @@ -397,26 +519,43 @@ struct ata_port { unsigned int mwdma_mask; unsigned int udma_mask; unsigned int cbl; /* cable type; ATA_CBL_xxx */ + unsigned int hw_sata_spd_limit; + unsigned int sata_spd_limit; /* SATA PHY speed limit */ + + /* record runtime error info, protected by host_set lock */ + struct ata_eh_info eh_info; + /* EH context owned by EH */ + struct ata_eh_context eh_context; struct ata_device device[ATA_MAX_DEVICES]; struct ata_queued_cmd qcmd[ATA_MAX_QUEUE]; - unsigned long qactive; + unsigned long qc_allocated; + unsigned int qc_active; + unsigned int active_tag; + u32 sactive; struct ata_host_stats stats; struct ata_host_set *host_set; struct device *dev; struct work_struct port_task; + struct work_struct hotplug_task; + struct work_struct scsi_rescan_task; unsigned int hsm_task_state; - unsigned long pio_task_timeout; u32 msg_enable; struct list_head eh_done_q; + wait_queue_head_t eh_wait_q; + + pm_message_t pm_mesg; + int *pm_result; void *private_data; + + u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */ }; struct ata_port_operations { @@ -438,7 +577,6 @@ struct ata_port_operations { void (*phy_reset) (struct ata_port *ap); /* obsolete */ void (*set_mode) (struct ata_port *ap); - int (*probe_reset) (struct ata_port *ap, unsigned int *classes); void (*post_set_mode) (struct ata_port *ap); @@ -447,10 +585,20 @@ struct ata_port_operations { void (*bmdma_setup) (struct ata_queued_cmd *qc); void (*bmdma_start) (struct ata_queued_cmd *qc); + void (*data_xfer) (struct ata_device *, unsigned char *, unsigned int, int); + void (*qc_prep) (struct ata_queued_cmd *qc); unsigned int (*qc_issue) (struct ata_queued_cmd *qc); - void (*eng_timeout) (struct ata_port *ap); + /* Error handlers. ->error_handler overrides ->eng_timeout and + * indicates that new-style EH is in place. + */ + void (*eng_timeout) (struct ata_port *ap); /* obsolete */ + + void (*freeze) (struct ata_port *ap); + void (*thaw) (struct ata_port *ap); + void (*error_handler) (struct ata_port *ap); + void (*post_internal_cmd) (struct ata_queued_cmd *qc); irqreturn_t (*irq_handler)(int, void *, struct pt_regs *); void (*irq_clear) (struct ata_port *); @@ -459,6 +607,9 @@ struct ata_port_operations { void (*scr_write) (struct ata_port *ap, unsigned int sc_reg, u32 val); + int (*port_suspend) (struct ata_port *ap, pm_message_t mesg); + int (*port_resume) (struct ata_port *ap); + int (*port_start) (struct ata_port *ap); void (*port_stop) (struct ata_port *ap); @@ -492,51 +643,71 @@ struct ata_timing { #define FIT(v,vmin,vmax) max_t(short,min_t(short,v,vmax),vmin) +extern const unsigned long sata_deb_timing_normal[]; +extern const unsigned long sata_deb_timing_hotplug[]; +extern const unsigned long sata_deb_timing_long[]; + +static inline const unsigned long * +sata_ehc_deb_timing(struct ata_eh_context *ehc) +{ + if (ehc->i.flags & ATA_EHI_HOTPLUGGED) + return sata_deb_timing_hotplug; + else + return sata_deb_timing_normal; +} + extern void ata_port_probe(struct ata_port *); extern void __sata_phy_reset(struct ata_port *ap); extern void sata_phy_reset(struct ata_port *ap); extern void ata_bus_reset(struct ata_port *ap); -extern int ata_drive_probe_reset(struct ata_port *ap, - ata_probeinit_fn_t probeinit, - ata_reset_fn_t softreset, ata_reset_fn_t hardreset, - ata_postreset_fn_t postreset, unsigned int *classes); -extern void ata_std_probeinit(struct ata_port *ap); -extern int ata_std_softreset(struct ata_port *ap, int verbose, - unsigned int *classes); -extern int sata_std_hardreset(struct ata_port *ap, int verbose, - unsigned int *class); +extern int sata_set_spd(struct ata_port *ap); +extern int sata_phy_debounce(struct ata_port *ap, const unsigned long *param); +extern int sata_phy_resume(struct ata_port *ap, const unsigned long *param); +extern int ata_std_prereset(struct ata_port *ap); +extern int ata_std_softreset(struct ata_port *ap, unsigned int *classes); +extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class); extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes); -extern int ata_dev_revalidate(struct ata_port *ap, struct ata_device *dev, - int post_reset); +extern int ata_dev_revalidate(struct ata_device *dev, int post_reset); extern void ata_port_disable(struct ata_port *); extern void ata_std_ports(struct ata_ioports *ioaddr); #ifdef CONFIG_PCI extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, unsigned int n_ports); extern void ata_pci_remove_one (struct pci_dev *pdev); +extern void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state); +extern void ata_pci_device_do_resume(struct pci_dev *pdev); extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state); extern int ata_pci_device_resume(struct pci_dev *pdev); extern int ata_pci_clear_simplex(struct pci_dev *pdev); #endif /* CONFIG_PCI */ extern int ata_device_add(const struct ata_probe_ent *ent); +extern void ata_port_detach(struct ata_port *ap); extern void ata_host_set_remove(struct ata_host_set *host_set); extern int ata_scsi_detect(struct scsi_host_template *sht); extern int ata_scsi_ioctl(struct scsi_device *dev, int cmd, void __user *arg); extern int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); -extern void ata_eh_qc_complete(struct ata_queued_cmd *qc); -extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); extern int ata_scsi_release(struct Scsi_Host *host); extern unsigned int ata_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); +extern int sata_scr_valid(struct ata_port *ap); +extern int sata_scr_read(struct ata_port *ap, int reg, u32 *val); +extern int sata_scr_write(struct ata_port *ap, int reg, u32 val); +extern int sata_scr_write_flush(struct ata_port *ap, int reg, u32 val); +extern int ata_port_online(struct ata_port *ap); +extern int ata_port_offline(struct ata_port *ap); extern int ata_scsi_device_resume(struct scsi_device *); extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state); -extern int ata_device_resume(struct ata_port *, struct ata_device *); -extern int ata_device_suspend(struct ata_port *, struct ata_device *, pm_message_t state); +extern int ata_host_set_suspend(struct ata_host_set *host_set, + pm_message_t mesg); +extern void ata_host_set_resume(struct ata_host_set *host_set); extern int ata_ratelimit(void); extern unsigned int ata_busy_sleep(struct ata_port *ap, unsigned long timeout_pat, unsigned long timeout); extern void ata_port_queue_task(struct ata_port *ap, void (*fn)(void *), void *data, unsigned long delay); +extern u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, + unsigned long interval_msec, + unsigned long timeout_msec); /* * Default driver ops implementations @@ -550,11 +721,16 @@ extern void ata_std_dev_select (struct a extern u8 ata_check_status(struct ata_port *ap); extern u8 ata_altstatus(struct ata_port *ap); extern void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf); -extern int ata_std_probe_reset(struct ata_port *ap, unsigned int *classes); extern int ata_port_start (struct ata_port *ap); extern void ata_port_stop (struct ata_port *ap); extern void ata_host_stop (struct ata_host_set *host_set); extern irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs); +extern void ata_mmio_data_xfer(struct ata_device *adev, unsigned char *buf, + unsigned int buflen, int write_data); +extern void ata_pio_data_xfer(struct ata_device *adev, unsigned char *buf, + unsigned int buflen, int write_data); +extern void ata_pio_data_xfer_noirq(struct ata_device *adev, unsigned char *buf, + unsigned int buflen, int write_data); extern void ata_qc_prep(struct ata_queued_cmd *qc); extern void ata_noop_qc_prep(struct ata_queued_cmd *qc); extern unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc); @@ -572,17 +748,29 @@ extern void ata_bmdma_start (struct ata_ extern void ata_bmdma_stop(struct ata_queued_cmd *qc); extern u8 ata_bmdma_status(struct ata_port *ap); extern void ata_bmdma_irq_clear(struct ata_port *ap); -extern void __ata_qc_complete(struct ata_queued_cmd *qc); -extern void ata_eng_timeout(struct ata_port *ap); -extern void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev, - struct scsi_cmnd *cmd, +extern void ata_bmdma_freeze(struct ata_port *ap); +extern void ata_bmdma_thaw(struct ata_port *ap); +extern void ata_bmdma_drive_eh(struct ata_port *ap, ata_prereset_fn_t prereset, + ata_reset_fn_t softreset, + ata_reset_fn_t hardreset, + ata_postreset_fn_t postreset); +extern void ata_bmdma_error_handler(struct ata_port *ap); +extern void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc); +extern int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, + u8 status, int in_wq); +extern void ata_qc_complete(struct ata_queued_cmd *qc); +extern int ata_qc_complete_multiple(struct ata_port *ap, u32 qc_active, + void (*finish_qc)(struct ata_queued_cmd *)); +extern void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)); extern int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]); extern int ata_scsi_slave_config(struct scsi_device *sdev); -extern struct ata_device *ata_dev_pair(struct ata_port *ap, - struct ata_device *adev); +extern void ata_scsi_slave_destroy(struct scsi_device *sdev); +extern int ata_scsi_change_queue_depth(struct scsi_device *sdev, + int queue_depth); +extern struct ata_device *ata_dev_pair(struct ata_device *adev); /* * Timing helpers @@ -628,7 +816,69 @@ extern int pci_test_config_bits(struct p extern unsigned long ata_pci_default_filter(const struct ata_port *, struct ata_device *, unsigned long); #endif /* CONFIG_PCI */ +/* + * EH + */ +extern void ata_eng_timeout(struct ata_port *ap); + +extern void ata_port_schedule_eh(struct ata_port *ap); +extern int ata_port_abort(struct ata_port *ap); +extern int ata_port_freeze(struct ata_port *ap); +extern void ata_eh_freeze_port(struct ata_port *ap); +extern void ata_eh_thaw_port(struct ata_port *ap); + +extern void ata_eh_qc_complete(struct ata_queued_cmd *qc); +extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); + +extern void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, + ata_reset_fn_t softreset, ata_reset_fn_t hardreset, + ata_postreset_fn_t postreset); + +/* + * printk helpers + */ +#define ata_port_printk(ap, lv, fmt, args...) \ + printk(lv"ata%u: "fmt, (ap)->id , ##args) + +#define ata_dev_printk(dev, lv, fmt, args...) \ + printk(lv"ata%u.%02u: "fmt, (dev)->ap->id, (dev)->devno , ##args) + +/* + * ata_eh_info helpers + */ +#define ata_ehi_push_desc(ehi, fmt, args...) do { \ + (ehi)->desc_len += scnprintf((ehi)->desc + (ehi)->desc_len, \ + ATA_EH_DESC_LEN - (ehi)->desc_len, \ + fmt , ##args); \ +} while (0) + +#define ata_ehi_clear_desc(ehi) do { \ + (ehi)->desc[0] = '\0'; \ + (ehi)->desc_len = 0; \ +} while (0) + +static inline void __ata_ehi_hotplugged(struct ata_eh_info *ehi) +{ + if (ehi->flags & ATA_EHI_HOTPLUGGED) + return; + + ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK; + ehi->hotplug_timestamp = jiffies; + + ehi->action |= ATA_EH_SOFTRESET; + ehi->probe_mask |= (1 << ATA_MAX_DEVICES) - 1; +} + +static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi) +{ + __ata_ehi_hotplugged(ehi); + ehi->err_mask |= AC_ERR_ATA_BUS; +} + +/* + * qc helpers + */ static inline int ata_sg_is_last(struct scatterlist *sg, struct ata_queued_cmd *qc) { @@ -671,16 +921,60 @@ static inline unsigned int ata_tag_valid return (tag < ATA_MAX_QUEUE) ? 1 : 0; } -static inline unsigned int ata_class_present(unsigned int class) +static inline unsigned int ata_tag_internal(unsigned int tag) +{ + return tag == ATA_MAX_QUEUE - 1; +} + +/* + * device helpers + */ +static inline unsigned int ata_class_enabled(unsigned int class) { return class == ATA_DEV_ATA || class == ATA_DEV_ATAPI; } -static inline unsigned int ata_dev_present(const struct ata_device *dev) +static inline unsigned int ata_class_disabled(unsigned int class) +{ + return class == ATA_DEV_ATA_UNSUP || class == ATA_DEV_ATAPI_UNSUP; +} + +static inline unsigned int ata_class_absent(unsigned int class) +{ + return !ata_class_enabled(class) && !ata_class_disabled(class); +} + +static inline unsigned int ata_dev_enabled(const struct ata_device *dev) +{ + return ata_class_enabled(dev->class); +} + +static inline unsigned int ata_dev_disabled(const struct ata_device *dev) +{ + return ata_class_disabled(dev->class); +} + +static inline unsigned int ata_dev_absent(const struct ata_device *dev) +{ + return ata_class_absent(dev->class); +} + +static inline unsigned int ata_dev_ready(const struct ata_device *dev) +{ + return ata_dev_enabled(dev) && !(dev->flags & ATA_DFLAG_SUSPENDED); +} + +/* + * port helpers + */ +static inline int ata_port_max_devices(const struct ata_port *ap) { - return ata_class_present(dev->class); + if (ap->flags & ATA_FLAG_SLAVE_POSS) + return 2; + return 1; } + static inline u8 ata_chk_status(struct ata_port *ap) { return ap->ops->check_status(ap); @@ -759,20 +1053,35 @@ static inline void ata_qc_set_polling(st qc->tf.ctl |= ATA_NIEN; } -static inline struct ata_queued_cmd *ata_qc_from_tag (struct ata_port *ap, - unsigned int tag) +static inline struct ata_queued_cmd *__ata_qc_from_tag(struct ata_port *ap, + unsigned int tag) { if (likely(ata_tag_valid(tag))) return &ap->qcmd[tag]; return NULL; } -static inline void ata_tf_init(struct ata_port *ap, struct ata_taskfile *tf, unsigned int device) +static inline struct ata_queued_cmd *ata_qc_from_tag(struct ata_port *ap, + unsigned int tag) +{ + struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); + + if (unlikely(!qc) || !ap->ops->error_handler) + return qc; + + if ((qc->flags & (ATA_QCFLAG_ACTIVE | + ATA_QCFLAG_FAILED)) == ATA_QCFLAG_ACTIVE) + return qc; + + return NULL; +} + +static inline void ata_tf_init(struct ata_device *dev, struct ata_taskfile *tf) { memset(tf, 0, sizeof(*tf)); - tf->ctl = ap->ctl; - if (device == 0) + tf->ctl = dev->ap->ctl; + if (dev->devno == 0) tf->device = ATA_DEVICE_OBS; else tf->device = ATA_DEVICE_OBS | ATA_DEV1; @@ -787,26 +1096,11 @@ static inline void ata_qc_reinit(struct qc->nbytes = qc->curbytes = 0; qc->err_mask = 0; - ata_tf_init(qc->ap, &qc->tf, qc->dev->devno); -} - -/** - * ata_qc_complete - Complete an active ATA command - * @qc: Command to complete - * @err_mask: ATA Status register contents - * - * Indicate to the mid and upper layers that an ATA - * command has completed, with either an ok or not-ok status. - * - * LOCKING: - * spin_lock_irqsave(host_set lock) - */ -static inline void ata_qc_complete(struct ata_queued_cmd *qc) -{ - if (unlikely(qc->flags & ATA_QCFLAG_EH_SCHEDULED)) - return; + ata_tf_init(qc->dev, &qc->tf); - __ata_qc_complete(qc); + /* init result_tf such that it indicates normal completion */ + qc->result_tf.command = ATA_DRDY; + qc->result_tf.feature = 0; } /** @@ -885,28 +1179,6 @@ static inline u8 ata_irq_ack(struct ata_ return status; } -static inline u32 scr_read(struct ata_port *ap, unsigned int reg) -{ - return ap->ops->scr_read(ap, reg); -} - -static inline void scr_write(struct ata_port *ap, unsigned int reg, u32 val) -{ - ap->ops->scr_write(ap, reg, val); -} - -static inline void scr_write_flush(struct ata_port *ap, unsigned int reg, - u32 val) -{ - ap->ops->scr_write(ap, reg, val); - (void) ap->ops->scr_read(ap, reg); -} - -static inline unsigned int sata_dev_present(struct ata_port *ap) -{ - return ((scr_read(ap, SCR_STATUS) & 0xf) == 0x3) ? 1 : 0; -} - static inline int ata_try_flush_cache(const struct ata_device *dev) { return ata_id_wcache_enabled(dev->id) || @@ -916,7 +1188,7 @@ static inline int ata_try_flush_cache(co static inline unsigned int ac_err_mask(u8 status) { - if (status & ATA_BUSY) + if (status & (ATA_BUSY | ATA_DRQ)) return AC_ERR_HSM; if (status & (ATA_ERR | ATA_DF)) return AC_ERR_DEV; @@ -944,4 +1216,9 @@ static inline void ata_pad_free(struct a dma_free_coherent(dev, ATA_DMA_PAD_BUF_SZ, ap->pad, ap->pad_dma); } +static inline struct ata_port *ata_shost_to_port(struct Scsi_Host *host) +{ + return (struct ata_port *) &host->hostdata[0]; +} + #endif /* __LINUX_LIBATA_H__ */ diff --git a/include/linux/license.h b/include/linux/license.h new file mode 100644 index 0000000..decdbf4 --- /dev/null +++ b/include/linux/license.h @@ -0,0 +1,14 @@ +#ifndef __LICENSE_H +#define __LICENSE_H + +static inline int license_is_gpl_compatible(const char *license) +{ + return (strcmp(license, "GPL") == 0 + || strcmp(license, "GPL v2") == 0 + || strcmp(license, "GPL and additional rights") == 0 + || strcmp(license, "Dual BSD/GPL") == 0 + || strcmp(license, "Dual MIT/GPL") == 0 + || strcmp(license, "Dual MPL/GPL") == 0); +} + +#endif diff --git a/include/linux/linkage.h b/include/linux/linkage.h index c08c998..932021f 100644 --- a/include/linux/linkage.h +++ b/include/linux/linkage.h @@ -1,7 +1,6 @@ #ifndef _LINUX_LINKAGE_H #define _LINUX_LINKAGE_H -#include #include #ifdef __cplusplus diff --git a/include/linux/list.h b/include/linux/list.h index 76f0571..6b74adf 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -4,18 +4,11 @@ #define _LINUX_LIST_H #ifdef __KERNEL__ #include +#include #include #include /* - * These are non-NULL pointers that will result in page faults - * under normal circumstances, used to verify that nobody uses - * non-initialized list entries. - */ -#define LIST_POISON1 ((void *) 0x00100100) -#define LIST_POISON2 ((void *) 0x00200200) - -/* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when @@ -197,12 +190,35 @@ static inline void list_del_rcu(struct l entry->prev = LIST_POISON2; } +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * Note: if 'old' was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + /* * list_replace_rcu - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * The old entry will be replaced with the new entry atomically. + * Note: 'old' should not be empty. */ static inline void list_replace_rcu(struct list_head *old, struct list_head *new) @@ -258,16 +274,17 @@ static inline int list_empty(const struc } /** - * list_empty_careful - tests whether a list is - * empty _and_ checks that no other CPU might be - * in the process of still modifying either member + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. - * - * @head: the list to test. */ static inline int list_empty_careful(const struct list_head *head) { @@ -327,7 +344,7 @@ #define list_entry(ptr, type, member) \ /** * list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. + * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ @@ -336,7 +353,7 @@ #define list_for_each(pos, head) \ /** * __list_for_each - iterate over a list - * @pos: the &struct list_head to use as a loop counter. + * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the @@ -349,7 +366,7 @@ #define __list_for_each(pos, head) \ /** * list_for_each_prev - iterate over a list backwards - * @pos: the &struct list_head to use as a loop counter. + * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ @@ -357,8 +374,8 @@ #define list_for_each_prev(pos, head) \ pos = pos->prev) /** - * list_for_each_safe - iterate over a list safe against removal of list entry - * @pos: the &struct list_head to use as a loop counter. + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ @@ -368,7 +385,7 @@ #define list_for_each_safe(pos, n, head) /** * list_for_each_entry - iterate over list of given type - * @pos: the type * to use as a loop counter. + * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ @@ -379,7 +396,7 @@ #define list_for_each_entry(pos, head, m /** * list_for_each_entry_reverse - iterate backwards over list of given type. - * @pos: the type * to use as a loop counter. + * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ @@ -389,21 +406,24 @@ #define list_for_each_entry_reverse(pos, pos = list_entry(pos->member.prev, typeof(*pos), member)) /** - * list_prepare_entry - prepare a pos entry for use as a start point in - * list_for_each_entry_continue + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue * @pos: the type * to use as a start point * @head: the head of the list * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue. */ #define list_prepare_entry(pos, head, member) \ ((pos) ? : list_entry(head, typeof(*pos), member)) /** - * list_for_each_entry_continue - iterate over list of given type - * continuing after existing point - * @pos: the type * to use as a loop counter. + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member); \ @@ -411,11 +431,12 @@ #define list_for_each_entry_continue(pos pos = list_entry(pos->member.next, typeof(*pos), member)) /** - * list_for_each_entry_from - iterate over list of given type - * continuing from existing point - * @pos: the type * to use as a loop counter. + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. */ #define list_for_each_entry_from(pos, head, member) \ for (; prefetch(pos->member.next), &pos->member != (head); \ @@ -423,7 +444,7 @@ #define list_for_each_entry_from(pos, he /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @pos: the type * to use as a loop counter. + * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. @@ -435,12 +456,14 @@ #define list_for_each_entry_safe(pos, n, pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** - * list_for_each_entry_safe_continue - iterate over list of given type - * continuing after existing point safe against removal of list entry - * @pos: the type * to use as a loop counter. + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_entry(pos->member.next, typeof(*pos), member), \ @@ -449,12 +472,14 @@ #define list_for_each_entry_safe_continu pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** - * list_for_each_entry_safe_from - iterate over list of given type - * from existing point safe against removal of list entry - * @pos: the type * to use as a loop counter. + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. */ #define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_entry(pos->member.next, typeof(*pos), member); \ @@ -462,12 +487,14 @@ #define list_for_each_entry_safe_from(po pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** - * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against - * removal of list entry - * @pos: the type * to use as a loop counter. + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. */ #define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member), \ @@ -477,7 +504,7 @@ #define list_for_each_entry_safe_reverse /** * list_for_each_rcu - iterate over an rcu-protected list - * @pos: the &struct list_head to use as a loop counter. + * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. * * This list-traversal primitive may safely run concurrently with @@ -495,12 +522,13 @@ #define __list_for_each_rcu(pos, head) \ pos = pos->next) /** - * list_for_each_safe_rcu - iterate over an rcu-protected list safe - * against removal of list entry - * @pos: the &struct list_head to use as a loop counter. + * list_for_each_safe_rcu + * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. * + * Iterate over an rcu-protected list, safe against removal of list entry. + * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). @@ -512,7 +540,7 @@ #define list_for_each_safe_rcu(pos, n, h /** * list_for_each_entry_rcu - iterate over rcu list of given type - * @pos: the type * to use as a loop counter. + * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * @@ -528,11 +556,12 @@ #define list_for_each_entry_rcu(pos, hea /** - * list_for_each_continue_rcu - iterate over an rcu-protected list - * continuing after existing point. - * @pos: the &struct list_head to use as a loop counter. + * list_for_each_continue_rcu + * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. * + * Iterate over an rcu-protected list, continuing after current point. + * * This list-traversal primitive may safely run concurrently with * the _rcu list-mutation primitives such as list_add_rcu() * as long as the traversal is guarded by rcu_read_lock(). @@ -658,11 +687,14 @@ static inline void hlist_add_head(struct /** - * hlist_add_head_rcu - adds the specified element to the specified hlist, - * while permitting racing traversals. + * hlist_add_head_rcu * @n: the element to add to the hash list. * @h: the list to add to. * + * Description: + * Adds the specified element to the specified hlist, + * while permitting racing traversals. + * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() @@ -707,11 +739,14 @@ static inline void hlist_add_after(struc } /** - * hlist_add_before_rcu - adds the specified element to the specified hlist - * before the specified node while permitting racing traversals. + * hlist_add_before_rcu * @n: the new element to add to the hash list. * @next: the existing element to add the new element before. * + * Description: + * Adds the specified element to the specified hlist + * before the specified node while permitting racing traversals. + * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() @@ -732,11 +767,14 @@ static inline void hlist_add_before_rcu( } /** - * hlist_add_after_rcu - adds the specified element to the specified hlist - * after the specified node while permitting racing traversals. + * hlist_add_after_rcu * @prev: the existing element to add the new element after. * @n: the new element to add to the hash list. * + * Description: + * Adds the specified element to the specified hlist + * after the specified node while permitting racing traversals. + * * The caller must take whatever precautions are necessary * (such as holding appropriate locks) to avoid racing * with another list-mutation primitive, such as hlist_add_head_rcu() @@ -769,8 +807,8 @@ #define hlist_for_each_safe(pos, n, head /** * hlist_for_each_entry - iterate over list of given type - * @tpos: the type * to use as a loop counter. - * @pos: the &struct hlist_node to use as a loop counter. + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ @@ -781,9 +819,9 @@ #define hlist_for_each_entry(tpos, pos, pos = pos->next) /** - * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point - * @tpos: the type * to use as a loop counter. - * @pos: the &struct hlist_node to use as a loop counter. + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue(tpos, pos, member) \ @@ -793,9 +831,9 @@ #define hlist_for_each_entry_continue(tp pos = pos->next) /** - * hlist_for_each_entry_from - iterate over a hlist continuing from existing point - * @tpos: the type * to use as a loop counter. - * @pos: the &struct hlist_node to use as a loop counter. + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_from(tpos, pos, member) \ @@ -805,8 +843,8 @@ #define hlist_for_each_entry_from(tpos, /** * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry - * @tpos: the type * to use as a loop counter. - * @pos: the &struct hlist_node to use as a loop counter. + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. @@ -819,8 +857,8 @@ #define hlist_for_each_entry_safe(tpos, /** * hlist_for_each_entry_rcu - iterate over rcu list of given type - * @tpos: the type * to use as a loop counter. - * @pos: the &struct hlist_node to use as a loop counter. + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. * @head: the head for your list. * @member: the name of the hlist_node within the struct. * diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 995f89d..aa4fe90 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -11,7 +11,6 @@ #define LINUX_LOCKD_LOCKD_H #ifdef __KERNEL__ -#include #include #include #include @@ -50,11 +49,12 @@ struct nlm_host { h_killed : 1, h_monitored : 1; wait_queue_head_t h_gracewait; /* wait while reclaiming */ + struct rw_semaphore h_rwsem; /* Reboot recovery lock */ u32 h_state; /* pseudo-state counter */ u32 h_nsmstate; /* true remote NSM state */ u32 h_pidcount; /* Pseudopids */ atomic_t h_count; /* reference count */ - struct semaphore h_sema; /* mutex for pmap binding */ + struct mutex h_mutex; /* mutex for pmap binding */ unsigned long h_nextrebind; /* next portmap call */ unsigned long h_expires; /* eligible for GC */ struct list_head h_lockowners; /* Lockowners for the client */ @@ -220,6 +220,7 @@ static __inline__ int nlm_compare_locks(const struct file_lock *fl1, const struct file_lock *fl2) { return fl1->fl_pid == fl2->fl_pid + && fl1->fl_owner == fl2->fl_owner && fl1->fl_start == fl2->fl_start && fl1->fl_end == fl2->fl_end &&(fl1->fl_type == fl2->fl_type || fl2->fl_type == F_UNLCK); diff --git a/include/linux/lockd/nlm.h b/include/linux/lockd/nlm.h index 869b630..d9d46e4 100644 --- a/include/linux/lockd/nlm.h +++ b/include/linux/lockd/nlm.h @@ -9,7 +9,6 @@ #ifndef LINUX_LOCKD_NLM_H #define LINUX_LOCKD_NLM_H -#include /* Maximum file offset in file_lock.fl_end */ # define NLM_OFFSET_MAX ((s32) 0x7fffffff) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h new file mode 100644 index 0000000..316e0fb --- /dev/null +++ b/include/linux/lockdep.h @@ -0,0 +1,353 @@ +/* + * Runtime locking correctness validator + * + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + * + * see Documentation/lockdep-design.txt for more details. + */ +#ifndef __LINUX_LOCKDEP_H +#define __LINUX_LOCKDEP_H + +#include +#include +#include +#include + +#ifdef CONFIG_LOCKDEP + +/* + * Lock-class usage-state bits: + */ +enum lock_usage_bit +{ + LOCK_USED = 0, + LOCK_USED_IN_HARDIRQ, + LOCK_USED_IN_SOFTIRQ, + LOCK_ENABLED_SOFTIRQS, + LOCK_ENABLED_HARDIRQS, + LOCK_USED_IN_HARDIRQ_READ, + LOCK_USED_IN_SOFTIRQ_READ, + LOCK_ENABLED_SOFTIRQS_READ, + LOCK_ENABLED_HARDIRQS_READ, + LOCK_USAGE_STATES +}; + +/* + * Usage-state bitmasks: + */ +#define LOCKF_USED (1 << LOCK_USED) +#define LOCKF_USED_IN_HARDIRQ (1 << LOCK_USED_IN_HARDIRQ) +#define LOCKF_USED_IN_SOFTIRQ (1 << LOCK_USED_IN_SOFTIRQ) +#define LOCKF_ENABLED_HARDIRQS (1 << LOCK_ENABLED_HARDIRQS) +#define LOCKF_ENABLED_SOFTIRQS (1 << LOCK_ENABLED_SOFTIRQS) + +#define LOCKF_ENABLED_IRQS (LOCKF_ENABLED_HARDIRQS | LOCKF_ENABLED_SOFTIRQS) +#define LOCKF_USED_IN_IRQ (LOCKF_USED_IN_HARDIRQ | LOCKF_USED_IN_SOFTIRQ) + +#define LOCKF_USED_IN_HARDIRQ_READ (1 << LOCK_USED_IN_HARDIRQ_READ) +#define LOCKF_USED_IN_SOFTIRQ_READ (1 << LOCK_USED_IN_SOFTIRQ_READ) +#define LOCKF_ENABLED_HARDIRQS_READ (1 << LOCK_ENABLED_HARDIRQS_READ) +#define LOCKF_ENABLED_SOFTIRQS_READ (1 << LOCK_ENABLED_SOFTIRQS_READ) + +#define LOCKF_ENABLED_IRQS_READ \ + (LOCKF_ENABLED_HARDIRQS_READ | LOCKF_ENABLED_SOFTIRQS_READ) +#define LOCKF_USED_IN_IRQ_READ \ + (LOCKF_USED_IN_HARDIRQ_READ | LOCKF_USED_IN_SOFTIRQ_READ) + +#define MAX_LOCKDEP_SUBCLASSES 8UL + +/* + * Lock-classes are keyed via unique addresses, by embedding the + * lockclass-key into the kernel (or module) .data section. (For + * static locks we use the lock address itself as the key.) + */ +struct lockdep_subclass_key { + char __one_byte; +} __attribute__ ((__packed__)); + +struct lock_class_key { + struct lockdep_subclass_key subkeys[MAX_LOCKDEP_SUBCLASSES]; +}; + +/* + * The lock-class itself: + */ +struct lock_class { + /* + * class-hash: + */ + struct list_head hash_entry; + + /* + * global list of all lock-classes: + */ + struct list_head lock_entry; + + struct lockdep_subclass_key *key; + unsigned int subclass; + + /* + * IRQ/softirq usage tracking bits: + */ + unsigned long usage_mask; + struct stack_trace usage_traces[LOCK_USAGE_STATES]; + + /* + * These fields represent a directed graph of lock dependencies, + * to every node we attach a list of "forward" and a list of + * "backward" graph nodes. + */ + struct list_head locks_after, locks_before; + + /* + * Generation counter, when doing certain classes of graph walking, + * to ensure that we check one node only once: + */ + unsigned int version; + + /* + * Statistics counter: + */ + unsigned long ops; + + const char *name; + int name_version; +}; + +/* + * Map the lock object (the lock instance) to the lock-class object. + * This is embedded into specific lock instances: + */ +struct lockdep_map { + struct lock_class_key *key; + struct lock_class *class[MAX_LOCKDEP_SUBCLASSES]; + const char *name; +}; + +/* + * Every lock has a list of other locks that were taken after it. + * We only grow the list, never remove from it: + */ +struct lock_list { + struct list_head entry; + struct lock_class *class; + struct stack_trace trace; +}; + +/* + * We record lock dependency chains, so that we can cache them: + */ +struct lock_chain { + struct list_head entry; + u64 chain_key; +}; + +struct held_lock { + /* + * One-way hash of the dependency chain up to this point. We + * hash the hashes step by step as the dependency chain grows. + * + * We use it for dependency-caching and we skip detection + * passes and dependency-updates if there is a cache-hit, so + * it is absolutely critical for 100% coverage of the validator + * to have a unique key value for every unique dependency path + * that can occur in the system, to make a unique hash value + * as likely as possible - hence the 64-bit width. + * + * The task struct holds the current hash value (initialized + * with zero), here we store the previous hash value: + */ + u64 prev_chain_key; + struct lock_class *class; + unsigned long acquire_ip; + struct lockdep_map *instance; + + /* + * The lock-stack is unified in that the lock chains of interrupt + * contexts nest ontop of process context chains, but we 'separate' + * the hashes by starting with 0 if we cross into an interrupt + * context, and we also keep do not add cross-context lock + * dependencies - the lock usage graph walking covers that area + * anyway, and we'd just unnecessarily increase the number of + * dependencies otherwise. [Note: hardirq and softirq contexts + * are separated from each other too.] + * + * The following field is used to detect when we cross into an + * interrupt context: + */ + int irq_context; + int trylock; + int read; + int check; + int hardirqs_off; +}; + +/* + * Initialization, self-test and debugging-output methods: + */ +extern void lockdep_init(void); +extern void lockdep_info(void); +extern void lockdep_reset(void); +extern void lockdep_reset_lock(struct lockdep_map *lock); +extern void lockdep_free_key_range(void *start, unsigned long size); + +extern void lockdep_off(void); +extern void lockdep_on(void); +extern int lockdep_internal(void); + +/* + * These methods are used by specific locking variants (spinlocks, + * rwlocks, mutexes and rwsems) to pass init/acquire/release events + * to lockdep: + */ + +extern void lockdep_init_map(struct lockdep_map *lock, const char *name, + struct lock_class_key *key); + +/* + * Reinitialize a lock key - for cases where there is special locking or + * special initialization of locks so that the validator gets the scope + * of dependencies wrong: they are either too broad (they need a class-split) + * or they are too narrow (they suffer from a false class-split): + */ +#define lockdep_set_class(lock, key) \ + lockdep_init_map(&(lock)->dep_map, #key, key) +#define lockdep_set_class_and_name(lock, key, name) \ + lockdep_init_map(&(lock)->dep_map, name, key) + +/* + * Acquire a lock. + * + * Values for "read": + * + * 0: exclusive (write) acquire + * 1: read-acquire (no recursion allowed) + * 2: read-acquire with same-instance recursion allowed + * + * Values for check: + * + * 0: disabled + * 1: simple checks (freeing, held-at-exit-time, etc.) + * 2: full validation + */ +extern void lock_acquire(struct lockdep_map *lock, unsigned int subclass, + int trylock, int read, int check, unsigned long ip); + +extern void lock_release(struct lockdep_map *lock, int nested, + unsigned long ip); + +# define INIT_LOCKDEP .lockdep_recursion = 0, + +#else /* !LOCKDEP */ + +static inline void lockdep_off(void) +{ +} + +static inline void lockdep_on(void) +{ +} + +static inline int lockdep_internal(void) +{ + return 0; +} + +# define lock_acquire(l, s, t, r, c, i) do { } while (0) +# define lock_release(l, n, i) do { } while (0) +# define lockdep_init() do { } while (0) +# define lockdep_info() do { } while (0) +# define lockdep_init_map(lock, name, key) do { (void)(key); } while (0) +# define lockdep_set_class(lock, key) do { (void)(key); } while (0) +# define lockdep_set_class_and_name(lock, key, name) \ + do { (void)(key); } while (0) +# define INIT_LOCKDEP +# define lockdep_reset() do { debug_locks = 1; } while (0) +# define lockdep_free_key_range(start, size) do { } while (0) +/* + * The class key takes no space if lockdep is disabled: + */ +struct lock_class_key { }; +#endif /* !LOCKDEP */ + +#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_GENERIC_HARDIRQS) +extern void early_init_irq_lock_class(void); +#else +# define early_init_irq_lock_class() do { } while (0) +#endif + +#ifdef CONFIG_TRACE_IRQFLAGS +extern void early_boot_irqs_off(void); +extern void early_boot_irqs_on(void); +#else +# define early_boot_irqs_off() do { } while (0) +# define early_boot_irqs_on() do { } while (0) +#endif + +/* + * For trivial one-depth nesting of a lock-class, the following + * global define can be used. (Subsystems with multiple levels + * of nesting should define their own lock-nesting subclasses.) + */ +#define SINGLE_DEPTH_NESTING 1 + +/* + * Map the dependency ops to NOP or to real lockdep ops, depending + * on the per lock-class debug mode: + */ + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# ifdef CONFIG_PROVE_LOCKING +# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) +# else +# define spin_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) +# endif +# define spin_release(l, n, i) lock_release(l, n, i) +#else +# define spin_acquire(l, s, t, i) do { } while (0) +# define spin_release(l, n, i) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# ifdef CONFIG_PROVE_LOCKING +# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) +# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 2, i) +# else +# define rwlock_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) +# define rwlock_acquire_read(l, s, t, i) lock_acquire(l, s, t, 2, 1, i) +# endif +# define rwlock_release(l, n, i) lock_release(l, n, i) +#else +# define rwlock_acquire(l, s, t, i) do { } while (0) +# define rwlock_acquire_read(l, s, t, i) do { } while (0) +# define rwlock_release(l, n, i) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# ifdef CONFIG_PROVE_LOCKING +# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) +# else +# define mutex_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) +# endif +# define mutex_release(l, n, i) lock_release(l, n, i) +#else +# define mutex_acquire(l, s, t, i) do { } while (0) +# define mutex_release(l, n, i) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# ifdef CONFIG_PROVE_LOCKING +# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 2, i) +# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 2, i) +# else +# define rwsem_acquire(l, s, t, i) lock_acquire(l, s, t, 0, 1, i) +# define rwsem_acquire_read(l, s, t, i) lock_acquire(l, s, t, 1, 1, i) +# endif +# define rwsem_release(l, n, i) lock_release(l, n, i) +#else +# define rwsem_acquire(l, s, t, i) do { } while (0) +# define rwsem_acquire_read(l, s, t, i) do { } while (0) +# define rwsem_release(l, n, i) do { } while (0) +#endif + +#endif /* __LINUX_LOCKDEP_H */ diff --git a/include/linux/m41t00.h b/include/linux/m41t00.h new file mode 100644 index 0000000..b423360 --- /dev/null +++ b/include/linux/m41t00.h @@ -0,0 +1,50 @@ +/* + * Definitions for the ST M41T00 family of i2c rtc chips. + * + * Author: Mark A. Greer + * + * 2005, 2006 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifndef _M41T00_H +#define _M41T00_H + +#define M41T00_DRV_NAME "m41t00" +#define M41T00_I2C_ADDR 0x68 + +#define M41T00_TYPE_M41T00 0 +#define M41T00_TYPE_M41T81 81 +#define M41T00_TYPE_M41T85 85 + +struct m41t00_platform_data { + u8 type; + u8 i2c_addr; + u8 sqw_freq; +}; + +/* SQW output disabled, this is default value by power on */ +#define M41T00_SQW_DISABLE (0) + +#define M41T00_SQW_32KHZ (1<<4) /* 32.768 KHz */ +#define M41T00_SQW_8KHZ (2<<4) /* 8.192 KHz */ +#define M41T00_SQW_4KHZ (3<<4) /* 4.096 KHz */ +#define M41T00_SQW_2KHZ (4<<4) /* 2.048 KHz */ +#define M41T00_SQW_1KHZ (5<<4) /* 1.024 KHz */ +#define M41T00_SQW_512HZ (6<<4) /* 512 Hz */ +#define M41T00_SQW_256HZ (7<<4) /* 256 Hz */ +#define M41T00_SQW_128HZ (8<<4) /* 128 Hz */ +#define M41T00_SQW_64HZ (9<<4) /* 64 Hz */ +#define M41T00_SQW_32HZ (10<<4) /* 32 Hz */ +#define M41T00_SQW_16HZ (11<<4) /* 16 Hz */ +#define M41T00_SQW_8HZ (12<<4) /* 8 Hz */ +#define M41T00_SQW_4HZ (13<<4) /* 4 Hz */ +#define M41T00_SQW_2HZ (14<<4) /* 2 Hz */ +#define M41T00_SQW_1HZ (15<<4) /* 1 Hz */ + +extern ulong m41t00_get_rtc_time(void); +extern int m41t00_set_rtc_time(ulong nowtime); + +#endif /* _M41T00_H */ diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 9112063..218501c 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -63,6 +63,76 @@ extern int online_pages(unsigned long, u /* reasonably generic interface to expand the physical pages in a zone */ extern int __add_pages(struct zone *zone, unsigned long start_pfn, unsigned long nr_pages); + +#ifdef CONFIG_NUMA +extern int memory_add_physaddr_to_nid(u64 start); +#else +static inline int memory_add_physaddr_to_nid(u64 start) +{ + return 0; +} +#endif + +#ifdef CONFIG_HAVE_ARCH_NODEDATA_EXTENSION +/* + * For supporting node-hotadd, we have to allocate a new pgdat. + * + * If an arch has generic style NODE_DATA(), + * node_data[nid] = kzalloc() works well. But it depends on the architecture. + * + * In general, generic_alloc_nodedata() is used. + * Now, arch_free_nodedata() is just defined for error path of node_hot_add. + * + */ +extern pg_data_t *arch_alloc_nodedata(int nid); +extern void arch_free_nodedata(pg_data_t *pgdat); +extern void arch_refresh_nodedata(int nid, pg_data_t *pgdat); + +#else /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */ + +#define arch_alloc_nodedata(nid) generic_alloc_nodedata(nid) +#define arch_free_nodedata(pgdat) generic_free_nodedata(pgdat) + +#ifdef CONFIG_NUMA +/* + * If ARCH_HAS_NODEDATA_EXTENSION=n, this func is used to allocate pgdat. + * XXX: kmalloc_node() can't work well to get new node's memory at this time. + * Because, pgdat for the new node is not allocated/initialized yet itself. + * To use new node's memory, more consideration will be necessary. + */ +#define generic_alloc_nodedata(nid) \ +({ \ + kzalloc(sizeof(pg_data_t), GFP_KERNEL); \ +}) +/* + * This definition is just for error path in node hotadd. + * For node hotremove, we have to replace this. + */ +#define generic_free_nodedata(pgdat) kfree(pgdat) + +extern pg_data_t *node_data[]; +static inline void arch_refresh_nodedata(int nid, pg_data_t *pgdat) +{ + node_data[nid] = pgdat; +} + +#else /* !CONFIG_NUMA */ + +/* never called */ +static inline pg_data_t *generic_alloc_nodedata(int nid) +{ + BUG(); + return NULL; +} +static inline void generic_free_nodedata(pg_data_t *pgdat) +{ +} +static inline void arch_refresh_nodedata(int nid, pg_data_t *pgdat) +{ +} +#endif /* CONFIG_NUMA */ +#endif /* CONFIG_HAVE_ARCH_NODEDATA_EXTENSION */ + #else /* ! CONFIG_MEMORY_HOTPLUG */ /* * Stub functions for when hotplug is off @@ -99,7 +169,8 @@ static inline int __remove_pages(struct return -ENOSYS; } -extern int add_memory(u64 start, u64 size); +extern int add_memory(int nid, u64 start, u64 size); +extern int arch_add_memory(int nid, u64 start, u64 size); extern int remove_memory(u64 start, u64 size); #endif /* __LINUX_MEMORY_HOTPLUG_H */ diff --git a/include/linux/mempolicy.h b/include/linux/mempolicy.h index f5fdca1..72440f0 100644 --- a/include/linux/mempolicy.h +++ b/include/linux/mempolicy.h @@ -28,7 +28,6 @@ #define MPOL_MF_INTERNAL (1<<3) /* Inter #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/linux/migrate.h b/include/linux/migrate.h index ff0a640..48148e0 100644 --- a/include/linux/migrate.h +++ b/include/linux/migrate.h @@ -1,36 +1,44 @@ #ifndef _LINUX_MIGRATE_H #define _LINUX_MIGRATE_H -#include #include +typedef struct page *new_page_t(struct page *, unsigned long private, int **); + #ifdef CONFIG_MIGRATION extern int isolate_lru_page(struct page *p, struct list_head *pagelist); extern int putback_lru_pages(struct list_head *l); -extern int migrate_page(struct page *, struct page *); -extern void migrate_page_copy(struct page *, struct page *); -extern int migrate_page_remove_references(struct page *, struct page *, int); -extern int migrate_pages(struct list_head *l, struct list_head *t, - struct list_head *moved, struct list_head *failed); -extern int migrate_pages_to(struct list_head *pagelist, - struct vm_area_struct *vma, int dest); -extern int fail_migrate_page(struct page *, struct page *); +extern int migrate_page(struct address_space *, + struct page *, struct page *); +extern int migrate_pages(struct list_head *l, new_page_t x, unsigned long); -extern int migrate_prep(void); +extern int fail_migrate_page(struct address_space *, + struct page *, struct page *); +extern int migrate_prep(void); +extern int migrate_vmas(struct mm_struct *mm, + const nodemask_t *from, const nodemask_t *to, + unsigned long flags); #else static inline int isolate_lru_page(struct page *p, struct list_head *list) { return -ENOSYS; } static inline int putback_lru_pages(struct list_head *l) { return 0; } -static inline int migrate_pages(struct list_head *l, struct list_head *t, - struct list_head *moved, struct list_head *failed) { return -ENOSYS; } +static inline int migrate_pages(struct list_head *l, new_page_t x, + unsigned long private) { return -ENOSYS; } static inline int migrate_pages_to(struct list_head *pagelist, struct vm_area_struct *vma, int dest) { return 0; } static inline int migrate_prep(void) { return -ENOSYS; } +static inline int migrate_vmas(struct mm_struct *mm, + const nodemask_t *from, const nodemask_t *to, + unsigned long flags) +{ + return -ENOSYS; +} + /* Possible settings for the migrate_page() method in address_operations */ #define migrate_page NULL #define fail_migrate_page NULL diff --git a/include/linux/mii.h b/include/linux/mii.h index 68f5a0f..beddc6d 100644 --- a/include/linux/mii.h +++ b/include/linux/mii.h @@ -9,7 +9,6 @@ #ifndef __LINUX_MII_H__ #define __LINUX_MII_H__ #include -#include /* Generic MII registers. */ @@ -136,6 +135,20 @@ #define LPA_1000REMRXOK 0x1000 #define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */ #define LPA_1000HALF 0x0400 /* Link partner 1000BASE-T half duplex */ +/* This structure is used in all SIOCxMIIxxx ioctl calls */ +struct mii_ioctl_data { + __u16 phy_id; + __u16 reg_num; + __u16 val_in; + __u16 val_out; +}; + +#ifdef __KERNEL__ + +#include + +struct ethtool_cmd; + struct mii_if_info { int phy_id; int advertising; @@ -151,9 +164,6 @@ struct mii_if_info { void (*mdio_write) (struct net_device *dev, int phy_id, int location, int val); }; -struct ethtool_cmd; -struct mii_ioctl_data; - extern int mii_link_ok (struct mii_if_info *mii); extern int mii_nway_restart (struct mii_if_info *mii); extern int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd); @@ -168,16 +178,6 @@ extern int generic_mii_ioctl(struct mii_ unsigned int *duplex_changed); - -/* This structure is used in all SIOCxMIIxxx ioctl calls */ -struct mii_ioctl_data { - u16 phy_id; - u16 reg_num; - u16 val_in; - u16 val_out; -}; - - static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) { return (struct mii_ioctl_data *) &rq->ifr_ifru; @@ -235,5 +235,5 @@ static inline unsigned int mii_duplex (u return 0; } - +#endif /* __KERNEL__ */ #endif /* __LINUX_MII_H__ */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 5b584da..b03cfb9 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -40,7 +40,6 @@ struct miscdevice { struct list_head list; struct device *dev; struct class_device *class; - char devfs_name[64]; }; extern int misc_register(struct miscdevice * misc); diff --git a/include/linux/mm.h b/include/linux/mm.h index 1154684..990957e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -7,7 +7,6 @@ #include #ifdef __KERNEL__ -#include #include #include #include @@ -15,6 +14,7 @@ #include #include #include #include +#include struct mempolicy; struct anon_vma; @@ -37,7 +37,6 @@ #endif #include #include #include -#include #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n)) @@ -146,7 +145,6 @@ #define VM_MAYSHARE 0x00000080 #define VM_GROWSDOWN 0x00000100 /* general info on the segment */ #define VM_GROWSUP 0x00000200 -#define VM_SHM 0x00000000 /* Means nothing: delete it later */ #define VM_PFNMAP 0x00000400 /* Page-ranges managed without "struct page", just pure PFN */ #define VM_DENYWRITE 0x00000800 /* ETXTBSY on write attempts.. */ @@ -200,10 +198,16 @@ struct vm_operations_struct { void (*close)(struct vm_area_struct * area); struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int *type); int (*populate)(struct vm_area_struct * area, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock); + + /* notification that a previously read-only page is about to become + * writable, if an error is returned it will cause a SIGBUS */ + int (*page_mkwrite)(struct vm_area_struct *vma, struct page *page); #ifdef CONFIG_NUMA int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new); struct mempolicy *(*get_policy)(struct vm_area_struct *vma, unsigned long addr); + int (*migrate)(struct vm_area_struct *vma, const nodemask_t *from, + const nodemask_t *to, unsigned long flags); #endif }; @@ -466,10 +470,13 @@ static inline unsigned long page_zonenum struct zone; extern struct zone *zone_table[]; +static inline int page_zone_id(struct page *page) +{ + return (page->flags >> ZONETABLE_PGSHIFT) & ZONETABLE_MASK; +} static inline struct zone *page_zone(struct page *page) { - return zone_table[(page->flags >> ZONETABLE_PGSHIFT) & - ZONETABLE_MASK]; + return zone_table[page_zone_id(page)]; } static inline unsigned long page_to_nid(struct page *page) @@ -508,6 +515,11 @@ static inline void set_page_links(struct set_page_section(page, pfn_to_section_nr(pfn)); } +/* + * Some inline functions in vmstat.h depend on page_zone() + */ +#include + #ifndef CONFIG_DISCONTIGMEM /* The array of struct pages - for discontigmem use pgdat->lmem_map */ extern struct page *mem_map; @@ -1028,8 +1040,8 @@ static inline void kernel_map_pages(struct page *page, int numpages, int enable) { if (!PageHighMem(page) && !enable) - mutex_debug_check_no_locks_freed(page_address(page), - numpages * PAGE_SIZE); + debug_check_no_locks_freed(page_address(page), + numpages * PAGE_SIZE); } #endif @@ -1058,5 +1070,7 @@ #else extern int randomize_va_space; #endif +const char *arch_vma_name(struct vm_area_struct *vma); + #endif /* __KERNEL__ */ #endif /* _LINUX_MM_H */ diff --git a/include/linux/mman.h b/include/linux/mman.h index 18a5689..87920a0 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -1,10 +1,6 @@ #ifndef _LINUX_MMAN_H #define _LINUX_MMAN_H -#include -#include - -#include #include #define MREMAP_MAYMOVE 1 @@ -13,6 +9,12 @@ #define MREMAP_FIXED 2 #define OVERCOMMIT_GUESS 0 #define OVERCOMMIT_ALWAYS 1 #define OVERCOMMIT_NEVER 2 + +#ifdef __KERNEL__ +#include + +#include + extern int sysctl_overcommit_memory; extern int sysctl_overcommit_ratio; extern atomic_t vm_committed_space; @@ -63,5 +65,5 @@ calc_vm_flag_bits(unsigned long flags) _calc_vm_trans(flags, MAP_EXECUTABLE, VM_EXECUTABLE) | _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED ); } - +#endif /* __KERNEL__ */ #endif /* _LINUX_MMAN_H */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2d83371..656b588 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -4,7 +4,6 @@ #define _LINUX_MMZONE_H #ifdef __KERNEL__ #ifndef __ASSEMBLY__ -#include #include #include #include @@ -47,6 +46,27 @@ #else #define ZONE_PADDING(name) #endif +enum zone_stat_item { + NR_ANON_PAGES, /* Mapped anonymous pages */ + NR_FILE_MAPPED, /* pagecache pages mapped into pagetables. + only modified from process context */ + NR_FILE_PAGES, + NR_SLAB, /* Pages used by slab allocator */ + NR_PAGETABLE, /* used for pagetables */ + NR_FILE_DIRTY, + NR_WRITEBACK, + NR_UNSTABLE_NFS, /* NFS unstable pages */ + NR_BOUNCE, +#ifdef CONFIG_NUMA + NUMA_HIT, /* allocated in intended node */ + NUMA_MISS, /* allocated in non intended node */ + NUMA_FOREIGN, /* was intended here, hit elsewhere */ + NUMA_INTERLEAVE_HIT, /* interleaver preferred this zone */ + NUMA_LOCAL, /* allocation from local node */ + NUMA_OTHER, /* allocation from other node */ +#endif + NR_VM_ZONE_STAT_ITEMS }; + struct per_cpu_pages { int count; /* number of pages in the list */ int high; /* high watermark, emptying needed */ @@ -56,13 +76,8 @@ struct per_cpu_pages { struct per_cpu_pageset { struct per_cpu_pages pcp[2]; /* 0: hot. 1: cold */ -#ifdef CONFIG_NUMA - unsigned long numa_hit; /* allocated in intended node */ - unsigned long numa_miss; /* allocated in non intended node */ - unsigned long numa_foreign; /* was intended here, hit elsewhere */ - unsigned long interleave_hit; /* interleaver prefered this zone */ - unsigned long local_node; /* allocation from local node */ - unsigned long other_node; /* allocation from other node */ +#ifdef CONFIG_SMP + s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS]; #endif } ____cacheline_aligned_in_smp; @@ -135,6 +150,10 @@ struct zone { unsigned long lowmem_reserve[MAX_NR_ZONES]; #ifdef CONFIG_NUMA + /* + * zone reclaim becomes active if more unmapped pages exist. + */ + unsigned long min_unmapped_ratio; struct per_cpu_pageset *pageset[NR_CPUS]; #else struct per_cpu_pageset pageset[NR_CPUS]; @@ -166,12 +185,8 @@ #endif /* A count of how many reclaimers are scanning this zone */ atomic_t reclaim_in_progress; - /* - * timestamp (in jiffies) of the last zone reclaim that did not - * result in freeing of pages. This is used to avoid repeated scans - * if all memory in the zone is in use. - */ - unsigned long last_unsuccessful_zone_reclaim; + /* Zone statistics */ + atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; /* * prev_priority holds the scanning priority for this zone. It is @@ -198,7 +213,7 @@ #endif /* * wait_table -- the array holding the hash table - * wait_table_size -- the size of the hash table array + * wait_table_hash_nr_entries -- the size of the hash table array * wait_table_bits -- wait_table_size == (1 << wait_table_bits) * * The purpose of all these is to keep track of the people @@ -221,7 +236,7 @@ #endif * free_area_init_core() performs the initialization of them. */ wait_queue_head_t * wait_table; - unsigned long wait_table_size; + unsigned long wait_table_hash_nr_entries; unsigned long wait_table_bits; /* @@ -334,6 +349,9 @@ void wakeup_kswapd(struct zone *zone, in int zone_watermark_ok(struct zone *z, int order, unsigned long mark, int classzone_idx, int alloc_flags); +extern int init_currently_empty_zone(struct zone *zone, unsigned long start_pfn, + unsigned long size); + #ifdef CONFIG_HAVE_MEMORY_PRESENT void memory_present(int nid, unsigned long start, unsigned long end); #else @@ -400,6 +418,8 @@ int lowmem_reserve_ratio_sysctl_handler( void __user *, size_t *, loff_t *); int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); +int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *, int, + struct file *, void __user *, size_t *, loff_t *); #include /* Returns the number of the current Node. */ @@ -507,6 +527,10 @@ struct mem_section { * pages. However, it is stored with some other magic. * (see sparse.c::sparse_init_one_section()) * + * Additionally during early boot we encode node id of + * the location of the section here to guide allocation. + * (see sparse.c::memory_present()) + * * Making it a UL at least makes someone do a cast * before using it wrong. */ @@ -546,6 +570,7 @@ #define SECTION_MARKED_PRESENT (1UL<<0) #define SECTION_HAS_MEM_MAP (1UL<<1) #define SECTION_MAP_LAST_BIT (1UL<<2) #define SECTION_MAP_MASK (~(SECTION_MAP_LAST_BIT-1)) +#define SECTION_NID_SHIFT 2 static inline struct page *__section_mem_map_addr(struct mem_section *section) { diff --git a/include/linux/module.h b/include/linux/module.h index eaec13d..d06c74f 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -6,7 +6,6 @@ #define _LINUX_MODULE_H * Rewritten by Richard Henderson Dec 1996 * Rewritten again by Rusty Russell, 2002 */ -#include #include #include #include @@ -106,6 +105,8 @@ #define MODULE_ALIAS(_alias) MODULE_INFO * "GPL and additional rights" [GNU Public License v2 rights and more] * "Dual BSD/GPL" [GNU Public License v2 * or BSD license choice] + * "Dual MIT/GPL" [GNU Public License v2 + * or MIT license choice] * "Dual MPL/GPL" [GNU Public License v2 * or Mozilla license choice] * @@ -202,6 +203,15 @@ #define EXPORT_SYMBOL_GPL(sym) \ #define EXPORT_SYMBOL_GPL_FUTURE(sym) \ __EXPORT_SYMBOL(sym, "_gpl_future") + +#ifdef CONFIG_UNUSED_SYMBOLS +#define EXPORT_UNUSED_SYMBOL(sym) __EXPORT_SYMBOL(sym, "_unused") +#define EXPORT_UNUSED_SYMBOL_GPL(sym) __EXPORT_SYMBOL(sym, "_unused_gpl") +#else +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) +#endif + #endif struct module_ref @@ -260,6 +270,15 @@ struct module unsigned int num_gpl_syms; const unsigned long *gpl_crcs; + /* unused exported symbols. */ + const struct kernel_symbol *unused_syms; + unsigned int num_unused_syms; + const unsigned long *unused_crcs; + /* GPL-only, unused exported symbols. */ + const struct kernel_symbol *unused_gpl_syms; + unsigned int num_unused_gpl_syms; + const unsigned long *unused_gpl_crcs; + /* symbols that will be GPL-only in the near future. */ const struct kernel_symbol *gpl_future_syms; unsigned int num_gpl_future_syms; @@ -284,6 +303,9 @@ struct module /* The size of the executable code in each section. */ unsigned long init_text_size, core_text_size; + /* The handle returned from unwind_add_table. */ + void *unwind_info; + /* Arch-specific module values */ struct mod_arch_specific arch; @@ -336,6 +358,7 @@ static inline int module_is_live(struct /* Is this address in a module? (second is with no locks, for oops) */ struct module *module_text_address(unsigned long addr); struct module *__module_text_address(unsigned long addr); +int is_module_address(unsigned long addr); /* Returns module and fills in value, defined and namebuf, or NULL if symnum out of range. */ @@ -452,6 +475,8 @@ #else /* !CONFIG_MODULES... */ #define EXPORT_SYMBOL(sym) #define EXPORT_SYMBOL_GPL(sym) #define EXPORT_SYMBOL_GPL_FUTURE(sym) +#define EXPORT_UNUSED_SYMBOL(sym) +#define EXPORT_UNUSED_SYMBOL_GPL(sym) /* Given an address, look for it in the exception tables. */ static inline const struct exception_table_entry * @@ -472,6 +497,11 @@ static inline struct module *__module_te return NULL; } +static inline int is_module_address(unsigned long addr) +{ + return 0; +} + /* Get/put a kernel symbol (calls should be symmetric) */ #define symbol_get(x) ({ extern typeof(x) x __attribute__((weak)); &(x); }) #define symbol_put(x) do { } while(0) @@ -557,13 +587,4 @@ #define symbol_request(x) try_then_reque #define __MODULE_STRING(x) __stringify(x) -/* Use symbol_get and symbol_put instead. You'll thank me. */ -#define HAVE_INTER_MODULE -extern void __deprecated inter_module_register(const char *, - struct module *, const void *); -extern void __deprecated inter_module_unregister(const char *); -extern const void * __deprecated inter_module_get_request(const char *, - const char *); -extern void __deprecated inter_module_put(const char *); - #endif /* _LINUX_MODULE_H */ diff --git a/include/linux/mount.h b/include/linux/mount.h index b7472ae..403d1a9 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -17,12 +17,19 @@ #include #include #include +struct super_block; +struct vfsmount; +struct dentry; +struct namespace; + #define MNT_NOSUID 0x01 #define MNT_NODEV 0x02 #define MNT_NOEXEC 0x04 #define MNT_NOATIME 0x08 #define MNT_NODIRATIME 0x10 +#define MNT_SHRINKABLE 0x100 + #define MNT_SHARED 0x1000 /* if the vfsmount is a shared mount */ #define MNT_UNBINDABLE 0x2000 /* if the vfsmount is a unbindable mount */ #define MNT_PNODE_MASK 0x3000 /* propogation flag mask */ @@ -73,12 +80,18 @@ extern struct vfsmount *alloc_vfsmnt(con extern struct vfsmount *do_kern_mount(const char *fstype, int flags, const char *name, void *data); +struct file_system_type; +extern struct vfsmount *vfs_kern_mount(struct file_system_type *type, + int flags, const char *name, + void *data); + struct nameidata; extern int do_add_mount(struct vfsmount *newmnt, struct nameidata *nd, int mnt_flags, struct list_head *fslist); extern void mark_mounts_for_expiry(struct list_head *mounts); +extern void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts); extern spinlock_t vfsmount_lock; extern dev_t name_to_dev_t(char *name); diff --git a/include/linux/msg.h b/include/linux/msg.h index 903e0ab..acc7c17 100644 --- a/include/linux/msg.h +++ b/include/linux/msg.h @@ -2,7 +2,6 @@ #ifndef _LINUX_MSG_H #define _LINUX_MSG_H #include -#include /* ipcs ctl commands */ #define MSG_STAT 11 @@ -63,6 +62,7 @@ #define __MSGSEG ((MSGPOOL*1024)/ MSGSSZ #define MSGSEG (__MSGSEG <= 0xffff ? __MSGSEG : 0xffff) #ifdef __KERNEL__ +#include /* one msg_msg structure for each message */ struct msg_msg { diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 7a7fbe8..1221b7c 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -19,21 +19,21 @@ #define NAND_MAX_CHIPS 8 /** * struct nand_bbt_descr - bad block table descriptor - * @param options options for this descriptor - * @param pages the page(s) where we find the bbt, used with + * @options: options for this descriptor + * @pages: the page(s) where we find the bbt, used with * option BBT_ABSPAGE when bbt is searched, * then we store the found bbts pages here. * Its an array and supports up to 8 chips now - * @param offs offset of the pattern in the oob area of the page - * @param veroffs offset of the bbt version counter in the oob are of the page - * @param version version read from the bbt page during scan - * @param len length of the pattern, if 0 no pattern check is performed - * @param maxblocks maximum number of blocks to search for a bbt. This number of - * blocks is reserved at the end of the device + * @offs: offset of the pattern in the oob area of the page + * @veroffs: offset of the bbt version counter in the oob area of the page + * @version: version read from the bbt page during scan + * @len: length of the pattern, if 0 no pattern check is performed + * @maxblocks: maximum number of blocks to search for a bbt. This + * number of blocks is reserved at the end of the device * where the tables are written. - * @param reserved_block_code if non-0, this pattern denotes a reserved + * @reserved_block_code: if non-0, this pattern denotes a reserved * (rather than bad) block in the stored bbt - * @param pattern pattern to identify bad block table or factory marked + * @pattern: pattern to identify bad block table or factory marked * good / bad blocks, can be NULL, if len = 0 * * Descriptor for the bad block table marker and the descriptor for the @@ -93,12 +93,15 @@ #define NAND_BBT_SCAN_MAXBLOCKS 4 #define ONENAND_BADBLOCK_POS 0 /** - * struct bbt_info - [GENERIC] Bad Block Table data structure - * @param bbt_erase_shift [INTERN] number of address bits in a bbt entry - * @param badblockpos [INTERN] position of the bad block marker in the oob area - * @param bbt [INTERN] bad block table pointer - * @param badblock_pattern [REPLACEABLE] bad block scan pattern used for initial bad block scan - * @param priv [OPTIONAL] pointer to private bbm date + * struct bbm_info - [GENERIC] Bad Block Table data structure + * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry + * @badblockpos: [INTERN] position of the bad block marker in the oob area + * @options: options for this descriptor + * @bbt: [INTERN] bad block table pointer + * @isbad_bbt: function to determine if a block is bad + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for + * initial bad block scan + * @priv: [OPTIONAL] pointer to private bbm date */ struct bbm_info { int bbt_erase_shift; diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 23a5689..09bfae6 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -7,7 +7,6 @@ #ifndef __MTD_CFI_H__ #define __MTD_CFI_H__ -#include #include #include #include diff --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h index d7eaa40..6977780 100644 --- a/include/linux/mtd/inftl.h +++ b/include/linux/mtd/inftl.h @@ -46,7 +46,7 @@ struct INFTLrecord { unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; - struct nand_oobinfo oobinfo; + struct nand_ecclayout oobinfo; }; int INFTL_mount(struct INFTLrecord *s); diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 7dfd6e1..28d461d 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -5,7 +5,6 @@ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ -#include #include #include #include diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b6f2fda..94a443d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -13,7 +13,6 @@ #ifndef __KERNEL__ #error This is a kernel header. Perhaps include mtd-user.h instead? #endif -#include #include #include #include @@ -56,18 +55,69 @@ struct mtd_erase_region_info { u_int32_t numblocks; /* Number of blocks of erasesize in this region */ }; +/* + * oob operation modes + * + * MTD_OOB_PLACE: oob data are placed at the given offset + * MTD_OOB_AUTO: oob data are automatically placed at the free areas + * which are defined by the ecclayout + * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data + * is inserted into the data. Thats a raw image of the + * flash contents. + */ +typedef enum { + MTD_OOB_PLACE, + MTD_OOB_AUTO, + MTD_OOB_RAW, +} mtd_oob_mode_t; + +/** + * struct mtd_oob_ops - oob operation operands + * @mode: operation mode + * + * @len: number of bytes to write/read. When a data buffer is given + * (datbuf != NULL) this is the number of data bytes. When + * no data buffer is available this is the number of oob bytes. + * + * @retlen: number of bytes written/read. When a data buffer is given + * (datbuf != NULL) this is the number of data bytes. When + * no data buffer is available this is the number of oob bytes. + * + * @ooblen: number of oob bytes per page + * @ooboffs: offset of oob data in the oob area (only relevant when + * mode = MTD_OOB_PLACE) + * @datbuf: data buffer - if NULL only oob data are read/written + * @oobbuf: oob data buffer + */ +struct mtd_oob_ops { + mtd_oob_mode_t mode; + size_t len; + size_t retlen; + size_t ooblen; + uint32_t ooboffs; + uint8_t *datbuf; + uint8_t *oobbuf; +}; + struct mtd_info { u_char type; u_int32_t flags; u_int32_t size; // Total size of the MTD - /* "Major" erase size for the device. Naïve users may take this + /* "Major" erase size for the device. Naïve users may take this * to be the only erase size available, or may use the more detailed * information below if they desire */ u_int32_t erasesize; + /* Minimal writable flash unit size. In case of NOR flash it is 1 (even + * though individual bits can be cleared), in case of NAND flash it is + * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR + * it is of ECC block size, etc. It is illegal to have writesize = 0. + * Any driver registering a struct mtd_info must ensure a writesize of + * 1 or larger. + */ + u_int32_t writesize; - u_int32_t oobblock; // Size of OOB blocks (e.g. 512) u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) u_int32_t ecctype; u_int32_t eccsize; @@ -79,7 +129,6 @@ struct mtd_info { * MTD_PROGRAM_REGIONS flag is set. * (Maybe we should have an union for those?) */ -#define MTD_PROGREGION_SIZE(mtd) (mtd)->oobblock #define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize #define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype @@ -87,9 +136,8 @@ #define MTD_PROGREGION_CTRLMODE_INVALID( char *name; int index; - // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) - struct nand_oobinfo oobinfo; - u_int32_t oobavail; // Number of bytes in OOB area available for fs + /* ecc layout structure pointer - read only ! */ + struct nand_ecclayout *ecclayout; /* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. @@ -112,11 +160,10 @@ #define MTD_PROGREGION_CTRLMODE_INVALID( int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); - int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); - int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); - - int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); - int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + int (*read_oob) (struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); + int (*write_oob) (struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops); /* * Methods to access the protection register area, present in some @@ -130,17 +177,11 @@ #define MTD_PROGREGION_CTRLMODE_INVALID( int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); - /* kvec-based read/write methods. We need these especially for NAND flash, - with its limited number of write cycles per erase. + /* kvec-based read/write methods. NB: The 'count' parameter is the number of _vectors_, each of which contains an (ofs, len) tuple. */ - int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen); - int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, - size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); - int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, - size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); /* Sync */ void (*sync) (struct mtd_info *mtd); @@ -159,6 +200,9 @@ #define MTD_PROGREGION_CTRLMODE_INVALID( struct notifier_block reboot_notifier; /* default mode before reboot */ + /* ECC status information */ + struct mtd_ecc_stats ecc_stats; + void *priv; struct module *owner; @@ -192,20 +236,6 @@ int default_mtd_writev(struct mtd_info * int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen); -#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) -#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d)) -#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) -#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args) -#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args) -#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args) -#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args) -#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args) -#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args) -#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) -#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) -#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) - - #ifdef CONFIG_MTD_PARTITIONS void mtd_erase_callback(struct erase_info *instr); #else @@ -226,7 +256,7 @@ #define MTD_DEBUG_LEVEL3 (3) /* Noisy #ifdef CONFIG_MTD_DEBUG #define DEBUG(n, args...) \ - do { \ + do { \ if (n <= CONFIG_MTD_DEBUG_VERBOSE) \ printk(KERN_INFO args); \ } while(0) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index da5e67b..0b4cd2f 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -11,52 +11,15 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Info: - * Contains standard defines and IDs for NAND flash devices + * Info: + * Contains standard defines and IDs for NAND flash devices * - * Changelog: - * 01-31-2000 DMW Created - * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers - * so it can be used by other NAND flash device - * drivers. I also changed the copyright since none - * of the original contents of this file are specific - * to DoC devices. David can whack me with a baseball - * bat later if I did something naughty. - * 10-11-2000 SJH Added private NAND flash structure for driver - * 10-24-2000 SJH Added prototype for 'nand_scan' function - * 10-29-2001 TG changed nand_chip structure to support - * hardwarespecific function for accessing control lines - * 02-21-2002 TG added support for different read/write adress and - * ready/busy line access function - * 02-26-2002 TG added chip_delay to nand_chip structure to optimize - * command delay times for different chips - * 04-28-2002 TG OOB config defines moved from nand.c to avoid duplicate - * defines in jffs2/wbuf.c - * 08-07-2002 TG forced bad block location to byte 5 of OOB, even if - * CONFIG_MTD_NAND_ECC_JFFS2 is not set - * 08-10-2002 TG extensions to nand_chip structure to support HW-ECC - * - * 08-29-2002 tglx nand_chip structure: data_poi for selecting - * internal / fs-driver buffer - * support for 6byte/512byte hardware ECC - * read_ecc, write_ecc extended for different oob-layout - * oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB, - * NAND_YAFFS_OOB - * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL - * Split manufacturer and device ID structures - * - * 02-08-2004 tglx added option field to nand structure for chip anomalities - * 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id - * update of nand_chip structure description - * 01-17-2005 dmarlin added extended commands for AG-AND device and added option - * for BBT_AUTO_REFRESH. - * 01-20-2005 dmarlin added optional pointer to hardware specific callback for - * extra error status checks. + * Changelog: + * See git changelog. */ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H -#include #include #include #include @@ -67,10 +30,6 @@ extern int nand_scan (struct mtd_info *m /* Free resources held by the NAND device */ extern void nand_release (struct mtd_info *mtd); -/* Read raw data from the device without ECC */ -extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); - - /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -79,44 +38,45 @@ #define NAND_MAX_CHIPS 8 * adjust this accordingly. */ #define NAND_MAX_OOBSIZE 64 +#define NAND_MAX_PAGESIZE 2048 /* * Constants for hardware specific CLE/ALE/NCE function -*/ + * + * These are bits which can be or'ed to set/clear multiple + * bits in one go. + */ /* Select the chip by setting nCE to low */ -#define NAND_CTL_SETNCE 1 -/* Deselect the chip by setting nCE to high */ -#define NAND_CTL_CLRNCE 2 +#define NAND_NCE 0x01 /* Select the command latch by setting CLE to high */ -#define NAND_CTL_SETCLE 3 -/* Deselect the command latch by setting CLE to low */ -#define NAND_CTL_CLRCLE 4 +#define NAND_CLE 0x02 /* Select the address latch by setting ALE to high */ -#define NAND_CTL_SETALE 5 -/* Deselect the address latch by setting ALE to low */ -#define NAND_CTL_CLRALE 6 -/* Set write protection by setting WP to high. Not used! */ -#define NAND_CTL_SETWP 7 -/* Clear write protection by setting WP to low. Not used! */ -#define NAND_CTL_CLRWP 8 +#define NAND_ALE 0x04 + +#define NAND_CTRL_CLE (NAND_NCE | NAND_CLE) +#define NAND_CTRL_ALE (NAND_NCE | NAND_ALE) +#define NAND_CTRL_CHANGE 0x80 /* * Standard NAND flash commands */ #define NAND_CMD_READ0 0 #define NAND_CMD_READ1 1 +#define NAND_CMD_RNDOUT 5 #define NAND_CMD_PAGEPROG 0x10 #define NAND_CMD_READOOB 0x50 #define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 #define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_SEQIN 0x80 +#define NAND_CMD_RNDIN 0x85 #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 +#define NAND_CMD_RNDOUTSTART 0xE0 #define NAND_CMD_CACHEDPROG 0x15 /* Extended commands for AG-AND device */ @@ -138,6 +98,8 @@ #define NAND_CMD_STATUS_ERROR3 0x76 #define NAND_CMD_STATUS_RESET 0x7f #define NAND_CMD_STATUS_CLEAR 0xff +#define NAND_CMD_NONE -1 + /* Status bits */ #define NAND_STATUS_FAIL 0x01 #define NAND_STATUS_FAIL_N1 0x02 @@ -148,21 +110,12 @@ #define NAND_STATUS_WP 0x80 /* * Constants for ECC_MODES */ - -/* No ECC. Usage is not recommended ! */ -#define NAND_ECC_NONE 0 -/* Software ECC 3 byte ECC per 256 Byte data */ -#define NAND_ECC_SOFT 1 -/* Hardware ECC 3 byte ECC per 256 Byte data */ -#define NAND_ECC_HW3_256 2 -/* Hardware ECC 3 byte ECC per 512 Byte data */ -#define NAND_ECC_HW3_512 3 -/* Hardware ECC 3 byte ECC per 512 Byte data */ -#define NAND_ECC_HW6_512 4 -/* Hardware ECC 8 byte ECC per 512 Byte data */ -#define NAND_ECC_HW8_512 6 -/* Hardware ECC 12 byte ECC per 2048 Byte data */ -#define NAND_ECC_HW12_2048 7 +typedef enum { + NAND_ECC_NONE, + NAND_ECC_SOFT, + NAND_ECC_HW, + NAND_ECC_HW_SYNDROME, +} nand_ecc_modes_t; /* * Constants for Hardware ECC @@ -201,6 +154,10 @@ #define NAND_4PAGE_ARRAY 0x00000040 * bits from adjacent blocks from 'leaking' in altering data. * This happens with the Renesas AG-AND chips, possibly others. */ #define BBT_AUTO_REFRESH 0x00000080 +/* Chip does not require ready check on read. True + * for all large page devices, as they do not support + * autoincrement.*/ +#define NAND_NO_READRDY 0x00000100 /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ @@ -219,18 +176,12 @@ #define NAND_CHIPOPTIONS_MSK (0x0000ffff /* Use a flash based bad block table. This option is passed to the * default bad block table function. */ #define NAND_USE_FLASH_BBT 0x00010000 -/* The hw ecc generator provides a syndrome instead a ecc value on read - * This can only work if we have the ecc bytes directly behind the - * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ -#define NAND_HWECC_SYNDROME 0x00020000 /* This option skips the bbt scan during initialization. */ -#define NAND_SKIP_BBTSCAN 0x00040000 +#define NAND_SKIP_BBTSCAN 0x00020000 /* Options set by nand scan */ -/* Nand scan has allocated oob_buf */ -#define NAND_OOBBUF_ALLOC 0x40000000 -/* Nand scan has allocated data_buf */ -#define NAND_DATABUF_ALLOC 0x80000000 +/* Nand scan has allocated controller struct */ +#define NAND_CONTROLLER_ALLOC 0x80000000 /* @@ -251,7 +202,7 @@ typedef enum { struct nand_chip; /** - * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices + * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * @lock: protection lock * @active: the mtd device which holds the controller currently * @wq: wait queue to sleep on if a NAND operation is in progress @@ -264,45 +215,109 @@ struct nand_hw_control { }; /** + * struct nand_ecc_ctrl - Control structure for ecc + * @mode: ecc mode + * @steps: number of ecc steps per page + * @size: data bytes per ecc step + * @bytes: ecc bytes per step + * @total: total number of ecc bytes per page + * @prepad: padding information for syndrome based ecc generators + * @postpad: padding information for syndrome based ecc generators + * @layout: ECC layout control struct pointer + * @hwctl: function to control hardware ecc generator. Must only + * be provided if an hardware ECC is available + * @calculate: function for ecc calculation or readback from ecc hardware + * @correct: function for ecc correction, matching to ecc generator (sw/hw) + * @read_page: function to read a page according to the ecc generator requirements + * @write_page: function to write a page according to the ecc generator requirements + * @read_oob: function to read chip OOB data + * @write_oob: function to write chip OOB data + */ +struct nand_ecc_ctrl { + nand_ecc_modes_t mode; + int steps; + int size; + int bytes; + int total; + int prepad; + int postpad; + struct nand_ecclayout *layout; + void (*hwctl)(struct mtd_info *mtd, int mode); + int (*calculate)(struct mtd_info *mtd, + const uint8_t *dat, + uint8_t *ecc_code); + int (*correct)(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, + uint8_t *calc_ecc); + int (*read_page)(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf); + void (*write_page)(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf); + int (*read_oob)(struct mtd_info *mtd, + struct nand_chip *chip, + int page, + int sndcmd); + int (*write_oob)(struct mtd_info *mtd, + struct nand_chip *chip, + int page); +}; + +/** + * struct nand_buffers - buffer structure for read/write + * @ecccalc: buffer for calculated ecc + * @ecccode: buffer for ecc read from flash + * @oobwbuf: buffer for write oob data + * @databuf: buffer for data - dynamically sized + * @oobrbuf: buffer to read oob data + * + * Do not change the order of buffers. databuf and oobrbuf must be in + * consecutive order. + */ +struct nand_buffers { + uint8_t ecccalc[NAND_MAX_OOBSIZE]; + uint8_t ecccode[NAND_MAX_OOBSIZE]; + uint8_t oobwbuf[NAND_MAX_OOBSIZE]; + uint8_t databuf[NAND_MAX_PAGESIZE]; + uint8_t oobrbuf[NAND_MAX_OOBSIZE]; +}; + +/** * struct nand_chip - NAND Private Flash Chip Data * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device * @read_byte: [REPLACEABLE] read one byte from the chip - * @write_byte: [REPLACEABLE] write one byte to the chip * @read_word: [REPLACEABLE] read one word from the chip - * @write_word: [REPLACEABLE] write one word to the chip * @write_buf: [REPLACEABLE] write data from the buffer to the chip * @read_buf: [REPLACEABLE] read data from the chip into the buffer * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data * @select_chip: [REPLACEABLE] select chip nr * @block_bad: [REPLACEABLE] check, if the block is bad * @block_markbad: [REPLACEABLE] mark the block bad - * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines + * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling + * ALE/CLE/nCE. Also used to write command and address * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line * If set to NULL no access to ready/busy is available and the ready/busy information * is read from the chip status register * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready - * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware - * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) - * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only - * be provided if a hardware ECC is available + * @ecc: [BOARDSPECIFIC] ecc control ctructure + * @buffers: buffer structure for read/write + * @hwcontrol: platform-specific hardware control structure + * @ops: oob operation operands * @erase_cmd: [INTERN] erase command write function, selectable due to AND support * @scan_bbt: [REPLACEABLE] function to scan bad block table - * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines - * @eccsize: [INTERN] databytes used per ecc-calculation - * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step - * @eccsteps: [INTERN] number of ecc calculation steps per page * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) - * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress - * @state: [INTERN] the current state of the NAND device + * @state: [INTERN] the current state of the NAND device + * @oob_poi: poison value buffer * @page_shift: [INTERN] number of address bits in a page (column address bits) * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry * @chip_shift: [INTERN] number of address bits in one chip - * @data_buf: [INTERN] internal buffer for one page + oob - * @oob_buf: [INTERN] oob buffer for one eraseblock + * @datbuf: [INTERN] internal buffer for one page + oob + * @oobbuf: [INTERN] oob buffer for one eraseblock * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized * @data_poi: [INTERN] pointer to a data buffer * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about @@ -312,12 +327,13 @@ struct nand_hw_control { * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf - * @autooob: [REPLACEABLE] the default (auto)placement scheme + * @ecclayout: [REPLACEABLE] the default ecc placement scheme * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan - * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices + * @controller: [REPLACEABLE] a pointer to a hardware controller structure + * which is shared among multiple independend devices * @priv: [OPTIONAL] pointer to private chip date * @errstat: [OPTIONAL] hardware specific function to perform additional error status checks * (determine if errors are correctable) @@ -325,58 +341,57 @@ struct nand_hw_control { struct nand_chip { void __iomem *IO_ADDR_R; - void __iomem *IO_ADDR_W; + void __iomem *IO_ADDR_W; - u_char (*read_byte)(struct mtd_info *mtd); - void (*write_byte)(struct mtd_info *mtd, u_char byte); + uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); - void (*write_word)(struct mtd_info *mtd, u16 word); - - void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); - void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); - int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); + int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); - void (*hwcontrol)(struct mtd_info *mtd, int cmd); - int (*dev_ready)(struct mtd_info *mtd); - void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); - int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); - int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); - int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); - void (*enable_hwecc)(struct mtd_info *mtd, int mode); + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, + unsigned int ctrl); + int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); - int eccmode; - int eccsize; - int eccbytes; - int eccsteps; - int chip_delay; - spinlock_t chip_lock; - wait_queue_head_t wq; - nand_state_t state; - int page_shift; + int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); + + int chip_delay; + unsigned int options; + + int page_shift; int phys_erase_shift; int bbt_erase_shift; int chip_shift; - u_char *data_buf; - u_char *oob_buf; - int oobdirty; - u_char *data_poi; - unsigned int options; - int badblockpos; int numchips; unsigned long chipsize; int pagemask; int pagebuf; - struct nand_oobinfo *autooob; + int badblockpos; + + nand_state_t state; + + uint8_t *oob_poi; + struct nand_hw_control *controller; + struct nand_ecclayout *ecclayout; + + struct nand_ecc_ctrl ecc; + struct nand_buffers buffers; + struct nand_hw_control hwcontrol; + + struct mtd_oob_ops ops; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; - struct nand_hw_control *controller; + void *priv; - int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); }; /* @@ -388,19 +403,18 @@ #define NAND_MFR_FUJITSU 0x04 #define NAND_MFR_NATIONAL 0x8f #define NAND_MFR_RENESAS 0x07 #define NAND_MFR_STMICRO 0x20 -#define NAND_MFR_HYNIX 0xad +#define NAND_MFR_HYNIX 0xad /** * struct nand_flash_dev - NAND Flash Device ID Structure - * - * @name: Identify the device type - * @id: device ID code - * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 + * @name: Identify the device type + * @id: device ID code + * @pagesize: Pagesize in bytes. Either 256 or 512 or 0 * If the pagesize is 0, then the real pagesize * and the eraseize are determined from the * extended id bytes in the chip - * @erasesize: Size of an erase block in the flash device. - * @chipsize: Total chipsize in Mega Bytes + * @erasesize: Size of an erase block in the flash device. + * @chipsize: Total chipsize in Mega Bytes * @options: Bitfield to store chip relevant options */ struct nand_flash_dev { @@ -415,7 +429,7 @@ struct nand_flash_dev { /** * struct nand_manufacturers - NAND Flash Manufacturer ID Structure * @name: Manufacturer name - * @id: manufacturer ID code of device. + * @id: manufacturer ID code of device. */ struct nand_manufacturers { int id; @@ -455,7 +469,7 @@ struct nand_bbt_descr { int veroffs; uint8_t version[NAND_MAX_CHIPS]; int len; - int maxblocks; + int maxblocks; int reserved_block_code; uint8_t *pattern; }; @@ -494,14 +508,14 @@ #define NAND_BBT_SCAN2NDPAGE 0x00004000 /* The maximum number of blocks to scan for a bbt */ #define NAND_BBT_SCAN_MAXBLOCKS 4 -extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd); -extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs); -extern int nand_default_bbt (struct mtd_info *mtd); -extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt); -extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); -extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf, u_char * oob_buf, - struct nand_oobinfo *oobsel, int flags); +extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd); +extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); +extern int nand_default_bbt(struct mtd_info *mtd); +extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); +extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, + int allowbbt); +extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, uint8_t * buf); /* * Constants for oob configuration @@ -509,4 +523,51 @@ extern int nand_do_read_ecc (struct mtd_ #define NAND_SMALL_BADBLOCK_POS 5 #define NAND_LARGE_BADBLOCK_POS 0 +/** + * struct platform_nand_chip - chip level device structure + * @nr_chips: max. number of chips to scan for + * @chip_offset: chip number offset + * @nr_partitions: number of partitions pointed to by partitions (or zero) + * @partitions: mtd partition list + * @chip_delay: R/B delay value in us + * @options: Option flags, e.g. 16bit buswidth + * @ecclayout: ecc layout info structure + * @priv: hardware controller specific settings + */ +struct platform_nand_chip { + int nr_chips; + int chip_offset; + int nr_partitions; + struct mtd_partition *partitions; + struct nand_ecclayout *ecclayout; + int chip_delay; + unsigned int options; + void *priv; +}; + +/** + * struct platform_nand_ctrl - controller level device structure + * @hwcontrol: platform specific hardware control structure + * @dev_ready: platform specific function to read ready/busy pin + * @select_chip: platform specific chip select function + * @priv: private data to transport driver specific settings + * + * All fields are optional and depend on the hardware driver requirements + */ +struct platform_nand_ctrl { + void (*hwcontrol)(struct mtd_info *mtd, int cmd); + int (*dev_ready)(struct mtd_info *mtd); + void (*select_chip)(struct mtd_info *mtd, int chip); + void *priv; +}; + +/* Some helpers to access the data structures */ +static inline +struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + return chip->priv; +} + #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/ndfc.h b/include/linux/mtd/ndfc.h new file mode 100644 index 0000000..d0558a9 --- /dev/null +++ b/include/linux/mtd/ndfc.h @@ -0,0 +1,67 @@ +/* + * linux/include/linux/mtd/ndfc.h + * + * Copyright (c) 2006 Thomas Gleixner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Info: + * Contains defines, datastructures for ndfc nand controller + * + */ +#ifndef __LINUX_MTD_NDFC_H +#define __LINUX_MTD_NDFC_H + +/* NDFC Register definitions */ +#define NDFC_CMD 0x00 +#define NDFC_ALE 0x04 +#define NDFC_DATA 0x08 +#define NDFC_ECC 0x10 +#define NDFC_BCFG0 0x30 +#define NDFC_BCFG1 0x34 +#define NDFC_BCFG2 0x38 +#define NDFC_BCFG3 0x3c +#define NDFC_CCR 0x40 +#define NDFC_STAT 0x44 +#define NDFC_HWCTL 0x48 +#define NDFC_REVID 0x50 + +#define NDFC_STAT_IS_READY 0x01000000 + +#define NDFC_CCR_RESET_CE 0x80000000 /* CE Reset */ +#define NDFC_CCR_RESET_ECC 0x40000000 /* ECC Reset */ +#define NDFC_CCR_RIE 0x20000000 /* Interrupt Enable on Device Rdy */ +#define NDFC_CCR_REN 0x10000000 /* Enable wait for Rdy in LinearR */ +#define NDFC_CCR_ROMEN 0x08000000 /* Enable ROM In LinearR */ +#define NDFC_CCR_ARE 0x04000000 /* Auto-Read Enable */ +#define NDFC_CCR_BS(x) (((x) & 0x3) << 24) /* Select Bank on CE[x] */ +#define NDFC_CCR_BS_MASK 0x03000000 /* Select Bank */ +#define NDFC_CCR_ARAC0 0x00000000 /* 3 Addr, 1 Col 2 Row 512b page */ +#define NDFC_CCR_ARAC1 0x00001000 /* 4 Addr, 1 Col 3 Row 512b page */ +#define NDFC_CCR_ARAC2 0x00002000 /* 4 Addr, 2 Col 2 Row 2K page */ +#define NDFC_CCR_ARAC3 0x00003000 /* 5 Addr, 2 Col 3 Row 2K page */ +#define NDFC_CCR_ARAC_MASK 0x00003000 /* Auto-Read mode Addr Cycles */ +#define NDFC_CCR_RPG 0x0000C000 /* Auto-Read Page */ +#define NDFC_CCR_EBCC 0x00000004 /* EBC Configuration Completed */ +#define NDFC_CCR_DHC 0x00000002 /* Direct Hardware Control Enable */ + +#define NDFC_BxCFG_EN 0x80000000 /* Bank Enable */ +#define NDFC_BxCFG_CED 0x40000000 /* nCE Style */ +#define NDFC_BxCFG_SZ_MASK 0x08000000 /* Bank Size */ +#define NDFC_BxCFG_SZ_8BIT 0x00000000 /* 8bit */ +#define NDFC_BxCFG_SZ_16BIT 0x08000000 /* 16bit */ + +#define NDFC_MAX_BANKS 4 + +struct ndfc_controller_settings { + uint32_t ccr_settings; + uint64_t ndfc_erpn; +}; + +struct ndfc_chip_settings { + uint32_t bank_settings; +}; + +#endif diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index d35d2c2..bcf2fb3 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -37,7 +37,7 @@ struct NFTLrecord { unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; - struct nand_oobinfo oobinfo; + struct nand_ecclayout oobinfo; }; int NFTL_mount(struct NFTLrecord *s); diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 7419b5f..1f49721 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -23,7 +23,7 @@ extern int onenand_scan(struct mtd_info /* Free resources held by the OneNAND device */ extern void onenand_release(struct mtd_info *mtd); -/** +/* * onenand_state_t - chip states * Enumeration for OneNAND flash chip state */ @@ -35,14 +35,16 @@ typedef enum { FL_SYNCING, FL_UNLOCKING, FL_LOCKING, + FL_RESETING, + FL_OTPING, FL_PM_SUSPENDED, } onenand_state_t; /** * struct onenand_bufferram - OneNAND BufferRAM Data - * @param block block address in BufferRAM - * @param page page address in BufferRAM - * @param valid valid flag + * @block: block address in BufferRAM + * @page: page address in BufferRAM + * @valid: valid flag */ struct onenand_bufferram { int block; @@ -52,32 +54,43 @@ struct onenand_bufferram { /** * struct onenand_chip - OneNAND Private Flash Chip Data - * @param base [BOARDSPECIFIC] address to access OneNAND - * @param chipsize [INTERN] the size of one chip for multichip arrays - * @param device_id [INTERN] device ID - * @param verstion_id [INTERN] version ID - * @param options [BOARDSPECIFIC] various chip options. They can partly be set to inform onenand_scan about - * @param erase_shift [INTERN] number of address bits in a block - * @param page_shift [INTERN] number of address bits in a page - * @param ppb_shift [INTERN] number of address bits in a pages per block - * @param page_mask [INTERN] a page per block mask - * @param bufferam_index [INTERN] BufferRAM index - * @param bufferam [INTERN] BufferRAM info - * @param readw [REPLACEABLE] hardware specific function for read short - * @param writew [REPLACEABLE] hardware specific function for write short - * @param command [REPLACEABLE] hardware specific function for writing commands to the chip - * @param wait [REPLACEABLE] hardware specific function for wait on ready - * @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area - * @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area - * @param read_word [REPLACEABLE] hardware specific function for read register of OneNAND - * @param write_word [REPLACEABLE] hardware specific function for write register of OneNAND - * @param scan_bbt [REPLACEALBE] hardware specific function for scaning Bad block Table - * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip - * @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress - * @param state [INTERN] the current state of the OneNAND device - * @param autooob [REPLACEABLE] the default (auto)placement scheme - * @param bbm [REPLACEABLE] pointer to Bad Block Management - * @param priv [OPTIONAL] pointer to private chip date + * @base: [BOARDSPECIFIC] address to access OneNAND + * @chipsize: [INTERN] the size of one chip for multichip arrays + * @device_id: [INTERN] device ID + * @density_mask: chip density, used for DDP devices + * @verstion_id: [INTERN] version ID + * @options: [BOARDSPECIFIC] various chip options. They can + * partly be set to inform onenand_scan about + * @erase_shift: [INTERN] number of address bits in a block + * @page_shift: [INTERN] number of address bits in a page + * @ppb_shift: [INTERN] number of address bits in a pages per block + * @page_mask: [INTERN] a page per block mask + * @bufferram_index: [INTERN] BufferRAM index + * @bufferram: [INTERN] BufferRAM info + * @readw: [REPLACEABLE] hardware specific function for read short + * @writew: [REPLACEABLE] hardware specific function for write short + * @command: [REPLACEABLE] hardware specific function for writing + * commands to the chip + * @wait: [REPLACEABLE] hardware specific function for wait on ready + * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area + * @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area + * @read_word: [REPLACEABLE] hardware specific function for read + * register of OneNAND + * @write_word: [REPLACEABLE] hardware specific function for write + * register of OneNAND + * @mmcontrol: sync burst read function + * @block_markbad: function to mark a block as bad + * @scan_bbt: [REPLACEALBE] hardware specific function for scanning + * Bad block Table + * @chip_lock: [INTERN] spinlock used to protect access to this + * structure and the chip + * @wq: [INTERN] wait queue to sleep on if a OneNAND + * operation is in progress + * @state: [INTERN] the current state of the OneNAND device + * @page_buf: data buffer + * @ecclayout: [REPLACEABLE] the default ecc placement scheme + * @bbm: [REPLACEABLE] pointer to Bad Block Management + * @priv: [OPTIONAL] pointer to private chip date */ struct onenand_chip { void __iomem *base; @@ -111,9 +124,9 @@ struct onenand_chip { onenand_state_t state; unsigned char *page_buf; - struct nand_oobinfo *autooob; + struct nand_ecclayout *ecclayout; - void *bbm; + void *bbm; void *priv; }; @@ -130,6 +143,9 @@ #define ONENAND_GET_SYS_CFG1(this) \ #define ONENAND_SET_SYS_CFG1(v, this) \ (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) +/* Check byte access in OneNAND */ +#define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) + /* * Options bits */ @@ -142,9 +158,9 @@ #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_MFR_SAMSUNG 0xec /** - * struct nand_manufacturers - NAND Flash Manufacturer ID Structure - * @param name: Manufacturer name - * @param id: manufacturer ID code of device. + * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure + * @name: Manufacturer name + * @id: manufacturer ID code of device. */ struct onenand_manufacturers { int id; diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index d7832ef..4a72818 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -112,6 +112,7 @@ #define ONENAND_CMD_LOCK (0x2A) #define ONENAND_CMD_LOCK_TIGHT (0x2C) #define ONENAND_CMD_ERASE (0x94) #define ONENAND_CMD_RESET (0xF0) +#define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_READID (0x90) /* NOTE: Those are not *REAL* commands */ @@ -152,6 +153,8 @@ #define ONENAND_CTRL_PROGRAM (1 << 12) #define ONENAND_CTRL_ERASE (1 << 11) #define ONENAND_CTRL_ERROR (1 << 10) #define ONENAND_CTRL_RSTB (1 << 7) +#define ONENAND_CTRL_OTP_L (1 << 6) +#define ONENAND_CTRL_OTP_BL (1 << 5) /* * Interrupt Status Register F241h (R) @@ -177,4 +180,9 @@ #define ONENAND_ECC_1BIT (1 << 0) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) +/* + * One-Time Programmable (OTP) + */ +#define ONENAND_OTP_LOCK_OFFSET (14) + #endif /* __ONENAND_REG_H */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index b03f512..da6b3d6 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -41,7 +41,7 @@ struct mtd_partition { u_int32_t size; /* partition size */ u_int32_t offset; /* offset within the master MTD space */ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ - struct nand_oobinfo *oobsel; /* out of band layout for this partition (NAND only)*/ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ struct mtd_info **mtdp; /* pointer to store the MTD object */ }; diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index c7b8bcd..86831e3 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -15,33 +15,26 @@ */ #ifndef __LINUX_MTD_PHYSMAP__ - -#include - -#if defined(CONFIG_MTD_PHYSMAP) +#define __LINUX_MTD_PHYSMAP__ #include #include #include -/* - * The map_info for physmap. Board can override size, buswidth, phys, - * (*set_vpp)(), etc in their initial setup routine. - */ -extern struct map_info physmap_map; +struct physmap_flash_data { + unsigned int width; + void (*set_vpp)(struct map_info *, int); + unsigned int nr_parts; + struct mtd_partition *parts; +}; /* * Board needs to specify the exact mapping during their setup time. */ -static inline void physmap_configure(unsigned long addr, unsigned long size, int bankwidth, void (*set_vpp)(struct map_info *, int) ) -{ - physmap_map.phys = addr; - physmap_map.size = size; - physmap_map.bankwidth = bankwidth; - physmap_map.set_vpp = set_vpp; -} +void physmap_configure(unsigned long addr, unsigned long size, + int bankwidth, void (*set_vpp)(struct map_info *, int) ); -#if defined(CONFIG_MTD_PARTITIONS) +#ifdef CONFIG_MTD_PARTITIONS /* * Machines that wish to do flash partition may want to call this function in @@ -55,7 +48,5 @@ #if defined(CONFIG_MTD_PARTITIONS) void physmap_set_partitions(struct mtd_partition *parts, int num_parts); #endif /* defined(CONFIG_MTD_PARTITIONS) */ -#endif /* defined(CONFIG_MTD) */ #endif /* __LINUX_MTD_PHYSMAP__ */ - diff --git a/include/linux/mtd/xip.h b/include/linux/mtd/xip.h index 220d50b..e9d40bd 100644 --- a/include/linux/mtd/xip.h +++ b/include/linux/mtd/xip.h @@ -18,7 +18,6 @@ #ifndef __LINUX_MTD_XIP_H__ #define __LINUX_MTD_XIP_H__ -#include #ifdef CONFIG_MTD_XIP diff --git a/include/linux/mutex-debug.h b/include/linux/mutex-debug.h index 8b5769f..2537285 100644 --- a/include/linux/mutex-debug.h +++ b/include/linux/mutex-debug.h @@ -2,22 +2,22 @@ #ifndef __LINUX_MUTEX_DEBUG_H #define __LINUX_MUTEX_DEBUG_H #include +#include /* * Mutexes - debugging helpers: */ -#define __DEBUG_MUTEX_INITIALIZER(lockname) \ - , .held_list = LIST_HEAD_INIT(lockname.held_list), \ - .name = #lockname , .magic = &lockname +#define __DEBUG_MUTEX_INITIALIZER(lockname) \ + , .magic = &lockname -#define mutex_init(sem) __mutex_init(sem, __FUNCTION__) +#define mutex_init(mutex) \ +do { \ + static struct lock_class_key __key; \ + \ + __mutex_init((mutex), #mutex, &__key); \ +} while (0) extern void FASTCALL(mutex_destroy(struct mutex *lock)); -extern void mutex_debug_show_all_locks(void); -extern void mutex_debug_show_held_locks(struct task_struct *filter); -extern void mutex_debug_check_no_locks_held(struct task_struct *task); -extern void mutex_debug_check_no_locks_freed(const void *from, unsigned long len); - #endif diff --git a/include/linux/mutex.h b/include/linux/mutex.h index f1ac507..27c48da 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -13,6 +13,7 @@ #define __LINUX_MUTEX_H #include #include #include +#include #include @@ -50,11 +51,12 @@ struct mutex { struct list_head wait_list; #ifdef CONFIG_DEBUG_MUTEXES struct thread_info *owner; - struct list_head held_list; - unsigned long acquire_ip; const char *name; void *magic; #endif +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif }; /* @@ -74,24 +76,34 @@ #ifdef CONFIG_DEBUG_MUTEXES # include #else # define __DEBUG_MUTEX_INITIALIZER(lockname) -# define mutex_init(mutex) __mutex_init(mutex, NULL) +# define mutex_init(mutex) \ +do { \ + static struct lock_class_key __key; \ + \ + __mutex_init((mutex), #mutex, &__key); \ +} while (0) # define mutex_destroy(mutex) do { } while (0) -# define mutex_debug_show_all_locks() do { } while (0) -# define mutex_debug_show_held_locks(p) do { } while (0) -# define mutex_debug_check_no_locks_held(task) do { } while (0) -# define mutex_debug_check_no_locks_freed(from, len) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ + , .dep_map = { .name = #lockname } +#else +# define __DEP_MAP_MUTEX_INITIALIZER(lockname) #endif #define __MUTEX_INITIALIZER(lockname) \ { .count = ATOMIC_INIT(1) \ , .wait_lock = SPIN_LOCK_UNLOCKED \ , .wait_list = LIST_HEAD_INIT(lockname.wait_list) \ - __DEBUG_MUTEX_INITIALIZER(lockname) } + __DEBUG_MUTEX_INITIALIZER(lockname) \ + __DEP_MAP_MUTEX_INITIALIZER(lockname) } #define DEFINE_MUTEX(mutexname) \ struct mutex mutexname = __MUTEX_INITIALIZER(mutexname) -extern void fastcall __mutex_init(struct mutex *lock, const char *name); +extern void __mutex_init(struct mutex *lock, const char *name, + struct lock_class_key *key); /*** * mutex_is_locked - is the mutex locked @@ -110,6 +122,13 @@ static inline int fastcall mutex_is_lock */ extern void fastcall mutex_lock(struct mutex *lock); extern int fastcall mutex_lock_interruptible(struct mutex *lock); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass); +#else +# define mutex_lock_nested(lock, subclass) mutex_lock(lock) +#endif + /* * NOTE: mutex_trylock() follows the spin_trylock() convention, * not the down_trylock() convention! diff --git a/include/linux/nbd.h b/include/linux/nbd.h index a6ce409..e712e7d 100644 --- a/include/linux/nbd.h +++ b/include/linux/nbd.h @@ -77,11 +77,11 @@ #define NBD_REPLY_MAGIC 0x67446698 * server. All data are in network byte order. */ struct nbd_request { - u32 magic; - u32 type; /* == READ || == WRITE */ + __be32 magic; + __be32 type; /* == READ || == WRITE */ char handle[8]; - u64 from; - u32 len; + __be64 from; + __be32 len; } #ifdef __GNUC__ __attribute__ ((packed)) @@ -93,8 +93,8 @@ #endif * it has completed an I/O request (or an error occurs). */ struct nbd_reply { - u32 magic; - u32 error; /* 0 = ok, else error */ + __be32 magic; + __be32 error; /* 0 = ok, else error */ char handle[8]; /* handle you got from request */ }; #endif diff --git a/include/linux/ncp_fs.h b/include/linux/ncp_fs.h index 96dc237..b208f0c 100644 --- a/include/linux/ncp_fs.h +++ b/include/linux/ncp_fs.h @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include #include @@ -146,7 +144,8 @@ #define NCP_MAXNAMELEN 14 #ifdef __KERNEL__ -#include +#include +#include /* undef because public define in umsdos_fs.h (ncp_fs.h isn't public) */ #undef PRINTK diff --git a/include/linux/net.h b/include/linux/net.h index 84a490e..b20c53c 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -18,9 +18,7 @@ #ifndef _LINUX_NET_H #define _LINUX_NET_H -#include #include -#include #include struct poll_table_struct; @@ -57,11 +55,13 @@ typedef enum { #define __SO_ACCEPTCON (1 << 16) /* performed a listen */ #ifdef __KERNEL__ +#include #define SOCK_ASYNC_NOSPACE 0 #define SOCK_ASYNC_WAITDATA 1 #define SOCK_NOSPACE 2 #define SOCK_PASSCRED 3 +#define SOCK_PASSSEC 4 #ifndef ARCH_HAS_SOCKET_TYPES /** diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f4169bb..85f99f6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -34,9 +34,9 @@ #include #include #include -#include #include #include +#include struct divert_blk; struct vlan_group; @@ -232,6 +232,7 @@ enum netdev_state_t __LINK_STATE_RX_SCHED, __LINK_STATE_LINKWATCH_PENDING, __LINK_STATE_DORMANT, + __LINK_STATE_QDISC_RUNNING, }; @@ -307,9 +308,20 @@ #define NETIF_F_HW_VLAN_TX 128 /* Transm #define NETIF_F_HW_VLAN_RX 256 /* Receive VLAN hw acceleration */ #define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */ #define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */ -#define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */ +#define NETIF_F_GSO 2048 /* Enable software GSO. */ #define NETIF_F_LLTX 4096 /* LockLess TX */ -#define NETIF_F_UFO 8192 /* Can offload UDP Large Send*/ + + /* Segmentation offload features */ +#define NETIF_F_GSO_SHIFT 16 +#define NETIF_F_GSO_MASK 0xffff0000 +#define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) +#define NETIF_F_UFO (SKB_GSO_UDP << NETIF_F_GSO_SHIFT) +#define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT) +#define NETIF_F_TSO_ECN (SKB_GSO_TCP_ECN << NETIF_F_GSO_SHIFT) +#define NETIF_F_TSO6 (SKB_GSO_TCPV6 << NETIF_F_GSO_SHIFT) + +#define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) +#define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) struct net_device *next_sched; @@ -398,6 +410,9 @@ #define NETIF_F_UFO 8192 struct list_head qdisc_list; unsigned long tx_queue_len; /* Max frames per queue allowed */ + /* Partially transmitted GSO packet. */ + struct sk_buff *gso_skb; + /* ingress path synchronizer */ spinlock_t ingress_lock; struct Qdisc *qdisc_ingress; @@ -406,7 +421,7 @@ #define NETIF_F_UFO 8192 * One part is mostly used on xmit path (device) */ /* hard_start_xmit synchronizer */ - spinlock_t xmit_lock ____cacheline_aligned_in_smp; + spinlock_t _xmit_lock ____cacheline_aligned_in_smp; /* cpu id of processor entered to hard_start_xmit or -1, if nobody entered there. */ @@ -532,6 +547,8 @@ struct packet_type { struct net_device *, struct packet_type *, struct net_device *); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, + int features); void *af_packet_priv; struct list_head list; }; @@ -593,6 +610,9 @@ struct softnet_data struct sk_buff *completion_queue; struct net_device backlog_dev; /* Sorry. 8) */ +#ifdef CONFIG_NET_DMA + struct dma_chan *net_dma; +#endif }; DECLARE_PER_CPU(struct softnet_data,softnet_data); @@ -679,11 +699,11 @@ extern int dev_change_name(struct net_d extern int dev_set_mtu(struct net_device *, int); extern int dev_set_mac_address(struct net_device *, struct sockaddr *); -extern void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev); +extern int dev_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev); extern void dev_init(void); -extern int netdev_nit; extern int netdev_budget; /* Called by rtnetlink.c:rtnl_unlock() */ @@ -889,11 +909,43 @@ static inline void __netif_rx_complete(s clear_bit(__LINK_STATE_RX_SCHED, &dev->state); } +static inline void netif_tx_lock(struct net_device *dev) +{ + spin_lock(&dev->_xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); +} + +static inline void netif_tx_lock_bh(struct net_device *dev) +{ + spin_lock_bh(&dev->_xmit_lock); + dev->xmit_lock_owner = smp_processor_id(); +} + +static inline int netif_tx_trylock(struct net_device *dev) +{ + int err = spin_trylock(&dev->_xmit_lock); + if (!err) + dev->xmit_lock_owner = smp_processor_id(); + return err; +} + +static inline void netif_tx_unlock(struct net_device *dev) +{ + dev->xmit_lock_owner = -1; + spin_unlock(&dev->_xmit_lock); +} + +static inline void netif_tx_unlock_bh(struct net_device *dev) +{ + dev->xmit_lock_owner = -1; + spin_unlock_bh(&dev->_xmit_lock); +} + static inline void netif_tx_disable(struct net_device *dev) { - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); netif_stop_queue(dev); - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); } /* These functions live elsewhere (drivers/net/net_init.c, but related) */ @@ -921,6 +973,7 @@ extern int netdev_max_backlog; extern int weight_p; extern int netdev_set_master(struct net_device *dev, struct net_device *master); extern int skb_checksum_help(struct sk_buff *skb, int inward); +extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features); #ifdef CONFIG_BUG extern void netdev_rx_csum_fault(struct net_device *dev); #else @@ -940,6 +993,23 @@ #endif extern void linkwatch_run_queue(void); +static inline int net_gso_ok(int features, int gso_type) +{ + int feature = gso_type << NETIF_F_GSO_SHIFT; + return (features & feature) == feature; +} + +static inline int skb_gso_ok(struct sk_buff *skb, int features) +{ + return net_gso_ok(features, skb_shinfo(skb)->gso_size ? + skb_shinfo(skb)->gso_type : 0); +} + +static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) +{ + return !skb_gso_ok(skb, dev->features); +} + #endif /* __KERNEL__ */ #endif /* _LINUX_DEV_H */ diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index b31a9bc..10168e2 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -40,7 +40,6 @@ #define NFC_ALTERED 0x8000 #endif #ifdef __KERNEL__ -#include #ifdef CONFIG_NETFILTER extern void netfilter_init(void); diff --git a/include/linux/netfilter/Kbuild b/include/linux/netfilter/Kbuild new file mode 100644 index 0000000..d06311a --- /dev/null +++ b/include/linux/netfilter/Kbuild @@ -0,0 +1,11 @@ +header-y := nf_conntrack_sctp.h nf_conntrack_tuple_common.h \ + nfnetlink_conntrack.h nfnetlink_log.h nfnetlink_queue.h \ + xt_CLASSIFY.h xt_comment.h xt_connbytes.h xt_connmark.h \ + xt_CONNMARK.h xt_conntrack.h xt_dccp.h xt_esp.h \ + xt_helper.h xt_length.h xt_limit.h xt_mac.h xt_mark.h \ + xt_MARK.h xt_multiport.h xt_NFQUEUE.h xt_pkttype.h \ + xt_policy.h xt_realm.h xt_sctp.h xt_state.h xt_string.h \ + xt_tcpmss.h xt_tcpudp.h + +unifdef-y := nf_conntrack_common.h nf_conntrack_ftp.h \ + nf_conntrack_tcp.h nfnetlink.h x_tables.h xt_physdev.h diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index 3ff88c8..d2e4bd7 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -69,6 +69,10 @@ enum ip_conntrack_status { /* Connection is dying (removed from lists), can not be unset. */ IPS_DYING_BIT = 9, IPS_DYING = (1 << IPS_DYING_BIT), + + /* Connection has fixed timeout. */ + IPS_FIXED_TIMEOUT_BIT = 10, + IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT), }; /* Connection tracking event bits */ diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 668ec94..b5883cc 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -27,13 +27,15 @@ enum ctattr_type { CTA_STATUS, CTA_PROTOINFO, CTA_HELP, - CTA_NAT, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ CTA_TIMEOUT, CTA_MARK, CTA_COUNTERS_ORIG, CTA_COUNTERS_REPLY, CTA_USE, CTA_ID, + CTA_NAT_DST, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) diff --git a/include/linux/netfilter/xt_CONNSECMARK.h b/include/linux/netfilter/xt_CONNSECMARK.h new file mode 100644 index 0000000..c6bd754 --- /dev/null +++ b/include/linux/netfilter/xt_CONNSECMARK.h @@ -0,0 +1,13 @@ +#ifndef _XT_CONNSECMARK_H_target +#define _XT_CONNSECMARK_H_target + +enum { + CONNSECMARK_SAVE = 1, + CONNSECMARK_RESTORE, +}; + +struct xt_connsecmark_target_info { + u_int8_t mode; +}; + +#endif /*_XT_CONNSECMARK_H_target */ diff --git a/include/linux/netfilter/xt_SECMARK.h b/include/linux/netfilter/xt_SECMARK.h new file mode 100644 index 0000000..c53fbff --- /dev/null +++ b/include/linux/netfilter/xt_SECMARK.h @@ -0,0 +1,26 @@ +#ifndef _XT_SECMARK_H_target +#define _XT_SECMARK_H_target + +/* + * This is intended for use by various security subsystems (but not + * at the same time). + * + * 'mode' refers to the specific security subsystem which the + * packets are being marked for. + */ +#define SECMARK_MODE_SEL 0x01 /* SELinux */ +#define SECMARK_SELCTX_MAX 256 + +struct xt_secmark_target_selinux_info { + u_int32_t selsid; + char selctx[SECMARK_SELCTX_MAX]; +}; + +struct xt_secmark_target_info { + u_int8_t mode; + union { + struct xt_secmark_target_selinux_info sel; + } u; +}; + +#endif /*_XT_SECMARK_H_target */ diff --git a/include/linux/netfilter/xt_conntrack.h b/include/linux/netfilter/xt_conntrack.h index 34f63cf..4c2d994 100644 --- a/include/linux/netfilter/xt_conntrack.h +++ b/include/linux/netfilter/xt_conntrack.h @@ -42,7 +42,7 @@ struct ip_conntrack_old_tuple } u; /* The protocol. */ - u16 protonum; + __u16 protonum; } dst; }; diff --git a/include/linux/netfilter/xt_quota.h b/include/linux/netfilter/xt_quota.h new file mode 100644 index 0000000..acd7fd7 --- /dev/null +++ b/include/linux/netfilter/xt_quota.h @@ -0,0 +1,16 @@ +#ifndef _XT_QUOTA_H +#define _XT_QUOTA_H + +enum xt_quota_flags { + XT_QUOTA_INVERT = 0x1, +}; +#define XT_QUOTA_MASK 0x1 + +struct xt_quota_info { + u_int32_t flags; + u_int32_t pad; + aligned_u64 quota; + struct xt_quota_info *master; +}; + +#endif /* _XT_QUOTA_H */ diff --git a/include/linux/netfilter/xt_statistic.h b/include/linux/netfilter/xt_statistic.h new file mode 100644 index 0000000..c344e99 --- /dev/null +++ b/include/linux/netfilter/xt_statistic.h @@ -0,0 +1,32 @@ +#ifndef _XT_STATISTIC_H +#define _XT_STATISTIC_H + +enum xt_statistic_mode { + XT_STATISTIC_MODE_RANDOM, + XT_STATISTIC_MODE_NTH, + __XT_STATISTIC_MODE_MAX +}; +#define XT_STATISTIC_MODE_MAX (__XT_STATISTIC_MODE_MAX - 1) + +enum xt_statistic_flags { + XT_STATISTIC_INVERT = 0x1, +}; +#define XT_STATISTIC_MASK 0x1 + +struct xt_statistic_info { + u_int16_t mode; + u_int16_t flags; + union { + struct { + u_int32_t probability; + } random; + struct { + u_int32_t every; + u_int32_t packet; + u_int32_t count; + } nth; + } u; + struct xt_statistic_info *master __attribute__((aligned(8))); +}; + +#endif /* _XT_STATISTIC_H */ diff --git a/include/linux/netfilter_arp.h b/include/linux/netfilter_arp.h index a3f8977..92bc6dd 100644 --- a/include/linux/netfilter_arp.h +++ b/include/linux/netfilter_arp.h @@ -5,7 +5,6 @@ #define __LINUX_ARP_NETFILTER_H * (C)2002 Rusty Russell IBM -- This code is GPL. */ -#include #include /* There is no PF_ARP. */ diff --git a/include/linux/netfilter_arp/Kbuild b/include/linux/netfilter_arp/Kbuild new file mode 100644 index 0000000..198ec5e --- /dev/null +++ b/include/linux/netfilter_arp/Kbuild @@ -0,0 +1,2 @@ +header-y := arpt_mangle.h +unifdef-y := arp_tables.h diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index a75b84b..8776402 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -4,7 +4,6 @@ #define __LINUX_BRIDGE_NETFILTER_H /* bridge-specific defines for netfilter. */ -#include #include #if defined(__KERNEL__) && defined(CONFIG_BRIDGE_NETFILTER) #include diff --git a/include/linux/netfilter_bridge/Kbuild b/include/linux/netfilter_bridge/Kbuild new file mode 100644 index 0000000..5b1aba6 --- /dev/null +++ b/include/linux/netfilter_bridge/Kbuild @@ -0,0 +1,4 @@ +header-y += ebt_among.h ebt_arp.h ebt_arpreply.h ebt_ip.h ebt_limit.h \ + ebt_log.h ebt_mark_m.h ebt_mark_t.h ebt_nat.h ebt_pkttype.h \ + ebt_redirect.h ebt_stp.h ebt_ulog.h ebt_vlan.h +unifdef-y := ebtables.h ebt_802_3.h diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index 85301c5..ce02c98 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -5,7 +5,6 @@ #define __LINUX_IP_NETFILTER_H * (C)1998 Rusty Russell -- This code is GPL. */ -#include #include /* only for userspace compatibility */ diff --git a/include/linux/netfilter_ipv4/Kbuild b/include/linux/netfilter_ipv4/Kbuild new file mode 100644 index 0000000..04e4d27 --- /dev/null +++ b/include/linux/netfilter_ipv4/Kbuild @@ -0,0 +1,21 @@ + +header-y := ip_conntrack_helper.h ip_conntrack_helper_h323_asn1.h \ + ip_conntrack_helper_h323_types.h ip_conntrack_protocol.h \ + ip_conntrack_sctp.h ip_conntrack_tcp.h ip_conntrack_tftp.h \ + ip_nat_pptp.h ipt_addrtype.h ipt_ah.h \ + ipt_CLASSIFY.h ipt_CLUSTERIP.h ipt_comment.h \ + ipt_connbytes.h ipt_connmark.h ipt_CONNMARK.h \ + ipt_conntrack.h ipt_dccp.h ipt_dscp.h ipt_DSCP.h ipt_ecn.h \ + ipt_ECN.h ipt_esp.h ipt_hashlimit.h ipt_helper.h \ + ipt_iprange.h ipt_length.h ipt_limit.h ipt_LOG.h ipt_mac.h \ + ipt_mark.h ipt_MARK.h ipt_multiport.h ipt_NFQUEUE.h \ + ipt_owner.h ipt_physdev.h ipt_pkttype.h ipt_policy.h \ + ipt_realm.h ipt_recent.h ipt_REJECT.h ipt_SAME.h \ + ipt_sctp.h ipt_state.h ipt_string.h ipt_tcpmss.h \ + ipt_TCPMSS.h ipt_tos.h ipt_TOS.h ipt_ttl.h ipt_TTL.h \ + ipt_ULOG.h + +unifdef-y := ip_conntrack.h ip_conntrack_h323.h ip_conntrack_irc.h \ + ip_conntrack_pptp.h ip_conntrack_proto_gre.h \ + ip_conntrack_tuple.h ip_nat.h ip_nat_rule.h ip_queue.h \ + ip_tables.h diff --git a/include/linux/netfilter_ipv4/ip_conntrack.h b/include/linux/netfilter_ipv4/ip_conntrack.h index d54d7b2..51dbec1 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack.h +++ b/include/linux/netfilter_ipv4/ip_conntrack.h @@ -4,7 +4,6 @@ #define _IP_CONNTRACK_H #include #ifdef __KERNEL__ -#include #include #include #include @@ -121,6 +120,10 @@ #if defined(CONFIG_IP_NF_CONNTRACK_MARK) u_int32_t mark; #endif +#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK + u_int32_t secmark; +#endif + /* Traversed often, so hopefully in different cacheline to top */ /* These are my tuples; original and reply */ struct ip_conntrack_tuple_hash tuplehash[IP_CT_DIR_MAX]; @@ -154,6 +157,7 @@ struct ip_conntrack_expect unsigned int flags; #ifdef CONFIG_IP_NF_NAT_NEEDED + u_int32_t saved_ip; /* This is the original per-proto part, used to map the * expected connection the way the recipient expects. */ union ip_conntrack_manip_proto saved_proto; @@ -293,6 +297,7 @@ static inline int is_dying(struct ip_con } extern unsigned int ip_conntrack_htable_size; +extern int ip_conntrack_checksum; #define CONNTRACK_STAT_INC(count) (__get_cpu_var(ip_conntrack_stat).count++) diff --git a/include/linux/netfilter_ipv4/ip_conntrack_h323.h b/include/linux/netfilter_ipv4/ip_conntrack_h323.h index eace86b..3cbff73 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_h323.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_h323.h @@ -71,6 +71,13 @@ extern int (*nat_h245_hook) (struct sk_b unsigned char **data, int dataoff, TransportAddress * addr, u_int16_t port, struct ip_conntrack_expect * exp); +extern int (*nat_callforwarding_hook) (struct sk_buff ** pskb, + struct ip_conntrack * ct, + enum ip_conntrack_info ctinfo, + unsigned char **data, int dataoff, + TransportAddress * addr, + u_int16_t port, + struct ip_conntrack_expect * exp); extern int (*nat_q931_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, enum ip_conntrack_info ctinfo, unsigned char **data, TransportAddress * addr, diff --git a/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_types.h b/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_types.h index cc98f7a..3d4a773 100644 --- a/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_types.h +++ b/include/linux/netfilter_ipv4/ip_conntrack_helper_h323_types.h @@ -1,4 +1,4 @@ -/* Generated by Jing Min Zhao's ASN.1 parser, Mar 15 2006 +/* Generated by Jing Min Zhao's ASN.1 parser, Apr 20 2006 * * Copyright (c) 2006 Jing Min Zhao * @@ -412,6 +412,7 @@ typedef struct Facility_UUIE { /* SEQUEN eFacility_UUIE_destinationInfo = (1 << 14), eFacility_UUIE_h245SecurityMode = (1 << 13), } options; + TransportAddress alternativeAddress; FacilityReason reason; TransportAddress h245Address; Facility_UUIE_fastStart fastStart; diff --git a/include/linux/netfilter_ipv4/ip_conntrack_sip.h b/include/linux/netfilter_ipv4/ip_conntrack_sip.h new file mode 100644 index 0000000..913dad6 --- /dev/null +++ b/include/linux/netfilter_ipv4/ip_conntrack_sip.h @@ -0,0 +1,44 @@ +#ifndef __IP_CONNTRACK_SIP_H__ +#define __IP_CONNTRACK_SIP_H__ +#ifdef __KERNEL__ + +#define SIP_PORT 5060 +#define SIP_TIMEOUT 3600 + +#define POS_VIA 0 +#define POS_CONTACT 1 +#define POS_CONTENT 2 +#define POS_MEDIA 3 +#define POS_OWNER 4 +#define POS_CONNECTION 5 +#define POS_REQ_HEADER 6 +#define POS_SDP_HEADER 7 + +struct sip_header_nfo { + const char *lname; + const char *sname; + const char *ln_str; + size_t lnlen; + size_t snlen; + size_t ln_strlen; + int (*match_len)(const char *, const char *, int *); +}; + +extern unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack *ct, + const char **dptr); +extern unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *exp, + const char *dptr); + +extern int ct_sip_get_info(const char *dptr, size_t dlen, + unsigned int *matchoff, + unsigned int *matchlen, + struct sip_header_nfo *hnfo); +extern int ct_sip_lnlen(const char *line, const char *limit); +extern const char *ct_sip_search(const char *needle, const char *haystack, + size_t needle_len, size_t haystack_len); +#endif /* __KERNEL__ */ +#endif /* __IP_CONNTRACK_SIP_H__ */ diff --git a/include/linux/netfilter_ipv4/listhelp.h b/include/linux/netfilter_ipv4/listhelp.h index 360429f..5d92cf0 100644 --- a/include/linux/netfilter_ipv4/listhelp.h +++ b/include/linux/netfilter_ipv4/listhelp.h @@ -1,6 +1,5 @@ #ifndef _LISTHELP_H #define _LISTHELP_H -#include #include /* Header to do more comprehensive job than linux/list.h; assume list diff --git a/include/linux/netfilter_ipv6/Kbuild b/include/linux/netfilter_ipv6/Kbuild new file mode 100644 index 0000000..913ddbf --- /dev/null +++ b/include/linux/netfilter_ipv6/Kbuild @@ -0,0 +1,6 @@ +header-y += ip6t_HL.h ip6t_LOG.h ip6t_MARK.h ip6t_REJECT.h ip6t_ah.h \ + ip6t_esp.h ip6t_frag.h ip6t_hl.h ip6t_ipv6header.h \ + ip6t_length.h ip6t_limit.h ip6t_mac.h ip6t_mark.h \ + ip6t_multiport.h ip6t_opts.h ip6t_owner.h ip6t_policy.h \ + ip6t_physdev.h ip6t_rt.h +unifdef-y := ip6_tables.h diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 87b8a57..855b446 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -5,7 +5,7 @@ #include /* for sa_fami #include #define NETLINK_ROUTE 0 /* Routing/device hook */ -#define NETLINK_W1 1 /* 1-wire subsystem */ +#define NETLINK_UNUSED 1 /* Unused number */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Firewalling hook */ #define NETLINK_INET_DIAG 4 /* INET socket monitoring */ diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h index ca5a873..1efe60c 100644 --- a/include/linux/netpoll.h +++ b/include/linux/netpoll.h @@ -31,6 +31,7 @@ struct netpoll_info { int rx_flags; spinlock_t rx_lock; struct netpoll *rx_np; /* netpoll that registered an rx_hook */ + struct sk_buff_head arp_tx; /* list of arp requests to reply to */ }; void netpoll_poll(struct netpoll *np); diff --git a/include/linux/nfs.h b/include/linux/nfs.h index ca2ffa6..54af92c 100644 --- a/include/linux/nfs.h +++ b/include/linux/nfs.h @@ -7,9 +7,6 @@ #ifndef _LINUX_NFS_H #define _LINUX_NFS_H -#include -#include - #define NFS_PROGRAM 100003 #define NFS_PORT 2049 #define NFS_MAXDATA 8192 @@ -129,7 +126,10 @@ enum nfs_ftype { NFFIFO = 8 }; -#if defined(__KERNEL__) +#ifdef __KERNEL__ +#include +#include + /* * This is the kernel NFS client file handle representation */ diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 0c1c306..5f681d5 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -14,7 +14,6 @@ #ifndef _LINUX_NFS4_H #define _LINUX_NFS4_H #include -#include #define NFS4_VERIFIER_SIZE 8 #define NFS4_FHSIZE 128 @@ -97,6 +96,9 @@ enum nfs4_acl_whotype { NFS4_ACL_WHO_EVERYONE, }; +#ifdef __KERNEL__ +#include + struct nfs4_ace { uint32_t type; uint32_t flag; @@ -345,8 +347,6 @@ #define NFSPROC4_COMPOUND 1 #define NFS4_MINOR_VERSION 0 #define NFS4_DEBUG 1 -#ifdef __KERNEL__ - /* Index of predefined Linux client operations */ enum { @@ -384,6 +384,7 @@ enum { NFSPROC4_CLNT_DELEGRETURN, NFSPROC4_CLNT_GETACL, NFSPROC4_CLNT_SETACL, + NFSPROC4_CLNT_FS_LOCATIONS, }; #endif diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index c71227d..55ea853 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -9,15 +9,12 @@ #ifndef _LINUX_NFS_FS_H #define _LINUX_NFS_FS_H -#include #include #include #include #include #include -#include - #include #include #include @@ -27,6 +24,9 @@ #include #include #include #include + +#include + #include #include @@ -61,6 +61,7 @@ #define FLUSH_STABLE 4 /* commit to sta #define FLUSH_LOWPRI 8 /* low priority background flush */ #define FLUSH_HIGHPRI 16 /* high priority memory reclaim flush */ #define FLUSH_NOCOMMIT 32 /* Don't send the NFSv3/v4 COMMIT */ +#define FLUSH_INVALIDATE 64 /* Invalidate the page cache */ #ifdef __KERNEL__ @@ -234,8 +235,12 @@ static inline int nfs_caches_unstable(st static inline void nfs_mark_for_revalidate(struct inode *inode) { + struct nfs_inode *nfsi = NFS_I(inode); + spin_lock(&inode->i_lock); - NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS; + nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS; + if (S_ISDIR(inode->i_mode)) + nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA; spin_unlock(&inode->i_lock); } @@ -297,7 +302,7 @@ extern int nfs_release(struct inode *, s extern int nfs_attribute_timeout(struct inode *inode); extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); -extern void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping); +extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping); extern int nfs_setattr(struct dentry *, struct iattr *); extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); extern void nfs_begin_attr_update(struct inode *); @@ -307,6 +312,10 @@ extern void nfs_end_data_update(struct i extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode); +extern struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, + const struct dentry *dentry, + struct nfs_fh *fh, + struct nfs_fattr *fattr); /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ extern u32 root_nfs_parse_addr(char *name); /*__init*/ @@ -325,7 +334,7 @@ #ifdef CONFIG_NFS_V3 extern struct inode_operations nfs3_file_inode_operations; #endif /* CONFIG_NFS_V3 */ extern const struct file_operations nfs_file_operations; -extern struct address_space_operations nfs_file_aops; +extern const struct address_space_operations nfs_file_aops; static inline struct rpc_cred *nfs_file_cred(struct file *file) { @@ -393,6 +402,15 @@ #define nfs_unregister_sysctl() do { } w #endif /* + * linux/fs/nfs/namespace.c + */ +extern struct list_head nfs_automount_list; +extern struct inode_operations nfs_mountpoint_inode_operations; +extern struct inode_operations nfs_referral_inode_operations; +extern int nfs_mountpoint_expiry_timeout; +extern void nfs_release_automount_timer(void); + +/* * linux/fs/nfs/unlink.c */ extern int nfs_async_unlink(struct dentry *); diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 65dec21..6b4a13c 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -35,6 +35,7 @@ struct nfs_server { char * hostname; /* remote hostname */ struct nfs_fh fh; struct sockaddr_in addr; + struct nfs_fsid fsid; unsigned long mount_time; /* when this fs was mounted */ #ifdef CONFIG_NFS_V4 /* Our own IP address, as a null-terminated string. diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 66e2ed6..1f7bd28 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -13,7 +13,6 @@ #define _LINUX_NFS_PAGE_H #include #include #include -#include #include #include @@ -63,8 +62,8 @@ extern void nfs_release_request(struct n extern int nfs_scan_lock_dirty(struct nfs_inode *nfsi, struct list_head *dst, unsigned long idx_start, unsigned int npages); -extern int nfs_scan_list(struct list_head *, struct list_head *, - unsigned long, unsigned int); +extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct list_head *dst, + unsigned long idx_start, unsigned int npages); extern int nfs_coalesce_requests(struct list_head *, struct list_head *, unsigned int); extern int nfs_wait_on_request(struct nfs_page *); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 7fafc4c..2d3fb64 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -14,11 +14,19 @@ #define NFS_MAX_FILE_IO_SIZE (1048576U) #define NFS_DEF_FILE_IO_SIZE (4096U) #define NFS_MIN_FILE_IO_SIZE (1024U) -struct nfs4_fsid { - __u64 major; - __u64 minor; +struct nfs_fsid { + uint64_t major; + uint64_t minor; }; +/* + * Helper for checking equality between 2 fsids. + */ +static inline int nfs_fsid_equal(const struct nfs_fsid *a, const struct nfs_fsid *b) +{ + return a->major == b->major && a->minor == b->minor; +} + struct nfs_fattr { unsigned short valid; /* which fields are valid */ __u64 pre_size; /* pre_op_attr.size */ @@ -40,10 +48,7 @@ struct nfs_fattr { } nfs3; } du; dev_t rdev; - union { - __u64 nfs3; /* also nfs2 */ - struct nfs4_fsid nfs4; - } fsid_u; + struct nfs_fsid fsid; __u64 fileid; struct timespec atime; struct timespec mtime; @@ -57,8 +62,8 @@ struct nfs_fattr { #define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */ #define NFS_ATTR_FATTR 0x0002 /* post-op attributes */ #define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */ -#define NFS_ATTR_FATTR_V4 0x0008 -#define NFS_ATTR_PRE_CHANGE 0x0010 +#define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */ +#define NFS_ATTR_FATTR_V4_REFERRAL 0x0010 /* NFSv4 referral */ /* * Info on the file system @@ -675,6 +680,40 @@ struct nfs4_server_caps_res { u32 has_symlinks; }; +struct nfs4_string { + unsigned int len; + char *data; +}; + +#define NFS4_PATHNAME_MAXCOMPONENTS 512 +struct nfs4_pathname { + unsigned int ncomponents; + struct nfs4_string components[NFS4_PATHNAME_MAXCOMPONENTS]; +}; + +#define NFS4_FS_LOCATION_MAXSERVERS 10 +struct nfs4_fs_location { + unsigned int nservers; + struct nfs4_string servers[NFS4_FS_LOCATION_MAXSERVERS]; + struct nfs4_pathname rootpath; +}; + +#define NFS4_FS_LOCATIONS_MAXENTRIES 10 +struct nfs4_fs_locations { + struct nfs_fattr fattr; + const struct nfs_server *server; + struct nfs4_pathname fs_path; + int nlocations; + struct nfs4_fs_location locations[NFS4_FS_LOCATIONS_MAXENTRIES]; +}; + +struct nfs4_fs_locations_arg { + const struct nfs_fh *dir_fh; + const struct qstr *name; + struct page *page; + const u32 *bitmask; +}; + #endif /* CONFIG_NFS_V4 */ struct nfs_page; @@ -690,12 +729,13 @@ struct nfs_read_data { struct list_head pages; /* Coalesced read requests */ struct nfs_page *req; /* multi ops per nfs_page */ struct page **pagevec; + unsigned int npages; /* active pages in pagevec */ struct nfs_readargs args; struct nfs_readres res; #ifdef CONFIG_NFS_V4 unsigned long timestamp; /* For lease renewal */ #endif - struct page *page_array[NFS_PAGEVEC_SIZE + 1]; + struct page *page_array[NFS_PAGEVEC_SIZE]; }; struct nfs_write_data { @@ -708,12 +748,13 @@ struct nfs_write_data { struct list_head pages; /* Coalesced requests we wish to flush */ struct nfs_page *req; /* multi ops per nfs_page */ struct page **pagevec; + unsigned int npages; /* active pages in pagevec */ struct nfs_writeargs args; /* argument struct */ struct nfs_writeres res; /* result struct */ #ifdef CONFIG_NFS_V4 unsigned long timestamp; /* For lease renewal */ #endif - struct page *page_array[NFS_PAGEVEC_SIZE + 1]; + struct page *page_array[NFS_PAGEVEC_SIZE]; }; struct nfs_access_entry; diff --git a/include/linux/nfsd/Kbuild b/include/linux/nfsd/Kbuild new file mode 100644 index 0000000..c8c5456 --- /dev/null +++ b/include/linux/nfsd/Kbuild @@ -0,0 +1,2 @@ +unifdef-y := const.h export.h stats.h syscall.h nfsfh.h debug.h auth.h + diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index ec7c2e8..2dcad29 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -10,7 +10,6 @@ #ifndef LINUX_NFSD_NFSD_H #define LINUX_NFSD_NFSD_H -#include #include #include #include diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index 0798b77..f9edcd2 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h @@ -16,7 +16,6 @@ #define _LINUX_NFSD_FH_H #include #ifdef __KERNEL__ -# include # include # include # include diff --git a/include/linux/nfsd/syscall.h b/include/linux/nfsd/syscall.h index 781efbf..dae0fae 100644 --- a/include/linux/nfsd/syscall.h +++ b/include/linux/nfsd/syscall.h @@ -11,7 +11,6 @@ #define NFSD_SYSCALL_H #include #ifdef __KERNEL__ -# include # include # include #endif diff --git a/include/linux/node.h b/include/linux/node.h index 254dc3d..81dcec8 100644 --- a/include/linux/node.h +++ b/include/linux/node.h @@ -26,8 +26,25 @@ struct node { struct sys_device sysdev; }; +extern struct node node_devices[]; + extern int register_node(struct node *, int, struct node *); extern void unregister_node(struct node *node); +extern int register_one_node(int nid); +extern void unregister_one_node(int nid); +#ifdef CONFIG_NUMA +extern int register_cpu_under_node(unsigned int cpu, unsigned int nid); +extern int unregister_cpu_under_node(unsigned int cpu, unsigned int nid); +#else +static inline int register_cpu_under_node(unsigned int cpu, unsigned int nid) +{ + return 0; +} +static inline int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) +{ + return 0; +} +#endif #define to_node(sys_device) container_of(sys_device, struct node, sysdev) diff --git a/include/linux/notifier.h b/include/linux/notifier.h index 51dbab9..7ff386a 100644 --- a/include/linux/notifier.h +++ b/include/linux/notifier.h @@ -65,7 +65,7 @@ #define RAW_INIT_NOTIFIER_HEAD(name) do } while (0) #define ATOMIC_NOTIFIER_INIT(name) { \ - .lock = SPIN_LOCK_UNLOCKED, \ + .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .head = NULL } #define BLOCKING_NOTIFIER_INIT(name) { \ .rwsem = __RWSEM_INITIALIZER((name).rwsem), \ diff --git a/include/linux/nsc_gpio.h b/include/linux/nsc_gpio.h new file mode 100644 index 0000000..135742c --- /dev/null +++ b/include/linux/nsc_gpio.h @@ -0,0 +1,42 @@ +/** + nsc_gpio.c + + National Semiconductor GPIO common access methods. + + struct nsc_gpio_ops abstracts the low-level access + operations for the GPIO units on 2 NSC chip families; the GEODE + integrated CPU, and the PC-8736[03456] integrated PC-peripheral + chips. + + The GPIO units on these chips have the same pin architecture, but + the access methods differ. Thus, scx200_gpio and pc8736x_gpio + implement their own versions of these routines; and use the common + file-operations routines implemented in nsc_gpio module. + + Copyright (c) 2005 Jim Cromie + + NB: this work was tested on the Geode SC-1100 and PC-87366 chips. + NSC sold the GEODE line to AMD, and the PC-8736x line to Winbond. +*/ + +struct nsc_gpio_ops { + struct module* owner; + u32 (*gpio_config) (unsigned iminor, u32 mask, u32 bits); + void (*gpio_dump) (struct nsc_gpio_ops *amp, unsigned iminor); + int (*gpio_get) (unsigned iminor); + void (*gpio_set) (unsigned iminor, int state); + void (*gpio_set_high)(unsigned iminor); + void (*gpio_set_low) (unsigned iminor); + void (*gpio_change) (unsigned iminor); + int (*gpio_current) (unsigned iminor); + struct device* dev; /* for dev_dbg() support, set in init */ +}; + +extern ssize_t nsc_gpio_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos); + +extern ssize_t nsc_gpio_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos); + +extern void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index); + diff --git a/include/linux/numa.h b/include/linux/numa.h index e481feb..a31a730 100644 --- a/include/linux/numa.h +++ b/include/linux/numa.h @@ -1,7 +1,6 @@ #ifndef _LINUX_NUMA_H #define _LINUX_NUMA_H -#include #ifdef CONFIG_NODES_SHIFT #define NODES_SHIFT CONFIG_NODES_SHIFT diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index d276a4e..5748642 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -5,9 +5,7 @@ #ifndef PAGE_FLAGS_H #define PAGE_FLAGS_H -#include -#include -#include +#include /* * Various page->flags bits: @@ -88,135 +86,17 @@ #define PG_reclaim 17 /* To be reclaime #define PG_nosave_free 18 /* Free, should not be written */ #define PG_buddy 19 /* Page is free, on buddy lists */ -#define PG_uncached 20 /* Page has been mapped as uncached */ +#if (BITS_PER_LONG > 32) /* - * Global page accounting. One instance per CPU. Only unsigned longs are - * allowed. + * 64-bit-only flags build down from bit 31 * - * - Fields can be modified with xxx_page_state and xxx_page_state_zone at - * any time safely (which protects the instance from modification by - * interrupt. - * - The __xxx_page_state variants can be used safely when interrupts are - * disabled. - * - The __xxx_page_state variants can be used if the field is only - * modified from process context and protected from preemption, or only - * modified from interrupt context. In this case, the field should be - * commented here. + * 32 bit -------------------------------| FIELDS | FLAGS | + * 64 bit | FIELDS | ?????? FLAGS | + * 63 32 0 */ -struct page_state { - unsigned long nr_dirty; /* Dirty writeable pages */ - unsigned long nr_writeback; /* Pages under writeback */ - unsigned long nr_unstable; /* NFS unstable pages */ - unsigned long nr_page_table_pages;/* Pages used for pagetables */ - unsigned long nr_mapped; /* mapped into pagetables. - * only modified from process context */ - unsigned long nr_slab; /* In slab */ -#define GET_PAGE_STATE_LAST nr_slab - - /* - * The below are zeroed by get_page_state(). Use get_full_page_state() - * to add up all these. - */ - unsigned long pgpgin; /* Disk reads */ - unsigned long pgpgout; /* Disk writes */ - unsigned long pswpin; /* swap reads */ - unsigned long pswpout; /* swap writes */ - - unsigned long pgalloc_high; /* page allocations */ - unsigned long pgalloc_normal; - unsigned long pgalloc_dma32; - unsigned long pgalloc_dma; - - unsigned long pgfree; /* page freeings */ - unsigned long pgactivate; /* pages moved inactive->active */ - unsigned long pgdeactivate; /* pages moved active->inactive */ - - unsigned long pgfault; /* faults (major+minor) */ - unsigned long pgmajfault; /* faults (major only) */ - - unsigned long pgrefill_high; /* inspected in refill_inactive_zone */ - unsigned long pgrefill_normal; - unsigned long pgrefill_dma32; - unsigned long pgrefill_dma; - - unsigned long pgsteal_high; /* total highmem pages reclaimed */ - unsigned long pgsteal_normal; - unsigned long pgsteal_dma32; - unsigned long pgsteal_dma; - - unsigned long pgscan_kswapd_high;/* total highmem pages scanned */ - unsigned long pgscan_kswapd_normal; - unsigned long pgscan_kswapd_dma32; - unsigned long pgscan_kswapd_dma; - - unsigned long pgscan_direct_high;/* total highmem pages scanned */ - unsigned long pgscan_direct_normal; - unsigned long pgscan_direct_dma32; - unsigned long pgscan_direct_dma; - - unsigned long pginodesteal; /* pages reclaimed via inode freeing */ - unsigned long slabs_scanned; /* slab objects scanned */ - unsigned long kswapd_steal; /* pages reclaimed by kswapd */ - unsigned long kswapd_inodesteal;/* reclaimed via kswapd inode freeing */ - unsigned long pageoutrun; /* kswapd's calls to page reclaim */ - unsigned long allocstall; /* direct reclaim calls */ - - unsigned long pgrotated; /* pages rotated to tail of the LRU */ - unsigned long nr_bounce; /* pages for bounce buffers */ -}; - -extern void get_page_state(struct page_state *ret); -extern void get_page_state_node(struct page_state *ret, int node); -extern void get_full_page_state(struct page_state *ret); -extern unsigned long read_page_state_offset(unsigned long offset); -extern void mod_page_state_offset(unsigned long offset, unsigned long delta); -extern void __mod_page_state_offset(unsigned long offset, unsigned long delta); - -#define read_page_state(member) \ - read_page_state_offset(offsetof(struct page_state, member)) - -#define mod_page_state(member, delta) \ - mod_page_state_offset(offsetof(struct page_state, member), (delta)) - -#define __mod_page_state(member, delta) \ - __mod_page_state_offset(offsetof(struct page_state, member), (delta)) - -#define inc_page_state(member) mod_page_state(member, 1UL) -#define dec_page_state(member) mod_page_state(member, 0UL - 1) -#define add_page_state(member,delta) mod_page_state(member, (delta)) -#define sub_page_state(member,delta) mod_page_state(member, 0UL - (delta)) - -#define __inc_page_state(member) __mod_page_state(member, 1UL) -#define __dec_page_state(member) __mod_page_state(member, 0UL - 1) -#define __add_page_state(member,delta) __mod_page_state(member, (delta)) -#define __sub_page_state(member,delta) __mod_page_state(member, 0UL - (delta)) - -#define page_state(member) (*__page_state(offsetof(struct page_state, member))) - -#define state_zone_offset(zone, member) \ -({ \ - unsigned offset; \ - if (is_highmem(zone)) \ - offset = offsetof(struct page_state, member##_high); \ - else if (is_normal(zone)) \ - offset = offsetof(struct page_state, member##_normal); \ - else if (is_dma32(zone)) \ - offset = offsetof(struct page_state, member##_dma32); \ - else \ - offset = offsetof(struct page_state, member##_dma); \ - offset; \ -}) - -#define __mod_page_state_zone(zone, member, delta) \ - do { \ - __mod_page_state_offset(state_zone_offset(zone, member), (delta)); \ - } while (0) - -#define mod_page_state_zone(zone, member, delta) \ - do { \ - mod_page_state_offset(state_zone_offset(zone, member), (delta)); \ - } while (0) +#define PG_uncached 31 /* Page has been mapped as uncached */ +#endif /* * Manipulation of page state flags @@ -242,7 +122,14 @@ #define ClearPageReferenced(page) clear_ #define TestClearPageReferenced(page) test_and_clear_bit(PG_referenced, &(page)->flags) #define PageUptodate(page) test_bit(PG_uptodate, &(page)->flags) -#ifndef SetPageUptodate +#ifdef CONFIG_S390 +#define SetPageUptodate(_page) \ + do { \ + struct page *__page = (_page); \ + if (!test_and_set_bit(PG_uptodate, &__page->flags)) \ + page_test_and_clear_dirty(_page); \ + } while (0) +#else #define SetPageUptodate(page) set_bit(PG_uptodate, &(page)->flags) #endif #define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags) @@ -294,7 +181,7 @@ #define SetPageWriteback(page) \ do { \ if (!test_and_set_bit(PG_writeback, \ &(page)->flags)) \ - inc_page_state(nr_writeback); \ + inc_zone_page_state(page, NR_WRITEBACK); \ } while (0) #define TestSetPageWriteback(page) \ ({ \ @@ -302,14 +189,14 @@ #define TestSetPageWriteback(page) \ ret = test_and_set_bit(PG_writeback, \ &(page)->flags); \ if (!ret) \ - inc_page_state(nr_writeback); \ + inc_zone_page_state(page, NR_WRITEBACK); \ ret; \ }) #define ClearPageWriteback(page) \ do { \ if (test_and_clear_bit(PG_writeback, \ &(page)->flags)) \ - dec_page_state(nr_writeback); \ + dec_zone_page_state(page, NR_WRITEBACK); \ } while (0) #define TestClearPageWriteback(page) \ ({ \ @@ -317,7 +204,7 @@ #define TestClearPageWriteback(page) ret = test_and_clear_bit(PG_writeback, \ &(page)->flags); \ if (ret) \ - dec_page_state(nr_writeback); \ + dec_zone_page_state(page, NR_WRITEBACK); \ ret; \ }) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 7a1af57..0a2f5d2 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -99,6 +99,13 @@ extern struct page * read_cache_page(str extern int read_cache_pages(struct address_space *mapping, struct list_head *pages, filler_t *filler, void *data); +static inline struct page *read_mapping_page(struct address_space *mapping, + unsigned long index, void *data) +{ + filler_t *filler = (filler_t *)mapping->a_ops->readpage; + return read_cache_page(mapping, index, filler, data); +} + int add_to_page_cache(struct page *page, struct address_space *mapping, unsigned long index, gfp_t gfp_mask); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, @@ -106,51 +113,6 @@ int add_to_page_cache_lru(struct page *p extern void remove_from_page_cache(struct page *page); extern void __remove_from_page_cache(struct page *page); -extern atomic_t nr_pagecache; - -#ifdef CONFIG_SMP - -#define PAGECACHE_ACCT_THRESHOLD max(16, NR_CPUS * 2) -DECLARE_PER_CPU(long, nr_pagecache_local); - -/* - * pagecache_acct implements approximate accounting for pagecache. - * vm_enough_memory() do not need high accuracy. Writers will keep - * an offset in their per-cpu arena and will spill that into the - * global count whenever the absolute value of the local count - * exceeds the counter's threshold. - * - * MUST be protected from preemption. - * current protection is mapping->page_lock. - */ -static inline void pagecache_acct(int count) -{ - long *local; - - local = &__get_cpu_var(nr_pagecache_local); - *local += count; - if (*local > PAGECACHE_ACCT_THRESHOLD || *local < -PAGECACHE_ACCT_THRESHOLD) { - atomic_add(*local, &nr_pagecache); - *local = 0; - } -} - -#else - -static inline void pagecache_acct(int count) -{ - atomic_add(count, &nr_pagecache); -} -#endif - -static inline unsigned long get_page_cache_size(void) -{ - int ret = atomic_read(&nr_pagecache); - if (unlikely(ret < 0)) - ret = 0; - return ret; -} - /* * Return byte-offset into filesystem object for page. */ diff --git a/include/linux/parport.h b/include/linux/parport.h index 008d736..5bf321e 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -96,7 +96,6 @@ #define PARPORT_W91284PIC (1<<1) /* hav /* The rest is for the kernel only */ #ifdef __KERNEL__ -#include #include #include #include @@ -128,6 +127,10 @@ struct amiga_parport_state { unsigned char statusdir;/* ciab.ddrb & 7 */ }; +struct ax88796_parport_state { + unsigned char cpr; +}; + struct ip32_parport_state { unsigned int dcr; unsigned int ecr; @@ -139,6 +142,7 @@ struct parport_state { /* ARC has no state. */ struct ax_parport_state ax; struct amiga_parport_state amiga; + struct ax88796_parport_state ax88796; /* Atari has not state. */ struct ip32_parport_state ip32; void *misc; diff --git a/include/linux/pci.h b/include/linux/pci.h index 3a6a4e3..983fca2 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -17,8 +17,6 @@ #ifndef LINUX_PCI_H #define LINUX_PCI_H -#include - /* Include the pci register defines */ #include @@ -46,8 +44,9 @@ #define PCIIOC_WRITE_COMBINE (PCIIOC_BAS #ifdef __KERNEL__ +#include + #include -#include #include #include #include @@ -163,6 +162,9 @@ struct pci_dev { unsigned int is_busmaster:1; /* device is busmaster */ unsigned int no_msi:1; /* device may not use msi */ unsigned int block_ucfg_access:1; /* userspace config space access is blocked */ + unsigned int broken_parity_status:1; /* Device generates false positive parity */ + unsigned int msi_enabled:1; + unsigned int msix_enabled:1; u32 saved_config_space[16]; /* config space saved at suspend time */ struct hlist_head saved_cap_space; @@ -402,8 +404,8 @@ int pcibios_enable_device(struct pci_dev char *pcibios_setup (char *str); /* Used only when drivers/pci/setup.c is used */ -void pcibios_align_resource(void *, struct resource *, - unsigned long, unsigned long); +void pcibios_align_resource(void *, struct resource *, resource_size_t, + resource_size_t); void pcibios_update_irq(struct pci_dev *, int irq); /* Generic PCI functions used internally */ @@ -442,6 +444,7 @@ struct pci_dev *pci_find_device_reverse struct pci_dev *pci_find_slot (unsigned int bus, unsigned int devfn); int pci_find_capability (struct pci_dev *dev, int cap); int pci_find_next_capability (struct pci_dev *dev, u8 pos, int cap); +int pci_find_ext_capability (struct pci_dev *dev, int cap); struct pci_bus * pci_find_next_bus(const struct pci_bus *from); struct pci_dev *pci_get_device (unsigned int vendor, unsigned int device, struct pci_dev *from); @@ -496,6 +499,7 @@ int pci_set_dma_mask(struct pci_dev *dev int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask); void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno); int pci_assign_resource(struct pci_dev *dev, int i); +int pci_assign_resource_fixed(struct pci_dev *dev, int i); void pci_restore_bars(struct pci_dev *dev); /* ROM control related routines */ @@ -528,10 +532,10 @@ void pci_release_region(struct pci_dev * /* drivers/pci/bus.c */ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, - unsigned long size, unsigned long align, - unsigned long min, unsigned int type_mask, + resource_size_t size, resource_size_t align, + resource_size_t min, unsigned int type_mask, void (*alignf)(void *, struct resource *, - unsigned long, unsigned long), + resource_size_t, resource_size_t), void *alignf_data); void pci_enable_bridges(struct pci_bus *bus); @@ -662,6 +666,7 @@ static inline int pci_register_driver(st static inline void pci_unregister_driver(struct pci_driver *drv) { } static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; } static inline int pci_find_next_capability (struct pci_dev *dev, u8 post, int cap) { return 0; } +static inline int pci_find_ext_capability (struct pci_dev *dev, int cap) {return 0; } static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; } /* Power management related routines */ @@ -725,7 +730,8 @@ static inline char *pci_name(struct pci_ */ #ifndef HAVE_ARCH_PCI_RESOURCE_TO_USER static inline void pci_resource_to_user(const struct pci_dev *dev, int bar, - const struct resource *rsrc, u64 *start, u64 *end) + const struct resource *rsrc, resource_size_t *start, + resource_size_t *end) { *start = rsrc->start; *end = rsrc->end; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 590dc6d..c09396d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -352,8 +352,11 @@ #define PCI_DEVICE_ID_ATI_RS400_200 #define PCI_DEVICE_ID_ATI_RS480 0x5950 /* ATI IXP Chipset */ #define PCI_DEVICE_ID_ATI_IXP200_IDE 0x4349 +#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353 +#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363 #define PCI_DEVICE_ID_ATI_IXP300_IDE 0x4369 #define PCI_DEVICE_ID_ATI_IXP300_SATA 0x436e +#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372 #define PCI_DEVICE_ID_ATI_IXP400_IDE 0x4376 #define PCI_DEVICE_ID_ATI_IXP400_SATA 0x4379 #define PCI_DEVICE_ID_ATI_IXP400_SATA2 0x437a @@ -725,7 +728,9 @@ #define PCI_VENDOR_ID_TI 0x104c #define PCI_DEVICE_ID_TI_TVP4020 0x3d07 #define PCI_DEVICE_ID_TI_4450 0x8011 #define PCI_DEVICE_ID_TI_XX21_XX11 0x8031 +#define PCI_DEVICE_ID_TI_XX21_XX11_SD 0x8034 #define PCI_DEVICE_ID_TI_X515 0x8036 +#define PCI_DEVICE_ID_TI_XX12 0x8039 #define PCI_DEVICE_ID_TI_1130 0xac12 #define PCI_DEVICE_ID_TI_1031 0xac13 #define PCI_DEVICE_ID_TI_1131 0xac15 @@ -848,7 +853,12 @@ #define PCI_DEVICE_ID_YAMAHA_754 0x0012 #define PCI_VENDOR_ID_QLOGIC 0x1077 +#define PCI_DEVICE_ID_QLOGIC_ISP10160 0x1016 #define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020 +#define PCI_DEVICE_ID_QLOGIC_ISP1080 0x1080 +#define PCI_DEVICE_ID_QLOGIC_ISP12160 0x1216 +#define PCI_DEVICE_ID_QLOGIC_ISP1240 0x1240 +#define PCI_DEVICE_ID_QLOGIC_ISP1280 0x1280 #define PCI_DEVICE_ID_QLOGIC_ISP2100 0x2100 #define PCI_DEVICE_ID_QLOGIC_ISP2200 0x2200 #define PCI_DEVICE_ID_QLOGIC_ISP2300 0x2300 @@ -935,6 +945,7 @@ #define PCI_DEVICE_ID_PLX_SPCOM200 0x110 #define PCI_DEVICE_ID_PLX_DJINN_ITOO 0x1151 #define PCI_DEVICE_ID_PLX_R753 0x1152 #define PCI_DEVICE_ID_PLX_OLITEC 0x1187 +#define PCI_DEVICE_ID_PLX_PCI200SYN 0x3196 #define PCI_DEVICE_ID_PLX_9050 0x9050 #define PCI_DEVICE_ID_PLX_9080 0x9080 #define PCI_DEVICE_ID_PLX_GTEK_SERIAL2 0xa001 @@ -1017,6 +1028,7 @@ #define PCI_DEVICE_ID_NVIDIA_NFORCE_CK80 #define PCI_DEVICE_ID_NVIDIA_NVENET_8 0x0056 #define PCI_DEVICE_ID_NVIDIA_NVENET_9 0x0057 #define PCI_DEVICE_ID_NVIDIA_CK804_AUDIO 0x0059 +#define PCI_DEVICE_ID_NVIDIA_CK804_PCIE 0x005d #define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064 #define PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE 0x0065 #define PCI_DEVICE_ID_NVIDIA_NVENET_2 0x0066 @@ -1126,9 +1138,11 @@ #define PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI #define PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL 0x0258 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL 0x0259 #define PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL 0x025B +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE 0x0265 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA 0x0266 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2 0x0267 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE 0x036E #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA 0x037E #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2 0x037F @@ -1182,6 +1196,19 @@ #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_G #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E #define PCI_DEVICE_ID_NVIDIA_NVENET_14 0x0372 #define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 +#define PCI_DEVICE_ID_NVIDIA_NVENET_16 0x03E5 +#define PCI_DEVICE_ID_NVIDIA_NVENET_17 0x03E6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE 0x03EC +#define PCI_DEVICE_ID_NVIDIA_NVENET_18 0x03EE +#define PCI_DEVICE_ID_NVIDIA_NVENET_19 0x03EF +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2 0x03F6 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3 0x03F7 +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE 0x0448 +#define PCI_DEVICE_ID_NVIDIA_NVENET_20 0x0450 +#define PCI_DEVICE_ID_NVIDIA_NVENET_21 0x0451 +#define PCI_DEVICE_ID_NVIDIA_NVENET_22 0x0452 +#define PCI_DEVICE_ID_NVIDIA_NVENET_23 0x0453 #define PCI_VENDOR_ID_IMS 0x10e0 #define PCI_DEVICE_ID_IMS_TT128 0x9128 @@ -1235,6 +1262,7 @@ #define PCI_DEVICE_ID_VIA_PT880ULTRA 0x0 #define PCI_DEVICE_ID_VIA_PX8X0_0 0x0259 #define PCI_DEVICE_ID_VIA_3269_0 0x0269 #define PCI_DEVICE_ID_VIA_K8T800PRO_0 0x0282 +#define PCI_DEVICE_ID_VIA_3296_0 0x0296 #define PCI_DEVICE_ID_VIA_8363_0 0x0305 #define PCI_DEVICE_ID_VIA_P4M800CE 0x0314 #define PCI_DEVICE_ID_VIA_8371_0 0x0391 @@ -1242,6 +1270,7 @@ #define PCI_DEVICE_ID_VIA_8501_0 0x0501 #define PCI_DEVICE_ID_VIA_82C561 0x0561 #define PCI_DEVICE_ID_VIA_82C586_1 0x0571 #define PCI_DEVICE_ID_VIA_82C576 0x0576 +#define PCI_DEVICE_ID_VIA_SATA_EIDE 0x0581 #define PCI_DEVICE_ID_VIA_82C586_0 0x0586 #define PCI_DEVICE_ID_VIA_82C596 0x0596 #define PCI_DEVICE_ID_VIA_82C597_0 0x0597 @@ -1282,10 +1311,11 @@ #define PCI_DEVICE_ID_VIA_8378_0 0x3205 #define PCI_DEVICE_ID_VIA_8783_0 0x3208 #define PCI_DEVICE_ID_VIA_8237 0x3227 #define PCI_DEVICE_ID_VIA_8251 0x3287 -#define PCI_DEVICE_ID_VIA_3296_0 0x0296 +#define PCI_DEVICE_ID_VIA_8237A 0x3337 #define PCI_DEVICE_ID_VIA_8231 0x8231 #define PCI_DEVICE_ID_VIA_8231_4 0x8235 #define PCI_DEVICE_ID_VIA_8365_1 0x8305 +#define PCI_DEVICE_ID_VIA_CX700 0x8324 #define PCI_DEVICE_ID_VIA_8371_1 0x8391 #define PCI_DEVICE_ID_VIA_82C598_1 0x8598 #define PCI_DEVICE_ID_VIA_838X_1 0xB188 @@ -1413,6 +1443,7 @@ #define PCI_DEVICE_ID_RICOH_RL5C466 0x04 #define PCI_DEVICE_ID_RICOH_RL5C475 0x0475 #define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 #define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 +#define PCI_DEVICE_ID_RICOH_R5C822 0x0822 #define PCI_VENDOR_ID_DLINK 0x1186 #define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 @@ -1827,6 +1858,7 @@ #define PCI_DEVICE_ID_OXSEMI_16PCI952 0x #define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_MYRICOM 0x14c1 #define PCI_VENDOR_ID_TITAN 0x14D2 #define PCI_DEVICE_ID_TITAN_010L 0x8001 @@ -1887,6 +1919,7 @@ #define PCI_DEVICE_ID_TIGON3_5751M 0x167 #define PCI_DEVICE_ID_TIGON3_5751F 0x167e #define PCI_DEVICE_ID_TIGON3_5787M 0x1693 #define PCI_DEVICE_ID_TIGON3_5782 0x1696 +#define PCI_DEVICE_ID_TIGON3_5786 0x169a #define PCI_DEVICE_ID_TIGON3_5787 0x169b #define PCI_DEVICE_ID_TIGON3_5788 0x169c #define PCI_DEVICE_ID_TIGON3_5789 0x169d @@ -1935,6 +1968,7 @@ #define PCI_DEVICE_ID_ZOLTRIX_2BD0 0x2bd #define PCI_VENDOR_ID_MELLANOX 0x15b3 #define PCI_DEVICE_ID_MELLANOX_TAVOR 0x5a44 +#define PCI_DEVICE_ID_MELLANOX_TAVOR_BRIDGE 0x5a46 #define PCI_DEVICE_ID_MELLANOX_ARBEL_COMPAT 0x6278 #define PCI_DEVICE_ID_MELLANOX_ARBEL 0x6282 #define PCI_DEVICE_ID_MELLANOX_SINAI_OLD 0x5e8c @@ -1958,6 +1992,9 @@ #define PCI_DEVICE_ID_BCM1250_HT 0x0002 #define PCI_VENDOR_ID_NETCELL 0x169c #define PCI_DEVICE_ID_REVOLUTION 0x0044 +#define PCI_VENDOR_ID_VITESSE 0x1725 +#define PCI_DEVICE_ID_VITESSE_VSC7174 0x7174 + #define PCI_VENDOR_ID_LINKSYS 0x1737 #define PCI_DEVICE_ID_LINKSYS_EG1064 0x1064 @@ -1982,6 +2019,13 @@ #define PCI_VENDOR_ID_TOPSPIN 0x1867 #define PCI_VENDOR_ID_TDI 0x192E #define PCI_DEVICE_ID_TDI_EHCI 0x0101 +#define PCI_VENDOR_ID_JMICRON 0x197B +#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 +#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 +#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 +#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 @@ -2043,6 +2087,7 @@ #define PCI_DEVICE_ID_INTEL_82439 0x1250 #define PCI_DEVICE_ID_INTEL_80960_RP 0x1960 #define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21 #define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30 +#define PCI_DEVICE_ID_INTEL_IOAT 0x1a38 #define PCI_DEVICE_ID_INTEL_82801AA_0 0x2410 #define PCI_DEVICE_ID_INTEL_82801AA_1 0x2411 #define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index d27a78b..6bce4a2 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -197,6 +197,7 @@ #define PCI_CAP_ID_MSI 0x05 /* Message #define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ #define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ #define PCI_CAP_ID_HT_IRQCONF 0x08 /* HyperTransport IRQ Configuration */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific capability */ #define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 6825255..f5aa593 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -6,18 +6,18 @@ #define _LINUX_PERCPU_COUNTER_H * WARNING: these things are HUGE. 4 kbytes per counter on 32-way P4. */ -#include #include #include #include #include +#include #ifdef CONFIG_SMP struct percpu_counter { spinlock_t lock; - long count; - long *counters; + s64 count; + s32 *counters; }; #if NR_CPUS >= 16 @@ -26,11 +26,11 @@ #else #define FBC_BATCH (NR_CPUS*4) #endif -static inline void percpu_counter_init(struct percpu_counter *fbc) +static inline void percpu_counter_init(struct percpu_counter *fbc, s64 amount) { spin_lock_init(&fbc->lock); - fbc->count = 0; - fbc->counters = alloc_percpu(long); + fbc->count = amount; + fbc->counters = alloc_percpu(s32); } static inline void percpu_counter_destroy(struct percpu_counter *fbc) @@ -38,10 +38,10 @@ static inline void percpu_counter_destro free_percpu(fbc->counters); } -void percpu_counter_mod(struct percpu_counter *fbc, long amount); -long percpu_counter_sum(struct percpu_counter *fbc); +void percpu_counter_mod(struct percpu_counter *fbc, s32 amount); +s64 percpu_counter_sum(struct percpu_counter *fbc); -static inline long percpu_counter_read(struct percpu_counter *fbc) +static inline s64 percpu_counter_read(struct percpu_counter *fbc) { return fbc->count; } @@ -49,13 +49,14 @@ static inline long percpu_counter_read(s /* * It is possible for the percpu_counter_read() to return a small negative * number for some counter which should never be negative. + * */ -static inline long percpu_counter_read_positive(struct percpu_counter *fbc) +static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc) { - long ret = fbc->count; + s64 ret = fbc->count; barrier(); /* Prevent reloads of fbc->count */ - if (ret > 0) + if (ret >= 0) return ret; return 1; } @@ -63,12 +64,12 @@ static inline long percpu_counter_read_p #else struct percpu_counter { - long count; + s64 count; }; -static inline void percpu_counter_init(struct percpu_counter *fbc) +static inline void percpu_counter_init(struct percpu_counter *fbc, s64 amount) { - fbc->count = 0; + fbc->count = amount; } static inline void percpu_counter_destroy(struct percpu_counter *fbc) @@ -76,24 +77,24 @@ static inline void percpu_counter_destro } static inline void -percpu_counter_mod(struct percpu_counter *fbc, long amount) +percpu_counter_mod(struct percpu_counter *fbc, s32 amount) { preempt_disable(); fbc->count += amount; preempt_enable(); } -static inline long percpu_counter_read(struct percpu_counter *fbc) +static inline s64 percpu_counter_read(struct percpu_counter *fbc) { return fbc->count; } -static inline long percpu_counter_read_positive(struct percpu_counter *fbc) +static inline s64 percpu_counter_read_positive(struct percpu_counter *fbc) { return fbc->count; } -static inline long percpu_counter_sum(struct percpu_counter *fbc) +static inline s64 percpu_counter_sum(struct percpu_counter *fbc) { return percpu_counter_read_positive(fbc); } diff --git a/include/linux/pfkeyv2.h b/include/linux/pfkeyv2.h index bac0fb3..d5dd471 100644 --- a/include/linux/pfkeyv2.h +++ b/include/linux/pfkeyv2.h @@ -159,7 +159,7 @@ struct sadb_spirange { struct sadb_x_kmprivate { uint16_t sadb_x_kmprivate_len; uint16_t sadb_x_kmprivate_exttype; - u_int32_t sadb_x_kmprivate_reserved; + uint32_t sadb_x_kmprivate_reserved; } __attribute__((packed)); /* sizeof(struct sadb_x_kmprivate) == 8 */ diff --git a/include/linux/plist.h b/include/linux/plist.h new file mode 100644 index 0000000..b95818a --- /dev/null +++ b/include/linux/plist.h @@ -0,0 +1,248 @@ +/* + * Descending-priority-sorted double-linked list + * + * (C) 2002-2003 Intel Corp + * Inaky Perez-Gonzalez . + * + * 2001-2005 (c) MontaVista Software, Inc. + * Daniel Walker + * + * (C) 2005 Thomas Gleixner + * + * Simplifications of the original code by + * Oleg Nesterov + * + * Licensed under the FSF's GNU Public License v2 or later. + * + * Based on simple lists (include/linux/list.h). + * + * This is a priority-sorted list of nodes; each node has a + * priority from INT_MIN (highest) to INT_MAX (lowest). + * + * Addition is O(K), removal is O(1), change of priority of a node is + * O(K) and K is the number of RT priority levels used in the system. + * (1 <= K <= 99) + * + * This list is really a list of lists: + * + * - The tier 1 list is the prio_list, different priority nodes. + * + * - The tier 2 list is the node_list, serialized nodes. + * + * Simple ASCII art explanation: + * + * |HEAD | + * | | + * |prio_list.prev|<------------------------------------| + * |prio_list.next|<->|pl|<->|pl|<--------------->|pl|<-| + * |10 | |10| |21| |21| |21| |40| (prio) + * | | | | | | | | | | | | + * | | | | | | | | | | | | + * |node_list.next|<->|nl|<->|nl|<->|nl|<->|nl|<->|nl|<-| + * |node_list.prev|<------------------------------------| + * + * The nodes on the prio_list list are sorted by priority to simplify + * the insertion of new nodes. There are no nodes with duplicate + * priorites on the list. + * + * The nodes on the node_list is ordered by priority and can contain + * entries which have the same priority. Those entries are ordered + * FIFO + * + * Addition means: look for the prio_list node in the prio_list + * for the priority of the node and insert it before the node_list + * entry of the next prio_list node. If it is the first node of + * that priority, add it to the prio_list in the right position and + * insert it into the serialized node_list list + * + * Removal means remove it from the node_list and remove it from + * the prio_list if the node_list list_head is non empty. In case + * of removal from the prio_list it must be checked whether other + * entries of the same priority are on the list or not. If there + * is another entry of the same priority then this entry has to + * replace the removed entry on the prio_list. If the entry which + * is removed is the only entry of this priority then a simple + * remove from both list is sufficient. + * + * INT_MIN is the highest priority, 0 is the medium highest, INT_MAX + * is lowest priority. + * + * No locking is done, up to the caller. + * + */ +#ifndef _LINUX_PLIST_H_ +#define _LINUX_PLIST_H_ + +#include +#include +#include + +struct plist_head { + struct list_head prio_list; + struct list_head node_list; +#ifdef CONFIG_DEBUG_PI_LIST + spinlock_t *lock; +#endif +}; + +struct plist_node { + int prio; + struct plist_head plist; +}; + +#ifdef CONFIG_DEBUG_PI_LIST +# define PLIST_HEAD_LOCK_INIT(_lock) .lock = _lock +#else +# define PLIST_HEAD_LOCK_INIT(_lock) +#endif + +/** + * #PLIST_HEAD_INIT - static struct plist_head initializer + * + * @head: struct plist_head variable name + */ +#define PLIST_HEAD_INIT(head, _lock) \ +{ \ + .prio_list = LIST_HEAD_INIT((head).prio_list), \ + .node_list = LIST_HEAD_INIT((head).node_list), \ + PLIST_HEAD_LOCK_INIT(&(_lock)) \ +} + +/** + * #PLIST_NODE_INIT - static struct plist_node initializer + * + * @node: struct plist_node variable name + * @__prio: initial node priority + */ +#define PLIST_NODE_INIT(node, __prio) \ +{ \ + .prio = (__prio), \ + .plist = PLIST_HEAD_INIT((node).plist, NULL), \ +} + +/** + * plist_head_init - dynamic struct plist_head initializer + * + * @head: &struct plist_head pointer + */ +static inline void +plist_head_init(struct plist_head *head, spinlock_t *lock) +{ + INIT_LIST_HEAD(&head->prio_list); + INIT_LIST_HEAD(&head->node_list); +#ifdef CONFIG_DEBUG_PI_LIST + head->lock = lock; +#endif +} + +/** + * plist_node_init - Dynamic struct plist_node initializer + * + * @node: &struct plist_node pointer + * @prio: initial node priority + */ +static inline void plist_node_init(struct plist_node *node, int prio) +{ + node->prio = prio; + plist_head_init(&node->plist, NULL); +} + +extern void plist_add(struct plist_node *node, struct plist_head *head); +extern void plist_del(struct plist_node *node, struct plist_head *head); + +/** + * plist_for_each - iterate over the plist + * + * @pos1: the type * to use as a loop counter. + * @head: the head for your list. + */ +#define plist_for_each(pos, head) \ + list_for_each_entry(pos, &(head)->node_list, plist.node_list) + +/** + * plist_for_each_entry_safe - iterate over a plist of given type safe + * against removal of list entry + * + * @pos1: the type * to use as a loop counter. + * @n1: another type * to use as temporary storage + * @head: the head for your list. + */ +#define plist_for_each_safe(pos, n, head) \ + list_for_each_entry_safe(pos, n, &(head)->node_list, plist.node_list) + +/** + * plist_for_each_entry - iterate over list of given type + * + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define plist_for_each_entry(pos, head, mem) \ + list_for_each_entry(pos, &(head)->node_list, mem.plist.node_list) + +/** + * plist_for_each_entry_safe - iterate over list of given type safe against + * removal of list entry + * + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @m: the name of the list_struct within the struct. + */ +#define plist_for_each_entry_safe(pos, n, head, m) \ + list_for_each_entry_safe(pos, n, &(head)->node_list, m.plist.node_list) + +/** + * plist_head_empty - return !0 if a plist_head is empty + * + * @head: &struct plist_head pointer + */ +static inline int plist_head_empty(const struct plist_head *head) +{ + return list_empty(&head->node_list); +} + +/** + * plist_node_empty - return !0 if plist_node is not on a list + * + * @node: &struct plist_node pointer + */ +static inline int plist_node_empty(const struct plist_node *node) +{ + return plist_head_empty(&node->plist); +} + +/* All functions below assume the plist_head is not empty. */ + +/** + * plist_first_entry - get the struct for the first entry + * + * @ptr: the &struct plist_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#ifdef CONFIG_DEBUG_PI_LIST +# define plist_first_entry(head, type, member) \ +({ \ + WARN_ON(plist_head_empty(head)); \ + container_of(plist_first(head), type, member); \ +}) +#else +# define plist_first_entry(head, type, member) \ + container_of(plist_first(head), type, member) +#endif + +/** + * plist_first - return the first node (and thus, highest priority) + * + * @head: the &struct plist_head pointer + * + * Assumes the plist is _not_ empty. + */ +static inline struct plist_node* plist_first(const struct plist_head *head) +{ + return list_entry(head->node_list.next, + struct plist_node, plist.node_list); +} + +#endif diff --git a/include/linux/pm.h b/include/linux/pm.h index 66be589..658c1b9 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -23,7 +23,6 @@ #define _LINUX_PM_H #ifdef __KERNEL__ -#include #include #include diff --git a/include/linux/pm_legacy.h b/include/linux/pm_legacy.h index 008932d..78027c5 100644 --- a/include/linux/pm_legacy.h +++ b/include/linux/pm_legacy.h @@ -1,7 +1,6 @@ #ifndef __LINUX_PM_LEGACY_H__ #define __LINUX_PM_LEGACY_H__ -#include #ifdef CONFIG_PM_LEGACY diff --git a/include/linux/pmu.h b/include/linux/pmu.h index 217d3da..2ed807d 100644 --- a/include/linux/pmu.h +++ b/include/linux/pmu.h @@ -6,7 +6,6 @@ * Copyright (C) 1998 Paul Mackerras. */ -#include #define PMU_DRIVER_VERSION 2 @@ -231,4 +230,8 @@ extern int pmu_battery_count; extern struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES]; extern unsigned int pmu_power_flags; +/* Backlight */ +extern int disable_kernel_backlight; +extern void pmu_backlight_init(struct device_node*); + #endif /* __KERNEL__ */ diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 93b0959..ab8a8dd 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -389,7 +389,8 @@ int pnp_start_dev(struct pnp_dev *dev); int pnp_stop_dev(struct pnp_dev *dev); int pnp_activate_dev(struct pnp_dev *dev); int pnp_disable_dev(struct pnp_dev *dev); -void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size); +void pnp_resource_change(struct resource *resource, resource_size_t start, + resource_size_t size); /* protocol helpers */ int pnp_is_active(struct pnp_dev * dev); @@ -434,7 +435,9 @@ static inline int pnp_start_dev(struct p static inline int pnp_stop_dev(struct pnp_dev *dev) { return -ENODEV; } static inline int pnp_activate_dev(struct pnp_dev *dev) { return -ENODEV; } static inline int pnp_disable_dev(struct pnp_dev *dev) { return -ENODEV; } -static inline void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size) { } +static inline void pnp_resource_change(struct resource *resource, + resource_size_t start, + resource_size_t size) { } /* protocol helpers */ static inline int pnp_is_active(struct pnp_dev * dev) { return 0; } diff --git a/include/linux/poison.h b/include/linux/poison.h new file mode 100644 index 0000000..3e628f9 --- /dev/null +++ b/include/linux/poison.h @@ -0,0 +1,63 @@ +#ifndef _LINUX_POISON_H +#define _LINUX_POISON_H + +/********** include/linux/list.h **********/ +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/********** mm/slab.c **********/ +/* + * Magic nums for obj red zoning. + * Placed in the first word before and the first word after an obj. + */ +#define RED_INACTIVE 0x5A2CF071UL /* when obj is inactive */ +#define RED_ACTIVE 0x170FC2A5UL /* when obj is active */ + +/* ...and for poisoning */ +#define POISON_INUSE 0x5a /* for use-uninitialised poisoning */ +#define POISON_FREE 0x6b /* for use-after-free poisoning */ +#define POISON_END 0xa5 /* end-byte of poisoning */ + +/********** arch/$ARCH/mm/init.c **********/ +#define POISON_FREE_INITMEM 0xcc + +/********** arch/x86_64/mm/init.c **********/ +#define POISON_FREE_INITDATA 0xba + +/********** arch/ia64/hp/common/sba_iommu.c **********/ +/* + * arch/ia64/hp/common/sba_iommu.c uses a 16-byte poison string with a + * value of "SBAIOMMU POISON\0" for spill-over poisoning. + */ + +/********** fs/jbd/journal.c **********/ +#define JBD_POISON_FREE 0x5b + +/********** drivers/base/dmapool.c **********/ +#define POOL_POISON_FREED 0xa7 /* !inuse */ +#define POOL_POISON_ALLOCATED 0xa9 /* !initted */ + +/********** drivers/atm/ **********/ +#define ATM_POISON_FREE 0x12 +#define ATM_POISON 0xdeadbeef + +/********** net/ **********/ +#define NEIGHBOR_DEAD 0xdeadbeef +#define NETFILTER_LINK_POISON 0xdead57ac + +/********** kernel/mutexes **********/ +#define MUTEX_DEBUG_INIT 0x11 +#define MUTEX_DEBUG_FREE 0x22 + +/********** security/ **********/ +#define KEY_DESTROY 0xbd + +/********** sound/oss/ **********/ +#define OSS_POISON_FREE 0xAB + +#endif diff --git a/include/linux/ppp_defs.h b/include/linux/ppp_defs.h index 402056c..c6b13ff 100644 --- a/include/linux/ppp_defs.h +++ b/include/linux/ppp_defs.h @@ -42,8 +42,6 @@ #ifndef _PPP_DEFS_H_ #define _PPP_DEFS_H_ -#include - /* * The basic PPP frame. */ @@ -97,7 +95,11 @@ #define PPP_CBCP 0xc029 /* Callback Cont #define PPP_INITFCS 0xffff /* Initial FCS value */ #define PPP_GOODFCS 0xf0b8 /* Good final FCS value */ + +#ifdef __KERNEL__ +#include #define PPP_FCS(fcs, c) crc_ccitt_byte(fcs, c) +#endif /* * Extended asyncmap - allows any character to be escaped. @@ -179,12 +181,4 @@ struct ppp_idle { time_t recv_idle; /* time since last NP packet received */ }; -#ifndef __P -#ifdef __STDC__ -#define __P(x) x -#else -#define __P(x) () -#endif -#endif - #endif /* _PPP_DEFS_H_ */ diff --git a/include/linux/prctl.h b/include/linux/prctl.h index bf022c4..52a9be4 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -52,4 +52,11 @@ # define PR_TIMING_TIMESTAMP 1 #define PR_SET_NAME 15 /* Set process name */ #define PR_GET_NAME 16 /* Get process name */ +/* Get/set process endian */ +#define PR_GET_ENDIAN 19 +#define PR_SET_ENDIAN 20 +# define PR_ENDIAN_BIG 0 +# define PR_ENDIAN_LITTLE 1 /* True little endian mode */ +# define PR_ENDIAN_PPC_LITTLE 2 /* "PowerPC" pseudo little endian */ + #endif /* _LINUX_PRCTL_H */ diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 5769d14..d0926d6 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -6,7 +6,6 @@ #define __LINUX_PREEMPT_H * preempt_count (used for kernel preemption, interrupt count, etc.) */ -#include #include #include diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 4b47a02..17e7578 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -1,7 +1,6 @@ #ifndef _LINUX_PROC_FS_H #define _LINUX_PROC_FS_H -#include #include #include #include @@ -100,9 +99,8 @@ extern void proc_misc_init(void); struct mm_struct; +void proc_flush_task(struct task_struct *task); struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *); -struct dentry *proc_pid_unhash(struct task_struct *p); -void proc_pid_flush(struct dentry *proc_dentry); int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir); unsigned long task_vsize(struct mm_struct *); int task_statm(struct mm_struct *, int *, int *, int *, int *); @@ -212,8 +210,7 @@ #define proc_net_fops_create(name, mode, #define proc_net_create(name, mode, info) ({ (void)(mode), NULL; }) static inline void proc_net_remove(const char *name) {} -static inline struct dentry *proc_pid_unhash(struct task_struct *p) { return NULL; } -static inline void proc_pid_flush(struct dentry *proc_dentry) { } +static inline void proc_flush_task(struct task_struct *task) { } static inline struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { return NULL; } @@ -249,8 +246,8 @@ extern void kclist_add(struct kcore_list #endif struct proc_inode { - struct task_struct *task; - int type; + struct pid *pid; + int fd; union { int (*proc_get_link)(struct inode *, struct dentry **, struct vfsmount **); int (*proc_read)(struct task_struct *task, char *page); @@ -269,4 +266,10 @@ static inline struct proc_dir_entry *PDE return PROC_I(inode)->pde; } +struct proc_maps_private { + struct pid *pid; + struct task_struct *task; + struct vm_area_struct *tail_vma; +}; + #endif /* _LINUX_PROC_FS_H */ diff --git a/include/linux/profile.h b/include/linux/profile.h index 1f2fea6..e633004 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -4,7 +4,6 @@ #define _LINUX_PROFILE_H #ifdef __KERNEL__ #include -#include #include #include #include diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 0d36750..8b2749a 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -51,6 +51,10 @@ #include #ifdef __KERNEL__ /* * Ptrace flags + * + * The owner ship rules for task->ptrace which holds the ptrace + * flags is simple. When a task is running it owns it's task->ptrace + * flags. When the a task is stopped the ptracer owns task->ptrace. */ #define PT_PTRACED 0x00000001 @@ -84,7 +88,6 @@ extern int ptrace_readdata(struct task_s extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len); extern int ptrace_attach(struct task_struct *tsk); extern int ptrace_detach(struct task_struct *, unsigned int); -extern void __ptrace_detach(struct task_struct *, unsigned int); extern void ptrace_disable(struct task_struct *); extern int ptrace_check_attach(struct task_struct *task, int kill); extern int ptrace_request(struct task_struct *child, long request, long addr, long data); diff --git a/include/linux/quota.h b/include/linux/quota.h index 2dab71e..b8fbf26 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -37,8 +37,6 @@ #define _LINUX_QUOTA_ #include #include -#include -#include #define __DQUOT_VERSION__ "dquot_6.5.1" #define __DQUOT_NUM_VERSION__ 6*10000+5*100+1 @@ -133,6 +131,8 @@ struct if_dqinfo { }; #ifdef __KERNEL__ +#include +#include #include #include diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 21e5a91..5110201 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -10,7 +10,6 @@ #ifndef _LINUX_QUOTAOPS_ #define _LINUX_QUOTAOPS_ -#include #include #include diff --git a/include/linux/radix-tree.h b/include/linux/radix-tree.h index dd83cca..9158a68 100644 --- a/include/linux/radix-tree.h +++ b/include/linux/radix-tree.h @@ -23,6 +23,9 @@ #include #include #include +#define RADIX_TREE_MAX_TAGS 2 + +/* root tags are stored in gfp_mask, shifted by __GFP_BITS_SHIFT */ struct radix_tree_root { unsigned int height; gfp_t gfp_mask; @@ -45,8 +48,6 @@ do { \ (root)->rnode = NULL; \ } while (0) -#define RADIX_TREE_MAX_TAGS 2 - int radix_tree_insert(struct radix_tree_root *, unsigned long, void *); void *radix_tree_lookup(struct radix_tree_root *, unsigned long); void **radix_tree_lookup_slot(struct radix_tree_root *, unsigned long); diff --git a/include/linux/raid/Kbuild b/include/linux/raid/Kbuild new file mode 100644 index 0000000..73fa27a --- /dev/null +++ b/include/linux/raid/Kbuild @@ -0,0 +1 @@ +header-y += md_p.h md_u.h diff --git a/include/linux/raid/bitmap.h b/include/linux/raid/bitmap.h index 8994378..63df898 100644 --- a/include/linux/raid/bitmap.h +++ b/include/linux/raid/bitmap.h @@ -140,6 +140,7 @@ #define BITMAP_MAGIC 0x6d746962 enum bitmap_state { BITMAP_ACTIVE = 0x001, /* the bitmap is in use */ BITMAP_STALE = 0x002, /* the bitmap file is out of date or had -EIO */ + BITMAP_WRITE_ERROR = 0x004, /* A write error has occurred */ BITMAP_HOSTENDIAN = 0x8000, }; @@ -244,15 +245,9 @@ struct bitmap { unsigned long daemon_lastrun; /* jiffies of last run */ unsigned long daemon_sleep; /* how many seconds between updates? */ - /* - * bitmap_writeback_daemon waits for file-pages that have been written, - * as there is no way to get a call-back when a page write completes. - */ - mdk_thread_t *writeback_daemon; - spinlock_t write_lock; + atomic_t pending_writes; /* pending writes to the bitmap file */ wait_queue_head_t write_wait; - struct list_head complete_pages; - mempool_t *write_pool; + }; /* the bitmap API */ diff --git a/include/linux/raid/linear.h b/include/linux/raid/linear.h index 7eaf290..ba15469 100644 --- a/include/linux/raid/linear.h +++ b/include/linux/raid/linear.h @@ -13,8 +13,10 @@ typedef struct dev_info dev_info_t; struct linear_private_data { + struct linear_private_data *prev; /* earlier version */ dev_info_t **hash_table; sector_t hash_spacing; + sector_t array_size; int preshift; /* shift before dividing by hash_spacing */ dev_info_t disks[0]; }; diff --git a/include/linux/raid/md.h b/include/linux/raid/md.h index 66b44e5..eb3e547 100644 --- a/include/linux/raid/md.h +++ b/include/linux/raid/md.h @@ -85,8 +85,6 @@ extern void md_done_sync(mddev_t *mddev, extern void md_error (mddev_t *mddev, mdk_rdev_t *rdev); extern void md_unplug_mddev(mddev_t *mddev); -extern void md_print_devices (void); - extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, sector_t sector, int size, struct page *page); extern void md_super_wait(mddev_t *mddev); @@ -97,7 +95,5 @@ extern void md_new_event(mddev_t *mddev) extern void md_update_sb(mddev_t * mddev); -#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); } - #endif diff --git a/include/linux/raid/md_k.h b/include/linux/raid/md_k.h index e2df61f..c1e0ac5 100644 --- a/include/linux/raid/md_k.h +++ b/include/linux/raid/md_k.h @@ -40,7 +40,8 @@ #define MAX_MD_DEVS 256 /* Max number o * options passed in raidrun: */ -#define MAX_CHUNK_SIZE (4096*1024) +/* Currently this must fix in an 'int' */ +#define MAX_CHUNK_SIZE (1<<30) /* * MD's 'extended' device @@ -57,6 +58,7 @@ struct mdk_rdev_s struct page *sb_page; int sb_loaded; + __u64 sb_events; sector_t data_offset; /* start of data in array */ sector_t sb_offset; int sb_size; /* bytes in the superblock */ @@ -87,6 +89,10 @@ #define BarriersNotsupp 5 /* BIO_RW_BAR * array and could again if we did a partial * resync from the bitmap */ + sector_t recovery_offset;/* If this device has been partially + * recovered, this is where we were + * up to. + */ atomic_t nr_pending; /* number of pending requests. * only maintained for arrays that @@ -182,6 +188,8 @@ #define MD_RECOVERY_NEEDED 5 #define MD_RECOVERY_REQUESTED 6 #define MD_RECOVERY_CHECK 7 #define MD_RECOVERY_RESHAPE 8 +#define MD_RECOVERY_FROZEN 9 + unsigned long recovery; int in_sync; /* know to not need resync */ diff --git a/include/linux/raid/md_p.h b/include/linux/raid/md_p.h index f1fbae7..b6ebc69 100644 --- a/include/linux/raid/md_p.h +++ b/include/linux/raid/md_p.h @@ -265,9 +265,12 @@ #define WriteMostly1 1 /* mask for write /* feature_map bits */ #define MD_FEATURE_BITMAP_OFFSET 1 +#define MD_FEATURE_RECOVERY_OFFSET 2 /* recovery_offset is present and + * must be honoured + */ #define MD_FEATURE_RESHAPE_ACTIVE 4 -#define MD_FEATURE_ALL 5 +#define MD_FEATURE_ALL (1|2|4) #endif diff --git a/include/linux/raid/raid10.h b/include/linux/raid/raid10.h index b110329..c41e56a 100644 --- a/include/linux/raid/raid10.h +++ b/include/linux/raid/raid10.h @@ -24,11 +24,16 @@ struct r10_private_data_s { int far_copies; /* number of copies layed out * at large strides across drives */ + int far_offset; /* far_copies are offset by 1 stripe + * instead of many + */ int copies; /* near_copies * far_copies. * must be <= raid_disks */ sector_t stride; /* distance between far copies. - * This is size / far_copies + * This is size / far_copies unless + * far_offset, in which case it is + * 1 stripe. */ int chunk_shift; /* shift from chunks to sectors */ diff --git a/include/linux/raid/raid5.h b/include/linux/raid/raid5.h index 914af66..20ed4c9 100644 --- a/include/linux/raid/raid5.h +++ b/include/linux/raid/raid5.h @@ -212,6 +212,7 @@ struct raid5_private_data { mddev_t *mddev; struct disk_info *spare; int chunk_size, level, algorithm; + int max_degraded; int raid_disks, working_disks, failed_disks; int max_nr_stripes; diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index 78ecfa2..00b340b 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -2,8 +2,8 @@ #ifndef _LINUX_RAMFS_H #define _LINUX_RAMFS_H struct inode *ramfs_get_inode(struct super_block *sb, int mode, dev_t dev); -struct super_block *ramfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data); +extern int ramfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt); #ifndef CONFIG_MMU extern unsigned long ramfs_nommu_get_unmapped_area(struct file *file, diff --git a/include/linux/rbtree.h b/include/linux/rbtree.h index 4b7cc4f..8d5382e 100644 --- a/include/linux/rbtree.h +++ b/include/linux/rbtree.h @@ -99,22 +99,43 @@ #include struct rb_node { - struct rb_node *rb_parent; - int rb_color; + unsigned long rb_parent_color; #define RB_RED 0 #define RB_BLACK 1 struct rb_node *rb_right; struct rb_node *rb_left; -}; +} __attribute__((aligned(sizeof(long)))); + /* The alignment might seem pointless, but allegedly CRIS needs it */ struct rb_root { struct rb_node *rb_node; }; + +#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) +#define rb_color(r) ((r)->rb_parent_color & 1) +#define rb_is_red(r) (!rb_color(r)) +#define rb_is_black(r) rb_color(r) +#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) +#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) + +static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) +{ + rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; +} +static inline void rb_set_color(struct rb_node *rb, int color) +{ + rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; +} + #define RB_ROOT (struct rb_root) { NULL, } #define rb_entry(ptr, type, member) container_of(ptr, type, member) +#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) +#define RB_EMPTY_NODE(node) (rb_parent(node) != node) +#define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) + extern void rb_insert_color(struct rb_node *, struct rb_root *); extern void rb_erase(struct rb_node *, struct rb_root *); @@ -131,8 +152,7 @@ extern void rb_replace_node(struct rb_no static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, struct rb_node ** rb_link) { - node->rb_parent = parent; - node->rb_color = RB_RED; + node->rb_parent_color = (unsigned long )parent; node->rb_left = node->rb_right = NULL; *rb_link = node; diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 970284f..b4ca73d 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -163,14 +163,22 @@ extern int rcu_needs_cpu(int cpu); * * It is illegal to block while in an RCU read-side critical section. */ -#define rcu_read_lock() preempt_disable() +#define rcu_read_lock() \ + do { \ + preempt_disable(); \ + __acquire(RCU); \ + } while(0) /** * rcu_read_unlock - marks the end of an RCU read-side critical section. * * See rcu_read_lock() for more information. */ -#define rcu_read_unlock() preempt_enable() +#define rcu_read_unlock() \ + do { \ + __release(RCU); \ + preempt_enable(); \ + } while(0) /* * So where is rcu_write_lock()? It does not exist, as there is no @@ -193,14 +201,22 @@ #define rcu_read_unlock() preempt_enable * can use just rcu_read_lock(). * */ -#define rcu_read_lock_bh() local_bh_disable() +#define rcu_read_lock_bh() \ + do { \ + local_bh_disable(); \ + __acquire(RCU_BH); \ + } while(0) /* * rcu_read_unlock_bh - marks the end of a softirq-only RCU critical section * * See rcu_read_lock_bh() for more information. */ -#define rcu_read_unlock_bh() local_bh_enable() +#define rcu_read_unlock_bh() \ + do { \ + __release(RCU_BH); \ + local_bh_enable(); \ + } while(0) /** * rcu_dereference - fetch an RCU-protected pointer in an @@ -246,7 +262,7 @@ #define rcu_assign_pointer(p, v) ({ \ * softirq handlers will have completed, since in some kernels, these * handlers can run in process context, and can block. * - * This primitive provides the guarantees made by the (deprecated) + * This primitive provides the guarantees made by the (now removed) * synchronize_kernel() API. In contrast, synchronize_rcu() only * guarantees that rcu_read_lock() sections will have completed. * In "classic RCU", these two guarantees happen to be one and @@ -258,13 +274,13 @@ extern void rcu_init(void); extern void rcu_check_callbacks(int cpu, int user); extern void rcu_restart_cpu(int cpu); extern long rcu_batches_completed(void); +extern long rcu_batches_completed_bh(void); /* Exported interfaces */ extern void FASTCALL(call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *head))); extern void FASTCALL(call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *head))); -extern __deprecated_for_modules void synchronize_kernel(void); extern void synchronize_rcu(void); void synchronize_idle(void); extern void rcu_barrier(void); diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 015297f..1dd1c70 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -59,13 +59,13 @@ extern void machine_crash_shutdown(struc * Architecture independent implemenations of sys_reboot commands. */ -extern void kernel_restart_prepare(char *cmd); extern void kernel_shutdown_prepare(enum system_states state); extern void kernel_restart(char *cmd); extern void kernel_halt(void); extern void kernel_power_off(void); -extern void kernel_kexec(void); + +void ctrl_alt_del(void); /* * Emergency restart, callable from an interrupt handler. diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 5676c42..daa2d83 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -1973,7 +1973,7 @@ void reiserfs_unmap_buffer(struct buffer /* file.c */ extern struct inode_operations reiserfs_file_inode_operations; extern const struct file_operations reiserfs_file_operations; -extern struct address_space_operations reiserfs_address_space_operations; +extern const struct address_space_operations reiserfs_address_space_operations; /* fix_nodes.c */ diff --git a/include/linux/reiserfs_xattr.h b/include/linux/reiserfs_xattr.h index 5353afb..5e96103 100644 --- a/include/linux/reiserfs_xattr.h +++ b/include/linux/reiserfs_xattr.h @@ -2,8 +2,6 @@ File: linux/reiserfs_xattr.h */ -#include -#include #include /* Magic value in header */ @@ -15,6 +13,7 @@ struct reiserfs_xattr_header { }; #ifdef __KERNEL__ +#include struct reiserfs_xattr_handler { char *prefix; diff --git a/include/linux/relay.h b/include/linux/relay.h index 4bcc153..24accb4 100644 --- a/include/linux/relay.h +++ b/include/linux/relay.h @@ -10,7 +10,6 @@ #ifndef _LINUX_RELAY_H #define _LINUX_RELAY_H -#include #include #include #include diff --git a/include/linux/resource.h b/include/linux/resource.h index 21a86cb..ae13db7 100644 --- a/include/linux/resource.h +++ b/include/linux/resource.h @@ -3,6 +3,8 @@ #define _LINUX_RESOURCE_H #include +struct task_struct; + /* * Resource control/accounting header file for linux */ @@ -67,4 +69,6 @@ #define MLOCK_LIMIT (8 * PAGE_SIZE) */ #include +int getrusage(struct task_struct *p, int who, struct rusage __user *ru); + #endif diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h new file mode 100644 index 0000000..a376bd4 --- /dev/null +++ b/include/linux/resume-trace.h @@ -0,0 +1,30 @@ +#ifndef RESUME_TRACE_H +#define RESUME_TRACE_H + +#ifdef CONFIG_PM_TRACE + +struct device; +extern void set_trace_device(struct device *); +extern void generate_resume_trace(void *tracedata, unsigned int user); + +#define TRACE_DEVICE(dev) set_trace_device(dev) +#define TRACE_RESUME(user) do { \ + void *tracedata; \ + asm volatile("movl $1f,%0\n" \ + ".section .tracedata,\"a\"\n" \ + "1:\t.word %c1\n" \ + "\t.long %c2\n" \ + ".previous" \ + :"=r" (tracedata) \ + : "i" (__LINE__), "i" (__FILE__)); \ + generate_resume_trace(tracedata, user); \ +} while (0) + +#else + +#define TRACE_DEVICE(dev) do { } while (0) +#define TRACE_RESUME(dev) do { } while (0) + +#endif + +#endif diff --git a/include/linux/rio.h b/include/linux/rio.h index c7e907f..d938570 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -17,7 +17,6 @@ #define LINUX_RIO_H #ifdef __KERNEL__ #include -#include #include #include #include diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h index f54772d..7adb2a1 100644 --- a/include/linux/rio_drv.h +++ b/include/linux/rio_drv.h @@ -16,7 +16,6 @@ #define LINUX_RIO_DRV_H #ifdef __KERNEL__ #include -#include #include #include #include diff --git a/include/linux/rmap.h b/include/linux/rmap.h index d6b9bcd..bf97b09 100644 --- a/include/linux/rmap.h +++ b/include/linux/rmap.h @@ -4,7 +4,6 @@ #define _LINUX_RMAP_H * Declarations for Reverse Mapping functions in mm/rmap.c */ -#include #include #include #include @@ -92,7 +91,6 @@ static inline void page_dup_rmap(struct */ int page_referenced(struct page *, int is_locked); int try_to_unmap(struct page *, int ignore_refs); -void remove_from_swap(struct page *page); /* * Called from mm/filemap_xip.c to unmap empty zero page diff --git a/include/linux/rtc-v3020.h b/include/linux/rtc-v3020.h new file mode 100644 index 0000000..bf74e63 --- /dev/null +++ b/include/linux/rtc-v3020.h @@ -0,0 +1,35 @@ +/* + * v3020.h - Registers definition and platform data structure for the v3020 RTC. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2006, 8D Technologies inc. + */ +#ifndef __LINUX_V3020_H +#define __LINUX_V3020_H + +/* The v3020 has only one data pin but which one + * is used depends on the board. */ +struct v3020_platform_data { + int leftshift; /* (1<<(leftshift)) & readl() */ +}; + +#define V3020_STATUS_0 0x00 +#define V3020_STATUS_1 0x01 +#define V3020_SECONDS 0x02 +#define V3020_MINUTES 0x03 +#define V3020_HOURS 0x04 +#define V3020_MONTH_DAY 0x05 +#define V3020_MONTH 0x06 +#define V3020_YEAR 0x07 +#define V3020_WEEK_DAY 0x08 +#define V3020_WEEK 0x09 + +#define V3020_IS_COMMAND(val) ((val)>=0x0E) + +#define V3020_CMD_RAM2CLOCK 0x0E +#define V3020_CMD_CLOCK2RAM 0x0F + +#endif /* __LINUX_V3020_H */ diff --git a/include/linux/rtc.h b/include/linux/rtc.h index ab61cd1..5371e4e 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -34,8 +34,8 @@ struct rtc_time { * alarm API. */ struct rtc_wkalrm { - unsigned char enabled; /* 0 = alarm disable, 1 = alarm disabled */ - unsigned char pending; /* 0 = alarm pending, 1 = alarm not pending */ + unsigned char enabled; /* 0 = alarm disabled, 1 = alarm enabled */ + unsigned char pending; /* 0 = alarm not pending, 1 = alarm pending */ struct rtc_time time; /* time the alarm is set to */ }; @@ -102,6 +102,7 @@ #ifdef __KERNEL__ #include extern int rtc_month_days(unsigned int month, unsigned int year); +extern int rtc_year_days(unsigned int day, unsigned int month, unsigned int year); extern int rtc_valid_tm(struct rtc_time *tm); extern int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time); extern void rtc_time_to_tm(unsigned long time, struct rtc_time *tm); @@ -155,6 +156,17 @@ struct rtc_device struct rtc_task *irq_task; spinlock_t irq_task_lock; int irq_freq; + int max_user_freq; +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + struct work_struct uie_task; + struct timer_list uie_timer; + /* Those fields are protected by rtc->irq_lock */ + unsigned int oldsecs; + unsigned int irq_active:1; + unsigned int stop_uie_polling:1; + unsigned int uie_task_active:1; + unsigned int uie_timer_active:1; +#endif }; #define to_rtc_device(d) container_of(d, struct rtc_device, class_dev) diff --git a/include/linux/rtmutex.h b/include/linux/rtmutex.h new file mode 100644 index 0000000..5d41dee --- /dev/null +++ b/include/linux/rtmutex.h @@ -0,0 +1,107 @@ +/* + * RT Mutexes: blocking mutual exclusion locks with PI support + * + * started by Ingo Molnar and Thomas Gleixner: + * + * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2006, Timesys Corp., Thomas Gleixner + * + * This file contains the public data structure and API definitions. + */ + +#ifndef __LINUX_RT_MUTEX_H +#define __LINUX_RT_MUTEX_H + +#include +#include +#include + +/* + * The rt_mutex structure + * + * @wait_lock: spinlock to protect the structure + * @wait_list: pilist head to enqueue waiters in priority order + * @owner: the mutex owner + */ +struct rt_mutex { + spinlock_t wait_lock; + struct plist_head wait_list; + struct task_struct *owner; +#ifdef CONFIG_DEBUG_RT_MUTEXES + int save_state; + const char *name, *file; + int line; + void *magic; +#endif +}; + +struct rt_mutex_waiter; +struct hrtimer_sleeper; + +#ifdef CONFIG_DEBUG_RT_MUTEXES + extern int rt_mutex_debug_check_no_locks_freed(const void *from, + unsigned long len); + extern void rt_mutex_debug_check_no_locks_held(struct task_struct *task); +#else + static inline int rt_mutex_debug_check_no_locks_freed(const void *from, + unsigned long len) + { + return 0; + } +# define rt_mutex_debug_check_no_locks_held(task) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_RT_MUTEXES +# define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) \ + , .name = #mutexname, .file = __FILE__, .line = __LINE__ +# define rt_mutex_init(mutex) __rt_mutex_init(mutex, __FUNCTION__) + extern void rt_mutex_debug_task_free(struct task_struct *tsk); +#else +# define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) +# define rt_mutex_init(mutex) __rt_mutex_init(mutex, NULL) +# define rt_mutex_debug_task_free(t) do { } while (0) +#endif + +#define __RT_MUTEX_INITIALIZER(mutexname) \ + { .wait_lock = SPIN_LOCK_UNLOCKED \ + , .wait_list = PLIST_HEAD_INIT(mutexname.wait_list, mutexname.wait_lock) \ + , .owner = NULL \ + __DEBUG_RT_MUTEX_INITIALIZER(mutexname)} + +#define DEFINE_RT_MUTEX(mutexname) \ + struct rt_mutex mutexname = __RT_MUTEX_INITIALIZER(mutexname) + +/*** + * rt_mutex_is_locked - is the mutex locked + * @lock: the mutex to be queried + * + * Returns 1 if the mutex is locked, 0 if unlocked. + */ +static inline int rt_mutex_is_locked(struct rt_mutex *lock) +{ + return lock->owner != NULL; +} + +extern void __rt_mutex_init(struct rt_mutex *lock, const char *name); +extern void rt_mutex_destroy(struct rt_mutex *lock); + +extern void rt_mutex_lock(struct rt_mutex *lock); +extern int rt_mutex_lock_interruptible(struct rt_mutex *lock, + int detect_deadlock); +extern int rt_mutex_timed_lock(struct rt_mutex *lock, + struct hrtimer_sleeper *timeout, + int detect_deadlock); + +extern int rt_mutex_trylock(struct rt_mutex *lock); + +extern void rt_mutex_unlock(struct rt_mutex *lock); + +#ifdef CONFIG_RT_MUTEXES +# define INIT_RT_MUTEXES(tsk) \ + .pi_waiters = PLIST_HEAD_INIT(tsk.pi_waiters, tsk.pi_lock), \ + INIT_RT_MUTEX_DEBUG(tsk) +#else +# define INIT_RT_MUTEXES(tsk) +#endif + +#endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index df0cdd4..facd9ee 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -909,7 +909,6 @@ #define TCAA_MAX 1 #ifdef __KERNEL__ -#include #include extern size_t rtattr_strlcpy(char *dest, const struct rtattr *rta, size_t size); diff --git a/include/linux/rwsem-spinlock.h b/include/linux/rwsem-spinlock.h index f30f805..ae1fcad 100644 --- a/include/linux/rwsem-spinlock.h +++ b/include/linux/rwsem-spinlock.h @@ -32,30 +32,37 @@ struct rw_semaphore { __s32 activity; spinlock_t wait_lock; struct list_head wait_list; -#if RWSEM_DEBUG - int debug; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; #endif }; -/* - * initialisation - */ -#if RWSEM_DEBUG -#define __RWSEM_DEBUG_INIT , 0 +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname } #else -#define __RWSEM_DEBUG_INIT /* */ +# define __RWSEM_DEP_MAP_INIT(lockname) #endif #define __RWSEM_INITIALIZER(name) \ -{ 0, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) __RWSEM_DEBUG_INIT } +{ 0, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) } #define DECLARE_RWSEM(name) \ struct rw_semaphore name = __RWSEM_INITIALIZER(name) -extern void FASTCALL(init_rwsem(struct rw_semaphore *sem)); +extern void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key); + +#define init_rwsem(sem) \ +do { \ + static struct lock_class_key __key; \ + \ + __init_rwsem((sem), #sem, &__key); \ +} while (0) + extern void FASTCALL(__down_read(struct rw_semaphore *sem)); extern int FASTCALL(__down_read_trylock(struct rw_semaphore *sem)); extern void FASTCALL(__down_write(struct rw_semaphore *sem)); +extern void FASTCALL(__down_write_nested(struct rw_semaphore *sem, int subclass)); extern int FASTCALL(__down_write_trylock(struct rw_semaphore *sem)); extern void FASTCALL(__up_read(struct rw_semaphore *sem)); extern void FASTCALL(__up_write(struct rw_semaphore *sem)); diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index bfb9888..658afb3 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -9,11 +9,8 @@ #define _LINUX_RWSEM_H #include -#define RWSEM_DEBUG 0 - #ifdef __KERNEL__ -#include #include #include #include @@ -27,89 +24,58 @@ #else #include /* use an arch-specific implementation */ #endif -#ifndef rwsemtrace -#if RWSEM_DEBUG -extern void FASTCALL(rwsemtrace(struct rw_semaphore *sem, const char *str)); -#else -#define rwsemtrace(SEM,FMT) -#endif -#endif - /* * lock for reading */ -static inline void down_read(struct rw_semaphore *sem) -{ - might_sleep(); - rwsemtrace(sem,"Entering down_read"); - __down_read(sem); - rwsemtrace(sem,"Leaving down_read"); -} +extern void down_read(struct rw_semaphore *sem); /* * trylock for reading -- returns 1 if successful, 0 if contention */ -static inline int down_read_trylock(struct rw_semaphore *sem) -{ - int ret; - rwsemtrace(sem,"Entering down_read_trylock"); - ret = __down_read_trylock(sem); - rwsemtrace(sem,"Leaving down_read_trylock"); - return ret; -} +extern int down_read_trylock(struct rw_semaphore *sem); /* * lock for writing */ -static inline void down_write(struct rw_semaphore *sem) -{ - might_sleep(); - rwsemtrace(sem,"Entering down_write"); - __down_write(sem); - rwsemtrace(sem,"Leaving down_write"); -} +extern void down_write(struct rw_semaphore *sem); /* * trylock for writing -- returns 1 if successful, 0 if contention */ -static inline int down_write_trylock(struct rw_semaphore *sem) -{ - int ret; - rwsemtrace(sem,"Entering down_write_trylock"); - ret = __down_write_trylock(sem); - rwsemtrace(sem,"Leaving down_write_trylock"); - return ret; -} +extern int down_write_trylock(struct rw_semaphore *sem); /* * release a read lock */ -static inline void up_read(struct rw_semaphore *sem) -{ - rwsemtrace(sem,"Entering up_read"); - __up_read(sem); - rwsemtrace(sem,"Leaving up_read"); -} +extern void up_read(struct rw_semaphore *sem); /* * release a write lock */ -static inline void up_write(struct rw_semaphore *sem) -{ - rwsemtrace(sem,"Entering up_write"); - __up_write(sem); - rwsemtrace(sem,"Leaving up_write"); -} +extern void up_write(struct rw_semaphore *sem); /* * downgrade write lock to read lock */ -static inline void downgrade_write(struct rw_semaphore *sem) -{ - rwsemtrace(sem,"Entering downgrade_write"); - __downgrade_write(sem); - rwsemtrace(sem,"Leaving downgrade_write"); -} +extern void downgrade_write(struct rw_semaphore *sem); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +/* + * nested locking: + */ +extern void down_read_nested(struct rw_semaphore *sem, int subclass); +extern void down_write_nested(struct rw_semaphore *sem, int subclass); +/* + * Take/release a lock when not the owner will release it: + */ +extern void down_read_non_owner(struct rw_semaphore *sem); +extern void up_read_non_owner(struct rw_semaphore *sem); +#else +# define down_read_nested(sem, subclass) down_read(sem) +# define down_write_nested(sem, subclass) down_write(sem) +# define down_read_non_owner(sem) down_read(sem) +# define up_read_non_owner(sem) up_read(sem) +#endif #endif /* __KERNEL__ */ #endif /* _LINUX_RWSEM_H */ diff --git a/include/linux/scc.h b/include/linux/scc.h index 885a4a0..3495bd9 100644 --- a/include/linux/scc.h +++ b/include/linux/scc.h @@ -3,7 +3,6 @@ #ifndef _SCC_H #define _SCC_H -#include /* selection of hardware types */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 29b7d4f..1c876e2 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1,9 +1,46 @@ #ifndef _LINUX_SCHED_H #define _LINUX_SCHED_H +#include /* For AT_VECTOR_SIZE */ + +/* + * cloning flags: + */ +#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */ +#define CLONE_VM 0x00000100 /* set if VM shared between processes */ +#define CLONE_FS 0x00000200 /* set if fs info shared between processes */ +#define CLONE_FILES 0x00000400 /* set if open files shared between processes */ +#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */ +#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ +#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ +#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */ +#define CLONE_THREAD 0x00010000 /* Same thread group? */ +#define CLONE_NEWNS 0x00020000 /* New namespace group? */ +#define CLONE_SYSVSEM 0x00040000 /* share system V SEM_UNDO semantics */ +#define CLONE_SETTLS 0x00080000 /* create a new TLS for the child */ +#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */ +#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */ +#define CLONE_DETACHED 0x00400000 /* Unused, ignored */ +#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */ +#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */ +#define CLONE_STOPPED 0x02000000 /* Start in stopped state */ + +/* + * Scheduling policies + */ +#define SCHED_NORMAL 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_BATCH 3 + +#ifdef __KERNEL__ + +struct sched_param { + int sched_priority; +}; + #include /* for HZ */ -#include #include #include #include @@ -36,32 +73,18 @@ #include #include #include #include +#include -#include /* For AT_VECTOR_SIZE */ +#include +#include +#include +#include +#include -struct exec_domain; +#include -/* - * cloning flags: - */ -#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */ -#define CLONE_VM 0x00000100 /* set if VM shared between processes */ -#define CLONE_FS 0x00000200 /* set if fs info shared between processes */ -#define CLONE_FILES 0x00000400 /* set if open files shared between processes */ -#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */ -#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ -#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ -#define CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */ -#define CLONE_THREAD 0x00010000 /* Same thread group? */ -#define CLONE_NEWNS 0x00020000 /* New namespace group? */ -#define CLONE_SYSVSEM 0x00040000 /* share system V SEM_UNDO semantics */ -#define CLONE_SETTLS 0x00080000 /* create a new TLS for the child */ -#define CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */ -#define CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */ -#define CLONE_DETACHED 0x00400000 /* Unused, ignored */ -#define CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force CLONE_PTRACE on this clone */ -#define CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */ -#define CLONE_STOPPED 0x02000000 /* Start in stopped state */ +struct exec_domain; +struct futex_pi_state; /* * List of flags we want to share for kernel threads, @@ -102,14 +125,8 @@ extern unsigned long nr_running(void); extern unsigned long nr_uninterruptible(void); extern unsigned long nr_active(void); extern unsigned long nr_iowait(void); +extern unsigned long weighted_cpuload(const int cpu); -#include -#include -#include -#include -#include - -#include /* * Task state bitmask. NOTE! These bits are also @@ -156,20 +173,6 @@ #define set_current_state(state_value) /* Task command name length */ #define TASK_COMM_LEN 16 -/* - * Scheduling policies - */ -#define SCHED_NORMAL 0 -#define SCHED_FIFO 1 -#define SCHED_RR 2 -#define SCHED_BATCH 3 - -struct sched_param { - int sched_priority; -}; - -#ifdef __KERNEL__ - #include /* @@ -181,11 +184,11 @@ #include extern rwlock_t tasklist_lock; extern spinlock_t mmlist_lock; -typedef struct task_struct task_t; +struct task_struct; extern void sched_init(void); extern void sched_init_smp(void); -extern void init_idle(task_t *idle, int cpu); +extern void init_idle(struct task_struct *idle, int cpu); extern cpumask_t nohz_cpu_mask; @@ -358,6 +361,14 @@ struct sighand_struct { spinlock_t siglock; }; +struct pacct_struct { + int ac_flag; + long ac_exitcode; + unsigned long ac_mem; + cputime_t ac_utime, ac_stime; + unsigned long ac_minflt, ac_majflt; +}; + /* * NOTE! "signal_struct" does not have it's own * locking, because a shared signal_struct always @@ -372,7 +383,7 @@ struct signal_struct { wait_queue_head_t wait_chldexit; /* for wait4() */ /* current thread group signal load-balancing target: */ - task_t *curr_target; + struct task_struct *curr_target; /* shared signal handling: */ struct sigpending shared_pending; @@ -449,6 +460,9 @@ #ifdef CONFIG_KEYS struct key *session_keyring; /* keyring inherited over fork */ struct key *process_keyring; /* keyring private to this process */ #endif +#ifdef CONFIG_BSD_PROCESS_ACCT + struct pacct_struct pacct; /* per-process accounting information */ +#endif }; /* Context switch must be unlocked if interrupts are to be enabled */ @@ -483,8 +497,11 @@ #define MAX_RT_PRIO MAX_USER_RT_PRIO #define MAX_PRIO (MAX_RT_PRIO + 40) -#define rt_task(p) (unlikely((p)->prio < MAX_RT_PRIO)) +#define rt_prio(prio) unlikely((prio) < MAX_RT_PRIO) +#define rt_task(p) rt_prio((p)->prio) #define batch_task(p) (unlikely((p)->policy == SCHED_BATCH)) +#define has_rt_policy(p) \ + unlikely((p)->policy != SCHED_NORMAL && (p)->policy != SCHED_BATCH) /* * Some day this will be a full-fledged user tracking system.. @@ -494,7 +511,7 @@ struct user_struct { atomic_t processes; /* How many processes does this user have? */ atomic_t files; /* How many open files does this user have? */ atomic_t sigpending; /* How many pending signals does this user have? */ -#ifdef CONFIG_INOTIFY +#ifdef CONFIG_INOTIFY_USER atomic_t inotify_watches; /* How many inotify watches does this user have? */ atomic_t inotify_devs; /* How many inotify devs does this user have opened? */ #endif @@ -517,7 +534,6 @@ extern struct user_struct *find_user(uid extern struct user_struct root_user; #define INIT_USER (&root_user) -typedef struct prio_array prio_array_t; struct backing_dev_info; struct reclaim_state; @@ -547,9 +563,9 @@ enum idle_type /* * sched-domains (multiprocessor balancing) declarations: */ -#ifdef CONFIG_SMP #define SCHED_LOAD_SCALE 128UL /* increase resolution of load */ +#ifdef CONFIG_SMP #define SD_LOAD_BALANCE 1 /* Do load balancing on this domain. */ #define SD_BALANCE_NEWIDLE 2 /* Balance when about to become idle */ #define SD_BALANCE_EXEC 4 /* Balance on exec */ @@ -558,6 +574,11 @@ #define SD_WAKE_IDLE 16 /* Wake to idle #define SD_WAKE_AFFINE 32 /* Wake task to waking CPU */ #define SD_WAKE_BALANCE 64 /* Perform balancing at task wakeup */ #define SD_SHARE_CPUPOWER 128 /* Domain members share cpu power */ +#define SD_POWERSAVINGS_BALANCE 256 /* Balance for power savings */ + +#define BALANCE_FOR_POWER ((sched_mc_power_savings || sched_smt_power_savings) \ + ? SD_POWERSAVINGS_BALANCE : 0) + struct sched_group { struct sched_group *next; /* Must be a circular list */ @@ -627,7 +648,7 @@ #ifdef CONFIG_SCHEDSTATS #endif }; -extern void partition_sched_domains(cpumask_t *partition1, +extern int partition_sched_domains(cpumask_t *partition1, cpumask_t *partition2); /* @@ -677,7 +698,7 @@ #define GROUP_AT(gi, i) \ ((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK]) #ifdef ARCH_HAS_PREFETCH_SWITCH_STACK -extern void prefetch_stack(struct task_struct*); +extern void prefetch_stack(struct task_struct *t); #else static inline void prefetch_stack(struct task_struct *t) { } #endif @@ -693,6 +714,8 @@ enum sleep_type { SLEEP_INTERRUPTED, }; +struct prio_array; + struct task_struct { volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ struct thread_info *thread_info; @@ -702,12 +725,15 @@ struct task_struct { int lock_depth; /* BKL lock depth */ -#if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW) +#ifdef CONFIG_SMP +#ifdef __ARCH_WANT_UNLOCKED_CTXSW int oncpu; #endif - int prio, static_prio; +#endif + int load_weight; /* for niceness load balancing purposes */ + int prio, static_prio, normal_prio; struct list_head run_list; - prio_array_t *array; + struct prio_array *array; unsigned short ioprio; unsigned int btrace_seq; @@ -831,13 +857,43 @@ #endif u32 self_exec_id; /* Protection of (de-)allocation: mm, files, fs, tty, keyrings */ spinlock_t alloc_lock; -/* Protection of proc_dentry: nesting proc_lock, dcache_lock, write_lock_irq(&tasklist_lock); */ - spinlock_t proc_lock; + + /* Protection of the PI data structures: */ + spinlock_t pi_lock; + +#ifdef CONFIG_RT_MUTEXES + /* PI waiters blocked on a rt_mutex held by this task */ + struct plist_head pi_waiters; + /* Deadlock detection and priority inheritance handling */ + struct rt_mutex_waiter *pi_blocked_on; +#endif #ifdef CONFIG_DEBUG_MUTEXES /* mutex deadlock detection */ struct mutex_waiter *blocked_on; #endif +#ifdef CONFIG_TRACE_IRQFLAGS + unsigned int irq_events; + int hardirqs_enabled; + unsigned long hardirq_enable_ip; + unsigned int hardirq_enable_event; + unsigned long hardirq_disable_ip; + unsigned int hardirq_disable_event; + int softirqs_enabled; + unsigned long softirq_disable_ip; + unsigned int softirq_disable_event; + unsigned long softirq_enable_ip; + unsigned int softirq_enable_event; + int hardirq_context; + int softirq_context; +#endif +#ifdef CONFIG_LOCKDEP +# define MAX_LOCK_DEPTH 30UL + u64 curr_chain_key; + int lockdep_depth; + struct held_lock held_locks[MAX_LOCK_DEPTH]; + unsigned int lockdep_recursion; +#endif /* journalling filesystem info */ void *journal_info; @@ -845,7 +901,6 @@ #endif /* VM state */ struct reclaim_state *reclaim_state; - struct dentry *proc_dentry; struct backing_dev_info *backing_dev_info; struct io_context *io_context; @@ -880,6 +935,8 @@ #endif #ifdef CONFIG_COMPAT struct compat_robust_list_head __user *compat_robust_list; #endif + struct list_head pi_state_list; + struct futex_pi_state *pi_state_cache; atomic_t fs_excl; /* holding fs exclusive resources */ struct rcu_head rcu; @@ -941,13 +998,13 @@ #define PF_FSTRANS 0x00020000 /* inside #define PF_KSWAPD 0x00040000 /* I am kswapd */ #define PF_SWAPOFF 0x00080000 /* I am in swapoff */ #define PF_LESS_THROTTLE 0x00100000 /* Throttle me less: I clean memory */ -#define PF_SYNCWRITE 0x00200000 /* I am doing a sync write */ -#define PF_BORROWED_MM 0x00400000 /* I am a kthread doing use_mm */ -#define PF_RANDOMIZE 0x00800000 /* randomize virtual address space */ -#define PF_SWAPWRITE 0x01000000 /* Allowed to write to swap */ -#define PF_SPREAD_PAGE 0x04000000 /* Spread page cache over cpuset */ -#define PF_SPREAD_SLAB 0x08000000 /* Spread some slab caches over cpuset */ +#define PF_BORROWED_MM 0x00200000 /* I am a kthread doing use_mm */ +#define PF_RANDOMIZE 0x00400000 /* randomize virtual address space */ +#define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */ +#define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */ +#define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */ #define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */ +#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */ /* * Only the _current_ task can read/write to tsk->flags, but other @@ -975,9 +1032,9 @@ #define tsk_used_math(p) ((p)->flags & P #define used_math() tsk_used_math(current) #ifdef CONFIG_SMP -extern int set_cpus_allowed(task_t *p, cpumask_t new_mask); +extern int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask); #else -static inline int set_cpus_allowed(task_t *p, cpumask_t new_mask) +static inline int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) { if (!cpu_isset(0, new_mask)) return -EINVAL; @@ -986,7 +1043,8 @@ static inline int set_cpus_allowed(task_ #endif extern unsigned long long sched_clock(void); -extern unsigned long long current_sched_time(const task_t *current_task); +extern unsigned long long +current_sched_time(const struct task_struct *current_task); /* sched_exec is called by processes performing an exec */ #ifdef CONFIG_SMP @@ -1002,16 +1060,29 @@ static inline void idle_task_exit(void) #endif extern void sched_idle_next(void); -extern void set_user_nice(task_t *p, long nice); -extern int task_prio(const task_t *p); -extern int task_nice(const task_t *p); -extern int can_nice(const task_t *p, const int nice); -extern int task_curr(const task_t *p); + +#ifdef CONFIG_RT_MUTEXES +extern int rt_mutex_getprio(struct task_struct *p); +extern void rt_mutex_setprio(struct task_struct *p, int prio); +extern void rt_mutex_adjust_pi(struct task_struct *p); +#else +static inline int rt_mutex_getprio(struct task_struct *p) +{ + return p->normal_prio; +} +# define rt_mutex_adjust_pi(p) do { } while (0) +#endif + +extern void set_user_nice(struct task_struct *p, long nice); +extern int task_prio(const struct task_struct *p); +extern int task_nice(const struct task_struct *p); +extern int can_nice(const struct task_struct *p, const int nice); +extern int task_curr(const struct task_struct *p); extern int idle_cpu(int cpu); extern int sched_setscheduler(struct task_struct *, int, struct sched_param *); -extern task_t *idle_task(int cpu); -extern task_t *curr_task(int cpu); -extern void set_curr_task(int cpu, task_t *p); +extern struct task_struct *idle_task(int cpu); +extern struct task_struct *curr_task(int cpu); +extern void set_curr_task(int cpu, struct task_struct *p); void yield(void); @@ -1068,8 +1139,8 @@ #ifdef CONFIG_SMP #else static inline void kick_process(struct task_struct *tsk) { } #endif -extern void FASTCALL(sched_fork(task_t * p, int clone_flags)); -extern void FASTCALL(sched_exit(task_t * p)); +extern void FASTCALL(sched_fork(struct task_struct * p, int clone_flags)); +extern void FASTCALL(sched_exit(struct task_struct * p)); extern int in_group_p(gid_t); extern int in_egroup_p(gid_t); @@ -1102,7 +1173,7 @@ extern int force_sig_info(int, struct si extern int __kill_pg_info(int sig, struct siginfo *info, pid_t pgrp); extern int kill_pg_info(int, struct siginfo *, pid_t); extern int kill_proc_info(int, struct siginfo *, pid_t); -extern int kill_proc_info_as_uid(int, struct siginfo *, pid_t, uid_t, uid_t); +extern int kill_proc_info_as_uid(int, struct siginfo *, pid_t, uid_t, uid_t, u32); extern void do_notify_parent(struct task_struct *, int); extern void force_sig(int, struct task_struct *); extern void force_sig_specific(int, struct task_struct *); @@ -1174,17 +1245,17 @@ extern NORET_TYPE void do_group_exit(int extern void daemonize(const char *, ...); extern int allow_signal(int); extern int disallow_signal(int); -extern task_t *child_reaper; +extern struct task_struct *child_reaper; extern int do_execve(char *, char __user * __user *, char __user * __user *, struct pt_regs *); extern long do_fork(unsigned long, unsigned long, struct pt_regs *, unsigned long, int __user *, int __user *); -task_t *fork_idle(int); +struct task_struct *fork_idle(int); extern void set_task_comm(struct task_struct *tsk, char *from); extern void get_task_comm(char *to, struct task_struct *tsk); #ifdef CONFIG_SMP -extern void wait_task_inactive(task_t * p); +extern void wait_task_inactive(struct task_struct * p); #else #define wait_task_inactive(p) do { } while (0) #endif @@ -1210,13 +1281,13 @@ #define while_each_thread(g, t) \ /* de_thread depends on thread_group_leader not being a pid based check */ #define thread_group_leader(p) (p == p->group_leader) -static inline task_t *next_thread(const task_t *p) +static inline struct task_struct *next_thread(const struct task_struct *p) { return list_entry(rcu_dereference(p->thread_group.next), - task_t, thread_group); + struct task_struct, thread_group); } -static inline int thread_group_empty(task_t *p) +static inline int thread_group_empty(struct task_struct *p) { return list_empty(&p->thread_group); } @@ -1225,7 +1296,7 @@ #define delay_group_leader(p) \ (thread_group_leader(p) && !thread_group_empty(p)) /* - * Protects ->fs, ->files, ->mm, ->ptrace, ->group_info, ->comm, keyring + * Protects ->fs, ->files, ->mm, ->group_info, ->comm, keyring * subscriptions and synchronises with wait4(). Also used in procfs. Also * pins the final release of task.io_context. Also protects ->cpuset. * @@ -1401,6 +1472,11 @@ #endif extern long sched_setaffinity(pid_t pid, cpumask_t new_mask); extern long sched_getaffinity(pid_t pid, cpumask_t *mask); +#include +extern int sched_mc_power_savings, sched_smt_power_savings; +extern struct sysdev_attribute attr_sched_mc_power_savings, attr_sched_smt_power_savings; +extern int sched_create_sysfs_power_savings_entries(struct sysdev_class *cls); + extern void normalize_rt_tasks(void); #ifdef CONFIG_PM diff --git a/include/linux/scx200.h b/include/linux/scx200.h index a22f9e1..693c055 100644 --- a/include/linux/scx200.h +++ b/include/linux/scx200.h @@ -49,10 +49,3 @@ #define SCx200_IID 0x3c /* IA On a Chip #define SCx200_REV 0x3d /* Revision Register */ #define SCx200_CBA 0x3e /* Configuration Base Address Register */ #define SCx200_CBA_SCRATCH 0x64 /* Configuration Base Address Scratchpad */ - -/* - Local variables: - compile-command: "make -C ../.. bzImage modules" - c-basic-offset: 8 - End: -*/ diff --git a/include/linux/scx200_gpio.h b/include/linux/scx200_gpio.h index 30cdd64..90dd069 100644 --- a/include/linux/scx200_gpio.h +++ b/include/linux/scx200_gpio.h @@ -1,6 +1,6 @@ #include -u32 scx200_gpio_configure(int index, u32 set, u32 clear); +u32 scx200_gpio_configure(unsigned index, u32 set, u32 clear); extern unsigned scx200_gpio_base; extern long scx200_gpio_shadow[2]; @@ -17,7 +17,7 @@ #define __SCx200_GPIO_OUT __asm__ __vola /* returns the value of the GPIO pin */ -static inline int scx200_gpio_get(int index) { +static inline int scx200_gpio_get(unsigned index) { __SCx200_GPIO_BANK; __SCx200_GPIO_IOADDR + 0x04; __SCx200_GPIO_INDEX; @@ -29,7 +29,7 @@ static inline int scx200_gpio_get(int in driven if the GPIO is configured as an output, it might not be the state of the GPIO right now if the GPIO is configured as an input) */ -static inline int scx200_gpio_current(int index) { +static inline int scx200_gpio_current(unsigned index) { __SCx200_GPIO_BANK; __SCx200_GPIO_INDEX; @@ -38,7 +38,7 @@ static inline int scx200_gpio_current(in /* drive the GPIO signal high */ -static inline void scx200_gpio_set_high(int index) { +static inline void scx200_gpio_set_high(unsigned index) { __SCx200_GPIO_BANK; __SCx200_GPIO_IOADDR; __SCx200_GPIO_SHADOW; @@ -49,7 +49,7 @@ static inline void scx200_gpio_set_high( /* drive the GPIO signal low */ -static inline void scx200_gpio_set_low(int index) { +static inline void scx200_gpio_set_low(unsigned index) { __SCx200_GPIO_BANK; __SCx200_GPIO_IOADDR; __SCx200_GPIO_SHADOW; @@ -60,7 +60,7 @@ static inline void scx200_gpio_set_low(i /* drive the GPIO signal to state */ -static inline void scx200_gpio_set(int index, int state) { +static inline void scx200_gpio_set(unsigned index, int state) { __SCx200_GPIO_BANK; __SCx200_GPIO_IOADDR; __SCx200_GPIO_SHADOW; @@ -73,7 +73,7 @@ static inline void scx200_gpio_set(int i } /* toggle the GPIO signal */ -static inline void scx200_gpio_change(int index) { +static inline void scx200_gpio_change(unsigned index) { __SCx200_GPIO_BANK; __SCx200_GPIO_IOADDR; __SCx200_GPIO_SHADOW; @@ -87,10 +87,3 @@ #undef __SCx200_GPIO_IOADDR #undef __SCx200_GPIO_SHADOW #undef __SCx200_GPIO_INDEX #undef __SCx200_GPIO_OUT - -/* - Local variables: - compile-command: "make -C ../.. bzImage modules" - c-basic-offset: 8 - End: -*/ diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index cd2773b..3e8b1cf 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -1,7 +1,6 @@ #ifndef _LINUX_SECCOMP_H #define _LINUX_SECCOMP_H -#include #ifdef CONFIG_SECCOMP diff --git a/include/linux/security.h b/include/linux/security.h index 1bab48f..f753038 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -67,7 +67,7 @@ struct xfrm_state; struct xfrm_user_sec_ctx; extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb); -extern int cap_netlink_recv(struct sk_buff *skb); +extern int cap_netlink_recv(struct sk_buff *skb, int cap); /* * Values used in the task_security_ops calls @@ -171,9 +171,9 @@ #ifdef CONFIG_SECURITY * Deallocate and clear the sb->s_security field. * @sb contains the super_block structure to be modified. * @sb_statfs: - * Check permission before obtaining filesystem statistics for the @sb - * filesystem. - * @sb contains the super_block structure for the filesystem. + * Check permission before obtaining filesystem statistics for the @mnt + * mountpoint. + * @dentry is a handle on the superblock for the filesystem. * Return 0 if permission is granted. * @sb_mount: * Check permission before an object specified by @dev_name is mounted on @@ -567,6 +567,9 @@ #ifdef CONFIG_SECURITY * @p. * @p contains the task_struct for the process. * Return 0 if permission is granted. + * @task_getsecid: + * Retrieve the security identifier of the process @p. + * @p contains the task_struct for the process and place is into @secid. * @task_setgroups: * Check permission before setting the supplementary group set of the * current process. @@ -577,6 +580,15 @@ #ifdef CONFIG_SECURITY * @p contains the task_struct of process. * @nice contains the new nice value. * Return 0 if permission is granted. + * @task_setioprio + * Check permission before setting the ioprio value of @p to @ioprio. + * @p contains the task_struct of process. + * @ioprio contains the new ioprio value + * Return 0 if permission is granted. + * @task_getioprio + * Check permission before getting the ioprio value of @p. + * @p contains the task_struct of process. + * Return 0 if permission is granted. * @task_setrlimit: * Check permission before setting the resource limits of the current * process for @resource to @new_rlim. The old resource limit values can @@ -596,6 +608,10 @@ #ifdef CONFIG_SECURITY * @p. * @p contains the task_struct for process. * Return 0 if permission is granted. + * @task_movememory + * Check permission before moving memory owned by process @p. + * @p contains the task_struct for process. + * Return 0 if permission is granted. * @task_kill: * Check permission before sending signal @sig to @p. @info can be NULL, * the constant 1, or a pointer to a siginfo structure. If @info is 1 or @@ -606,6 +622,7 @@ #ifdef CONFIG_SECURITY * @p contains the task_struct for process. * @info contains the signal information. * @sig contains the signal value. + * @secid contains the sid of the process where the signal originated * Return 0 if permission is granted. * @task_wait: * Check permission before allowing a process to reap a child process @p @@ -647,6 +664,7 @@ #ifdef CONFIG_SECURITY * Check permission before processing the received netlink message in * @skb. * @skb contains the sk_buff structure for the netlink message. + * @cap indicates the capability required * Return 0 if permission is granted. * * Security hooks for Unix domain networking. @@ -805,31 +823,37 @@ #ifdef CONFIG_SECURITY * used by the XFRM system. * @sec_ctx contains the security context information being provided by * the user-level policy update program (e.g., setkey). - * Allocate a security structure to the xp->selector.security field. + * Allocate a security structure to the xp->security field. * The security field is initialized to NULL when the xfrm_policy is * allocated. * Return 0 if operation was successful (memory to allocate, legal context) * @xfrm_policy_clone_security: * @old contains an existing xfrm_policy in the SPD. * @new contains a new xfrm_policy being cloned from old. - * Allocate a security structure to the new->selector.security field - * that contains the information from the old->selector.security field. + * Allocate a security structure to the new->security field + * that contains the information from the old->security field. * Return 0 if operation was successful (memory to allocate). * @xfrm_policy_free_security: * @xp contains the xfrm_policy - * Deallocate xp->selector.security. + * Deallocate xp->security. + * @xfrm_policy_delete_security: + * @xp contains the xfrm_policy. + * Authorize deletion of xp->security. * @xfrm_state_alloc_security: * @x contains the xfrm_state being added to the Security Association * Database by the XFRM system. * @sec_ctx contains the security context information being provided by * the user-level SA generation program (e.g., setkey or racoon). - * Allocate a security structure to the x->sel.security field. The + * Allocate a security structure to the x->security field. The * security field is initialized to NULL when the xfrm_state is * allocated. * Return 0 if operation was successful (memory to allocate, legal context). * @xfrm_state_free_security: * @x contains the xfrm_state. - * Deallocate x>sel.security. + * Deallocate x->security. + * @xfrm_state_delete_security: + * @x contains the xfrm_state. + * Authorize deletion of x->security. * @xfrm_policy_lookup: * @xp contains the xfrm_policy for which the access control is being * checked. @@ -847,6 +871,7 @@ #ifdef CONFIG_SECURITY * Permit allocation of a key and assign security data. Note that key does * not have a serial number assigned at this point. * @key points to the key. + * @flags is the allocation flags * Return 0 if permission is granted, -ve error otherwise. * @key_free: * Notification of destruction; free security data. @@ -1121,7 +1146,7 @@ struct security_operations { int (*sb_copy_data)(struct file_system_type *type, void *orig, void *copy); int (*sb_kern_mount) (struct super_block *sb, void *data); - int (*sb_statfs) (struct super_block * sb); + int (*sb_statfs) (struct dentry *dentry); int (*sb_mount) (char *dev_name, struct nameidata * nd, char *type, unsigned long flags, void *data); int (*sb_check_sb) (struct vfsmount * mnt, struct nameidata * nd); @@ -1202,14 +1227,18 @@ struct security_operations { int (*task_setpgid) (struct task_struct * p, pid_t pgid); int (*task_getpgid) (struct task_struct * p); int (*task_getsid) (struct task_struct * p); + void (*task_getsecid) (struct task_struct * p, u32 * secid); int (*task_setgroups) (struct group_info *group_info); int (*task_setnice) (struct task_struct * p, int nice); + int (*task_setioprio) (struct task_struct * p, int ioprio); + int (*task_getioprio) (struct task_struct * p); int (*task_setrlimit) (unsigned int resource, struct rlimit * new_rlim); int (*task_setscheduler) (struct task_struct * p, int policy, struct sched_param * lp); int (*task_getscheduler) (struct task_struct * p); + int (*task_movememory) (struct task_struct * p); int (*task_kill) (struct task_struct * p, - struct siginfo * info, int sig); + struct siginfo * info, int sig, u32 secid); int (*task_wait) (struct task_struct * p); int (*task_prctl) (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, @@ -1248,7 +1277,7 @@ struct security_operations { struct sembuf * sops, unsigned nsops, int alter); int (*netlink_send) (struct sock * sk, struct sk_buff * skb); - int (*netlink_recv) (struct sk_buff * skb); + int (*netlink_recv) (struct sk_buff * skb, int cap); /* allow module stacking */ int (*register_security) (const char *name, @@ -1298,14 +1327,16 @@ #ifdef CONFIG_SECURITY_NETWORK_XFRM int (*xfrm_policy_alloc_security) (struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx); int (*xfrm_policy_clone_security) (struct xfrm_policy *old, struct xfrm_policy *new); void (*xfrm_policy_free_security) (struct xfrm_policy *xp); + int (*xfrm_policy_delete_security) (struct xfrm_policy *xp); int (*xfrm_state_alloc_security) (struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx); void (*xfrm_state_free_security) (struct xfrm_state *x); + int (*xfrm_state_delete_security) (struct xfrm_state *x); int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 sk_sid, u8 dir); #endif /* CONFIG_SECURITY_NETWORK_XFRM */ /* key management security hooks */ #ifdef CONFIG_KEYS - int (*key_alloc)(struct key *key); + int (*key_alloc)(struct key *key, struct task_struct *tsk, unsigned long flags); void (*key_free)(struct key *key); int (*key_permission)(key_ref_t key_ref, struct task_struct *context, @@ -1442,9 +1473,9 @@ static inline int security_sb_kern_mount return security_ops->sb_kern_mount (sb, data); } -static inline int security_sb_statfs (struct super_block *sb) +static inline int security_sb_statfs (struct dentry *dentry) { - return security_ops->sb_statfs (sb); + return security_ops->sb_statfs (dentry); } static inline int security_sb_mount (char *dev_name, struct nameidata *nd, @@ -1818,6 +1849,11 @@ static inline int security_task_getsid ( return security_ops->task_getsid (p); } +static inline void security_task_getsecid (struct task_struct *p, u32 *secid) +{ + security_ops->task_getsecid (p, secid); +} + static inline int security_task_setgroups (struct group_info *group_info) { return security_ops->task_setgroups (group_info); @@ -1828,6 +1864,16 @@ static inline int security_task_setnice return security_ops->task_setnice (p, nice); } +static inline int security_task_setioprio (struct task_struct *p, int ioprio) +{ + return security_ops->task_setioprio (p, ioprio); +} + +static inline int security_task_getioprio (struct task_struct *p) +{ + return security_ops->task_getioprio (p); +} + static inline int security_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) { @@ -1846,10 +1892,16 @@ static inline int security_task_getsched return security_ops->task_getscheduler (p); } +static inline int security_task_movememory (struct task_struct *p) +{ + return security_ops->task_movememory (p); +} + static inline int security_task_kill (struct task_struct *p, - struct siginfo *info, int sig) + struct siginfo *info, int sig, + u32 secid) { - return security_ops->task_kill (p, info, sig); + return security_ops->task_kill (p, info, sig, secid); } static inline int security_task_wait (struct task_struct *p) @@ -2002,9 +2054,9 @@ static inline int security_netlink_send( return security_ops->netlink_send(sk, skb); } -static inline int security_netlink_recv(struct sk_buff * skb) +static inline int security_netlink_recv(struct sk_buff * skb, int cap) { - return security_ops->netlink_recv(skb); + return security_ops->netlink_recv(skb, cap); } /* prototypes */ @@ -2154,7 +2206,7 @@ static inline int security_sb_kern_mount return 0; } -static inline int security_sb_statfs (struct super_block *sb) +static inline int security_sb_statfs (struct dentry *dentry) { return 0; } @@ -2460,6 +2512,9 @@ static inline int security_task_getsid ( return 0; } +static inline void security_task_getsecid (struct task_struct *p, u32 *secid) +{ } + static inline int security_task_setgroups (struct group_info *group_info) { return 0; @@ -2470,6 +2525,16 @@ static inline int security_task_setnice return 0; } +static inline int security_task_setioprio (struct task_struct *p, int ioprio) +{ + return 0; +} + +static inline int security_task_getioprio (struct task_struct *p) +{ + return 0; +} + static inline int security_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) { @@ -2488,8 +2553,14 @@ static inline int security_task_getsched return 0; } +static inline int security_task_movememory (struct task_struct *p) +{ + return 0; +} + static inline int security_task_kill (struct task_struct *p, - struct siginfo *info, int sig) + struct siginfo *info, int sig, + u32 secid) { return 0; } @@ -2630,9 +2701,9 @@ static inline int security_netlink_send return cap_netlink_send (sk, skb); } -static inline int security_netlink_recv (struct sk_buff *skb) +static inline int security_netlink_recv (struct sk_buff *skb, int cap) { - return cap_netlink_recv (skb); + return cap_netlink_recv (skb, cap); } static inline struct dentry *securityfs_create_dir(const char *name, @@ -2934,11 +3005,21 @@ static inline void security_xfrm_policy_ security_ops->xfrm_policy_free_security(xp); } +static inline int security_xfrm_policy_delete(struct xfrm_policy *xp) +{ + return security_ops->xfrm_policy_delete_security(xp); +} + static inline int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx) { return security_ops->xfrm_state_alloc_security(x, sec_ctx); } +static inline int security_xfrm_state_delete(struct xfrm_state *x) +{ + return security_ops->xfrm_state_delete_security(x); +} + static inline void security_xfrm_state_free(struct xfrm_state *x) { security_ops->xfrm_state_free_security(x); @@ -2963,6 +3044,11 @@ static inline void security_xfrm_policy_ { } +static inline int security_xfrm_policy_delete(struct xfrm_policy *xp) +{ + return 0; +} + static inline int security_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx) { return 0; @@ -2972,6 +3058,11 @@ static inline void security_xfrm_state_f { } +static inline int security_xfrm_state_delete(struct xfrm_state *x) +{ + return 0; +} + static inline int security_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) { return 0; @@ -2980,9 +3071,11 @@ #endif /* CONFIG_SECURITY_NETWORK_XFRM * #ifdef CONFIG_KEYS #ifdef CONFIG_SECURITY -static inline int security_key_alloc(struct key *key) +static inline int security_key_alloc(struct key *key, + struct task_struct *tsk, + unsigned long flags) { - return security_ops->key_alloc(key); + return security_ops->key_alloc(key, tsk, flags); } static inline void security_key_free(struct key *key) @@ -2999,7 +3092,9 @@ static inline int security_key_permissio #else -static inline int security_key_alloc(struct key *key) +static inline int security_key_alloc(struct key *key, + struct task_struct *tsk, + unsigned long flags) { return 0; } diff --git a/include/linux/selinux.h b/include/linux/selinux.h index 4047bcd..aad4e39 100644 --- a/include/linux/selinux.h +++ b/include/linux/selinux.h @@ -118,6 +118,27 @@ void selinux_get_ipc_sid(const struct ke */ void selinux_get_task_sid(struct task_struct *tsk, u32 *sid); +/** + * selinux_string_to_sid - map a security context string to a security ID + * @str: the security context string to be mapped + * @sid: ID value returned via this. + * + * Returns 0 if successful, with the SID stored in sid. A value + * of zero for sid indicates no SID could be determined (but no error + * occurred). + */ +int selinux_string_to_sid(char *str, u32 *sid); + +/** + * selinux_relabel_packet_permission - check permission to relabel a packet + * @sid: ID value to be applied to network packet (via SECMARK, most likely) + * + * Returns 0 if the current task is allowed to label packets with the + * supplied security ID. Note that it is implicit that the packet is always + * being relabeled from the default unlabled value, and that the access + * control decision is made in the AVC. + */ +int selinux_relabel_packet_permission(u32 sid); #else @@ -172,6 +193,17 @@ static inline void selinux_get_task_sid( *sid = 0; } +static inline int selinux_string_to_sid(const char *str, u32 *sid) +{ + *sid = 0; + return 0; +} + +static inline int selinux_relabel_packet_permission(u32 sid) +{ + return 0; +} + #endif /* CONFIG_SECURITY_SELINUX */ #endif /* _LINUX_SELINUX_H */ diff --git a/include/linux/sem.h b/include/linux/sem.h index 3c1f112..9aaffb0 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -2,7 +2,6 @@ #ifndef _LINUX_SEM_H #define _LINUX_SEM_H #include -#include /* semop flags */ #define SEM_UNDO 0x1000 /* undo the operation on exit */ @@ -78,6 +77,7 @@ #define SEMMAP SEMMNS /* # of #define SEMUSZ 20 /* sizeof struct sem_undo */ #ifdef __KERNEL__ +#include struct task_struct; diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index 5a09557..4600093 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -26,7 +26,6 @@ #define __LINUX_SEQLOCK_H * by Keith Owens and Andrea Arcangeli */ -#include #include #include @@ -39,9 +38,17 @@ typedef struct { * These macros triggered gcc-3.x compile-time problems. We think these are * OK now. Be cautious. */ -#define SEQLOCK_UNLOCKED { 0, SPIN_LOCK_UNLOCKED } -#define seqlock_init(x) do { *(x) = (seqlock_t) SEQLOCK_UNLOCKED; } while (0) +#define __SEQLOCK_UNLOCKED(lockname) \ + { 0, __SPIN_LOCK_UNLOCKED(lockname) } +#define SEQLOCK_UNLOCKED \ + __SEQLOCK_UNLOCKED(old_style_seqlock_init) + +#define seqlock_init(x) \ + do { *(x) = (seqlock_t) __SEQLOCK_UNLOCKED(x); } while (0) + +#define DEFINE_SEQLOCK(x) \ + seqlock_t x = __SEQLOCK_UNLOCKED(x) /* Lock out other writers and update the count. * Acts like a normal spin_lock/unlock. diff --git a/include/linux/serialP.h b/include/linux/serialP.h index 2b9e6b9..e811a61 100644 --- a/include/linux/serialP.h +++ b/include/linux/serialP.h @@ -19,7 +19,6 @@ #define _LINUX_SERIALP_H * For definitions of the flags field, see tty.h */ -#include #include #include #include diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index bd14858..058cba7 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -67,8 +67,8 @@ #define PORT_DZ 47 /* Parisc type numbers. */ #define PORT_MUX 48 -/* Atmel AT91RM9200 SoC */ -#define PORT_AT91RM9200 49 +/* Atmel AT91xxx SoC */ +#define PORT_AT91 49 /* Macintosh Zilog type numbers */ #define PORT_MAC_ZILOG 50 /* m68k : not yet implemented */ @@ -130,9 +130,11 @@ #define PORT_NETX 71 /* SUN4V Hypervisor Console */ #define PORT_SUNHV 72 +#define PORT_S3C2412 73 + + #ifdef __KERNEL__ -#include #include #include #include @@ -214,10 +216,11 @@ struct uart_port { unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ - unsigned char fifosize; /* tx fifo size */ + unsigned int fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ unsigned char iotype; /* io access style */ + unsigned char unused1; #define UPIO_PORT (0) #define UPIO_HUB6 (1) @@ -334,7 +337,6 @@ struct uart_driver { struct module *owner; const char *driver_name; const char *dev_name; - const char *devfs_name; int major; int minor; int nr; diff --git a/include/linux/signal.h b/include/linux/signal.h index 70739f5..117135e 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -1,38 +1,12 @@ #ifndef _LINUX_SIGNAL_H #define _LINUX_SIGNAL_H -#include -#include #include #include #ifdef __KERNEL__ - -/* - * These values of sa_flags are used only by the kernel as part of the - * irq handling routines. - * - * SA_INTERRUPT is also used by the irq handling routines. - * SA_SHIRQ is for shared interrupt support on PCI and EISA. - * SA_PROBEIRQ is set by callers when they expect sharing mismatches to occur - */ -#define SA_SAMPLE_RANDOM SA_RESTART -#define SA_SHIRQ 0x04000000 -#define SA_PROBEIRQ 0x08000000 - -/* - * As above, these correspond to the IORESOURCE_IRQ_* defines in - * linux/ioport.h to select the interrupt line behaviour. When - * requesting an interrupt without specifying a SA_TRIGGER, the - * setting should be assumed to be "as already configured", which - * may be as per machine or firmware initialisation. - */ -#define SA_TRIGGER_LOW 0x00000008 -#define SA_TRIGGER_HIGH 0x00000004 -#define SA_TRIGGER_FALLING 0x00000002 -#define SA_TRIGGER_RISING 0x00000001 -#define SA_TRIGGER_MASK (SA_TRIGGER_HIGH|SA_TRIGGER_LOW|\ - SA_TRIGGER_RISING|SA_TRIGGER_FALLING) +#include +#include /* * Real Time signals may be queued. diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f8f2347..3597b4f 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -14,7 +14,6 @@ #ifndef _LINUX_SKBUFF_H #define _LINUX_SKBUFF_H -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include #include +#include #define HAVE_ALLOC_SKB /* For the drivers to know */ #define HAVE_ALIGNABLE_SKB /* Ditto 8) */ @@ -134,9 +134,10 @@ struct skb_frag_struct { struct skb_shared_info { atomic_t dataref; unsigned short nr_frags; - unsigned short tso_size; - unsigned short tso_segs; - unsigned short ufo_size; + unsigned short gso_size; + /* Warning: this field is not always filled in (UFO)! */ + unsigned short gso_segs; + unsigned short gso_type; unsigned int ip6_frag_id; struct sk_buff *frag_list; skb_frag_t frags[MAX_SKB_FRAGS]; @@ -168,6 +169,19 @@ enum { SKB_FCLONE_CLONE, }; +enum { + SKB_GSO_TCPV4 = 1 << 0, + SKB_GSO_UDP = 1 << 1, + + /* This indicates the skb is from an untrusted source. */ + SKB_GSO_DODGY = 1 << 2, + + /* This indicates the tcp segment has CWR set. */ + SKB_GSO_TCP_ECN = 1 << 3, + + SKB_GSO_TCPV6 = 1 << 4, +}; + /** * struct sk_buff - socket buffer * @next: Next buffer in list @@ -209,6 +223,9 @@ enum { * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c * @tc_index: Traffic control index * @tc_verd: traffic control verdict + * @dma_cookie: a cookie to one of several possible DMA operations + * done by skb DMA functions + * @secmark: security marking */ struct sk_buff { @@ -285,6 +302,12 @@ #ifdef CONFIG_NET_CLS_ACT __u16 tc_verd; /* traffic control verdict */ #endif #endif +#ifdef CONFIG_NET_DMA + dma_cookie_t dma_cookie; +#endif +#ifdef CONFIG_NETWORK_SECMARK + __u32 secmark; +#endif /* These elements must be at the end, see alloc_skb() for details. */ @@ -338,7 +361,7 @@ extern struct sk_buff *skb_realloc_headr extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom, int newtailroom, gfp_t priority); -extern struct sk_buff * skb_pad(struct sk_buff *skb, int pad); +extern int skb_pad(struct sk_buff *skb, int pad); #define dev_kfree_skb(a) kfree_skb(a) extern void skb_over_panic(struct sk_buff *skb, int len, void *here); @@ -581,9 +604,12 @@ static inline __u32 skb_queue_len(const return list_->qlen; } +extern struct lock_class_key skb_queue_lock_key; + static inline void skb_queue_head_init(struct sk_buff_head *list) { spin_lock_init(&list->lock); + lockdep_set_class(&list->lock, &skb_queue_lock_key); list->prev = list->next = (struct sk_buff *)list; list->qlen = 0; } @@ -967,15 +993,16 @@ #ifndef NET_SKB_PAD #define NET_SKB_PAD 16 #endif -extern int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc); +extern int ___pskb_trim(struct sk_buff *skb, unsigned int len); static inline void __skb_trim(struct sk_buff *skb, unsigned int len) { - if (!skb->data_len) { - skb->len = len; - skb->tail = skb->data + len; - } else - ___pskb_trim(skb, len, 0); + if (unlikely(skb->data_len)) { + WARN_ON(1); + return; + } + skb->len = len; + skb->tail = skb->data + len; } /** @@ -985,6 +1012,7 @@ static inline void __skb_trim(struct sk_ * * Cut the length of a buffer down by removing data from the tail. If * the buffer is already under the length specified it is not modified. + * The skb must be linear. */ static inline void skb_trim(struct sk_buff *skb, unsigned int len) { @@ -995,12 +1023,10 @@ static inline void skb_trim(struct sk_bu static inline int __pskb_trim(struct sk_buff *skb, unsigned int len) { - if (!skb->data_len) { - skb->len = len; - skb->tail = skb->data+len; - return 0; - } - return ___pskb_trim(skb, len, 1); + if (skb->data_len) + return ___pskb_trim(skb, len); + __skb_trim(skb, len); + return 0; } static inline int pskb_trim(struct sk_buff *skb, unsigned int len) @@ -1115,16 +1141,15 @@ static inline int skb_cow(struct sk_buff * * Pads up a buffer to ensure the trailing bytes exist and are * blanked. If the buffer already contains sufficient data it - * is untouched. Returns the buffer, which may be a replacement - * for the original, or NULL for out of memory - in which case - * the original buffer is still freed. + * is untouched. Otherwise it is extended. Returns zero on + * success. The skb is freed on error. */ -static inline struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len) +static inline int skb_padto(struct sk_buff *skb, unsigned int len) { unsigned int size = skb->len; if (likely(size >= len)) - return skb; + return 0; return skb_pad(skb, len-size); } @@ -1161,18 +1186,34 @@ static inline int skb_can_coalesce(struc return 0; } +static inline int __skb_linearize(struct sk_buff *skb) +{ + return __pskb_pull_tail(skb, skb->data_len) ? 0 : -ENOMEM; +} + /** * skb_linearize - convert paged skb to linear one * @skb: buffer to linarize - * @gfp: allocation mode * * If there is no free memory -ENOMEM is returned, otherwise zero * is returned and the old skb data released. */ -extern int __skb_linearize(struct sk_buff *skb, gfp_t gfp); -static inline int skb_linearize(struct sk_buff *skb, gfp_t gfp) +static inline int skb_linearize(struct sk_buff *skb) { - return __skb_linearize(skb, gfp); + return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0; +} + +/** + * skb_linearize_cow - make sure skb is linear and writable + * @skb: buffer to process + * + * If there is no free memory -ENOMEM is returned, otherwise zero + * is returned and the old skb data released. + */ +static inline int skb_linearize_cow(struct sk_buff *skb) +{ + return skb_is_nonlinear(skb) || skb_cloned(skb) ? + __skb_linearize(skb) : 0; } /** @@ -1268,7 +1309,7 @@ extern void skb_copy_and_csum_dev extern void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); -extern void skb_release_data(struct sk_buff *skb); +extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) @@ -1396,5 +1437,23 @@ #else /* CONFIG_NETFILTER */ static inline void nf_reset(struct sk_buff *skb) {} #endif /* CONFIG_NETFILTER */ +#ifdef CONFIG_NETWORK_SECMARK +static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) +{ + to->secmark = from->secmark; +} + +static inline void skb_init_secmark(struct sk_buff *skb) +{ + skb->secmark = 0; +} +#else +static inline void skb_copy_secmark(struct sk_buff *to, const struct sk_buff *from) +{ } + +static inline void skb_init_secmark(struct sk_buff *skb) +{ } +#endif + #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ diff --git a/include/linux/slab.h b/include/linux/slab.h index 2d985d5..45ad55b 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -11,7 +11,6 @@ #if defined(__KERNEL__) typedef struct kmem_cache kmem_cache_t; -#include /* kmalloc_sizes.h needs CONFIG_ options */ #include #include #include @@ -87,6 +86,51 @@ #define ____kmalloc(size, flags) \ __kmalloc_track_caller(size, flags, __builtin_return_address(0)) #endif +/** + * kmalloc - allocate memory + * @size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * kmalloc is the normal method of allocating memory + * in the kernel. + * + * The @flags argument may be one of: + * + * %GFP_USER - Allocate memory on behalf of user. May sleep. + * + * %GFP_KERNEL - Allocate normal kernel ram. May sleep. + * + * %GFP_ATOMIC - Allocation will not sleep. + * For example, use this inside interrupt handlers. + * + * %GFP_HIGHUSER - Allocate pages from high memory. + * + * %GFP_NOIO - Do not do any I/O at all while trying to get memory. + * + * %GFP_NOFS - Do not make any fs calls while trying to get memory. + * + * Also it is possible to set different flags by OR'ing + * in one or more of the following additional @flags: + * + * %__GFP_COLD - Request cache-cold pages instead of + * trying to return cache-warm pages. + * + * %__GFP_DMA - Request memory from the DMA-capable zone. + * + * %__GFP_HIGH - This allocation has high priority and may use emergency pools. + * + * %__GFP_HIGHMEM - Allocated memory may be from highmem. + * + * %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail + * (think twice before using). + * + * %__GFP_NORETRY - If memory is not immediately available, + * then give up at once. + * + * %__GFP_NOWARN - If allocation fails, don't issue any warnings. + * + * %__GFP_REPEAT - If allocation fails initially, try once more before failing. + */ static inline void *kmalloc(size_t size, gfp_t flags) { if (__builtin_constant_p(size)) { @@ -112,6 +156,11 @@ found: extern void *__kzalloc(size_t, gfp_t); +/** + * kzalloc - allocate memory. The memory is set to zero. + * @size: how many bytes of memory are required. + * @flags: the type of memory to allocate (see kmalloc). + */ static inline void *kzalloc(size_t size, gfp_t flags) { if (__builtin_constant_p(size)) { diff --git a/include/linux/smb_fs.h b/include/linux/smb_fs.h index 621a3d3..367d6c3 100644 --- a/include/linux/smb_fs.h +++ b/include/linux/smb_fs.h @@ -10,8 +10,6 @@ #ifndef _LINUX_SMB_FS_H #define _LINUX_SMB_FS_H #include -#include -#include /* * ioctl commands @@ -24,6 +22,8 @@ #define SMB_IOC_GETMOUNTUID32 _IOR('u', #ifdef __KERNEL__ +#include +#include #include #include diff --git a/include/linux/smp.h b/include/linux/smp.h index e2fa3ab..837e8bc 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -6,7 +6,6 @@ #define __LINUX_SMP_H * Alan Cox. */ -#include extern void cpu_idle(void); @@ -126,4 +125,6 @@ #define get_cpu() ({ preempt_disable(); #define put_cpu() preempt_enable() #define put_cpu_no_resched() preempt_enable_no_resched() +void smp_setup_processor_id(void); + #endif /* __LINUX_SMP_H */ diff --git a/include/linux/smp_lock.h b/include/linux/smp_lock.h index fa1ff3b..cf715a4 100644 --- a/include/linux/smp_lock.h +++ b/include/linux/smp_lock.h @@ -1,7 +1,6 @@ #ifndef __LINUX_SMPLOCK_H #define __LINUX_SMPLOCK_H -#include #ifdef CONFIG_LOCK_KERNEL #include #include diff --git a/include/linux/socket.h b/include/linux/socket.h index 9ab2ddd..3614090 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -18,8 +18,6 @@ struct __kernel_sockaddr_storage { #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2) -#include /* for CONFIG_COMPAT */ -#include #include /* arch-dependent defines */ #include /* the SIOCxxx I/O controls */ #include /* iovec support */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e928c0d..c8bb680 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -642,10 +642,14 @@ struct spi_board_info { u16 bus_num; u16 chip_select; + /* mode becomes spi_device.mode, and is essential for chips + * where the default of SPI_CS_HIGH = 0 is wrong. + */ + u8 mode; + /* ... may need additional spi_device chip config data here. * avoid stuff protocol drivers can set; but include stuff * needed to behave without being bound to a driver: - * - chipselect polarity * - quirks like clock rate mattering when not selected */ }; diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 799be67..31473db 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -46,7 +46,6 @@ #define __LINUX_SPINLOCK_H * linux/spinlock.h: builds the final spin_*() APIs. */ -#include #include #include #include @@ -83,14 +82,40 @@ extern int __lockfunc generic__raw_read_ /* * Pull the __raw*() functions/declarations (UP-nondebug doesnt need them): */ -#if defined(CONFIG_SMP) +#ifdef CONFIG_SMP # include #else # include #endif -#define spin_lock_init(lock) do { *(lock) = SPIN_LOCK_UNLOCKED; } while (0) -#define rwlock_init(lock) do { *(lock) = RW_LOCK_UNLOCKED; } while (0) +#ifdef CONFIG_DEBUG_SPINLOCK + extern void __spin_lock_init(spinlock_t *lock, const char *name, + struct lock_class_key *key); +# define spin_lock_init(lock) \ +do { \ + static struct lock_class_key __key; \ + \ + __spin_lock_init((lock), #lock, &__key); \ +} while (0) + +#else +# define spin_lock_init(lock) \ + do { *(lock) = SPIN_LOCK_UNLOCKED; } while (0) +#endif + +#ifdef CONFIG_DEBUG_SPINLOCK + extern void __rwlock_init(rwlock_t *lock, const char *name, + struct lock_class_key *key); +# define rwlock_init(lock) \ +do { \ + static struct lock_class_key __key; \ + \ + __rwlock_init((lock), #lock, &__key); \ +} while (0) +#else +# define rwlock_init(lock) \ + do { *(lock) = RW_LOCK_UNLOCKED; } while (0) +#endif #define spin_is_locked(lock) __raw_spin_is_locked(&(lock)->raw_lock) @@ -114,7 +139,6 @@ #ifdef CONFIG_DEBUG_SPINLOCK #define _raw_spin_lock_flags(lock, flags) _raw_spin_lock(lock) extern int _raw_spin_trylock(spinlock_t *lock); extern void _raw_spin_unlock(spinlock_t *lock); - extern void _raw_read_lock(rwlock_t *lock); extern int _raw_read_trylock(rwlock_t *lock); extern void _raw_read_unlock(rwlock_t *lock); @@ -122,17 +146,17 @@ #define _raw_spin_lock_flags(lock, flags extern int _raw_write_trylock(rwlock_t *lock); extern void _raw_write_unlock(rwlock_t *lock); #else -# define _raw_spin_unlock(lock) __raw_spin_unlock(&(lock)->raw_lock) -# define _raw_spin_trylock(lock) __raw_spin_trylock(&(lock)->raw_lock) # define _raw_spin_lock(lock) __raw_spin_lock(&(lock)->raw_lock) # define _raw_spin_lock_flags(lock, flags) \ __raw_spin_lock_flags(&(lock)->raw_lock, *(flags)) +# define _raw_spin_trylock(lock) __raw_spin_trylock(&(lock)->raw_lock) +# define _raw_spin_unlock(lock) __raw_spin_unlock(&(lock)->raw_lock) # define _raw_read_lock(rwlock) __raw_read_lock(&(rwlock)->raw_lock) -# define _raw_write_lock(rwlock) __raw_write_lock(&(rwlock)->raw_lock) -# define _raw_read_unlock(rwlock) __raw_read_unlock(&(rwlock)->raw_lock) -# define _raw_write_unlock(rwlock) __raw_write_unlock(&(rwlock)->raw_lock) # define _raw_read_trylock(rwlock) __raw_read_trylock(&(rwlock)->raw_lock) +# define _raw_read_unlock(rwlock) __raw_read_unlock(&(rwlock)->raw_lock) +# define _raw_write_lock(rwlock) __raw_write_lock(&(rwlock)->raw_lock) # define _raw_write_trylock(rwlock) __raw_write_trylock(&(rwlock)->raw_lock) +# define _raw_write_unlock(rwlock) __raw_write_unlock(&(rwlock)->raw_lock) #endif #define read_can_lock(rwlock) __raw_read_can_lock(&(rwlock)->raw_lock) @@ -148,6 +172,13 @@ #define read_trylock(lock) __cond_lock( #define write_trylock(lock) __cond_lock(_write_trylock(lock)) #define spin_lock(lock) _spin_lock(lock) + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define spin_lock_nested(lock, subclass) _spin_lock_nested(lock, subclass) +#else +# define spin_lock_nested(lock, subclass) _spin_lock(lock) +#endif + #define write_lock(lock) _write_lock(lock) #define read_lock(lock) _read_lock(lock) @@ -173,21 +204,18 @@ #define write_lock_bh(lock) _write_lock /* * We inline the unlock functions in the nondebug case: */ -#if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || !defined(CONFIG_SMP) +#if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || \ + !defined(CONFIG_SMP) # define spin_unlock(lock) _spin_unlock(lock) # define read_unlock(lock) _read_unlock(lock) # define write_unlock(lock) _write_unlock(lock) -#else -# define spin_unlock(lock) __raw_spin_unlock(&(lock)->raw_lock) -# define read_unlock(lock) __raw_read_unlock(&(lock)->raw_lock) -# define write_unlock(lock) __raw_write_unlock(&(lock)->raw_lock) -#endif - -#if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || !defined(CONFIG_SMP) # define spin_unlock_irq(lock) _spin_unlock_irq(lock) # define read_unlock_irq(lock) _read_unlock_irq(lock) # define write_unlock_irq(lock) _write_unlock_irq(lock) #else +# define spin_unlock(lock) __raw_spin_unlock(&(lock)->raw_lock) +# define read_unlock(lock) __raw_read_unlock(&(lock)->raw_lock) +# define write_unlock(lock) __raw_write_unlock(&(lock)->raw_lock) # define spin_unlock_irq(lock) \ do { __raw_spin_unlock(&(lock)->raw_lock); local_irq_enable(); } while (0) # define read_unlock_irq(lock) \ diff --git a/include/linux/spinlock_api_smp.h b/include/linux/spinlock_api_smp.h index 78e6989..b2c4f82 100644 --- a/include/linux/spinlock_api_smp.h +++ b/include/linux/spinlock_api_smp.h @@ -20,6 +20,8 @@ int in_lock_functions(unsigned long addr #define assert_spin_locked(x) BUG_ON(!spin_is_locked(x)) void __lockfunc _spin_lock(spinlock_t *lock) __acquires(spinlock_t); +void __lockfunc _spin_lock_nested(spinlock_t *lock, int subclass) + __acquires(spinlock_t); void __lockfunc _read_lock(rwlock_t *lock) __acquires(rwlock_t); void __lockfunc _write_lock(rwlock_t *lock) __acquires(rwlock_t); void __lockfunc _spin_lock_bh(spinlock_t *lock) __acquires(spinlock_t); diff --git a/include/linux/spinlock_api_up.h b/include/linux/spinlock_api_up.h index cd81cee..67faa04 100644 --- a/include/linux/spinlock_api_up.h +++ b/include/linux/spinlock_api_up.h @@ -49,6 +49,7 @@ #define __UNLOCK_IRQRESTORE(lock, flags) do { local_irq_restore(flags); __UNLOCK(lock); } while (0) #define _spin_lock(lock) __LOCK(lock) +#define _spin_lock_nested(lock, subclass) __LOCK(lock) #define _read_lock(lock) __LOCK(lock) #define _write_lock(lock) __LOCK(lock) #define _spin_lock_bh(lock) __LOCK_BH(lock) diff --git a/include/linux/spinlock_types.h b/include/linux/spinlock_types.h index 9cb51e0..dc5fb69 100644 --- a/include/linux/spinlock_types.h +++ b/include/linux/spinlock_types.h @@ -9,6 +9,8 @@ #define __LINUX_SPINLOCK_TYPES_H * Released under the General Public License (GPL). */ +#include + #if defined(CONFIG_SMP) # include #else @@ -24,6 +26,9 @@ #ifdef CONFIG_DEBUG_SPINLOCK unsigned int magic, owner_cpu; void *owner; #endif +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif } spinlock_t; #define SPINLOCK_MAGIC 0xdead4ead @@ -37,31 +42,53 @@ #ifdef CONFIG_DEBUG_SPINLOCK unsigned int magic, owner_cpu; void *owner; #endif +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif } rwlock_t; #define RWLOCK_MAGIC 0xdeaf1eed #define SPINLOCK_OWNER_INIT ((void *)-1L) +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define SPIN_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } +#else +# define SPIN_DEP_MAP_INIT(lockname) +#endif + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define RW_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname } +#else +# define RW_DEP_MAP_INIT(lockname) +#endif + #ifdef CONFIG_DEBUG_SPINLOCK -# define SPIN_LOCK_UNLOCKED \ +# define __SPIN_LOCK_UNLOCKED(lockname) \ (spinlock_t) { .raw_lock = __RAW_SPIN_LOCK_UNLOCKED, \ .magic = SPINLOCK_MAGIC, \ .owner = SPINLOCK_OWNER_INIT, \ - .owner_cpu = -1 } -#define RW_LOCK_UNLOCKED \ + .owner_cpu = -1, \ + SPIN_DEP_MAP_INIT(lockname) } +#define __RW_LOCK_UNLOCKED(lockname) \ (rwlock_t) { .raw_lock = __RAW_RW_LOCK_UNLOCKED, \ .magic = RWLOCK_MAGIC, \ .owner = SPINLOCK_OWNER_INIT, \ - .owner_cpu = -1 } + .owner_cpu = -1, \ + RW_DEP_MAP_INIT(lockname) } #else -# define SPIN_LOCK_UNLOCKED \ - (spinlock_t) { .raw_lock = __RAW_SPIN_LOCK_UNLOCKED } -#define RW_LOCK_UNLOCKED \ - (rwlock_t) { .raw_lock = __RAW_RW_LOCK_UNLOCKED } +# define __SPIN_LOCK_UNLOCKED(lockname) \ + (spinlock_t) { .raw_lock = __RAW_SPIN_LOCK_UNLOCKED, \ + SPIN_DEP_MAP_INIT(lockname) } +#define __RW_LOCK_UNLOCKED(lockname) \ + (rwlock_t) { .raw_lock = __RAW_RW_LOCK_UNLOCKED, \ + RW_DEP_MAP_INIT(lockname) } #endif -#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED -#define DEFINE_RWLOCK(x) rwlock_t x = RW_LOCK_UNLOCKED +#define SPIN_LOCK_UNLOCKED __SPIN_LOCK_UNLOCKED(old_style_spin_init) +#define RW_LOCK_UNLOCKED __RW_LOCK_UNLOCKED(old_style_rw_init) + +#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) +#define DEFINE_RWLOCK(x) rwlock_t x = __RW_LOCK_UNLOCKED(x) #endif /* __LINUX_SPINLOCK_TYPES_H */ diff --git a/include/linux/spinlock_types_up.h b/include/linux/spinlock_types_up.h index 04135b0..27644af 100644 --- a/include/linux/spinlock_types_up.h +++ b/include/linux/spinlock_types_up.h @@ -12,10 +12,14 @@ #endif * Released under the General Public License (GPL). */ -#ifdef CONFIG_DEBUG_SPINLOCK +#if defined(CONFIG_DEBUG_SPINLOCK) || \ + defined(CONFIG_DEBUG_LOCK_ALLOC) typedef struct { volatile unsigned int slock; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif } raw_spinlock_t; #define __RAW_SPIN_LOCK_UNLOCKED { 1 } @@ -30,6 +34,9 @@ #endif typedef struct { /* no debug version on UP */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif } raw_rwlock_t; #define __RAW_RW_LOCK_UNLOCKED { } diff --git a/include/linux/spinlock_up.h b/include/linux/spinlock_up.h index 31accf2..ea54c4c 100644 --- a/include/linux/spinlock_up.h +++ b/include/linux/spinlock_up.h @@ -18,7 +18,6 @@ #endif */ #ifdef CONFIG_DEBUG_SPINLOCK - #define __raw_spin_is_locked(x) ((x)->slock == 0) static inline void __raw_spin_lock(raw_spinlock_t *lock) diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h new file mode 100644 index 0000000..9cc81e5 --- /dev/null +++ b/include/linux/stacktrace.h @@ -0,0 +1,20 @@ +#ifndef __LINUX_STACKTRACE_H +#define __LINUX_STACKTRACE_H + +#ifdef CONFIG_STACKTRACE +struct stack_trace { + unsigned int nr_entries, max_entries; + unsigned long *entries; +}; + +extern void save_stack_trace(struct stack_trace *trace, + struct task_struct *task, int all_contexts, + unsigned int skip); + +extern void print_stack_trace(struct stack_trace *trace, int spaces); +#else +# define save_stack_trace(trace, task, all, skip) do { } while (0) +# define print_stack_trace(trace) do { } while (0) +#endif + +#endif diff --git a/include/linux/stop_machine.h b/include/linux/stop_machine.h index 151a803..5bfc553 100644 --- a/include/linux/stop_machine.h +++ b/include/linux/stop_machine.h @@ -4,7 +4,6 @@ #define _LINUX_STOP_MACHINE very heavy lock, which is equivalent to grabbing every spinlock (and more). So the "read" side to such a lock is anything which diables preeempt. */ -#include #include #include diff --git a/include/linux/string.h b/include/linux/string.h index c61306d..e4c7558 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -56,6 +56,7 @@ #endif #ifndef __HAVE_ARCH_STRRCHR extern char * strrchr(const char *,int); #endif +extern char * strstrip(char *); #ifndef __HAVE_ARCH_STRSTR extern char * strstr(const char *,const char *); #endif diff --git a/include/linux/sunrpc/Kbuild b/include/linux/sunrpc/Kbuild new file mode 100644 index 0000000..0d1d768 --- /dev/null +++ b/include/linux/sunrpc/Kbuild @@ -0,0 +1 @@ +unifdef-y := debug.h diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index be4772e..a6de332 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -11,7 +11,6 @@ #define _LINUX_SUNRPC_AUTH_H #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h index 1a42d90..e4729aa 100644 --- a/include/linux/sunrpc/debug.h +++ b/include/linux/sunrpc/debug.h @@ -9,19 +9,6 @@ #ifndef _LINUX_SUNRPC_DEBUG_H_ #define _LINUX_SUNRPC_DEBUG_H_ -#include - -#include -#include - -/* - * Enable RPC debugging/profiling. - */ -#ifdef CONFIG_SYSCTL -#define RPC_DEBUG -#endif -/* #define RPC_PROFILE */ - /* * RPC debug facilities */ @@ -41,6 +28,17 @@ #define RPCDBG_ALL 0x7fff #ifdef __KERNEL__ +#include +#include + +/* + * Enable RPC debugging/profiling. + */ +#ifdef CONFIG_SYSCTL +#define RPC_DEBUG +#endif +/* #define RPC_PROFILE */ + /* * Debugging macros etc */ diff --git a/include/linux/sunrpc/gss_api.h b/include/linux/sunrpc/gss_api.h index 9b8bcf1..6e112cc 100644 --- a/include/linux/sunrpc/gss_api.h +++ b/include/linux/sunrpc/gss_api.h @@ -126,7 +126,7 @@ struct gss_api_mech *gss_mech_get_by_pse /* Just increments the mechanism's reference count and returns its input: */ struct gss_api_mech * gss_mech_get(struct gss_api_mech *); -/* For every succesful gss_mech_get or gss_mech_get_by_* call there must be a +/* For every successful gss_mech_get or gss_mech_get_by_* call there must be a * corresponding call to gss_mech_put. */ void gss_mech_put(struct gss_api_mech *); diff --git a/include/linux/sunrpc/stats.h b/include/linux/sunrpc/stats.h index d93c24b..5fa0f20 100644 --- a/include/linux/sunrpc/stats.h +++ b/include/linux/sunrpc/stats.h @@ -9,7 +9,6 @@ #ifndef _LINUX_SUNRPC_STATS_H #define _LINUX_SUNRPC_STATS_H -#include #include struct rpc_stat { diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 5035643..7b27c09 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -159,7 +159,9 @@ struct svc_rqst { * determine what device number * to report (real or virtual) */ - + int rq_sendfile_ok; /* turned off in gss privacy + * to prevent encrypting page + * cache pages */ wait_queue_head_t rq_wait; /* synchronization */ }; diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h index 84c35d4..e6d3d34 100644 --- a/include/linux/sunrpc/xdr.h +++ b/include/linux/sunrpc/xdr.h @@ -194,6 +194,7 @@ extern void xdr_write_pages(struct xdr_s extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, uint32_t *p); extern uint32_t *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); +extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); #endif /* __KERNEL__ */ diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 37c1c76..96e31aa 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -6,7 +6,6 @@ #include #endif #include #include -#include #include #include diff --git a/include/linux/swap.h b/include/linux/swap.h index f03c247..5e59184 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -1,7 +1,6 @@ #ifndef _LINUX_SWAP_H #define _LINUX_SWAP_H -#include #include #include #include @@ -29,7 +28,14 @@ static inline int current_is_kswapd(void * the type/offset into the pte as 5/27 as well. */ #define MAX_SWAPFILES_SHIFT 5 +#ifndef CONFIG_MIGRATION #define MAX_SWAPFILES (1 << MAX_SWAPFILES_SHIFT) +#else +/* Use last two entries for page migration swap entries */ +#define MAX_SWAPFILES ((1 << MAX_SWAPFILES_SHIFT)-2) +#define SWP_MIGRATION_READ MAX_SWAPFILES +#define SWP_MIGRATION_WRITE (MAX_SWAPFILES + 1) +#endif /* * Magic header for a swap area. The first part of the union is @@ -49,12 +55,14 @@ union swap_header { char magic[10]; /* SWAP-SPACE or SWAPSPACE2 */ } magic; struct { - char bootbits[1024]; /* Space for disklabel etc. */ - unsigned int version; - unsigned int last_page; - unsigned int nr_badpages; - unsigned int padding[125]; - unsigned int badpages[1]; + char bootbits[1024]; /* Space for disklabel etc. */ + __u32 version; + __u32 last_page; + __u32 nr_badpages; + unsigned char sws_uuid[16]; + unsigned char sws_volume[16]; + __u32 padding[117]; + __u32 badpages[1]; } info; }; @@ -177,24 +185,11 @@ extern unsigned long try_to_free_pages(s extern unsigned long shrink_all_memory(unsigned long nr_pages); extern int vm_swappiness; extern int remove_mapping(struct address_space *mapping, struct page *page); - -/* possible outcome of pageout() */ -typedef enum { - /* failed to write page out, page is locked */ - PAGE_KEEP, - /* move page to the active list, page is locked */ - PAGE_ACTIVATE, - /* page has been sent to the disk successfully, page is unlocked */ - PAGE_SUCCESS, - /* page is clean and locked */ - PAGE_CLEAN, -} pageout_t; - -extern pageout_t pageout(struct page *page, struct address_space *mapping); +extern long vm_total_pages; #ifdef CONFIG_NUMA extern int zone_reclaim_mode; -extern int zone_reclaim_interval; +extern int sysctl_min_unmapped_ratio; extern int zone_reclaim(struct zone *, gfp_t, unsigned int); #else #define zone_reclaim_mode 0 @@ -204,6 +199,8 @@ static inline int zone_reclaim(struct zo } #endif +extern int kswapd_run(int nid); + #ifdef CONFIG_MMU /* linux/mm/shmem.c */ extern int shmem_unuse(swp_entry_t entry, struct page *page); @@ -251,7 +248,6 @@ extern int remove_exclusive_swap_page(st struct backing_dev_info; extern spinlock_t swap_lock; -extern int remove_vma_swap(struct vm_area_struct *vma, struct page *page); /* linux/mm/thrash.c */ extern struct mm_struct * swap_token_mm; @@ -289,18 +285,60 @@ #define free_page_and_swap_cache(page) \ #define free_pages_and_swap_cache(pages, nr) \ release_pages((pages), (nr), 0); -#define show_swap_cache_info() /*NOTHING*/ -#define free_swap_and_cache(swp) /*NOTHING*/ -#define swap_duplicate(swp) /*NOTHING*/ -#define swap_free(swp) /*NOTHING*/ -#define read_swap_cache_async(swp,vma,addr) NULL -#define lookup_swap_cache(swp) NULL -#define valid_swaphandles(swp, off) 0 +static inline void show_swap_cache_info(void) +{ +} + +static inline void free_swap_and_cache(swp_entry_t swp) +{ +} + +static inline int swap_duplicate(swp_entry_t swp) +{ + return 0; +} + +static inline void swap_free(swp_entry_t swp) +{ +} + +static inline struct page *read_swap_cache_async(swp_entry_t swp, + struct vm_area_struct *vma, unsigned long addr) +{ + return NULL; +} + +static inline struct page *lookup_swap_cache(swp_entry_t swp) +{ + return NULL; +} + +static inline int valid_swaphandles(swp_entry_t entry, unsigned long *offset) +{ + return 0; +} + #define can_share_swap_page(p) (page_mapcount(p) == 1) -#define move_to_swap_cache(p, swp) 1 -#define move_from_swap_cache(p, i, m) 1 -#define __delete_from_swap_cache(p) /*NOTHING*/ -#define delete_from_swap_cache(p) /*NOTHING*/ + +static inline int move_to_swap_cache(struct page *page, swp_entry_t entry) +{ + return 1; +} + +static inline int move_from_swap_cache(struct page *page, unsigned long index, + struct address_space *mapping) +{ + return 1; +} + +static inline void __delete_from_swap_cache(struct page *page) +{ +} + +static inline void delete_from_swap_cache(struct page *page) +{ +} + #define swap_token_default_timeout 0 static inline int remove_exclusive_swap_page(struct page *p) diff --git a/include/linux/swapops.h b/include/linux/swapops.h index 87b9d14..ec639aa 100644 --- a/include/linux/swapops.h +++ b/include/linux/swapops.h @@ -67,3 +67,56 @@ static inline pte_t swp_entry_to_pte(swp BUG_ON(pte_file(__swp_entry_to_pte(arch_entry))); return __swp_entry_to_pte(arch_entry); } + +#ifdef CONFIG_MIGRATION +static inline swp_entry_t make_migration_entry(struct page *page, int write) +{ + BUG_ON(!PageLocked(page)); + return swp_entry(write ? SWP_MIGRATION_WRITE : SWP_MIGRATION_READ, + page_to_pfn(page)); +} + +static inline int is_migration_entry(swp_entry_t entry) +{ + return unlikely(swp_type(entry) == SWP_MIGRATION_READ || + swp_type(entry) == SWP_MIGRATION_WRITE); +} + +static inline int is_write_migration_entry(swp_entry_t entry) +{ + return unlikely(swp_type(entry) == SWP_MIGRATION_WRITE); +} + +static inline struct page *migration_entry_to_page(swp_entry_t entry) +{ + struct page *p = pfn_to_page(swp_offset(entry)); + /* + * Any use of migration entries may only occur while the + * corresponding page is locked + */ + BUG_ON(!PageLocked(p)); + return p; +} + +static inline void make_migration_entry_read(swp_entry_t *entry) +{ + *entry = swp_entry(SWP_MIGRATION_READ, swp_offset(*entry)); +} + +extern void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, + unsigned long address); +#else + +#define make_migration_entry(page, write) swp_entry(0, 0) +#define is_migration_entry(swp) 0 +#define migration_entry_to_page(swp) NULL +static inline void make_migration_entry_read(swp_entry_t *entryp) { } +static inline void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, + unsigned long address) { } +static inline int is_write_migration_entry(swp_entry_t entry) +{ + return 0; +} + +#endif + diff --git a/include/linux/synclink.h b/include/linux/synclink.h index 2993302..0577f52 100644 --- a/include/linux/synclink.h +++ b/include/linux/synclink.h @@ -1,7 +1,7 @@ /* * SyncLink Multiprotocol Serial Adapter Driver * - * $Id: synclink.h,v 3.11 2006/02/06 21:20:29 paulkf Exp $ + * $Id: synclink.h,v 3.13 2006/05/23 18:25:06 paulkf Exp $ * * Copyright (C) 1998-2000 by Microgate Corporation * @@ -97,6 +97,8 @@ #define HDLC_TXIDLE_ONES 3 #define HDLC_TXIDLE_ALT_MARK_SPACE 4 #define HDLC_TXIDLE_SPACE 5 #define HDLC_TXIDLE_MARK 6 +#define HDLC_TXIDLE_CUSTOM_8 0x10000000 +#define HDLC_TXIDLE_CUSTOM_16 0x20000000 #define HDLC_ENCODING_NRZ 0 #define HDLC_ENCODING_NRZB 1 @@ -170,6 +172,7 @@ #define SYNCLINK_SCA_DEVICE_ID 0x0030 #define SYNCLINK_GT_DEVICE_ID 0x0070 #define SYNCLINK_GT4_DEVICE_ID 0x0080 #define SYNCLINK_AC_DEVICE_ID 0x0090 +#define SYNCLINK_GT2_DEVICE_ID 0x00A0 #define MGSL_MAX_SERIAL_NUMBER 30 /* diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 60d49e5..008f04c 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -54,7 +54,6 @@ struct compat_stat; struct compat_timeval; struct robust_list_head; -#include #include #include #include @@ -175,9 +174,9 @@ asmlinkage long sys_waitid(int which, pi int options, struct rusage __user *ru); asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options); asmlinkage long sys_set_tid_address(int __user *tidptr); -asmlinkage long sys_futex(u32 __user *uaddr, int op, int val, +asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, struct timespec __user *utime, u32 __user *uaddr2, - int val3); + u32 val3); asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs); @@ -517,6 +516,16 @@ asmlinkage long sys_set_mempolicy(int mo asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode, const unsigned long __user *from, const unsigned long __user *to); +asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages, + const void __user * __user *pages, + const int __user *nodes, + int __user *status, + int flags); +asmlinkage long compat_sys_move_pages(pid_t pid, unsigned long nr_page, + __u32 __user *pages, + const int __user *nodes, + int __user *status, + int flags); asmlinkage long sys_mbind(unsigned long start, unsigned long len, unsigned long mode, unsigned long __user *nmask, diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 76eaeff..e4b1a4d 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -55,7 +55,7 @@ enum CTL_KERN=1, /* General kernel info and control */ CTL_VM=2, /* VM management */ CTL_NET=3, /* Networking */ - CTL_PROC=4, /* Process info */ + /* was CTL_PROC */ CTL_FS=5, /* Filesystems */ CTL_DEBUG=6, /* Debugging */ CTL_DEV=7, /* Devices */ @@ -148,9 +148,12 @@ enum KERN_SPIN_RETRY=70, /* int: number of spinlock retries */ KERN_ACPI_VIDEO_FLAGS=71, /* int: flags for setting up video after ACPI sleep */ KERN_IA64_UNALIGNED=72, /* int: ia64 unaligned userland trap enable */ + KERN_COMPAT_LOG=73, /* int: print compat layer messages */ + KERN_MAX_LOCK_DEPTH=74, }; + /* CTL_VM names: */ enum { @@ -185,7 +188,9 @@ enum VM_DROP_PAGECACHE=29, /* int: nuke lots of pagecache */ VM_PERCPU_PAGELIST_FRACTION=30,/* int: fraction of pages in each percpu_pagelist */ VM_ZONE_RECLAIM_MODE=31, /* reclaim local zone memory before going off node */ - VM_ZONE_RECLAIM_INTERVAL=32, /* time period to wait after reclaim failure */ + VM_MIN_UNMAPPED=32, /* Set min percent of unmapped pages */ + VM_PANIC_ON_OOM=33, /* panic at out-of-memory */ + VM_VDSO_ENABLED=34, /* map VDSO into new processes? */ }; @@ -313,6 +318,7 @@ enum NET_NF_CONNTRACK_FRAG6_TIMEOUT=29, NET_NF_CONNTRACK_FRAG6_LOW_THRESH=30, NET_NF_CONNTRACK_FRAG6_HIGH_THRESH=31, + NET_NF_CONNTRACK_CHECKSUM=32, }; /* /proc/sys/net/ipv4 */ @@ -403,6 +409,8 @@ enum NET_TCP_MTU_PROBING=113, NET_TCP_BASE_MSS=114, NET_IPV4_TCP_WORKAROUND_SIGNED_WINDOWS=115, + NET_TCP_DMA_COPYBREAK=116, + NET_TCP_SLOW_START_AFTER_IDLE=117, }; enum { @@ -491,6 +499,7 @@ enum NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_RECD=25, NET_IPV4_NF_CONNTRACK_SCTP_TIMEOUT_SHUTDOWN_ACK_SENT=26, NET_IPV4_NF_CONNTRACK_COUNT=27, + NET_IPV4_NF_CONNTRACK_CHECKSUM=28, }; /* /proc/sys/net/ipv6 */ @@ -762,8 +771,6 @@ enum { NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4, }; -/* CTL_PROC names: */ - /* CTL_FS names: */ enum { diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index 2a4b432..166a2e5 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -37,11 +37,27 @@ struct sysdev_class { struct kset kset; }; +struct sysdev_class_attribute { + struct attribute attr; + ssize_t (*show)(struct sysdev_class *, char *); + ssize_t (*store)(struct sysdev_class *, const char *, size_t); +}; + +#define SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ +struct sysdev_class_attribute attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +}; + extern int sysdev_class_register(struct sysdev_class *); extern void sysdev_class_unregister(struct sysdev_class *); - +extern int sysdev_class_create_file(struct sysdev_class *, + struct sysdev_class_attribute *); +extern void sysdev_class_remove_file(struct sysdev_class *, + struct sysdev_class_attribute *); /** * Auxillary system device drivers. */ diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h index ea819b8..4812ff6 100644 --- a/include/linux/sysrq.h +++ b/include/linux/sysrq.h @@ -11,7 +11,6 @@ * based upon discusions in irc://irc.openprojects.net/#kernelnewbies */ -#include struct pt_regs; struct tty_struct; diff --git a/include/linux/tc_act/Kbuild b/include/linux/tc_act/Kbuild new file mode 100644 index 0000000..5251a50 --- /dev/null +++ b/include/linux/tc_act/Kbuild @@ -0,0 +1 @@ +header-y += tc_gact.h tc_ipt.h tc_mirred.h tc_pedit.h diff --git a/include/linux/tc_ematch/Kbuild b/include/linux/tc_ematch/Kbuild new file mode 100644 index 0000000..381e930 --- /dev/null +++ b/include/linux/tc_ematch/Kbuild @@ -0,0 +1 @@ +headers-y := tc_em_cmp.h tc_em_meta.h tc_em_nbyte.h tc_em_text.h diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 542d395..8ebf497 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -159,8 +159,8 @@ struct tcp_info #ifdef __KERNEL__ -#include #include +#include #include #include #include @@ -233,6 +233,13 @@ struct tcp_sock { struct iovec *iov; int memory; int len; +#ifdef CONFIG_NET_DMA + /* members for async copy */ + struct dma_chan *dma_chan; + int wakeup; + struct dma_pinned_list *pinned_list; + dma_cookie_t dma_cookie; +#endif } ucopy; __u32 snd_wl1; /* Sequence for window update */ diff --git a/include/linux/threads.h b/include/linux/threads.h index e646bcd..38d1a5d 100644 --- a/include/linux/threads.h +++ b/include/linux/threads.h @@ -1,7 +1,6 @@ #ifndef _LINUX_THREADS_H #define _LINUX_THREADS_H -#include /* * The default limit for the nr of threads is now in diff --git a/include/linux/time.h b/include/linux/time.h index 0cd696c..c05f8bb 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -28,10 +28,13 @@ struct timezone { #ifdef __KERNEL__ /* Parameters used to convert the timespec values: */ -#define MSEC_PER_SEC 1000L -#define USEC_PER_SEC 1000000L -#define NSEC_PER_SEC 1000000000L -#define NSEC_PER_USEC 1000L +#define MSEC_PER_SEC 1000L +#define USEC_PER_MSEC 1000L +#define NSEC_PER_USEC 1000L +#define NSEC_PER_MSEC 1000000L +#define USEC_PER_SEC 1000000L +#define NSEC_PER_SEC 1000000000L +#define FSEC_PER_SEC 1000000000000000L static inline int timespec_equal(struct timespec *a, struct timespec *b) { @@ -77,6 +80,8 @@ extern struct timespec xtime; extern struct timespec wall_to_monotonic; extern seqlock_t xtime_lock; +void timekeeping_init(void); + static inline unsigned long get_seconds(void) { return xtime.tv_sec; @@ -100,6 +105,7 @@ extern int do_getitimer(int which, struc extern void getnstimeofday(struct timespec *tv); extern struct timespec timespec_trunc(struct timespec t, unsigned gran); +extern int timekeeping_is_continuous(void); /** * timespec_to_ns - Convert timespec to nanoseconds @@ -142,6 +148,20 @@ extern struct timespec ns_to_timespec(co */ extern struct timeval ns_to_timeval(const s64 nsec); +/** + * timespec_add_ns - Adds nanoseconds to a timespec + * @a: pointer to timespec to be incremented + * @ns: unsigned nanoseconds value to be added + */ +static inline void timespec_add_ns(struct timespec *a, u64 ns) +{ + ns += a->tv_nsec; + while(unlikely(ns >= NSEC_PER_SEC)) { + ns -= NSEC_PER_SEC; + a->tv_sec++; + } + a->tv_nsec = ns; +} #endif /* __KERNEL__ */ #define NFDBITS __NFDBITS diff --git a/include/linux/timer.h b/include/linux/timer.h index 0a485be..c982304 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -1,7 +1,6 @@ #ifndef _LINUX_TIMER_H #define _LINUX_TIMER_H -#include #include #include #include diff --git a/include/linux/timex.h b/include/linux/timex.h index 03914b7..19bb653 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -53,7 +53,6 @@ #ifndef _LINUX_TIMEX_H #define _LINUX_TIMEX_H -#include #include #include @@ -304,6 +303,8 @@ time_interpolator_reset(void) #endif /* !CONFIG_TIME_INTERPOLATION */ +#define TICK_LENGTH_SHIFT 32 + /* Returns how long ticks are at present, in ns / 2^(SHIFT_SCALE-10). */ extern u64 current_tick_length(void); diff --git a/include/linux/topology.h b/include/linux/topology.h index a305ae2..ec1eca8 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -134,7 +134,8 @@ #define SD_CPU_INIT (struct sched_domain .flags = SD_LOAD_BALANCE \ | SD_BALANCE_NEWIDLE \ | SD_BALANCE_EXEC \ - | SD_WAKE_AFFINE, \ + | SD_WAKE_AFFINE \ + | BALANCE_FOR_POWER, \ .last_balance = jiffies, \ .balance_interval = 1, \ .nr_balance_failed = 0, \ diff --git a/include/linux/tty.h b/include/linux/tty.h index f13f49a..b3b807e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -16,7 +16,6 @@ #define MAX_NR_USER_CONSOLES 63 /* must consoles 16 and higher (since it returns a short) */ #ifdef __KERNEL__ -#include #include #include #include @@ -58,7 +57,6 @@ struct tty_buffer { unsigned char *flag_buf_ptr; int used; int size; - int active; int commit; int read; /* Data points here */ @@ -260,7 +258,6 @@ #define TTY_DEBUG 4 /* Debugging */ #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ #define TTY_PUSH 6 /* n_tty private */ #define TTY_CLOSING 7 /* ->close() in progress */ -#define TTY_DONT_FLIP 8 /* Defer buffer flip */ #define TTY_LDISC 9 /* Line discipline attached */ #define TTY_HW_COOK_OUT 14 /* Hardware can do output cooking */ #define TTY_HW_COOK_IN 15 /* Hardware can do input cooking */ @@ -291,7 +288,9 @@ extern int tty_register_ldisc(int disc, extern int tty_unregister_ldisc(int disc); extern int tty_register_driver(struct tty_driver *driver); extern int tty_unregister_driver(struct tty_driver *driver); -extern void tty_register_device(struct tty_driver *driver, unsigned index, struct device *dev); +extern struct class_device *tty_register_device(struct tty_driver *driver, + unsigned index, + struct device *dev); extern void tty_unregister_device(struct tty_driver *driver, unsigned index); extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp, int buflen); diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index b368b29..58c961c 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -157,7 +157,6 @@ struct tty_driver { struct cdev cdev; struct module *owner; const char *driver_name; - const char *devfs_name; const char *name; int name_base; /* offset of printed name */ int major; /* major device number */ @@ -242,8 +241,15 @@ #define TTY_DRIVER_MAGIC 0x5402 * is also a promise, if the above case is true, not to signal * overruns, either.) * - * TTY_DRIVER_NO_DEVFS --- if set, do not create devfs entries. This - * is only used by tty_register_driver(). + * TTY_DRIVER_DYNAMIC_DEV --- if set, the individual tty devices need + * to be registered with a call to tty_register_driver() when the + * device is found in the system and unregistered with a call to + * tty_unregister_device() so the devices will be show up + * properly in sysfs. If not set, driver->num entries will be + * created by the tty core in sysfs when tty_register_driver() is + * called. This is to be used by drivers that have tty devices + * that can appear and disappear while the main tty driver is + * registered with the tty core. * * TTY_DRIVER_DEVPTS_MEM -- don't use the standard arrays, instead * use dynamic memory keyed through the devpts filesystem. This @@ -252,7 +258,7 @@ #define TTY_DRIVER_MAGIC 0x5402 #define TTY_DRIVER_INSTALLED 0x0001 #define TTY_DRIVER_RESET_TERMIOS 0x0002 #define TTY_DRIVER_REAL_RAW 0x0004 -#define TTY_DRIVER_NO_DEVFS 0x0008 +#define TTY_DRIVER_DYNAMIC_DEV 0x0008 #define TTY_DRIVER_DEVPTS_MEM 0x0010 /* tty driver types */ diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h index 3154830..eb677cf 100644 --- a/include/linux/tty_flip.h +++ b/include/linux/tty_flip.h @@ -12,7 +12,7 @@ static inline int tty_insert_flip_char(s unsigned char ch, char flag) { struct tty_buffer *tb = tty->buf.tail; - if (tb && tb->active && tb->used < tb->size) { + if (tb && tb->used < tb->size) { tb->flag_buf_ptr[tb->used] = flag; tb->char_buf_ptr[tb->used++] = ch; return 1; diff --git a/include/linux/types.h b/include/linux/types.h index 1046c7a..3f23566 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -2,7 +2,6 @@ #ifndef _LINUX_TYPES_H #define _LINUX_TYPES_H #ifdef __KERNEL__ -#include #define BITS_TO_LONGS(bits) \ (((bits)+BITS_PER_LONG-1)/BITS_PER_LONG) @@ -178,8 +177,15 @@ #endif #ifdef __KERNEL__ typedef unsigned __bitwise__ gfp_t; + +#ifdef CONFIG_RESOURCES_64BIT +typedef u64 resource_size_t; +#else +typedef u32 resource_size_t; #endif +#endif /* __KERNEL__ */ + struct ustat { __kernel_daddr_t f_tfree; __kernel_ino_t f_tinode; diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h new file mode 100644 index 0000000..391e7ed --- /dev/null +++ b/include/linux/uaccess.h @@ -0,0 +1,22 @@ +#ifndef __LINUX_UACCESS_H__ +#define __LINUX_UACCESS_H__ + +#include + +#ifndef ARCH_HAS_NOCACHE_UACCESS + +static inline unsigned long __copy_from_user_inatomic_nocache(void *to, + const void __user *from, unsigned long n) +{ + return __copy_from_user_inatomic(to, from, n); +} + +static inline unsigned long __copy_from_user_nocache(void *to, + const void __user *from, unsigned long n) +{ + return __copy_from_user(to, from, n); +} + +#endif /* ARCH_HAS_NOCACHE_UACCESS */ + +#endif /* __LINUX_UACCESS_H__ */ diff --git a/include/linux/udp.h b/include/linux/udp.h index 85a5565..90223f0 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -35,7 +35,6 @@ #define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* #define UDP_ENCAP_ESPINUDP 2 /* draft-ietf-ipsec-udp-encaps-06 */ #ifdef __KERNEL__ -#include #include #include @@ -47,7 +46,7 @@ struct udp_sock { unsigned int corkflag; /* Cork is required */ __u16 encap_type; /* Is this an Encapsulation socket? */ /* - * Following member retains the infomation to create a UDP header + * Following member retains the information to create a UDP header * when the socket is uncorked. */ __u16 len; /* total length of pending frames */ diff --git a/include/linux/ufs_fs.h b/include/linux/ufs_fs.h index 843aeaa..fc62887 100644 --- a/include/linux/ufs_fs.h +++ b/include/linux/ufs_fs.h @@ -32,7 +32,6 @@ #define __LINUX_UFS_FS_H #include #include -#include #include #include @@ -221,6 +220,19 @@ #define ufs_test_opt(o,opt) ((o) & UFS_M */ #define UFS_MINFREE 5 #define UFS_DEFAULTOPT UFS_OPTTIME + +/* + * Debug code + */ +#ifdef CONFIG_UFS_DEBUG +# define UFSD(f, a...) { \ + printk ("UFSD (%s, %d): %s:", \ + __FILE__, __LINE__, __FUNCTION__); \ + printk (f, ## a); \ + } +#else +# define UFSD(f, a...) /**/ +#endif /* * Turn file system block numbers into disk block addresses. @@ -340,7 +352,22 @@ struct ufs2_csum_total { }; /* + * File system flags + */ +#define UFS_UNCLEAN 0x01 /* file system not clean at mount (unused) */ +#define UFS_DOSOFTDEP 0x02 /* file system using soft dependencies */ +#define UFS_NEEDSFSCK 0x04 /* needs sync fsck (FreeBSD compat, unused) */ +#define UFS_INDEXDIRS 0x08 /* kernel supports indexed directories */ +#define UFS_ACLS 0x10 /* file system has ACLs enabled */ +#define UFS_MULTILABEL 0x20 /* file system is MAC multi-label */ +#define UFS_FLAGS_UPDATED 0x80 /* flags have been moved to new location */ + +#if 0 +/* * This is the actual superblock, as it is laid out on the disk. + * Do NOT use this structure, because of sizeof(ufs_super_block) > 512 and + * it may occupy several blocks, use + * struct ufs_super_block_(first,second,third) instead. */ struct ufs_super_block { __fs32 fs_link; /* UNUSED */ @@ -417,7 +444,7 @@ struct ufs_super_block { __s8 fs_fmod; /* super block modified flag */ __s8 fs_clean; /* file system is clean flag */ __s8 fs_ronly; /* mounted read-only flag */ - __s8 fs_flags; /* currently unused flag */ + __s8 fs_flags; union { struct { __s8 fs_fsmnt[UFS_MAXMNTLEN];/* name mounted on */ @@ -486,6 +513,7 @@ struct ufs_super_block { __fs32 fs_magic; /* magic number */ __u8 fs_space[1]; /* list of blocks for each rotation */ }; +#endif/*struct ufs_super_block*/ /* * Preference for optimization. @@ -667,7 +695,7 @@ struct ufs_buffer_head { }; struct ufs_cg_private_info { - struct ufs_cylinder_group ucg; + struct ufs_buffer_head c_ubh; __u32 c_cgx; /* number of cylidner group */ __u16 c_ncyl; /* number of cyl's this cg */ __u16 c_niblk; /* number of inode blocks this cg */ @@ -687,6 +715,7 @@ struct ufs_cg_private_info { struct ufs_sb_private_info { struct ufs_buffer_head s_ubh; /* buffer containing super block */ + struct ufs2_csum_total cs_total; __u32 s_sblkno; /* offset of super-blocks in filesys */ __u32 s_cblkno; /* offset of cg-block in filesys */ __u32 s_iblkno; /* offset of inode-blocks in filesys */ @@ -825,16 +854,54 @@ struct ufs_super_block_first { }; struct ufs_super_block_second { - __s8 fs_fsmnt[212]; - __fs32 fs_cgrotor; - __fs32 fs_csp[UFS_MAXCSBUFS]; - __fs32 fs_maxcluster; - __fs32 fs_cpc; - __fs16 fs_opostbl[82]; -}; + union { + struct { + __s8 fs_fsmnt[212]; + __fs32 fs_cgrotor; + __fs32 fs_csp[UFS_MAXCSBUFS]; + __fs32 fs_maxcluster; + __fs32 fs_cpc; + __fs16 fs_opostbl[82]; + } fs_u1; + struct { + __s8 fs_fsmnt[UFS2_MAXMNTLEN - UFS_MAXMNTLEN + 212]; + __u8 fs_volname[UFS2_MAXVOLLEN]; + __fs64 fs_swuid; + __fs32 fs_pad; + __fs32 fs_cgrotor; + __fs32 fs_ocsp[UFS2_NOCSPTRS]; + __fs32 fs_contigdirs; + __fs32 fs_csp; + __fs32 fs_maxcluster; + __fs32 fs_active; + __fs32 fs_old_cpc; + __fs32 fs_maxbsize; + __fs64 fs_sparecon64[17]; + __fs64 fs_sblockloc; + __fs64 cs_ndir; + __fs64 cs_nbfree; + } fs_u2; + } fs_un; +}; struct ufs_super_block_third { - __fs16 fs_opostbl[46]; + union { + struct { + __fs16 fs_opostbl[46]; + } fs_u1; + struct { + __fs64 cs_nifree; /* number of free inodes */ + __fs64 cs_nffree; /* number of free frags */ + __fs64 cs_numclusters; /* number of free clusters */ + __fs64 cs_spare[3]; /* future expansion */ + struct ufs_timeval fs_time; /* last time written */ + __fs64 fs_size; /* number of blocks in fs */ + __fs64 fs_dsize; /* number of data blocks in fs */ + __fs64 fs_csaddr; /* blk addr of cyl grp summary area */ + __fs64 fs_pendingblocks;/* blocks in process of being freed */ + __fs32 fs_pendinginodes;/*inodes in process of being freed */ + } fs_u2; + } fs_un1; union { struct { __fs32 fs_sparecon[53];/* reserved for future constants */ @@ -862,7 +929,7 @@ struct ufs_super_block_third { __fs32 fs_qfmask[2]; /* ~usb_fmask */ __fs32 fs_state; /* file system state time stamp */ } fs_44; - } fs_u2; + } fs_un2; __fs32 fs_postblformat; __fs32 fs_nrpos; __fs32 fs_postbloff; @@ -876,7 +943,8 @@ #ifdef __KERNEL__ /* balloc.c */ extern void ufs_free_fragments (struct inode *, unsigned, unsigned); extern void ufs_free_blocks (struct inode *, unsigned, unsigned); -extern unsigned ufs_new_fragments (struct inode *, __fs32 *, unsigned, unsigned, unsigned, int *); +extern unsigned ufs_new_fragments(struct inode *, __fs32 *, unsigned, unsigned, + unsigned, int *, struct page *); /* cylinder.c */ extern struct ufs_cg_private_info * ufs_load_cylinder (struct super_block *, unsigned); @@ -887,30 +955,29 @@ extern struct inode_operations ufs_dir_i extern int ufs_add_link (struct dentry *, struct inode *); extern ino_t ufs_inode_by_name(struct inode *, struct dentry *); extern int ufs_make_empty(struct inode *, struct inode *); -extern struct ufs_dir_entry * ufs_find_entry (struct dentry *, struct buffer_head **); -extern int ufs_delete_entry (struct inode *, struct ufs_dir_entry *, struct buffer_head *); +extern struct ufs_dir_entry *ufs_find_entry(struct inode *, struct dentry *, struct page **); +extern int ufs_delete_entry(struct inode *, struct ufs_dir_entry *, struct page *); extern int ufs_empty_dir (struct inode *); -extern struct ufs_dir_entry * ufs_dotdot (struct inode *, struct buffer_head **); -extern void ufs_set_link(struct inode *, struct ufs_dir_entry *, struct buffer_head *, struct inode *); +extern struct ufs_dir_entry *ufs_dotdot(struct inode *, struct page **); +extern void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, + struct page *page, struct inode *inode); /* file.c */ extern struct inode_operations ufs_file_inode_operations; extern const struct file_operations ufs_file_operations; -extern struct address_space_operations ufs_aops; +extern const struct address_space_operations ufs_aops; /* ialloc.c */ extern void ufs_free_inode (struct inode *inode); extern struct inode * ufs_new_inode (struct inode *, int); /* inode.c */ -extern u64 ufs_frag_map (struct inode *, sector_t); extern void ufs_read_inode (struct inode *); extern void ufs_put_inode (struct inode *); extern int ufs_write_inode (struct inode *, int); extern int ufs_sync_inode (struct inode *); extern void ufs_delete_inode (struct inode *); -extern struct buffer_head * ufs_getfrag (struct inode *, unsigned, int, int *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); extern int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create); @@ -926,7 +993,7 @@ extern void ufs_panic (struct super_bloc extern struct inode_operations ufs_fast_symlink_inode_operations; /* truncate.c */ -extern void ufs_truncate (struct inode *); +extern int ufs_truncate (struct inode *, loff_t); static inline struct ufs_sb_info *UFS_SB(struct super_block *sb) { diff --git a/include/linux/ufs_fs_i.h b/include/linux/ufs_fs_i.h index 21665a9..f50ce3b 100644 --- a/include/linux/ufs_fs_i.h +++ b/include/linux/ufs_fs_i.h @@ -27,6 +27,7 @@ struct ufs_inode_info { __u32 i_oeftflag; __u16 i_osync; __u32 i_lastfrag; + __u32 i_dir_start_lookup; struct inode vfs_inode; }; diff --git a/include/linux/unistd.h b/include/linux/unistd.h index 10ed983..c18c60f 100644 --- a/include/linux/unistd.h +++ b/include/linux/unistd.h @@ -1,7 +1,9 @@ #ifndef _LINUX_UNISTD_H_ #define _LINUX_UNISTD_H_ +#ifdef __KERNEL__ extern int errno; +#endif /* * Include machine specific syscallX macros diff --git a/include/linux/unwind.h b/include/linux/unwind.h new file mode 100644 index 0000000..ce48e2c --- /dev/null +++ b/include/linux/unwind.h @@ -0,0 +1,127 @@ +#ifndef _LINUX_UNWIND_H +#define _LINUX_UNWIND_H + +/* + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich + * This code is released under version 2 of the GNU GPL. + * + * A simple API for unwinding kernel stacks. This is used for + * debugging and error reporting purposes. The kernel doesn't need + * full-blown stack unwinding with all the bells and whistles, so there + * is not much point in implementing the full Dwarf2 unwind API. + */ + +#include + +struct module; + +#ifdef CONFIG_STACK_UNWIND + +#include + +#ifndef ARCH_UNWIND_SECTION_NAME +#define ARCH_UNWIND_SECTION_NAME ".eh_frame" +#endif + +/* + * Initialize unwind support. + */ +extern void unwind_init(void); + +#ifdef CONFIG_MODULES + +extern void *unwind_add_table(struct module *, + const void *table_start, + unsigned long table_size); + +extern void unwind_remove_table(void *handle, int init_only); + +#endif + +extern int unwind_init_frame_info(struct unwind_frame_info *, + struct task_struct *, + /*const*/ struct pt_regs *); + +/* + * Prepare to unwind a blocked task. + */ +extern int unwind_init_blocked(struct unwind_frame_info *, + struct task_struct *); + +/* + * Prepare to unwind the currently running thread. + */ +extern int unwind_init_running(struct unwind_frame_info *, + asmlinkage int (*callback)(struct unwind_frame_info *, + void *arg), + void *arg); + +/* + * Unwind to previous to frame. Returns 0 if successful, negative + * number in case of an error. + */ +extern int unwind(struct unwind_frame_info *); + +/* + * Unwind until the return pointer is in user-land (or until an error + * occurs). Returns 0 if successful, negative number in case of + * error. + */ +extern int unwind_to_user(struct unwind_frame_info *); + +#else + +struct unwind_frame_info {}; + +static inline void unwind_init(void) {} + +#ifdef CONFIG_MODULES + +static inline void *unwind_add_table(struct module *mod, + const void *table_start, + unsigned long table_size) +{ + return NULL; +} + +#endif + +static inline void unwind_remove_table(void *handle, int init_only) +{ +} + +static inline int unwind_init_frame_info(struct unwind_frame_info *info, + struct task_struct *tsk, + const struct pt_regs *regs) +{ + return -ENOSYS; +} + +static inline int unwind_init_blocked(struct unwind_frame_info *info, + struct task_struct *tsk) +{ + return -ENOSYS; +} + +static inline int unwind_init_running(struct unwind_frame_info *info, + asmlinkage int (*cb)(struct unwind_frame_info *, + void *arg), + void *arg) +{ + return -ENOSYS; +} + +static inline int unwind(struct unwind_frame_info *info) +{ + return -ENOSYS; +} + +static inline int unwind_to_user(struct unwind_frame_info *info) +{ + return -ENOSYS; +} + +#endif + +#endif /* _LINUX_UNWIND_H */ diff --git a/include/linux/usb.h b/include/linux/usb.h index e34e5e3..8dead32 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -10,7 +10,6 @@ #define USB_DEVICE_MAJOR 189 #ifdef __KERNEL__ -#include #include /* for -ENODEV */ #include /* for mdelay() */ #include /* for in_interrupt() */ @@ -41,6 +40,8 @@ struct usb_driver; * Devices may also have class-specific or vendor-specific descriptors. */ +struct ep_device; + /** * struct usb_host_endpoint - host-side endpoint descriptor and queue * @desc: descriptor for this endpoint, wMaxPacketSize in native byteorder @@ -58,7 +59,7 @@ struct usb_host_endpoint { struct usb_endpoint_descriptor desc; struct list_head urb_list; void *hcpriv; - struct kobject *kobj; /* For sysfs info */ + struct ep_device *ep_dev; /* For sysfs info */ unsigned char *extra; /* Extra descriptors */ int extralen; @@ -102,7 +103,8 @@ enum usb_interface_condition { * @condition: binding state of the interface: not bound, binding * (in probe()), bound to a driver, or unbinding (in disconnect()) * @dev: driver model's view of this device - * @class_dev: driver model's class view of this device. + * @usb_dev: if an interface is bound to the USB major, this will point + * to the sysfs representation for that device. * * USB device drivers attach to interfaces on a physical device. Each * interface encapsulates a single high level function, such as feeding @@ -142,7 +144,7 @@ struct usb_interface { * bound to */ enum usb_interface_condition condition; /* state of binding */ struct device dev; /* interface specific device info */ - struct class_device *class_dev; + struct device *usb_dev; /* pointer to the usb class's device, if any */ }; #define to_usb_interface(d) container_of(d, struct usb_interface, dev) #define interface_to_usbdev(intf) \ @@ -359,7 +361,7 @@ struct usb_device { char *serial; /* iSerialNumber string, if present */ struct list_head filelist; - struct class_device *class_dev; + struct device *usbfs_dev; struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */ /* @@ -387,6 +389,8 @@ extern int usb_lock_device_for_reset(str /* USB port reset for device reinitialization */ extern int usb_reset_device(struct usb_device *dev); +extern int usb_reset_composite_device(struct usb_device *dev, + struct usb_interface *iface); extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); @@ -555,6 +559,10 @@ struct usb_dynids { * do (or don't) show up otherwise in the filesystem. * @suspend: Called when the device is going to be suspended by the system. * @resume: Called when the device is being resumed by the system. + * @pre_reset: Called by usb_reset_composite_device() when the device + * is about to be reset. + * @post_reset: Called by usb_reset_composite_device() after the device + * has been reset. * @id_table: USB drivers use ID table to support hotplugging. * Export this with MODULE_DEVICE_TABLE(usb,...). This must be set * or your driver's probe function will never get called. @@ -593,6 +601,9 @@ struct usb_driver { int (*suspend) (struct usb_interface *intf, pm_message_t message); int (*resume) (struct usb_interface *intf); + void (*pre_reset) (struct usb_interface *intf); + void (*post_reset) (struct usb_interface *intf); + const struct usb_device_id *id_table; struct usb_dynids dynids; @@ -1009,6 +1020,8 @@ void usb_buffer_unmap_sg (struct usb_dev extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout); +extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout); extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout); diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h new file mode 100644 index 0000000..ba617c3 --- /dev/null +++ b/include/linux/usb/cdc.h @@ -0,0 +1,205 @@ +/* + * USB Communications Device Class (CDC) definitions + * + * CDC says how to talk to lots of different types of network adapters, + * notably ethernet adapters and various modems. It's used mostly with + * firmware based USB peripherals. + */ + +#define USB_CDC_SUBCLASS_ACM 0x02 +#define USB_CDC_SUBCLASS_ETHERNET 0x06 +#define USB_CDC_SUBCLASS_WHCM 0x08 +#define USB_CDC_SUBCLASS_DMM 0x09 +#define USB_CDC_SUBCLASS_MDLM 0x0a +#define USB_CDC_SUBCLASS_OBEX 0x0b + +#define USB_CDC_PROTO_NONE 0 + +#define USB_CDC_ACM_PROTO_AT_V25TER 1 +#define USB_CDC_ACM_PROTO_AT_PCCA101 2 +#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3 +#define USB_CDC_ACM_PROTO_AT_GSM 4 +#define USB_CDC_ACM_PROTO_AT_3G 5 +#define USB_CDC_ACM_PROTO_AT_CDMA 6 +#define USB_CDC_ACM_PROTO_VENDOR 0xff + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific descriptors ... there are a couple dozen of them + */ + +#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ +#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ +#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ +#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ +#define USB_CDC_COUNTRY_TYPE 0x07 +#define USB_CDC_NETWORK_TERMINAL_TYPE 0x0a /* network_terminal_desc */ +#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ +#define USB_CDC_WHCM_TYPE 0x11 +#define USB_CDC_MDLM_TYPE 0x12 /* mdlm_desc */ +#define USB_CDC_MDLM_DETAIL_TYPE 0x13 /* mdlm_detail_desc */ +#define USB_CDC_DMM_TYPE 0x14 +#define USB_CDC_OBEX_TYPE 0x15 + +/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ +struct usb_cdc_header_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdCDC; +} __attribute__ ((packed)); + +/* "Call Management Descriptor" from CDC spec 5.2.3.2 */ +struct usb_cdc_call_mgmt_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 +#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 + + __u8 bDataInterface; +} __attribute__ ((packed)); + +/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */ +struct usb_cdc_acm_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bmCapabilities; +} __attribute__ ((packed)); + +/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ +struct usb_cdc_union_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bMasterInterface0; + __u8 bSlaveInterface0; + /* ... and there could be other slave interfaces */ +} __attribute__ ((packed)); + +/* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ +struct usb_cdc_network_terminal_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 bEntityId; + __u8 iName; + __u8 bChannelIndex; + __u8 bPhysicalInterface; +} __attribute__ ((packed)); + +/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ +struct usb_cdc_ether_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __u8 iMACAddress; + __le32 bmEthernetStatistics; + __le16 wMaxSegmentSize; + __le16 wNumberMCFilters; + __u8 bNumberPowerFilters; +} __attribute__ ((packed)); + +/* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */ +struct usb_cdc_mdlm_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + __le16 bcdVersion; + __u8 bGUID[16]; +} __attribute__ ((packed)); + +/* "MDLM Detail Functional Descriptor" from CDC WMC spec 6.7.2.4 */ +struct usb_cdc_mdlm_detail_desc { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubType; + + /* type is associated with mdlm_desc.bGUID */ + __u8 bGuidDescriptorType; + __u8 bDetailData[0]; +} __attribute__ ((packed)); + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Control Requests (6.2) + * + * section 3.6.2.1 table 4 has the ACM profile, for modems. + * section 3.8.2 table 10 has the ethernet profile. + * + * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant, + * heavily dependent on the encapsulated (proprietary) command mechanism. + */ + +#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 +#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 +#define USB_CDC_REQ_SET_LINE_CODING 0x20 +#define USB_CDC_REQ_GET_LINE_CODING 0x21 +#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 +#define USB_CDC_REQ_SEND_BREAK 0x23 +#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 +#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 +#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 +#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 +#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 + +/* Line Coding Structure from CDC spec 6.2.13 */ +struct usb_cdc_line_coding { + __le32 dwDTERate; + __u8 bCharFormat; +#define USB_CDC_1_STOP_BITS 0 +#define USB_CDC_1_5_STOP_BITS 1 +#define USB_CDC_2_STOP_BITS 2 + + __u8 bParityType; +#define USB_CDC_NO_PARITY 0 +#define USB_CDC_ODD_PARITY 1 +#define USB_CDC_EVEN_PARITY 2 +#define USB_CDC_MARK_PARITY 3 +#define USB_CDC_SPACE_PARITY 4 + + __u8 bDataBits; +} __attribute__ ((packed)); + +/* table 62; bits in multicast filter */ +#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) +#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ +#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2) +#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3) +#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ + + +/*-------------------------------------------------------------------------*/ + +/* + * Class-Specific Notifications (6.3) sent by interrupt transfers + * + * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications + * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS + * RNDIS also defines its own bit-incompatible notifications + */ + +#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00 +#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 +#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 +#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a + +struct usb_cdc_notification { + __u8 bmRequestType; + __u8 bNotificationType; + __le16 wValue; + __le16 wIndex; + __le16 wLength; +} __attribute__ ((packed)); + diff --git a/include/linux/usb/input.h b/include/linux/usb/input.h new file mode 100644 index 0000000..716e0cc --- /dev/null +++ b/include/linux/usb/input.h @@ -0,0 +1,25 @@ +#ifndef __USB_INPUT_H +#define __USB_INPUT_H + +/* + * Copyright (C) 2005 Dmitry Torokhov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include + +static inline void +usb_to_input_id(const struct usb_device *dev, struct input_id *id) +{ + id->bustype = BUS_USB; + id->vendor = le16_to_cpu(dev->descriptor.idVendor); + id->product = le16_to_cpu(dev->descriptor.idProduct); + id->version = le16_to_cpu(dev->descriptor.bcdDevice); +} + +#endif diff --git a/include/linux/usb/isp116x.h b/include/linux/usb/isp116x.h new file mode 100644 index 0000000..436dd8a --- /dev/null +++ b/include/linux/usb/isp116x.h @@ -0,0 +1,29 @@ + +/* + * Board initialization code should put one of these into dev->platform_data + * and place the isp116x onto platform_bus. + */ + +struct isp116x_platform_data { + /* Enable internal resistors on downstream ports */ + unsigned sel15Kres:1; + /* On-chip overcurrent detection */ + unsigned oc_enable:1; + /* INT output polarity */ + unsigned int_act_high:1; + /* INT edge or level triggered */ + unsigned int_edge_triggered:1; + /* Enable wakeup by devices on usb bus (e.g. wakeup + by attachment/detachment or by device activity + such as moving a mouse). When chosen, this option + prevents stopping internal clock, increasing + thereby power consumption in suspended state. */ + unsigned remote_wakeup_enable:1; + /* Inter-io delay (ns). The chip is picky about access timings; it + expects at least: + 150ns delay between consecutive accesses to DATA_REG, + 300ns delay between access to ADDR_REG and DATA_REG + OE, WE MUST NOT be changed during these intervals + */ + void (*delay) (struct device * dev, int delay); +}; diff --git a/include/linux/usb/sl811.h b/include/linux/usb/sl811.h new file mode 100644 index 0000000..397ee3b --- /dev/null +++ b/include/linux/usb/sl811.h @@ -0,0 +1,26 @@ + +/* + * board initialization should put one of these into dev->platform_data + * and place the sl811hs onto platform_bus named "sl811-hcd". + */ + +struct sl811_platform_data { + unsigned can_wakeup:1; + + /* given port_power, msec/2 after power on till power good */ + u8 potpg; + + /* mA/2 power supplied on this port (max = default = 250) */ + u8 power; + + /* sl811 relies on an external source of VBUS current */ + void (*port_power)(struct device *dev, int is_on); + + /* pulse sl811 nRST (probably with a GPIO) */ + void (*reset)(struct device *dev); + + // some boards need something like these: + // int (*check_overcurrent)(struct device *dev); + // void (*clock_enable)(struct device *dev, int is_on); +}; + diff --git a/include/linux/usb_cdc.h b/include/linux/usb_cdc.h deleted file mode 100644 index ba617c3..0000000 --- a/include/linux/usb_cdc.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * USB Communications Device Class (CDC) definitions - * - * CDC says how to talk to lots of different types of network adapters, - * notably ethernet adapters and various modems. It's used mostly with - * firmware based USB peripherals. - */ - -#define USB_CDC_SUBCLASS_ACM 0x02 -#define USB_CDC_SUBCLASS_ETHERNET 0x06 -#define USB_CDC_SUBCLASS_WHCM 0x08 -#define USB_CDC_SUBCLASS_DMM 0x09 -#define USB_CDC_SUBCLASS_MDLM 0x0a -#define USB_CDC_SUBCLASS_OBEX 0x0b - -#define USB_CDC_PROTO_NONE 0 - -#define USB_CDC_ACM_PROTO_AT_V25TER 1 -#define USB_CDC_ACM_PROTO_AT_PCCA101 2 -#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3 -#define USB_CDC_ACM_PROTO_AT_GSM 4 -#define USB_CDC_ACM_PROTO_AT_3G 5 -#define USB_CDC_ACM_PROTO_AT_CDMA 6 -#define USB_CDC_ACM_PROTO_VENDOR 0xff - -/*-------------------------------------------------------------------------*/ - -/* - * Class-Specific descriptors ... there are a couple dozen of them - */ - -#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ -#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ -#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ -#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ -#define USB_CDC_COUNTRY_TYPE 0x07 -#define USB_CDC_NETWORK_TERMINAL_TYPE 0x0a /* network_terminal_desc */ -#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ -#define USB_CDC_WHCM_TYPE 0x11 -#define USB_CDC_MDLM_TYPE 0x12 /* mdlm_desc */ -#define USB_CDC_MDLM_DETAIL_TYPE 0x13 /* mdlm_detail_desc */ -#define USB_CDC_DMM_TYPE 0x14 -#define USB_CDC_OBEX_TYPE 0x15 - -/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ -struct usb_cdc_header_desc { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __le16 bcdCDC; -} __attribute__ ((packed)); - -/* "Call Management Descriptor" from CDC spec 5.2.3.2 */ -struct usb_cdc_call_mgmt_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __u8 bmCapabilities; -#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 -#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 - - __u8 bDataInterface; -} __attribute__ ((packed)); - -/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */ -struct usb_cdc_acm_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __u8 bmCapabilities; -} __attribute__ ((packed)); - -/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ -struct usb_cdc_union_desc { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __u8 bMasterInterface0; - __u8 bSlaveInterface0; - /* ... and there could be other slave interfaces */ -} __attribute__ ((packed)); - -/* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */ -struct usb_cdc_network_terminal_desc { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __u8 bEntityId; - __u8 iName; - __u8 bChannelIndex; - __u8 bPhysicalInterface; -} __attribute__ ((packed)); - -/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ -struct usb_cdc_ether_desc { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __u8 iMACAddress; - __le32 bmEthernetStatistics; - __le16 wMaxSegmentSize; - __le16 wNumberMCFilters; - __u8 bNumberPowerFilters; -} __attribute__ ((packed)); - -/* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */ -struct usb_cdc_mdlm_desc { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - __le16 bcdVersion; - __u8 bGUID[16]; -} __attribute__ ((packed)); - -/* "MDLM Detail Functional Descriptor" from CDC WMC spec 6.7.2.4 */ -struct usb_cdc_mdlm_detail_desc { - __u8 bLength; - __u8 bDescriptorType; - __u8 bDescriptorSubType; - - /* type is associated with mdlm_desc.bGUID */ - __u8 bGuidDescriptorType; - __u8 bDetailData[0]; -} __attribute__ ((packed)); - -/*-------------------------------------------------------------------------*/ - -/* - * Class-Specific Control Requests (6.2) - * - * section 3.6.2.1 table 4 has the ACM profile, for modems. - * section 3.8.2 table 10 has the ethernet profile. - * - * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant, - * heavily dependent on the encapsulated (proprietary) command mechanism. - */ - -#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 -#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 -#define USB_CDC_REQ_SET_LINE_CODING 0x20 -#define USB_CDC_REQ_GET_LINE_CODING 0x21 -#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 -#define USB_CDC_REQ_SEND_BREAK 0x23 -#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 -#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 -#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 -#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 -#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 - -/* Line Coding Structure from CDC spec 6.2.13 */ -struct usb_cdc_line_coding { - __le32 dwDTERate; - __u8 bCharFormat; -#define USB_CDC_1_STOP_BITS 0 -#define USB_CDC_1_5_STOP_BITS 1 -#define USB_CDC_2_STOP_BITS 2 - - __u8 bParityType; -#define USB_CDC_NO_PARITY 0 -#define USB_CDC_ODD_PARITY 1 -#define USB_CDC_EVEN_PARITY 2 -#define USB_CDC_MARK_PARITY 3 -#define USB_CDC_SPACE_PARITY 4 - - __u8 bDataBits; -} __attribute__ ((packed)); - -/* table 62; bits in multicast filter */ -#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) -#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ -#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2) -#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3) -#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ - - -/*-------------------------------------------------------------------------*/ - -/* - * Class-Specific Notifications (6.3) sent by interrupt transfers - * - * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications - * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS - * RNDIS also defines its own bit-incompatible notifications - */ - -#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00 -#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 -#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 -#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a - -struct usb_cdc_notification { - __u8 bmRequestType; - __u8 bNotificationType; - __le16 wValue; - __le16 wIndex; - __le16 wLength; -} __attribute__ ((packed)); - diff --git a/include/linux/usb_input.h b/include/linux/usb_input.h deleted file mode 100644 index 716e0cc..0000000 --- a/include/linux/usb_input.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __USB_INPUT_H -#define __USB_INPUT_H - -/* - * Copyright (C) 2005 Dmitry Torokhov - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ - -#include -#include -#include - -static inline void -usb_to_input_id(const struct usb_device *dev, struct input_id *id) -{ - id->bustype = BUS_USB; - id->vendor = le16_to_cpu(dev->descriptor.idVendor); - id->product = le16_to_cpu(dev->descriptor.idProduct); - id->version = le16_to_cpu(dev->descriptor.bcdDevice); -} - -#endif diff --git a/include/linux/usb_isp116x.h b/include/linux/usb_isp116x.h deleted file mode 100644 index 436dd8a..0000000 --- a/include/linux/usb_isp116x.h +++ /dev/null @@ -1,29 +0,0 @@ - -/* - * Board initialization code should put one of these into dev->platform_data - * and place the isp116x onto platform_bus. - */ - -struct isp116x_platform_data { - /* Enable internal resistors on downstream ports */ - unsigned sel15Kres:1; - /* On-chip overcurrent detection */ - unsigned oc_enable:1; - /* INT output polarity */ - unsigned int_act_high:1; - /* INT edge or level triggered */ - unsigned int_edge_triggered:1; - /* Enable wakeup by devices on usb bus (e.g. wakeup - by attachment/detachment or by device activity - such as moving a mouse). When chosen, this option - prevents stopping internal clock, increasing - thereby power consumption in suspended state. */ - unsigned remote_wakeup_enable:1; - /* Inter-io delay (ns). The chip is picky about access timings; it - expects at least: - 150ns delay between consecutive accesses to DATA_REG, - 300ns delay between access to ADDR_REG and DATA_REG - OE, WE MUST NOT be changed during these intervals - */ - void (*delay) (struct device * dev, int delay); -}; diff --git a/include/linux/usb_sl811.h b/include/linux/usb_sl811.h deleted file mode 100644 index 4f2d012..0000000 --- a/include/linux/usb_sl811.h +++ /dev/null @@ -1,26 +0,0 @@ - -/* - * board initialization should put one of these into dev->platform_data - * and place the sl811hs onto platform_bus named "sl811-hcd". - */ - -struct sl811_platform_data { - unsigned can_wakeup:1; - - /* given port_power, msec/2 after power on till power good */ - u8 potpg; - - /* mA/2 power supplied on this port (max = default = 250) */ - u8 power; - - /* sl811 relies on an external source of VBUS current */ - void (*port_power)(struct device *dev, int is_on); - - /* pulse sl811 nRST (probably with a GPIO) */ - void (*reset)(struct device *dev); - - // some boards need something like these: - // int (*check_overcurrent)(struct device *dev); - // void (*clock_enable)(struct device *dev, int is_on); -}; - diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index b2d0898..608487a 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -9,7 +9,6 @@ #ifndef __LINUX_USB_USUAL_H #define __LINUX_USB_USUAL_H -#include /* We should do this for cleanliness... But other usb_foo.h do not do this. */ /* #include */ diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 8859f0b..7b7aadb 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -123,6 +123,7 @@ struct usbdevfs_hub_portinfo { char port [127]; /* e.g. port 3 connects to device 27 */ }; +#ifdef __KERNEL__ #ifdef CONFIG_COMPAT #include struct usbdevfs_urb32 { @@ -147,6 +148,7 @@ struct usbdevfs_ioctl32 { compat_caddr_t data; }; #endif +#endif /* __KERNEL__ */ #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer) #define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer) diff --git a/include/linux/vermagic.h b/include/linux/vermagic.h index dc7c621..46919f9 100644 --- a/include/linux/vermagic.h +++ b/include/linux/vermagic.h @@ -1,4 +1,4 @@ -#include +#include #include /* Simply sanity version stamp for modules. */ diff --git a/include/linux/videodev.h b/include/linux/videodev.h index 9114009..41bc7e9 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -1,49 +1,21 @@ +/* + * Video for Linux version 1 - OBSOLETE + * + * Header file for v4l1 drivers and applications, for + * Linux kernels 2.2.x or 2.4.x. + * + * Provides header for legacy drivers and applications + * + * See http://linuxtv.org for more info + * + */ #ifndef __LINUX_VIDEODEV_H #define __LINUX_VIDEODEV_H -#include - #define HAVE_V4L1 1 #include -#ifdef __KERNEL__ - -#include - -extern struct video_device* video_devdata(struct file*); - -#define to_video_device(cd) container_of(cd, struct video_device, class_dev) -static inline void -video_device_create_file(struct video_device *vfd, - struct class_device_attribute *attr) -{ - class_device_create_file(&vfd->class_dev, attr); -} -static inline void -video_device_remove_file(struct video_device *vfd, - struct class_device_attribute *attr) -{ - class_device_remove_file(&vfd->class_dev, attr); -} - -#if OBSOLETE_OWNER /* to be removed in 2.6.15 */ -/* helper functions to access driver private data. */ -static inline void *video_get_drvdata(struct video_device *dev) -{ - return dev->priv; -} - -static inline void video_set_drvdata(struct video_device *dev, void *data) -{ - dev->priv = data; -} -#endif - -extern int video_exclusive_open(struct inode *inode, struct file *file); -extern int video_exclusive_release(struct inode *inode, struct file *file); -#endif /* __KERNEL__ */ - struct video_capability { char name[32]; @@ -363,6 +335,7 @@ #define VID_HARDWARE_W9968CF 36 #define VID_HARDWARE_SAA7114H 37 #define VID_HARDWARE_SN9C102 38 #define VID_HARDWARE_ARV 39 + #endif /* __LINUX_VIDEODEV_H */ /* diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index ad7fa9c..a62673d 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1,35 +1,33 @@ -#ifndef __LINUX_VIDEODEV2_H -#define __LINUX_VIDEODEV2_H /* * Video for Linux Two * - * Header file for v4l or V4L2 drivers and applications, for - * Linux kernels 2.2.x or 2.4.x. + * Header file for v4l or V4L2 drivers and applications + * with public API. + * All kernel-specific stuff were moved to media/v4l2-dev.h, so + * no #if __KERNEL tests are allowed here * - * See http://bytesex.org/v4l/ for API specs and other - * v4l2 documentation. + * See http://linuxtv.org for more info * * Author: Bill Dirks * Justin Schoeman * et al. */ +#ifndef __LINUX_VIDEODEV2_H +#define __LINUX_VIDEODEV2_H #ifdef __KERNEL__ -#include /* need struct timeval */ -#include -#include -#include -#endif +#include /* need struct timeval */ #include /* need __user */ +#else +#define __user +#endif +#include - -#define OBSOLETE_OWNER 1 /* It will be removed for 2.6.17 */ #define HAVE_V4L2 1 /* * Common stuff for both V4L1 and V4L2 * Moved from videodev.h */ - #define VIDEO_MAX_FRAME 32 #define VID_TYPE_CAPTURE 1 /* Can capture */ @@ -47,71 +45,6 @@ #define VID_TYPE_MPEG_ENCODER 2048 /* Ca #define VID_TYPE_MJPEG_DECODER 4096 /* Can decode MJPEG streams */ #define VID_TYPE_MJPEG_ENCODER 8192 /* Can encode MJPEG streams */ -#ifdef __KERNEL__ - -/* Minor device allocation */ -#define MINOR_VFL_TYPE_GRABBER_MIN 0 -#define MINOR_VFL_TYPE_GRABBER_MAX 63 -#define MINOR_VFL_TYPE_RADIO_MIN 64 -#define MINOR_VFL_TYPE_RADIO_MAX 127 -#define MINOR_VFL_TYPE_VTX_MIN 192 -#define MINOR_VFL_TYPE_VTX_MAX 223 -#define MINOR_VFL_TYPE_VBI_MIN 224 -#define MINOR_VFL_TYPE_VBI_MAX 255 - -#define VFL_TYPE_GRABBER 0 -#define VFL_TYPE_VBI 1 -#define VFL_TYPE_RADIO 2 -#define VFL_TYPE_VTX 3 - -struct video_device -{ - /* device info */ - struct device *dev; - char name[32]; - int type; /* v4l1 */ - int type2; /* v4l2 */ - int hardware; - int minor; - - /* device ops + callbacks */ - const struct file_operations *fops; - void (*release)(struct video_device *vfd); - - -#if OBSOLETE_OWNER /* to be removed in 2.6.15 */ - /* obsolete -- fops->owner is used instead */ - struct module *owner; - /* dev->driver_data will be used instead some day. - * Use the video_{get|set}_drvdata() helper functions, - * so the switch over will be transparent for you. - * Or use {pci|usb}_{get|set}_drvdata() directly. */ - void *priv; -#endif - - /* for videodev.c intenal usage -- please don't touch */ - int users; /* video_exclusive_{open|close} ... */ - struct mutex lock; /* ... helper function uses these */ - char devfs_name[64]; /* devfs */ - struct class_device class_dev; /* sysfs */ -}; - -#define VIDEO_MAJOR 81 - -extern int video_register_device(struct video_device *, int type, int nr); -extern void video_unregister_device(struct video_device *); -extern int video_usercopy(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg)); - -/* helper functions to alloc / release struct video_device, the - later can be used for video_device->release() */ -struct video_device *video_device_alloc(void); -void video_device_release(struct video_device *vfd); - -#endif - /* * M I S C E L L A N E O U S */ @@ -172,6 +105,8 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_BOOLEAN = 2, V4L2_CTRL_TYPE_MENU = 3, V4L2_CTRL_TYPE_BUTTON = 4, + V4L2_CTRL_TYPE_INTEGER64 = 5, + V4L2_CTRL_TYPE_CTRL_CLASS = 6, }; enum v4l2_tuner_type { @@ -270,7 +205,6 @@ #define V4L2_CAP_STREAMING /* * V I D E O I M A G E F O R M A T */ - struct v4l2_pix_format { __u32 width; @@ -283,7 +217,7 @@ struct v4l2_pix_format __u32 priv; /* private data, depends on pixelformat */ }; -/* Pixel format FOURCC depth Description */ +/* Pixel format FOURCC depth Description */ #define V4L2_PIX_FMT_RGB332 v4l2_fourcc('R','G','B','1') /* 8 RGB-3-3-2 */ #define V4L2_PIX_FMT_RGB555 v4l2_fourcc('R','G','B','O') /* 16 RGB-5-5-5 */ #define V4L2_PIX_FMT_RGB565 v4l2_fourcc('R','G','B','P') /* 16 RGB-5-6-5 */ @@ -311,6 +245,7 @@ #define V4L2_PIX_FMT_YUV410 v4l2_fourcc #define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y','U','1','2') /* 12 YUV 4:2:0 */ #define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ +#define V4L2_PIX_FMT_HM12 v4l2_fourcc('H','M','1','2') /* 8 YUV 4:1:1 16x16 macroblocks */ /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ @@ -319,7 +254,7 @@ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ #define V4L2_PIX_FMT_JPEG v4l2_fourcc('J','P','E','G') /* JFIF JPEG */ #define V4L2_PIX_FMT_DV v4l2_fourcc('d','v','s','d') /* 1394 */ -#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M','P','E','G') /* MPEG */ +#define V4L2_PIX_FMT_MPEG v4l2_fourcc('M','P','E','G') /* MPEG-1/2/4 */ /* Vendor-specific formats */ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W','N','V','A') /* Winnov hw compress */ @@ -343,7 +278,6 @@ struct v4l2_fmtdesc #define V4L2_FMT_FLAG_COMPRESSED 0x0001 - /* * T I M E C O D E */ @@ -373,16 +307,15 @@ #define V4L2_TC_USERBITS_USERDEFINED 0x0 #define V4L2_TC_USERBITS_8BITCHARS 0x0008 /* The above is based on SMPTE timecodes */ - +#ifdef __KERNEL__ /* * M P E G C O M P R E S S I O N P A R A M E T E R S * - * ### WARNING: this is still work-in-progress right now, most likely - * ### there will be some incompatible changes. + * ### WARNING: This experimental MPEG compression API is obsolete. + * ### It is replaced by the MPEG controls API. + * ### This old API will disappear in the near future! * */ - - enum v4l2_bitrate_mode { V4L2_BITRATE_NONE = 0, /* not specified */ V4L2_BITRATE_CBR, /* constant bitrate */ @@ -460,6 +393,7 @@ struct v4l2_mpeg_compression { /* I don't expect the above being perfect yet ;) */ __u32 reserved_5[8]; }; +#endif struct v4l2_jpegcompression { @@ -491,7 +425,6 @@ #define V4L2_JPEG_MARKER_APP (1<<7) / * allways use APP0 */ }; - /* * M E M O R Y - M A P P I N G B U F F E R S */ @@ -573,7 +506,6 @@ struct v4l2_window void __user *bitmap; }; - /* * C A P T U R E P A R A M E T E R S */ @@ -586,6 +518,7 @@ struct v4l2_captureparm __u32 readbuffers; /* # of buffers for read */ __u32 reserved[4]; }; + /* Flags for 'capability' and 'capturemode' fields */ #define V4L2_MODE_HIGHQUALITY 0x0001 /* High quality imaging mode */ #define V4L2_CAP_TIMEPERFRAME 0x1000 /* timeperframe field is supported */ @@ -603,7 +536,6 @@ struct v4l2_outputparm /* * I N P U T I M A G E C R O P P I N G */ - struct v4l2_cropcap { enum v4l2_buf_type type; struct v4l2_rect bounds; @@ -710,7 +642,6 @@ struct v4l2_standard __u32 reserved[4]; }; - /* * V I D E O I N P U T S */ @@ -725,6 +656,7 @@ struct v4l2_input __u32 status; __u32 reserved[4]; }; + /* Values for the 'type' field */ #define V4L2_INPUT_TYPE_TUNER 1 #define V4L2_INPUT_TYPE_CAMERA 2 @@ -775,6 +707,34 @@ struct v4l2_control __s32 value; }; +struct v4l2_ext_control +{ + __u32 id; + __u32 reserved2[2]; + union { + __s32 value; + __s64 value64; + void *reserved; + }; +}; + +struct v4l2_ext_controls +{ + __u32 ctrl_class; + __u32 count; + __u32 error_idx; + __u32 reserved[2]; + struct v4l2_ext_control *controls; +}; + +/* Values for ctrl_class field */ +#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */ +#define V4L2_CTRL_CLASS_MPEG 0x00990000 /* MPEG-compression controls */ + +#define V4L2_CTRL_ID_MASK (0x0fffffff) +#define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) +#define V4L2_CTRL_DRIVER_PRIV(id) (((id) & 0xffff) >= 0x1000) + /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ struct v4l2_queryctrl { @@ -801,12 +761,21 @@ struct v4l2_querymenu /* Control flags */ #define V4L2_CTRL_FLAG_DISABLED 0x0001 #define V4L2_CTRL_FLAG_GRABBED 0x0002 +#define V4L2_CTRL_FLAG_READ_ONLY 0x0004 +#define V4L2_CTRL_FLAG_UPDATE 0x0008 +#define V4L2_CTRL_FLAG_INACTIVE 0x0010 +#define V4L2_CTRL_FLAG_SLIDER 0x0020 + +/* Query flag, to be ORed with the control ID */ +#define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000 -/* Control IDs defined by V4L2 */ -#define V4L2_CID_BASE 0x00980900 +/* User-class control IDs defined by V4L2 */ +#define V4L2_CID_BASE (V4L2_CTRL_CLASS_USER | 0x900) +#define V4L2_CID_USER_BASE V4L2_CID_BASE /* IDs reserved for driver specific controls */ #define V4L2_CID_PRIVATE_BASE 0x08000000 +#define V4L2_CID_USER_CLASS (V4L2_CTRL_CLASS_USER | 1) #define V4L2_CID_BRIGHTNESS (V4L2_CID_BASE+0) #define V4L2_CID_CONTRAST (V4L2_CID_BASE+1) #define V4L2_CID_SATURATION (V4L2_CID_BASE+2) @@ -833,6 +802,188 @@ #define V4L2_CID_HCENTER (V4L2_CID_BASE #define V4L2_CID_VCENTER (V4L2_CID_BASE+23) #define V4L2_CID_LASTP1 (V4L2_CID_BASE+24) /* last CID + 1 */ +/* MPEG-class control IDs defined by V4L2 */ +#define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) +#define V4L2_CID_MPEG_CLASS (V4L2_CTRL_CLASS_MPEG | 1) + +/* MPEG streams */ +#define V4L2_CID_MPEG_STREAM_TYPE (V4L2_CID_MPEG_BASE+0) +enum v4l2_mpeg_stream_type { + V4L2_MPEG_STREAM_TYPE_MPEG2_PS = 0, /* MPEG-2 program stream */ + V4L2_MPEG_STREAM_TYPE_MPEG2_TS = 1, /* MPEG-2 transport stream */ + V4L2_MPEG_STREAM_TYPE_MPEG1_SS = 2, /* MPEG-1 system stream */ + V4L2_MPEG_STREAM_TYPE_MPEG2_DVD = 3, /* MPEG-2 DVD-compatible stream */ + V4L2_MPEG_STREAM_TYPE_MPEG1_VCD = 4, /* MPEG-1 VCD-compatible stream */ + V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD = 5, /* MPEG-2 SVCD-compatible stream */ +}; +#define V4L2_CID_MPEG_STREAM_PID_PMT (V4L2_CID_MPEG_BASE+1) +#define V4L2_CID_MPEG_STREAM_PID_AUDIO (V4L2_CID_MPEG_BASE+2) +#define V4L2_CID_MPEG_STREAM_PID_VIDEO (V4L2_CID_MPEG_BASE+3) +#define V4L2_CID_MPEG_STREAM_PID_PCR (V4L2_CID_MPEG_BASE+4) +#define V4L2_CID_MPEG_STREAM_PES_ID_AUDIO (V4L2_CID_MPEG_BASE+5) +#define V4L2_CID_MPEG_STREAM_PES_ID_VIDEO (V4L2_CID_MPEG_BASE+6) +#define V4L2_CID_MPEG_STREAM_VBI_FMT (V4L2_CID_MPEG_BASE+7) +enum v4l2_mpeg_stream_vbi_fmt { + V4L2_MPEG_STREAM_VBI_FMT_NONE = 0, /* No VBI in the MPEG stream */ + V4L2_MPEG_STREAM_VBI_FMT_IVTV = 1, /* VBI in private packets, IVTV format */ +}; + +/* MPEG audio */ +#define V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ (V4L2_CID_MPEG_BASE+100) +enum v4l2_mpeg_audio_sampling_freq { + V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100 = 0, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000 = 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000 = 2, +}; +#define V4L2_CID_MPEG_AUDIO_ENCODING (V4L2_CID_MPEG_BASE+101) +enum v4l2_mpeg_audio_encoding { + V4L2_MPEG_AUDIO_ENCODING_LAYER_1 = 0, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2 = 1, + V4L2_MPEG_AUDIO_ENCODING_LAYER_3 = 2, +}; +#define V4L2_CID_MPEG_AUDIO_L1_BITRATE (V4L2_CID_MPEG_BASE+102) +enum v4l2_mpeg_audio_l1_bitrate { + V4L2_MPEG_AUDIO_L1_BITRATE_32K = 0, + V4L2_MPEG_AUDIO_L1_BITRATE_64K = 1, + V4L2_MPEG_AUDIO_L1_BITRATE_96K = 2, + V4L2_MPEG_AUDIO_L1_BITRATE_128K = 3, + V4L2_MPEG_AUDIO_L1_BITRATE_160K = 4, + V4L2_MPEG_AUDIO_L1_BITRATE_192K = 5, + V4L2_MPEG_AUDIO_L1_BITRATE_224K = 6, + V4L2_MPEG_AUDIO_L1_BITRATE_256K = 7, + V4L2_MPEG_AUDIO_L1_BITRATE_288K = 8, + V4L2_MPEG_AUDIO_L1_BITRATE_320K = 9, + V4L2_MPEG_AUDIO_L1_BITRATE_352K = 10, + V4L2_MPEG_AUDIO_L1_BITRATE_384K = 11, + V4L2_MPEG_AUDIO_L1_BITRATE_416K = 12, + V4L2_MPEG_AUDIO_L1_BITRATE_448K = 13, +}; +#define V4L2_CID_MPEG_AUDIO_L2_BITRATE (V4L2_CID_MPEG_BASE+103) +enum v4l2_mpeg_audio_l2_bitrate { + V4L2_MPEG_AUDIO_L2_BITRATE_32K = 0, + V4L2_MPEG_AUDIO_L2_BITRATE_48K = 1, + V4L2_MPEG_AUDIO_L2_BITRATE_56K = 2, + V4L2_MPEG_AUDIO_L2_BITRATE_64K = 3, + V4L2_MPEG_AUDIO_L2_BITRATE_80K = 4, + V4L2_MPEG_AUDIO_L2_BITRATE_96K = 5, + V4L2_MPEG_AUDIO_L2_BITRATE_112K = 6, + V4L2_MPEG_AUDIO_L2_BITRATE_128K = 7, + V4L2_MPEG_AUDIO_L2_BITRATE_160K = 8, + V4L2_MPEG_AUDIO_L2_BITRATE_192K = 9, + V4L2_MPEG_AUDIO_L2_BITRATE_224K = 10, + V4L2_MPEG_AUDIO_L2_BITRATE_256K = 11, + V4L2_MPEG_AUDIO_L2_BITRATE_320K = 12, + V4L2_MPEG_AUDIO_L2_BITRATE_384K = 13, +}; +#define V4L2_CID_MPEG_AUDIO_L3_BITRATE (V4L2_CID_MPEG_BASE+104) +enum v4l2_mpeg_audio_l3_bitrate { + V4L2_MPEG_AUDIO_L3_BITRATE_32K = 0, + V4L2_MPEG_AUDIO_L3_BITRATE_40K = 1, + V4L2_MPEG_AUDIO_L3_BITRATE_48K = 2, + V4L2_MPEG_AUDIO_L3_BITRATE_56K = 3, + V4L2_MPEG_AUDIO_L3_BITRATE_64K = 4, + V4L2_MPEG_AUDIO_L3_BITRATE_80K = 5, + V4L2_MPEG_AUDIO_L3_BITRATE_96K = 6, + V4L2_MPEG_AUDIO_L3_BITRATE_112K = 7, + V4L2_MPEG_AUDIO_L3_BITRATE_128K = 8, + V4L2_MPEG_AUDIO_L3_BITRATE_160K = 9, + V4L2_MPEG_AUDIO_L3_BITRATE_192K = 10, + V4L2_MPEG_AUDIO_L3_BITRATE_224K = 11, + V4L2_MPEG_AUDIO_L3_BITRATE_256K = 12, + V4L2_MPEG_AUDIO_L3_BITRATE_320K = 13, +}; +#define V4L2_CID_MPEG_AUDIO_MODE (V4L2_CID_MPEG_BASE+105) +enum v4l2_mpeg_audio_mode { + V4L2_MPEG_AUDIO_MODE_STEREO = 0, + V4L2_MPEG_AUDIO_MODE_JOINT_STEREO = 1, + V4L2_MPEG_AUDIO_MODE_DUAL = 2, + V4L2_MPEG_AUDIO_MODE_MONO = 3, +}; +#define V4L2_CID_MPEG_AUDIO_MODE_EXTENSION (V4L2_CID_MPEG_BASE+106) +enum v4l2_mpeg_audio_mode_extension { + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4 = 0, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_8 = 1, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_12 = 2, + V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16 = 3, +}; +#define V4L2_CID_MPEG_AUDIO_EMPHASIS (V4L2_CID_MPEG_BASE+107) +enum v4l2_mpeg_audio_emphasis { + V4L2_MPEG_AUDIO_EMPHASIS_NONE = 0, + V4L2_MPEG_AUDIO_EMPHASIS_50_DIV_15_uS = 1, + V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17 = 2, +}; +#define V4L2_CID_MPEG_AUDIO_CRC (V4L2_CID_MPEG_BASE+108) +enum v4l2_mpeg_audio_crc { + V4L2_MPEG_AUDIO_CRC_NONE = 0, + V4L2_MPEG_AUDIO_CRC_CRC16 = 1, +}; + +/* MPEG video */ +#define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) +enum v4l2_mpeg_video_encoding { + V4L2_MPEG_VIDEO_ENCODING_MPEG_1 = 0, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2 = 1, +}; +#define V4L2_CID_MPEG_VIDEO_ASPECT (V4L2_CID_MPEG_BASE+201) +enum v4l2_mpeg_video_aspect { + V4L2_MPEG_VIDEO_ASPECT_1x1 = 0, + V4L2_MPEG_VIDEO_ASPECT_4x3 = 1, + V4L2_MPEG_VIDEO_ASPECT_16x9 = 2, + V4L2_MPEG_VIDEO_ASPECT_221x100 = 3, +}; +#define V4L2_CID_MPEG_VIDEO_B_FRAMES (V4L2_CID_MPEG_BASE+202) +#define V4L2_CID_MPEG_VIDEO_GOP_SIZE (V4L2_CID_MPEG_BASE+203) +#define V4L2_CID_MPEG_VIDEO_GOP_CLOSURE (V4L2_CID_MPEG_BASE+204) +#define V4L2_CID_MPEG_VIDEO_PULLDOWN (V4L2_CID_MPEG_BASE+205) +#define V4L2_CID_MPEG_VIDEO_BITRATE_MODE (V4L2_CID_MPEG_BASE+206) +enum v4l2_mpeg_video_bitrate_mode { + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR = 0, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR = 1, +}; +#define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207) +#define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208) +#define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209) + +/* MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */ +#define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) +#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+0) +enum v4l2_mpeg_cx2341x_video_spatial_filter_mode { + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL = 0, + V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO = 1, +}; +#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+1) +#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+2) +enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type { + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_OFF = 0, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR = 1, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_VERT = 2, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_HV_SEPARABLE = 3, + V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_2D_SYM_NON_SEPARABLE = 4, +}; +#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+3) +enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type { + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_OFF = 0, + V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR = 1, +}; +#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE (V4L2_CID_MPEG_CX2341X_BASE+4) +enum v4l2_mpeg_cx2341x_video_temporal_filter_mode { + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL = 0, + V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO = 1, +}; +#define V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER (V4L2_CID_MPEG_CX2341X_BASE+5) +#define V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE (V4L2_CID_MPEG_CX2341X_BASE+6) +enum v4l2_mpeg_cx2341x_video_median_filter_type { + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF = 0, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR = 1, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_VERT = 2, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_HOR_VERT = 3, + V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_DIAG = 4, +}; +#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+7) +#define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+8) +#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9) +#define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10) + /* * T U N I N G */ @@ -904,6 +1055,7 @@ struct v4l2_audio __u32 mode; __u32 reserved[2]; }; + /* Flags for the 'capability' field */ #define V4L2_AUDCAP_STEREO 0x00001 #define V4L2_AUDCAP_AVL 0x00002 @@ -927,7 +1079,6 @@ struct v4l2_audioout */ /* Raw VBI */ - struct v4l2_vbi_format { __u32 sampling_rate; /* in 1 Hz */ @@ -1034,8 +1185,6 @@ struct v4l2_streamparm } parm; }; - - /* * I O C T L C O D E S F O R V I D E O D E V I C E S * @@ -1045,8 +1194,10 @@ #define VIDIOC_RESERVED _IO ('V', 1) #define VIDIOC_ENUM_FMT _IOWR ('V', 2, struct v4l2_fmtdesc) #define VIDIOC_G_FMT _IOWR ('V', 4, struct v4l2_format) #define VIDIOC_S_FMT _IOWR ('V', 5, struct v4l2_format) +#ifdef __KERNEL__ #define VIDIOC_G_MPEGCOMP _IOR ('V', 6, struct v4l2_mpeg_compression) #define VIDIOC_S_MPEGCOMP _IOW ('V', 7, struct v4l2_mpeg_compression) +#endif #define VIDIOC_REQBUFS _IOWR ('V', 8, struct v4l2_requestbuffers) #define VIDIOC_QUERYBUF _IOWR ('V', 9, struct v4l2_buffer) #define VIDIOC_G_FBUF _IOR ('V', 10, struct v4l2_framebuffer) @@ -1096,7 +1247,11 @@ #if 1 #define VIDIOC_G_SLICED_VBI_CAP _IOR ('V', 69, struct v4l2_sliced_vbi_cap) #endif #define VIDIOC_LOG_STATUS _IO ('V', 70) +#define VIDIOC_G_EXT_CTRLS _IOWR ('V', 71, struct v4l2_ext_controls) +#define VIDIOC_S_EXT_CTRLS _IOWR ('V', 72, struct v4l2_ext_controls) +#define VIDIOC_TRY_EXT_CTRLS _IOWR ('V', 73, struct v4l2_ext_controls) +#ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ #define VIDIOC_OVERLAY_OLD _IOWR ('V', 14, int) #define VIDIOC_S_PARM_OLD _IOW ('V', 22, struct v4l2_streamparm) @@ -1104,57 +1259,10 @@ #define VIDIOC_S_CTRL_OLD _IOW (' #define VIDIOC_G_AUDIO_OLD _IOWR ('V', 33, struct v4l2_audio) #define VIDIOC_G_AUDOUT_OLD _IOWR ('V', 49, struct v4l2_audioout) #define VIDIOC_CROPCAP_OLD _IOR ('V', 58, struct v4l2_cropcap) - -#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ - - -#ifdef __KERNEL__ -/* - * - * V 4 L 2 D R I V E R H E L P E R A P I - * - * Some commonly needed functions for drivers (v4l2-common.o module) - */ -#include - -/* Video standard functions */ -extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs); -extern int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, char *name); - -/* prority handling */ -struct v4l2_prio_state { - atomic_t prios[4]; -}; -int v4l2_prio_init(struct v4l2_prio_state *global); -int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, - enum v4l2_priority new); -int v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local); -int v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority *local); -enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global); -int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local); - -/* names for fancy debug output */ -extern char *v4l2_field_names[]; -extern char *v4l2_type_names[]; - -/* Compatibility layer interface -- v4l1-compat module */ -typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); - -#ifdef CONFIG_VIDEO_V4L1_COMPAT -int v4l_compat_translate_ioctl(struct inode *inode, struct file *file, - int cmd, void *arg, v4l2_kioctl driver_ioctl); -#else -#define v4l_compat_translate_ioctl(inode,file,cmd,arg,ioctl) -EINVAL #endif -/* 32 Bits compatibility layer for 64 bits processors */ -extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, - unsigned long arg); - +#define BASE_VIDIOC_PRIVATE 192 /* 192-255 are private */ -#endif /* __KERNEL__ */ #endif /* __LINUX_VIDEODEV2_H */ /* diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 1d5577b..f6024ab 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -4,10 +4,13 @@ #define _LINUX_VMALLOC_H #include #include /* pgprot_t */ +struct vm_area_struct; + /* bits in vm_struct->flags */ #define VM_IOREMAP 0x00000001 /* ioremap() and friends */ #define VM_ALLOC 0x00000002 /* vmalloc() */ #define VM_MAP 0x00000004 /* vmap()ed pages */ +#define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */ /* bits [20..32] reserved for arch specific ioremap internals */ /* @@ -32,9 +35,11 @@ struct vm_struct { * Highlevel APIs for driver use */ extern void *vmalloc(unsigned long size); +extern void *vmalloc_user(unsigned long size); extern void *vmalloc_node(unsigned long size, int node); extern void *vmalloc_exec(unsigned long size); extern void *vmalloc_32(unsigned long size); +extern void *vmalloc_32_user(unsigned long size); extern void *__vmalloc(unsigned long size, gfp_t gfp_mask, pgprot_t prot); extern void *__vmalloc_area(struct vm_struct *area, gfp_t gfp_mask, pgprot_t prot); @@ -45,6 +50,9 @@ extern void vfree(void *addr); extern void *vmap(struct page **pages, unsigned int count, unsigned long flags, pgprot_t prot); extern void vunmap(void *addr); + +extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, + unsigned long pgoff); /* * Lowlevel-APIs (not for driver use!) diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h new file mode 100644 index 0000000..3e0daf5 --- /dev/null +++ b/include/linux/vmstat.h @@ -0,0 +1,215 @@ +#ifndef _LINUX_VMSTAT_H +#define _LINUX_VMSTAT_H + +#include +#include +#include +#include +#include + +#ifdef CONFIG_VM_EVENT_COUNTERS +/* + * Light weight per cpu counter implementation. + * + * Counters should only be incremented and no critical kernel component + * should rely on the counter values. + * + * Counters are handled completely inline. On many platforms the code + * generated will simply be the increment of a global address. + */ + +#define FOR_ALL_ZONES(x) x##_DMA, x##_DMA32, x##_NORMAL, x##_HIGH + +enum vm_event_item { PGPGIN, PGPGOUT, PSWPIN, PSWPOUT, + FOR_ALL_ZONES(PGALLOC), + PGFREE, PGACTIVATE, PGDEACTIVATE, + PGFAULT, PGMAJFAULT, + FOR_ALL_ZONES(PGREFILL), + FOR_ALL_ZONES(PGSTEAL), + FOR_ALL_ZONES(PGSCAN_KSWAPD), + FOR_ALL_ZONES(PGSCAN_DIRECT), + PGINODESTEAL, SLABS_SCANNED, KSWAPD_STEAL, KSWAPD_INODESTEAL, + PAGEOUTRUN, ALLOCSTALL, PGROTATED, + NR_VM_EVENT_ITEMS +}; + +struct vm_event_state { + unsigned long event[NR_VM_EVENT_ITEMS]; +}; + +DECLARE_PER_CPU(struct vm_event_state, vm_event_states); + +static inline void __count_vm_event(enum vm_event_item item) +{ + __get_cpu_var(vm_event_states.event[item])++; +} + +static inline void count_vm_event(enum vm_event_item item) +{ + get_cpu_var(vm_event_states.event[item])++; + put_cpu(); +} + +static inline void __count_vm_events(enum vm_event_item item, long delta) +{ + __get_cpu_var(vm_event_states.event[item]) += delta; +} + +static inline void count_vm_events(enum vm_event_item item, long delta) +{ + get_cpu_var(vm_event_states.event[item])++; + put_cpu(); +} + +extern void all_vm_events(unsigned long *); +extern void vm_events_fold_cpu(int cpu); + +#else + +/* Disable counters */ +#define get_cpu_vm_events(e) 0L +#define count_vm_event(e) do { } while (0) +#define count_vm_events(e,d) do { } while (0) +#define __count_vm_event(e) do { } while (0) +#define __count_vm_events(e,d) do { } while (0) +#define vm_events_fold_cpu(x) do { } while (0) + +#endif /* CONFIG_VM_EVENT_COUNTERS */ + +#define __count_zone_vm_events(item, zone, delta) \ + __count_vm_events(item##_DMA + zone_idx(zone), delta) + +/* + * Zone based page accounting with per cpu differentials. + */ +extern atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; + +static inline void zone_page_state_add(long x, struct zone *zone, + enum zone_stat_item item) +{ + atomic_long_add(x, &zone->vm_stat[item]); + atomic_long_add(x, &vm_stat[item]); +} + +static inline unsigned long global_page_state(enum zone_stat_item item) +{ + long x = atomic_long_read(&vm_stat[item]); +#ifdef CONFIG_SMP + if (x < 0) + x = 0; +#endif + return x; +} + +static inline unsigned long zone_page_state(struct zone *zone, + enum zone_stat_item item) +{ + long x = atomic_long_read(&zone->vm_stat[item]); +#ifdef CONFIG_SMP + if (x < 0) + x = 0; +#endif + return x; +} + +#ifdef CONFIG_NUMA +/* + * Determine the per node value of a stat item. This function + * is called frequently in a NUMA machine, so try to be as + * frugal as possible. + */ +static inline unsigned long node_page_state(int node, + enum zone_stat_item item) +{ + struct zone *zones = NODE_DATA(node)->node_zones; + + return +#ifndef CONFIG_DMA_IS_NORMAL +#if !defined(CONFIG_DMA_IS_DMA32) && BITS_PER_LONG >= 64 + zone_page_state(&zones[ZONE_DMA32], item) + +#endif + zone_page_state(&zones[ZONE_NORMAL], item) + +#endif +#ifdef CONFIG_HIGHMEM + zone_page_state(&zones[ZONE_HIGHMEM], item) + +#endif + zone_page_state(&zones[ZONE_DMA], item); +} + +extern void zone_statistics(struct zonelist *, struct zone *); + +#else + +#define node_page_state(node, item) global_page_state(item) +#define zone_statistics(_zl,_z) do { } while (0) + +#endif /* CONFIG_NUMA */ + +#define __add_zone_page_state(__z, __i, __d) \ + __mod_zone_page_state(__z, __i, __d) +#define __sub_zone_page_state(__z, __i, __d) \ + __mod_zone_page_state(__z, __i,-(__d)) + +#define add_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, __d) +#define sub_zone_page_state(__z, __i, __d) mod_zone_page_state(__z, __i, -(__d)) + +static inline void zap_zone_vm_stats(struct zone *zone) +{ + memset(zone->vm_stat, 0, sizeof(zone->vm_stat)); +} + +extern void inc_zone_state(struct zone *, enum zone_stat_item); + +#ifdef CONFIG_SMP +void __mod_zone_page_state(struct zone *, enum zone_stat_item item, int); +void __inc_zone_page_state(struct page *, enum zone_stat_item); +void __dec_zone_page_state(struct page *, enum zone_stat_item); + +void mod_zone_page_state(struct zone *, enum zone_stat_item, int); +void inc_zone_page_state(struct page *, enum zone_stat_item); +void dec_zone_page_state(struct page *, enum zone_stat_item); + +extern void inc_zone_state(struct zone *, enum zone_stat_item); + +void refresh_cpu_vm_stats(int); +void refresh_vm_stats(void); + +#else /* CONFIG_SMP */ + +/* + * We do not maintain differentials in a single processor configuration. + * The functions directly modify the zone and global counters. + */ +static inline void __mod_zone_page_state(struct zone *zone, + enum zone_stat_item item, int delta) +{ + zone_page_state_add(delta, zone, item); +} + +static inline void __inc_zone_page_state(struct page *page, + enum zone_stat_item item) +{ + atomic_long_inc(&page_zone(page)->vm_stat[item]); + atomic_long_inc(&vm_stat[item]); +} + +static inline void __dec_zone_page_state(struct page *page, + enum zone_stat_item item) +{ + atomic_long_dec(&page_zone(page)->vm_stat[item]); + atomic_long_dec(&vm_stat[item]); +} + +/* + * We only use atomic operations to update counters. So there is no need to + * disable interrupts. + */ +#define inc_zone_page_state __inc_zone_page_state +#define dec_zone_page_state __dec_zone_page_state +#define mod_zone_page_state __mod_zone_page_state + +static inline void refresh_cpu_vm_stats(int cpu) { } +static inline void refresh_vm_stats(void) { } +#endif + +#endif /* _LINUX_VMSTAT_H */ diff --git a/include/linux/vt_buffer.h b/include/linux/vt_buffer.h index 1f7ba36..057db7d 100644 --- a/include/linux/vt_buffer.h +++ b/include/linux/vt_buffer.h @@ -13,7 +13,6 @@ #ifndef _LINUX_VT_BUFFER_H_ #define _LINUX_VT_BUFFER_H_ -#include #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_MDA_CONSOLE) #include diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index fab5aed..940d026 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -6,7 +6,6 @@ #define _VT_KERN_H * with information needed by the vt package */ -#include #include #include #include diff --git a/include/linux/wait.h b/include/linux/wait.h index d285182..794be7a 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -19,7 +19,6 @@ #define P_PGID 2 #ifdef __KERNEL__ -#include #include #include #include @@ -69,7 +68,7 @@ #define DECLARE_WAITQUEUE(name, tsk) wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk) #define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \ - .lock = SPIN_LOCK_UNLOCKED, \ + .lock = __SPIN_LOCK_UNLOCKED(name.lock), \ .task_list = { &(name).task_list, &(name).task_list } } #define DECLARE_WAIT_QUEUE_HEAD(name) \ @@ -78,9 +77,15 @@ #define DECLARE_WAIT_QUEUE_HEAD(name) \ #define __WAIT_BIT_KEY_INITIALIZER(word, bit) \ { .flags = word, .bit_nr = bit, } +/* + * lockdep: we want one lock-class for all waitqueue locks. + */ +extern struct lock_class_key waitqueue_lock_key; + static inline void init_waitqueue_head(wait_queue_head_t *q) { spin_lock_init(&q->lock); + lockdep_set_class(&q->lock, &waitqueue_lock_key); INIT_LIST_HEAD(&q->task_list); } diff --git a/include/linux/wanrouter.h b/include/linux/wanrouter.h index 1b6b76a..2cd0501 100644 --- a/include/linux/wanrouter.h +++ b/include/linux/wanrouter.h @@ -44,8 +44,6 @@ * Jan 02, 1997 Gene Kozin Initial version (based on wanpipe.h). *****************************************************************************/ -#include /* Support for SMP Locking */ - #ifndef _ROUTER_H #define _ROUTER_H @@ -457,6 +455,8 @@ #ifdef __KERNEL__ #include /* support for device drivers */ #include /* proc filesystem pragmatics */ #include /* support for network drivers */ +#include /* Support for SMP Locking */ + /*---------------------------------------------------------------------------- * WAN device data space. */ diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 1192ed8..011bcfe 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -28,6 +28,9 @@ #define WDIOC_SETOPTIONS _IOR(WATCHDOG_I #define WDIOC_KEEPALIVE _IOR(WATCHDOG_IOCTL_BASE, 5, int) #define WDIOC_SETTIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 6, int) #define WDIOC_GETTIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 7, int) +#define WDIOC_SETPRETIMEOUT _IOWR(WATCHDOG_IOCTL_BASE, 8, int) +#define WDIOC_GETPRETIMEOUT _IOR(WATCHDOG_IOCTL_BASE, 9, int) +#define WDIOC_GETTIMELEFT _IOR(WATCHDOG_IOCTL_BASE, 10, int) #define WDIOF_UNKNOWN -1 /* Unknown flag error */ #define WDIOS_UNKNOWN -1 /* Unknown status error */ @@ -38,9 +41,10 @@ #define WDIOF_EXTERN1 0x0004 /* Externa #define WDIOF_EXTERN2 0x0008 /* External relay 2 */ #define WDIOF_POWERUNDER 0x0010 /* Power bad/power fault */ #define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */ -#define WDIOF_POWEROVER 0x0040 /* Power over voltage */ -#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ -#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */ +#define WDIOF_POWEROVER 0x0040 /* Power over voltage */ +#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ +#define WDIOF_MAGICCLOSE 0x0100 /* Supports magic close char */ +#define WDIOF_PRETIMEOUT 0x0200 /* Pretimeout (in seconds), get/set */ #define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ #define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 957c21c..9bca353 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -63,6 +63,8 @@ extern void destroy_workqueue(struct wor extern int FASTCALL(queue_work(struct workqueue_struct *wq, struct work_struct *work)); extern int FASTCALL(queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)); +extern int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct work_struct *work, unsigned long delay); extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq)); extern int FASTCALL(schedule_work(struct work_struct *work)); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 56f92fc..9e38b56 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -50,14 +50,15 @@ struct writeback_control { * a hint that the filesystem need only write out the pages inside that * byterange. The byte at `end' is included in the writeout request. */ - loff_t start; - loff_t end; + loff_t range_start; + loff_t range_end; unsigned nonblocking:1; /* Don't get stuck on request queues */ unsigned encountered_congestion:1; /* An output: a queue is full */ unsigned for_kupdate:1; /* A kupdate writeback */ unsigned for_reclaim:1; /* Invoked from the page allocator */ unsigned for_writepages:1; /* This is a writepages() call */ + unsigned range_cyclic:1; /* range_start is cyclic */ }; /* diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h index 6b42cc4..46a15c7 100644 --- a/include/linux/xfrm.h +++ b/include/linux/xfrm.h @@ -118,6 +118,10 @@ enum XFRM_SHARE_UNIQUE /* Use once */ }; +#define XFRM_MODE_TRANSPORT 0 +#define XFRM_MODE_TUNNEL 1 +#define XFRM_MODE_MAX 2 + /* Netlink configuration messages. */ enum { XFRM_MSG_BASE = 0x10, diff --git a/include/linux/zconf.h b/include/linux/zconf.h index f1cfd66..0beb75e 100644 --- a/include/linux/zconf.h +++ b/include/linux/zconf.h @@ -35,6 +35,18 @@ #ifndef MAX_WBITS # define MAX_WBITS 15 /* 32K LZ77 window */ #endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + /* Type declarations */ typedef unsigned char Byte; /* 8 bits */ diff --git a/include/linux/zlib.h b/include/linux/zlib.h index 4fa32f0..9e3192a 100644 --- a/include/linux/zlib.h +++ b/include/linux/zlib.h @@ -1,7 +1,6 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.1.3, July 9th, 1998 - Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -24,7 +23,7 @@ The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). */ @@ -33,7 +32,22 @@ #define _ZLIB_H #include -#define ZLIB_VERSION "1.1.3" +/* zlib deflate based on ZLIB_VERSION "1.1.3" */ +/* zlib inflate based on ZLIB_VERSION "1.2.3" */ + +/* + This is a modified version of zlib for use inside the Linux kernel. + The main changes are to perform all memory allocation in advance. + + Inflation Changes: + * Z_PACKET_FLUSH is added and used by ppp_deflate. Before returning + this checks there is no more input data available and the next data + is a STORED block. It also resets the mode to be read for the next + data, all as per PPP requirements. + * Addition of zlib_inflateIncomp which copies incompressible data into + the history window and adjusts the accoutning without calling + zlib_inflate itself to inflate the data. +*/ /* The 'zlib' compression library provides in-memory compression and @@ -48,9 +62,18 @@ #define ZLIB_VERSION "1.1.3" application must provide more input and/or consume the output (providing more output space) before each call. + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + The library also supports reading and writing files in gzip (.gz) format with an interface similar to that of stdio. + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + The library does not install any signal handler. The decoder checks the consistency of the compressed data, so the library should never crash even in case of corrupted input. @@ -119,7 +142,8 @@ #define Z_PACKET_FLUSH 2 #define Z_SYNC_FLUSH 3 #define Z_FULL_FLUSH 4 #define Z_FINISH 5 -/* Allowed flush values; see deflate() below for details */ +#define Z_BLOCK 6 /* Only for inflate at present */ +/* Allowed flush values; see deflate() and inflate() below for details */ #define Z_OK 0 #define Z_STREAM_END 1 @@ -155,13 +179,6 @@ #define Z_DEFLATED 8 /* basic functions */ -extern const char * zlib_zlibVersion (void); -/* The application can compare zlibVersion and ZLIB_VERSION for consistency. - If the first character differs, the library code actually used is - not compatible with the zlib.h header file used by the application. - This check is automatically made by deflateInit and inflateInit. - */ - extern int zlib_deflate_workspacesize (void); /* Returns the number of bytes that needs to be allocated for a per- @@ -315,9 +332,9 @@ extern int zlib_inflateInit (z_streamp s extern int zlib_inflate (z_streamp strm, int flush); /* inflate decompresses as much data as possible, and stops when the input - buffer becomes empty or the output buffer becomes full. It may some - introduce some output latency (reading input without producing any output) - except when forced to flush. + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. The detailed semantics are as follows. inflate performs one or both of the following actions: @@ -341,11 +358,26 @@ extern int zlib_inflate (z_streamp strm, must be called again after making room in the output buffer because there might be more output pending. - If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much - output as possible to the output buffer. The flushing behavior of inflate is - not specified for values of the flush parameter other than Z_SYNC_FLUSH - and Z_FINISH, but the current implementation actually flushes as much output - as possible anyway. + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. inflate() should normally be called until it returns Z_STREAM_END or an error. However if all decompression is to be performed in a single step @@ -355,29 +387,44 @@ extern int zlib_inflate (z_streamp strm, uncompressed data. (The size of the uncompressed data may have been saved by the compressor for this purpose.) The next operation on this stream must be inflateEnd to deallocate the decompression state. The use of Z_FINISH - is never required, but can be used to inform inflate that a faster routine + is never required, but can be used to inform inflate that a faster approach may be used for the single inflate() call. - If a preset dictionary is needed at this point (see inflateSetDictionary - below), inflate sets strm-adler to the adler32 checksum of the - dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise - it sets strm->adler to the adler32 checksum of all output produced - so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or - an error code as described below. At the end of the stream, inflate() - checks that its computed adler32 checksum is equal to that saved by the - compressor and returns Z_STREAM_END only if the checksum is correct. + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has been reached and all uncompressed output has been produced, Z_NEED_DICT if a preset dictionary is needed at this point, Z_DATA_ERROR if the input data was - corrupted (input stream not conforming to the zlib format or incorrect - adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent - (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not - enough memory, Z_BUF_ERROR if no progress is possible or if there was not - enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR - case, the application may then call inflateSync to look for a good - compression block. + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. */ @@ -547,16 +594,36 @@ extern int inflateInit2 (z_streamp strm, The windowBits parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 8..15 for this version of the library. The default value is 15 if inflateInit is used - instead. If a compressed stream with a larger window size is given as - input, inflate() will return with the error code Z_DATA_ERROR instead of - trying to allocate a larger window. - - inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough - memory, Z_STREAM_ERROR if a parameter is invalid (such as a negative - memLevel). msg is set to null if there is no error message. inflateInit2 - does not perform any decompression apart from reading the zlib header if - present: this will be done by inflate(). (So next_in and avail_in may be - modified, but next_out and avail_out are unchanged.) + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) */ extern int zlib_inflateSetDictionary (z_streamp strm, @@ -564,16 +631,19 @@ extern int zlib_inflateSetDictionary (z_ uInt dictLength); /* Initializes the decompression dictionary from the given uncompressed byte - sequence. This function must be called immediately after a call of inflate - if this call returned Z_NEED_DICT. The dictionary chosen by the compressor - can be determined from the Adler32 value returned by this call of - inflate. The compressor and decompressor must use exactly the same - dictionary (see deflateSetDictionary). + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (such as NULL dictionary) or the stream state is inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the - expected one (incorrect Adler32 value). inflateSetDictionary does not + expected one (incorrect adler32 value). inflateSetDictionary does not perform any decompression: this will be done by subsequent calls of inflate(). */ @@ -614,40 +684,19 @@ extern int zlib_inflateIncomp (z_stream containing the data at next_in (except that the data is not output). */ - /* various hacks, don't look :) */ - -/* deflateInit and inflateInit are macros to allow checking the zlib version - * and the compiler's view of z_stream: - */ -extern int zlib_deflateInit_ (z_streamp strm, int level, - const char *version, int stream_size); -extern int zlib_inflateInit_ (z_streamp strm, - const char *version, int stream_size); -extern int zlib_deflateInit2_ (z_streamp strm, int level, int method, - int windowBits, int memLevel, - int strategy, const char *version, - int stream_size); -extern int zlib_inflateInit2_ (z_streamp strm, int windowBits, - const char *version, int stream_size); #define zlib_deflateInit(strm, level) \ - zlib_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) + zlib_deflateInit2((strm), (level), Z_DEFLATED, MAX_WBITS, \ + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY) #define zlib_inflateInit(strm) \ - zlib_inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) -#define zlib_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ - zlib_deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) -#define zlib_inflateInit2(strm, windowBits) \ - zlib_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + zlib_inflateInit2((strm), DEF_WBITS) +extern int zlib_deflateInit2(z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy); +extern int zlib_inflateInit2(z_streamp strm, int windowBits); #if !defined(_Z_UTIL_H) && !defined(NO_DUMMY_DECL) struct internal_state {int dummy;}; /* hack for buggy compilers */ #endif -extern const char * zlib_zError (int err); -#if 0 -extern int zlib_inflateSyncPoint (z_streamp z); -#endif -extern const uLong * zlib_get_crc_table (void); - #endif /* _ZLIB_H */ diff --git a/include/linux/zorro.h b/include/linux/zorro.h index 2f135cf..913bfc2 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -11,8 +11,6 @@ #ifndef _LINUX_ZORRO_H #define _LINUX_ZORRO_H -#ifndef __ASSEMBLY__ - #include @@ -112,45 +110,6 @@ struct ConfigDev { __u32 cd_Unused[4]; /* for whatever the driver wants */ } __attribute__ ((packed)); -#else /* __ASSEMBLY__ */ - -LN_Succ = 0 -LN_Pred = LN_Succ+4 -LN_Type = LN_Pred+4 -LN_Pri = LN_Type+1 -LN_Name = LN_Pri+1 -LN_sizeof = LN_Name+4 - -ER_Type = 0 -ER_Product = ER_Type+1 -ER_Flags = ER_Product+1 -ER_Reserved03 = ER_Flags+1 -ER_Manufacturer = ER_Reserved03+1 -ER_SerialNumber = ER_Manufacturer+2 -ER_InitDiagVec = ER_SerialNumber+4 -ER_Reserved0c = ER_InitDiagVec+2 -ER_Reserved0d = ER_Reserved0c+1 -ER_Reserved0e = ER_Reserved0d+1 -ER_Reserved0f = ER_Reserved0e+1 -ER_sizeof = ER_Reserved0f+1 - -CD_Node = 0 -CD_Flags = CD_Node+LN_sizeof -CD_Pad = CD_Flags+1 -CD_Rom = CD_Pad+1 -CD_BoardAddr = CD_Rom+ER_sizeof -CD_BoardSize = CD_BoardAddr+4 -CD_SlotAddr = CD_BoardSize+4 -CD_SlotSize = CD_SlotAddr+2 -CD_Driver = CD_SlotSize+2 -CD_NextCD = CD_Driver+4 -CD_Unused = CD_NextCD+4 -CD_sizeof = CD_Unused+(4*4) - -#endif /* __ASSEMBLY__ */ - -#ifndef __ASSEMBLY__ - #define ZORRO_NUM_AUTO 16 #ifdef __KERNEL__ @@ -290,7 +249,6 @@ #define Z2RAM_CHUNKMASK (0x0000ffff) #define Z2RAM_CHUNKSHIFT (16) -#endif /* !__ASSEMBLY__ */ #endif /* __KERNEL__ */ #endif /* _LINUX_ZORRO_H */ diff --git a/include/linux/zutil.h b/include/linux/zutil.h index ee0c59c..6adfa9a 100644 --- a/include/linux/zutil.h +++ b/include/linux/zutil.h @@ -23,18 +23,6 @@ typedef unsigned long ulg; /* common constants */ -#ifndef DEF_WBITS -# define DEF_WBITS MAX_WBITS -#endif -/* default windowBits for decompression. MAX_WBITS is for compression only */ - -#if MAX_MEM_LEVEL >= 8 -# define DEF_MEM_LEVEL 8 -#else -# define DEF_MEM_LEVEL MAX_MEM_LEVEL -#endif -/* default memLevel */ - #define STORED_BLOCK 0 #define STATIC_TREES 1 #define DYN_TREES 2 diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h new file mode 100644 index 0000000..d91d88f --- /dev/null +++ b/include/media/cx2341x.h @@ -0,0 +1,195 @@ +/* + cx23415/6 header containing common defines. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CX2341X_H +#define CX2341X_H + +enum cx2341x_port { + CX2341X_PORT_MEMORY = 0, + CX2341X_PORT_STREAMING = 1, + CX2341X_PORT_SERIAL = 2 +}; + +enum cx2341x_cap { + CX2341X_CAP_HAS_SLICED_VBI = 1 << 0, +}; + +struct cx2341x_mpeg_params { + /* misc */ + u32 capabilities; + enum cx2341x_port port; + u16 width; + u16 height; + u16 is_50hz; + + /* stream */ + enum v4l2_mpeg_stream_type stream_type; + enum v4l2_mpeg_stream_vbi_fmt stream_vbi_fmt; + + /* audio */ + enum v4l2_mpeg_audio_sampling_freq audio_sampling_freq; + enum v4l2_mpeg_audio_encoding audio_encoding; + enum v4l2_mpeg_audio_l2_bitrate audio_l2_bitrate; + enum v4l2_mpeg_audio_mode audio_mode; + enum v4l2_mpeg_audio_mode_extension audio_mode_extension; + enum v4l2_mpeg_audio_emphasis audio_emphasis; + enum v4l2_mpeg_audio_crc audio_crc; + u8 audio_properties; + + /* video */ + enum v4l2_mpeg_video_encoding video_encoding; + enum v4l2_mpeg_video_aspect video_aspect; + u16 video_b_frames; + u16 video_gop_size; + u16 video_gop_closure; + u16 video_pulldown; + enum v4l2_mpeg_video_bitrate_mode video_bitrate_mode; + u32 video_bitrate; + u32 video_bitrate_peak; + u16 video_temporal_decimation; + + /* encoding filters */ + enum v4l2_mpeg_cx2341x_video_spatial_filter_mode video_spatial_filter_mode; + u16 video_spatial_filter; + enum v4l2_mpeg_cx2341x_video_luma_spatial_filter_type video_luma_spatial_filter_type; + enum v4l2_mpeg_cx2341x_video_chroma_spatial_filter_type video_chroma_spatial_filter_type; + enum v4l2_mpeg_cx2341x_video_temporal_filter_mode video_temporal_filter_mode; + u16 video_temporal_filter; + enum v4l2_mpeg_cx2341x_video_median_filter_type video_median_filter_type; + u16 video_luma_median_filter_top; + u16 video_luma_median_filter_bottom; + u16 video_chroma_median_filter_top; + u16 video_chroma_median_filter_bottom; +}; + +#define CX2341X_MBOX_MAX_DATA 16 + +extern const u32 cx2341x_mpeg_ctrls[]; +typedef int (*cx2341x_mbox_func)(void *priv, int cmd, int in, int out, + u32 data[CX2341X_MBOX_MAX_DATA]); +int cx2341x_update(void *priv, cx2341x_mbox_func func, + const struct cx2341x_mpeg_params *old, + const struct cx2341x_mpeg_params *new); +int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, + struct v4l2_queryctrl *qctrl); +const char **cx2341x_ctrl_get_menu(u32 id); +int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, + struct v4l2_ext_controls *ctrls, unsigned int cmd); +void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p); +void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); + +/* Firmware names */ +#define CX2341X_FIRM_ENC_FILENAME "v4l-cx2341x-enc.fw" +/* Decoder firmware for the cx23415 only */ +#define CX2341X_FIRM_DEC_FILENAME "v4l-cx2341x-dec.fw" + +/* Firmware API commands */ + +/* MPEG decoder API, specific to the cx23415 */ +#define CX2341X_DEC_PING_FW 0x00 +#define CX2341X_DEC_START_PLAYBACK 0x01 +#define CX2341X_DEC_STOP_PLAYBACK 0x02 +#define CX2341X_DEC_SET_PLAYBACK_SPEED 0x03 +#define CX2341X_DEC_STEP_VIDEO 0x05 +#define CX2341X_DEC_SET_DMA_BLOCK_SIZE 0x08 +#define CX2341X_DEC_GET_XFER_INFO 0x09 +#define CX2341X_DEC_GET_DMA_STATUS 0x0a +#define CX2341X_DEC_SCHED_DMA_FROM_HOST 0x0b +#define CX2341X_DEC_PAUSE_PLAYBACK 0x0d +#define CX2341X_DEC_HALT_FW 0x0e +#define CX2341X_DEC_SET_STANDARD 0x10 +#define CX2341X_DEC_GET_VERSION 0x11 +#define CX2341X_DEC_SET_STREAM_INPUT 0x14 +#define CX2341X_DEC_GET_TIMING_INFO 0x15 +#define CX2341X_DEC_SET_AUDIO_MODE 0x16 +#define CX2341X_DEC_SET_EVENT_NOTIFICATION 0x17 +#define CX2341X_DEC_SET_DISPLAY_BUFFERS 0x18 +#define CX2341X_DEC_EXTRACT_VBI 0x19 +#define CX2341X_DEC_SET_DECODER_SOURCE 0x1a +#define CX2341X_DEC_SET_AUDIO_OUTPUT 0x1b +#define CX2341X_DEC_SET_AV_DELAY 0x1c +#define CX2341X_DEC_SET_PREBUFFERING 0x1e + +/* MPEG encoder API */ +#define CX2341X_ENC_PING_FW 0x80 +#define CX2341X_ENC_START_CAPTURE 0x81 +#define CX2341X_ENC_STOP_CAPTURE 0x82 +#define CX2341X_ENC_SET_AUDIO_ID 0x89 +#define CX2341X_ENC_SET_VIDEO_ID 0x8b +#define CX2341X_ENC_SET_PCR_ID 0x8d +#define CX2341X_ENC_SET_FRAME_RATE 0x8f +#define CX2341X_ENC_SET_FRAME_SIZE 0x91 +#define CX2341X_ENC_SET_BIT_RATE 0x95 +#define CX2341X_ENC_SET_GOP_PROPERTIES 0x97 +#define CX2341X_ENC_SET_ASPECT_RATIO 0x99 +#define CX2341X_ENC_SET_DNR_FILTER_MODE 0x9b +#define CX2341X_ENC_SET_DNR_FILTER_PROPS 0x9d +#define CX2341X_ENC_SET_CORING_LEVELS 0x9f +#define CX2341X_ENC_SET_SPATIAL_FILTER_TYPE 0xa1 +#define CX2341X_ENC_SET_3_2_PULLDOWN 0xb1 +#define CX2341X_ENC_SET_VBI_LINE 0xb7 +#define CX2341X_ENC_SET_STREAM_TYPE 0xb9 +#define CX2341X_ENC_SET_OUTPUT_PORT 0xbb +#define CX2341X_ENC_SET_AUDIO_PROPERTIES 0xbd +#define CX2341X_ENC_HALT_FW 0xc3 +#define CX2341X_ENC_GET_VERSION 0xc4 +#define CX2341X_ENC_SET_GOP_CLOSURE 0xc5 +#define CX2341X_ENC_GET_SEQ_END 0xc6 +#define CX2341X_ENC_SET_PGM_INDEX_INFO 0xc7 +#define CX2341X_ENC_SET_VBI_CONFIG 0xc8 +#define CX2341X_ENC_SET_DMA_BLOCK_SIZE 0xc9 +#define CX2341X_ENC_GET_PREV_DMA_INFO_MB_10 0xca +#define CX2341X_ENC_GET_PREV_DMA_INFO_MB_9 0xcb +#define CX2341X_ENC_SCHED_DMA_TO_HOST 0xcc +#define CX2341X_ENC_INITIALIZE_INPUT 0xcd +#define CX2341X_ENC_SET_FRAME_DROP_RATE 0xd0 +#define CX2341X_ENC_PAUSE_ENCODER 0xd2 +#define CX2341X_ENC_REFRESH_INPUT 0xd3 +#define CX2341X_ENC_SET_COPYRIGHT 0xd4 +#define CX2341X_ENC_SET_EVENT_NOTIFICATION 0xd5 +#define CX2341X_ENC_SET_NUM_VSYNC_LINES 0xd6 +#define CX2341X_ENC_SET_PLACEHOLDER 0xd7 +#define CX2341X_ENC_MUTE_VIDEO 0xd9 +#define CX2341X_ENC_MUTE_AUDIO 0xda +#define CX2341X_ENC_UNKNOWN 0xdb +#define CX2341X_ENC_MISC 0xdc + +/* OSD API, specific to the cx23415 */ +#define CX2341X_OSD_GET_FRAMEBUFFER 0x41 +#define CX2341X_OSD_GET_PIXEL_FORMAT 0x42 +#define CX2341X_OSD_SET_PIXEL_FORMAT 0x43 +#define CX2341X_OSD_GET_STATE 0x44 +#define CX2341X_OSD_SET_STATE 0x45 +#define CX2341X_OSD_GET_OSD_COORDS 0x46 +#define CX2341X_OSD_SET_OSD_COORDS 0x47 +#define CX2341X_OSD_GET_SCREEN_COORDS 0x48 +#define CX2341X_OSD_SET_SCREEN_COORDS 0x49 +#define CX2341X_OSD_GET_GLOBAL_ALPHA 0x4a +#define CX2341X_OSD_SET_GLOBAL_ALPHA 0x4b +#define CX2341X_OSD_SET_BLEND_COORDS 0x4c +#define CX2341X_OSD_GET_FLICKER_STATE 0x4f +#define CX2341X_OSD_SET_FLICKER_STATE 0x50 +#define CX2341X_OSD_BLT_COPY 0x52 +#define CX2341X_OSD_BLT_FILL 0x53 +#define CX2341X_OSD_BLT_TEXT 0x54 +#define CX2341X_OSD_SET_FRAMEBUFFER_WINDOW 0x56 +#define CX2341X_OSD_SET_CHROMA_KEY 0x60 +#define CX2341X_OSD_GET_ALPHA_CONTENT_INDEX 0x61 +#define CX2341X_OSD_SET_ALPHA_CONTENT_INDEX 0x62 + +#endif /* CX2341X_H */ diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 302d5b3..7bab09b 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -73,7 +73,7 @@ extern IR_KEYTAB_TYPE ir_codes_cinergy_1 extern IR_KEYTAB_TYPE ir_codes_avertv_303[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_dntv_live_dvbt_pro[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_em_terratec[IR_KEYTAB_SIZE]; -extern IR_KEYTAB_TYPE ir_codes_em_pinnacle_usb[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_flyvideo[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_flydvb[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_cinergy[IR_KEYTAB_SIZE]; @@ -87,8 +87,9 @@ extern IR_KEYTAB_TYPE ir_codes_pctv_sedn extern IR_KEYTAB_TYPE ir_codes_pv951[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE]; -extern IR_KEYTAB_TYPE ir_codes_pinnacle[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_pinnacle_color[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE]; #endif diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index 730f21e..a455f7c 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -20,5 +20,6 @@ struct IR_i2c { int (*get_key)(struct IR_i2c*, u32*, u32*); }; -int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); +int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); +int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); #endif diff --git a/include/media/ovcamchip.h b/include/media/ovcamchip.h index 8138983..0f43451 100644 --- a/include/media/ovcamchip.h +++ b/include/media/ovcamchip.h @@ -15,6 +15,7 @@ #ifndef __LINUX_OVCAMCHIP_H #define __LINUX_OVCAMCHIP_H #include +#include #include /* --------------------------------- */ diff --git a/include/media/pwc-ioctl.h b/include/media/pwc-ioctl.h new file mode 100644 index 0000000..adc1254 --- /dev/null +++ b/include/media/pwc-ioctl.h @@ -0,0 +1,325 @@ +#ifndef PWC_IOCTL_H +#define PWC_IOCTL_H + +/* (C) 2001-2004 Nemosoft Unv. + (C) 2004-2006 Luc Saillard (luc@saillard.org) + + NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx + driver and thus may have bugs that are not present in the original version. + Please send bug reports and support requests to . + The decompression routines have been implemented by reverse-engineering the + Nemosoft binary pwcx module. Caveat emptor. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This is pwc-ioctl.h belonging to PWC 10.0.10 + It contains structures and defines to communicate from user space + directly to the driver. + */ + +/* + Changes + 2001/08/03 Alvarado Added ioctl constants to access methods for + changing white balance and red/blue gains + 2002/12/15 G. H. Fernandez-Toribio VIDIOCGREALSIZE + 2003/12/13 Nemosft Unv. Some modifications to make interfacing to + PWCX easier + 2006/01/01 Luc Saillard Add raw format definition + */ + +/* These are private ioctl() commands, specific for the Philips webcams. + They contain functions not found in other webcams, and settings not + specified in the Video4Linux API. + + The #define names are built up like follows: + VIDIOC VIDeo IOCtl prefix + PWC Philps WebCam + G optional: Get + S optional: Set + ... the function + */ + +#include +#include + + + /* Enumeration of image sizes */ +#define PSZ_SQCIF 0x00 +#define PSZ_QSIF 0x01 +#define PSZ_QCIF 0x02 +#define PSZ_SIF 0x03 +#define PSZ_CIF 0x04 +#define PSZ_VGA 0x05 +#define PSZ_MAX 6 + + +/* The frame rate is encoded in the video_window.flags parameter using + the upper 16 bits, since some flags are defined nowadays. The following + defines provide a mask and shift to filter out this value. + This value can also be passing using the private flag when using v4l2 and + VIDIOC_S_FMT ioctl. + + In 'Snapshot' mode the camera freezes its automatic exposure and colour + balance controls. + */ +#define PWC_FPS_SHIFT 16 +#define PWC_FPS_MASK 0x00FF0000 +#define PWC_FPS_FRMASK 0x003F0000 +#define PWC_FPS_SNAPSHOT 0x00400000 +#define PWC_QLT_MASK 0x03000000 +#define PWC_QLT_SHIFT 24 + + +/* structure for transferring x & y coordinates */ +struct pwc_coord +{ + int x, y; /* guess what */ + int size; /* size, or offset */ +}; + + +/* Used with VIDIOCPWCPROBE */ +struct pwc_probe +{ + char name[32]; + int type; +}; + +struct pwc_serial +{ + char serial[30]; /* String with serial number. Contains terminating 0 */ +}; + +/* pwc_whitebalance.mode values */ +#define PWC_WB_INDOOR 0 +#define PWC_WB_OUTDOOR 1 +#define PWC_WB_FL 2 +#define PWC_WB_MANUAL 3 +#define PWC_WB_AUTO 4 + +/* Used with VIDIOCPWC[SG]AWB (Auto White Balance). + Set mode to one of the PWC_WB_* values above. + *red and *blue are the respective gains of these colour components inside + the camera; range 0..65535 + When 'mode' == PWC_WB_MANUAL, 'manual_red' and 'manual_blue' are set or read; + otherwise undefined. + 'read_red' and 'read_blue' are read-only. +*/ +struct pwc_whitebalance +{ + int mode; + int manual_red, manual_blue; /* R/W */ + int read_red, read_blue; /* R/O */ +}; + +/* + 'control_speed' and 'control_delay' are used in automatic whitebalance mode, + and tell the camera how fast it should react to changes in lighting, and + with how much delay. Valid values are 0..65535. +*/ +struct pwc_wb_speed +{ + int control_speed; + int control_delay; + +}; + +/* Used with VIDIOCPWC[SG]LED */ +struct pwc_leds +{ + int led_on; /* Led on-time; range = 0..25000 */ + int led_off; /* Led off-time; range = 0..25000 */ +}; + +/* Image size (used with GREALSIZE) */ +struct pwc_imagesize +{ + int width; + int height; +}; + +/* Defines and structures for Motorized Pan & Tilt */ +#define PWC_MPT_PAN 0x01 +#define PWC_MPT_TILT 0x02 +#define PWC_MPT_TIMEOUT 0x04 /* for status */ + +/* Set angles; when absolute != 0, the angle is absolute and the + driver calculates the relative offset for you. This can only + be used with VIDIOCPWCSANGLE; VIDIOCPWCGANGLE always returns + absolute angles. + */ +struct pwc_mpt_angles +{ + int absolute; /* write-only */ + int pan; /* degrees * 100 */ + int tilt; /* degress * 100 */ +}; + +/* Range of angles of the camera, both horizontally and vertically. + */ +struct pwc_mpt_range +{ + int pan_min, pan_max; /* degrees * 100 */ + int tilt_min, tilt_max; +}; + +struct pwc_mpt_status +{ + int status; + int time_pan; + int time_tilt; +}; + + +/* This is used for out-of-kernel decompression. With it, you can get + all the necessary information to initialize and use the decompressor + routines in standalone applications. + */ +struct pwc_video_command +{ + int type; /* camera type (645, 675, 730, etc.) */ + int release; /* release number */ + + int size; /* one of PSZ_* */ + int alternate; + int command_len; /* length of USB video command */ + unsigned char command_buf[13]; /* Actual USB video command */ + int bandlength; /* >0 = compressed */ + int frame_size; /* Size of one (un)compressed frame */ +}; + +/* Flags for PWCX subroutines. Not all modules honour all flags. */ +#define PWCX_FLAG_PLANAR 0x0001 +#define PWCX_FLAG_BAYER 0x0008 + + +/* IOCTL definitions */ + + /* Restore user settings */ +#define VIDIOCPWCRUSER _IO('v', 192) + /* Save user settings */ +#define VIDIOCPWCSUSER _IO('v', 193) + /* Restore factory settings */ +#define VIDIOCPWCFACTORY _IO('v', 194) + + /* You can manipulate the compression factor. A compression preference of 0 + means use uncompressed modes when available; 1 is low compression, 2 is + medium and 3 is high compression preferred. Of course, the higher the + compression, the lower the bandwidth used but more chance of artefacts + in the image. The driver automatically chooses a higher compression when + the preferred mode is not available. + */ + /* Set preferred compression quality (0 = uncompressed, 3 = highest compression) */ +#define VIDIOCPWCSCQUAL _IOW('v', 195, int) + /* Get preferred compression quality */ +#define VIDIOCPWCGCQUAL _IOR('v', 195, int) + + +/* Retrieve serial number of camera */ +#define VIDIOCPWCGSERIAL _IOR('v', 198, struct pwc_serial) + + /* This is a probe function; since so many devices are supported, it + becomes difficult to include all the names in programs that want to + check for the enhanced Philips stuff. So in stead, try this PROBE; + it returns a structure with the original name, and the corresponding + Philips type. + To use, fill the structure with zeroes, call PROBE and if that succeeds, + compare the name with that returned from VIDIOCGCAP; they should be the + same. If so, you can be assured it is a Philips (OEM) cam and the type + is valid. + */ +#define VIDIOCPWCPROBE _IOR('v', 199, struct pwc_probe) + + /* Set AGC (Automatic Gain Control); int < 0 = auto, 0..65535 = fixed */ +#define VIDIOCPWCSAGC _IOW('v', 200, int) + /* Get AGC; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCGAGC _IOR('v', 200, int) + /* Set shutter speed; int < 0 = auto; >= 0 = fixed, range 0..65535 */ +#define VIDIOCPWCSSHUTTER _IOW('v', 201, int) + + /* Color compensation (Auto White Balance) */ +#define VIDIOCPWCSAWB _IOW('v', 202, struct pwc_whitebalance) +#define VIDIOCPWCGAWB _IOR('v', 202, struct pwc_whitebalance) + + /* Auto WB speed */ +#define VIDIOCPWCSAWBSPEED _IOW('v', 203, struct pwc_wb_speed) +#define VIDIOCPWCGAWBSPEED _IOR('v', 203, struct pwc_wb_speed) + + /* LEDs on/off/blink; int range 0..65535 */ +#define VIDIOCPWCSLED _IOW('v', 205, struct pwc_leds) +#define VIDIOCPWCGLED _IOR('v', 205, struct pwc_leds) + + /* Contour (sharpness); int < 0 = auto, 0..65536 = fixed */ +#define VIDIOCPWCSCONTOUR _IOW('v', 206, int) +#define VIDIOCPWCGCONTOUR _IOR('v', 206, int) + + /* Backlight compensation; 0 = off, otherwise on */ +#define VIDIOCPWCSBACKLIGHT _IOW('v', 207, int) +#define VIDIOCPWCGBACKLIGHT _IOR('v', 207, int) + + /* Flickerless mode; = 0 off, otherwise on */ +#define VIDIOCPWCSFLICKER _IOW('v', 208, int) +#define VIDIOCPWCGFLICKER _IOR('v', 208, int) + + /* Dynamic noise reduction; 0 off, 3 = high noise reduction */ +#define VIDIOCPWCSDYNNOISE _IOW('v', 209, int) +#define VIDIOCPWCGDYNNOISE _IOR('v', 209, int) + + /* Real image size as used by the camera; tells you whether or not there's a gray border around the image */ +#define VIDIOCPWCGREALSIZE _IOR('v', 210, struct pwc_imagesize) + + /* Motorized pan & tilt functions */ +#define VIDIOCPWCMPTRESET _IOW('v', 211, int) +#define VIDIOCPWCMPTGRANGE _IOR('v', 211, struct pwc_mpt_range) +#define VIDIOCPWCMPTSANGLE _IOW('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTGANGLE _IOR('v', 212, struct pwc_mpt_angles) +#define VIDIOCPWCMPTSTATUS _IOR('v', 213, struct pwc_mpt_status) + + /* Get the USB set-video command; needed for initializing libpwcx */ +#define VIDIOCPWCGVIDCMD _IOR('v', 215, struct pwc_video_command) +struct pwc_table_init_buffer { + int len; + char *buffer; + +}; +#define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) + +/* + * This is private command used when communicating with v4l2. + * In the future all private ioctl will be remove/replace to + * use interface offer by v4l2. + */ + +#define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8) + +struct pwc_raw_frame { + __le16 type; /* type of the webcam */ + __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */ + __u8 cmd[4]; /* the four byte of the command (in case of nala, + only the first 3 bytes is filled) */ + __u8 rawframe[0]; /* frame_size = H/4*vbandlength */ +} __attribute__ ((packed)); + + +#endif diff --git a/include/media/saa7115.h b/include/media/saa7115.h index 6b4836f..9f0e228 100644 --- a/include/media/saa7115.h +++ b/include/media/saa7115.h @@ -1,5 +1,5 @@ /* - saa7115.h - definition for saa7113/4/5 inputs + saa7115.h - definition for saa7113/4/5 inputs and frequency flags Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) @@ -33,5 +33,14 @@ #define SAA7115_SVIDEO1 7 #define SAA7115_SVIDEO2 8 #define SAA7115_SVIDEO3 9 +/* SAA7115 v4l2_crystal_freq frequency values */ +#define SAA7115_FREQ_32_11_MHZ 32110000 /* 32.11 MHz crystal, SAA7114/5 only */ +#define SAA7115_FREQ_24_576_MHZ 24576000 /* 24.576 MHz crystal */ + +/* SAA7115 v4l2_crystal_freq audio clock control flags */ +#define SAA7115_FREQ_FL_UCGC (1 << 0) /* SA 3A[7], UCGC, SAA7115 only */ +#define SAA7115_FREQ_FL_CGCDIV (1 << 1) /* SA 3A[6], CGCDIV, SAA7115 only */ +#define SAA7115_FREQ_FL_APLL (1 << 2) /* SA 3A[3], APLL, SAA7114/5 only */ + #endif diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 4507cb6..83fe2e3 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -2,7 +2,7 @@ #ifndef __SAA7146_VV__ #define __SAA7146_VV__ #include - +#include #include #include diff --git a/include/media/tuner-types.h b/include/media/tuner-types.h index ad9c171..3c43b95 100644 --- a/include/media/tuner-types.h +++ b/include/media/tuner-types.h @@ -20,6 +20,7 @@ struct tuner_range { struct tuner_params { enum param_type type; + /* Many Philips based tuners have a comment like this in their * datasheet: * @@ -39,6 +40,60 @@ struct tuner_params { * static unless the control byte was sent first. */ unsigned int cb_first_if_lower_freq:1; + /* Set to 1 if this tuner uses a tda9887 */ + unsigned int has_tda9887:1; + /* Many Philips tuners use tda9887 PORT1 to select the FM radio + sensitivity. If this setting is 1, then set PORT1 to 1 to + get proper FM reception. */ + unsigned int port1_fm_high_sensitivity:1; + /* Some Philips tuners use tda9887 PORT2 to select the FM radio + sensitivity. If this setting is 1, then set PORT2 to 1 to + get proper FM reception. */ + unsigned int port2_fm_high_sensitivity:1; + /* Most tuners with a tda9887 use QSS mode. Some (cheaper) tuners + use Intercarrier mode. If this setting is 1, then the tuner + needs to be set to intercarrier mode. */ + unsigned int intercarrier_mode:1; + /* This setting sets the default value for PORT1. + 0 means inactive, 1 means active. Note: the actual bit + value written to the tda9887 is inverted. So a 0 here + means a 1 in the B6 bit. */ + unsigned int port1_active:1; + /* This setting sets the default value for PORT2. + 0 means inactive, 1 means active. Note: the actual bit + value written to the tda9887 is inverted. So a 0 here + means a 1 in the B7 bit. */ + unsigned int port2_active:1; + /* Sometimes PORT1 is inverted when the SECAM-L' standard is selected. + Set this bit to 1 if this is needed. */ + unsigned int port1_invert_for_secam_lc:1; + /* Sometimes PORT2 is inverted when the SECAM-L' standard is selected. + Set this bit to 1 if this is needed. */ + unsigned int port2_invert_for_secam_lc:1; + /* Some cards require PORT1 to be 1 for mono Radio FM and 0 for stereo. */ + unsigned int port1_set_for_fm_mono:1; + /* Default tda9887 TOP value in dB for the low band. Default is 0. + Range: -16:+15 */ + signed int default_top_low:5; + /* Default tda9887 TOP value in dB for the mid band. Default is 0. + Range: -16:+15 */ + signed int default_top_mid:5; + /* Default tda9887 TOP value in dB for the high band. Default is 0. + Range: -16:+15 */ + signed int default_top_high:5; + /* Default tda9887 TOP value in dB for SECAM-L/L' for the low band. + Default is 0. Several tuners require a different TOP value for + the SECAM-L/L' standards. Range: -16:+15 */ + signed int default_top_secam_low:5; + /* Default tda9887 TOP value in dB for SECAM-L/L' for the mid band. + Default is 0. Several tuners require a different TOP value for + the SECAM-L/L' standards. Range: -16:+15 */ + signed int default_top_secam_mid:5; + /* Default tda9887 TOP value in dB for SECAM-L/L' for the high band. + Default is 0. Several tuners require a different TOP value for + the SECAM-L/L' standards. Range: -16:+15 */ + signed int default_top_secam_high:5; + unsigned int count; struct tuner_range *ranges; diff --git a/include/media/tuner.h b/include/media/tuner.h index 017fed7..2f7b00b 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -25,6 +25,8 @@ #define _TUNER_H #include #include +extern int tuner_debug; + #define ADDR_UNSET (255) #define TUNER_TEMIC_PAL 0 /* 4002 FH5 (3X 7756, 9483) */ @@ -108,7 +110,7 @@ #define TUNER_TENA_9533_DI 61 #define TUNER_TEA5767 62 /* Only FM Radio Tuner */ #define TUNER_PHILIPS_FMD1216ME_MK3 63 -#define TUNER_LG_TDVS_H062F 64 /* DViCO FusionHDTV 5 */ +#define TUNER_LG_TDVS_H06XF 64 /* TDVS H061F, H062F, H064F */ #define TUNER_YMEC_TVF66T5_B_DFF 65 /* Acorp Y878F */ #define TUNER_LG_TALN 66 #define TUNER_PHILIPS_TD1316 67 @@ -119,6 +121,8 @@ #define TUNER_SAMSUNG_TCPN_2121P30A #define TUNER_XCEIVE_XC3028 71 #define TUNER_THOMSON_FE6600 72 /* DViCO FusionHDTV DVB-T Hybrid */ +#define TUNER_SAMSUNG_TCPG_6121P30A 73 /* Hauppauge PVR-500 PAL */ +#define TUNER_TDA9887 74 /* This tuner should be used only internally */ /* tv card specific */ #define TDA9887_PRESENT (1<<0) @@ -190,6 +194,10 @@ struct tuner { int using_v4l2; + /* used by tda9887 */ + unsigned int tda9887_config; + unsigned char tda9887_data[4]; + /* used by MT2032 */ unsigned int xogc; unsigned int radio_if2; @@ -206,6 +214,8 @@ struct tuner { void (*set_radio_freq)(struct i2c_client *c, unsigned int freq); int (*has_signal)(struct i2c_client *c); int (*is_stereo)(struct i2c_client *c); + int (*get_afc)(struct i2c_client *c); + void (*tuner_status)(struct i2c_client *c); void (*standby)(struct i2c_client *c); }; @@ -218,6 +228,7 @@ extern int tda8290_probe(struct i2c_clie extern int tea5767_tuner_init(struct i2c_client *c); extern int default_tuner_init(struct i2c_client *c); extern int tea5767_autodetection(struct i2c_client *c); +extern int tda9887_tuner_init(struct i2c_client *c); #define tuner_warn(fmt, arg...) do {\ printk(KERN_WARNING "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ diff --git a/include/media/tvp5150.h b/include/media/tvp5150.h new file mode 100644 index 0000000..72bd2a2 --- /dev/null +++ b/include/media/tvp5150.h @@ -0,0 +1,34 @@ +/* + tvp5150.h - definition for tvp5150 inputs + + Copyright (C) 2006 Hans Verkuil (hverkuil@xs4all.nl) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef _TVP5150_H_ +#define _TVP5150_H_ + +/* TVP5150 HW inputs */ +#define TVP5150_COMPOSITE0 0 +#define TVP5150_COMPOSITE1 1 +#define TVP5150_SVIDEO 2 + +/* TVP5150 HW outputs */ +#define TVP5150_NORMAL 0 +#define TVP5150_BLACK_SCREEN 1 + +#endif + diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 642520a..5564db1 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -26,8 +26,14 @@ #ifndef V4L2_COMMON_H_ #define V4L2_COMMON_H_ +#include + /* v4l debugging and diagnostics */ +/* Debug bitmask flags to be used on V4L2 */ +#define V4L2_DEBUG_IOCTL 0x01 +#define V4L2_DEBUG_IOCTL_ARG 0x02 + /* Common printk constucts for v4l-i2c drivers. These macros create a unique prefix consisting of the driver name, the adapter number and the i2c address. */ @@ -78,6 +84,19 @@ #define v4l_i2c_print_ioctl(client, cmd) /* ------------------------------------------------------------------------- */ +/* Control helper functions */ + +int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, + const char **menu_items); +const char **v4l2_ctrl_get_menu(u32 id); +int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def); +int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl); +int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, + struct v4l2_queryctrl *qctrl, const char **menu_items); +u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id); + +/* ------------------------------------------------------------------------- */ + /* Internal ioctls */ /* VIDIOC_INT_G_REGISTER and VIDIOC_INT_S_REGISTER */ @@ -112,6 +131,8 @@ enum v4l2_chip_ident { V4L2_IDENT_SAA7129 = 159, /* module cx25840: reserved range 200-249 */ + V4L2_IDENT_CX25836 = 236, + V4L2_IDENT_CX25837 = 237, V4L2_IDENT_CX25840 = 240, V4L2_IDENT_CX25841 = 241, V4L2_IDENT_CX25842 = 242, @@ -211,4 +232,15 @@ #define VIDIOC_INT_G_AUDIO_ROUTING _IOR #define VIDIOC_INT_S_VIDEO_ROUTING _IOW ('d', 111, struct v4l2_routing) #define VIDIOC_INT_G_VIDEO_ROUTING _IOR ('d', 112, struct v4l2_routing) +struct v4l2_crystal_freq { + u32 freq; /* frequency in Hz of the crystal */ + u32 flags; /* device specific flags */ +}; + +/* Sets the frequency of the crystal used to generate the clocks. + An extra flags field allows device specific configuration regarding + clock frequency dividers, etc. If not used, then set flags to 0. + If the frequency is not supported, then -EINVAL is returned. */ +#define VIDIOC_INT_S_CRYSTAL_FREQ _IOW ('d', 113, struct v4l2_crystal_freq) + #endif /* V4L2_COMMON_H_ */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h new file mode 100644 index 0000000..62dae1a --- /dev/null +++ b/include/media/v4l2-dev.h @@ -0,0 +1,374 @@ +/* + * + * V 4 L 2 D R I V E R H E L P E R A P I + * + * Moved from videodev2.h + * + * Some commonly needed functions for drivers (v4l2-common.o module) + */ +#ifndef _V4L2_DEV_H +#define _V4L2_DEV_H + +#define OBSOLETE_OWNER 1 /* to be removed soon */ + +#include +#include +#include +#include +#include /* need __user */ +#ifdef CONFIG_VIDEO_V4L1 +#include +#else +#include +#endif + +#include + +#define VIDEO_MAJOR 81 +/* Minor device allocation */ +#define MINOR_VFL_TYPE_GRABBER_MIN 0 +#define MINOR_VFL_TYPE_GRABBER_MAX 63 +#define MINOR_VFL_TYPE_RADIO_MIN 64 +#define MINOR_VFL_TYPE_RADIO_MAX 127 +#define MINOR_VFL_TYPE_VTX_MIN 192 +#define MINOR_VFL_TYPE_VTX_MAX 223 +#define MINOR_VFL_TYPE_VBI_MIN 224 +#define MINOR_VFL_TYPE_VBI_MAX 255 + +#define VFL_TYPE_GRABBER 0 +#define VFL_TYPE_VBI 1 +#define VFL_TYPE_RADIO 2 +#define VFL_TYPE_VTX 3 + +/* Video standard functions */ +extern unsigned int v4l2_video_std_fps(struct v4l2_standard *vs); +extern int v4l2_video_std_construct(struct v4l2_standard *vs, + int id, char *name); + +/* prority handling */ +struct v4l2_prio_state { + atomic_t prios[4]; +}; +int v4l2_prio_init(struct v4l2_prio_state *global); +int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, + enum v4l2_priority new); +int v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local); +int v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority *local); +enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global); +int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority *local); + +/* names for fancy debug output */ +extern char *v4l2_field_names[]; +extern char *v4l2_type_names[]; + +/* Compatibility layer interface -- v4l1-compat module */ +typedef int (*v4l2_kioctl)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg); +#ifdef CONFIG_VIDEO_V4L1_COMPAT +int v4l_compat_translate_ioctl(struct inode *inode, struct file *file, + int cmd, void *arg, v4l2_kioctl driver_ioctl); +#else +#define v4l_compat_translate_ioctl(inode,file,cmd,arg,ioctl) -EINVAL +#endif + +/* 32 Bits compatibility layer for 64 bits processors */ +extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, + unsigned long arg); + +/* + * Newer version of video_device, handled by videodev2.c + * This version moves redundant code from video device code to + * the common handler + */ +struct v4l2_tvnorm { + char *name; + v4l2_std_id id; + + void *priv_data; +}; + +struct video_device +{ + /* device ops */ + const struct file_operations *fops; + + /* device info */ + struct device *dev; + char name[32]; + int type; /* v4l1 */ + int type2; /* v4l2 */ + int hardware; + int minor; + + int debug; /* Activates debug level*/ + + /* Video standard vars */ + int tvnormsize; /* Size of tvnorm array */ + v4l2_std_id current_norm; /* Current tvnorm */ + struct v4l2_tvnorm *tvnorms; + + /* callbacks */ + void (*release)(struct video_device *vfd); + + /* ioctl callbacks */ + + /* VIDIOC_QUERYCAP handler */ + int (*vidioc_querycap)(struct file *file, void *fh, struct v4l2_capability *cap); + + /* Priority handling */ + int (*vidioc_g_priority) (struct file *file, void *fh, + enum v4l2_priority *p); + int (*vidioc_s_priority) (struct file *file, void *fh, + enum v4l2_priority p); + + /* VIDIOC_ENUM_FMT handlers */ + int (*vidioc_enum_fmt_cap) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_overlay) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_vbi) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_vbi_capture) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_video_output)(struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_vbi_output) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh, + struct v4l2_fmtdesc *f); + + /* VIDIOC_G_FMT handlers */ + int (*vidioc_g_fmt_cap) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_overlay) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_vbi) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_vbi_output) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_vbi_capture)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_video_output)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_g_fmt_type_private)(struct file *file, void *fh, + struct v4l2_format *f); + + /* VIDIOC_S_FMT handlers */ + int (*vidioc_s_fmt_cap) (struct file *file, void *fh, + struct v4l2_format *f); + + int (*vidioc_s_fmt_overlay) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_s_fmt_vbi) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_s_fmt_vbi_output) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_s_fmt_vbi_capture)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_s_fmt_video_output)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_s_fmt_type_private)(struct file *file, void *fh, + struct v4l2_format *f); + + /* VIDIOC_TRY_FMT handlers */ + int (*vidioc_try_fmt_cap) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_overlay) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_vbi) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_vbi_output) (struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_vbi_capture)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_video_output)(struct file *file, void *fh, + struct v4l2_format *f); + int (*vidioc_try_fmt_type_private)(struct file *file, void *fh, + struct v4l2_format *f); + + /* Buffer handlers */ + int (*vidioc_reqbufs) (struct file *file, void *fh, struct v4l2_requestbuffers *b); + int (*vidioc_querybuf)(struct file *file, void *fh, struct v4l2_buffer *b); + int (*vidioc_qbuf) (struct file *file, void *fh, struct v4l2_buffer *b); + int (*vidioc_dqbuf) (struct file *file, void *fh, struct v4l2_buffer *b); + + + int (*vidioc_overlay) (struct file *file, void *fh, unsigned int i); +#ifdef HAVE_V4L1 + /* buffer type is struct vidio_mbuf * */ + int (*vidiocgmbuf) (struct file *file, void *fh, struct video_mbuf *p); +#endif + int (*vidioc_g_fbuf) (struct file *file, void *fh, + struct v4l2_framebuffer *a); + int (*vidioc_s_fbuf) (struct file *file, void *fh, + struct v4l2_framebuffer *a); + + /* Stream on/off */ + int (*vidioc_streamon) (struct file *file, void *fh, enum v4l2_buf_type i); + int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i); + + /* Standard handling + G_STD and ENUMSTD are handled by videodev.c + */ + int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id a); + int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a); + + /* Input handling */ + int (*vidioc_enum_input)(struct file *file, void *fh, + struct v4l2_input *inp); + int (*vidioc_g_input) (struct file *file, void *fh, unsigned int *i); + int (*vidioc_s_input) (struct file *file, void *fh, unsigned int i); + + /* Output handling */ + int (*vidioc_enumoutput) (struct file *file, void *fh, + struct v4l2_output *a); + int (*vidioc_g_output) (struct file *file, void *fh, unsigned int *i); + int (*vidioc_s_output) (struct file *file, void *fh, unsigned int i); + + /* Control handling */ + int (*vidioc_queryctrl) (struct file *file, void *fh, + struct v4l2_queryctrl *a); + int (*vidioc_g_ctrl) (struct file *file, void *fh, + struct v4l2_control *a); + int (*vidioc_s_ctrl) (struct file *file, void *fh, + struct v4l2_control *a); + int (*vidioc_g_ext_ctrls) (struct file *file, void *fh, + struct v4l2_ext_controls *a); + int (*vidioc_s_ext_ctrls) (struct file *file, void *fh, + struct v4l2_ext_controls *a); + int (*vidioc_try_ext_ctrls) (struct file *file, void *fh, + struct v4l2_ext_controls *a); + int (*vidioc_querymenu) (struct file *file, void *fh, + struct v4l2_querymenu *a); + + /* Audio ioctls */ + int (*vidioc_enumaudio) (struct file *file, void *fh, + struct v4l2_audio *a); + int (*vidioc_g_audio) (struct file *file, void *fh, + struct v4l2_audio *a); + int (*vidioc_s_audio) (struct file *file, void *fh, + struct v4l2_audio *a); + + /* Audio out ioctls */ + int (*vidioc_enumaudout) (struct file *file, void *fh, + struct v4l2_audioout *a); + int (*vidioc_g_audout) (struct file *file, void *fh, + struct v4l2_audioout *a); + int (*vidioc_s_audout) (struct file *file, void *fh, + struct v4l2_audioout *a); + int (*vidioc_g_modulator) (struct file *file, void *fh, + struct v4l2_modulator *a); + int (*vidioc_s_modulator) (struct file *file, void *fh, + struct v4l2_modulator *a); + /* Crop ioctls */ + int (*vidioc_cropcap) (struct file *file, void *fh, + struct v4l2_cropcap *a); + int (*vidioc_g_crop) (struct file *file, void *fh, + struct v4l2_crop *a); + int (*vidioc_s_crop) (struct file *file, void *fh, + struct v4l2_crop *a); + /* Compression ioctls */ + int (*vidioc_g_mpegcomp) (struct file *file, void *fh, + struct v4l2_mpeg_compression *a); + int (*vidioc_s_mpegcomp) (struct file *file, void *fh, + struct v4l2_mpeg_compression *a); + int (*vidioc_g_jpegcomp) (struct file *file, void *fh, + struct v4l2_jpegcompression *a); + int (*vidioc_s_jpegcomp) (struct file *file, void *fh, + struct v4l2_jpegcompression *a); + + /* Stream type-dependent parameter ioctls */ + int (*vidioc_g_parm) (struct file *file, void *fh, + struct v4l2_streamparm *a); + int (*vidioc_s_parm) (struct file *file, void *fh, + struct v4l2_streamparm *a); + + /* Tuner ioctls */ + int (*vidioc_g_tuner) (struct file *file, void *fh, + struct v4l2_tuner *a); + int (*vidioc_s_tuner) (struct file *file, void *fh, + struct v4l2_tuner *a); + int (*vidioc_g_frequency) (struct file *file, void *fh, + struct v4l2_frequency *a); + int (*vidioc_s_frequency) (struct file *file, void *fh, + struct v4l2_frequency *a); + + /* Sliced VBI cap */ + int (*vidioc_g_sliced_vbi_cap) (struct file *file, void *fh, + struct v4l2_sliced_vbi_cap *a); + + /* Log status ioctl */ + int (*vidioc_log_status) (struct file *file, void *fh); + + +#ifdef OBSOLETE_OWNER /* to be removed soon */ +/* obsolete -- fops->owner is used instead */ +struct module *owner; +/* dev->driver_data will be used instead some day. + * Use the video_{get|set}_drvdata() helper functions, + * so the switch over will be transparent for you. + * Or use {pci|usb}_{get|set}_drvdata() directly. */ +void *priv; +#endif + + /* for videodev.c intenal usage -- please don't touch */ + int users; /* video_exclusive_{open|close} ... */ + struct mutex lock; /* ... helper function uses these */ + struct class_device class_dev; /* sysfs */ +}; + +/* Version 2 functions */ +extern int video_register_device(struct video_device *vfd, int type, int nr); +void video_unregister_device(struct video_device *); +extern int video_ioctl2(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +/* helper functions to alloc / release struct video_device, the + later can be used for video_device->release() */ +struct video_device *video_device_alloc(void); +void video_device_release(struct video_device *vfd); + +/* Include support for obsoleted stuff */ +extern int video_usercopy(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg)); + + +#ifdef HAVE_V4L1 +#include + +extern struct video_device* video_devdata(struct file*); + +#define to_video_device(cd) container_of(cd, struct video_device, class_dev) +static inline void +video_device_create_file(struct video_device *vfd, + struct class_device_attribute *attr) +{ + class_device_create_file(&vfd->class_dev, attr); +} +static inline void +video_device_remove_file(struct video_device *vfd, + struct class_device_attribute *attr) +{ + class_device_remove_file(&vfd->class_dev, attr); +} + +#ifdef OBSOLETE_OWNER /* to be removed soon */ +/* helper functions to access driver private data. */ +static inline void *video_get_drvdata(struct video_device *dev) +{ + return dev->priv; +} + +static inline void video_set_drvdata(struct video_device *dev, void *data) +{ + dev->priv = data; +} +#endif + +extern int video_exclusive_open(struct inode *inode, struct file *file); +extern int video_exclusive_release(struct inode *inode, struct file *file); +#endif /* HAVE_V4L1 */ + +#endif /* _V4L2_DEV_H */ diff --git a/include/media/video-buf-dvb.h b/include/media/video-buf-dvb.h index b78d90f..8233caf 100644 --- a/include/media/video-buf-dvb.h +++ b/include/media/video-buf-dvb.h @@ -26,7 +26,8 @@ struct videobuf_dvb { int videobuf_dvb_register(struct videobuf_dvb *dvb, struct module *module, - void *adapter_priv); + void *adapter_priv, + struct device *device); void videobuf_dvb_unregister(struct videobuf_dvb *dvb); /* diff --git a/include/media/video-buf.h b/include/media/video-buf.h index fff3fd0..1115a25 100644 --- a/include/media/video-buf.h +++ b/include/media/video-buf.h @@ -23,6 +23,7 @@ */ #include +#include #define UNSET (-1U) diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild new file mode 100644 index 0000000..e1da2a5 --- /dev/null +++ b/include/mtd/Kbuild @@ -0,0 +1,2 @@ +unifdef-y := mtd-abi.h +header-y := inftl-user.h jffs2-user.h mtd-user.h nftl-user.h diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index b5994ea..1da3f7f 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -7,8 +7,9 @@ #ifndef __MTD_ABI_H__ #define __MTD_ABI_H__ -#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into - separate files was to avoid #ifdef __KERNEL__ */ +#ifndef __KERNEL__ +/* Urgh. The whole point of splitting this out into + separate files was to avoid #ifdef __KERNEL__ */ #define __user #endif @@ -28,28 +29,17 @@ #define MTD_RAM 1 #define MTD_ROM 2 #define MTD_NORFLASH 3 #define MTD_NANDFLASH 4 -#define MTD_PEROM 5 #define MTD_DATAFLASH 6 -#define MTD_OTHER 14 -#define MTD_UNKNOWN 15 - -#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash) -#define MTD_SET_BITS 2 // Bits can be set -#define MTD_ERASEABLE 4 // Has an erase function -#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible -#define MTD_VOLATILE 16 // Set for RAMs -#define MTD_XIP 32 // eXecute-In-Place possible -#define MTD_OOB 64 // Out-of-band data (NAND flash) -#define MTD_ECC 128 // Device capable of automatic ECC -#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed -#define MTD_PROGRAM_REGIONS 512 // Configurable Programming Regions + +#define MTD_WRITEABLE 0x400 /* Device is writeable */ +#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ +#define MTD_NO_ERASE 0x1000 /* No erase necessary */ // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 -#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) -#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) -#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) -#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS) +#define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE) +#define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) +#define MTD_CAP_NANDFLASH (MTD_WRITEABLE) // Types of automatic ECC/Checksum available @@ -74,7 +64,7 @@ struct mtd_info_user { uint32_t flags; uint32_t size; // Total size of the MTD uint32_t erasesize; - uint32_t oobblock; // Size of OOB blocks (e.g. 512) + uint32_t writesize; uint32_t oobsize; // Amount of OOB data per block (e.g. 16) uint32_t ecctype; uint32_t eccsize; @@ -94,12 +84,12 @@ struct otp_info { uint32_t locked; }; -#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) -#define MEMERASE _IOW('M', 2, struct erase_info_user) -#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) -#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) -#define MEMLOCK _IOW('M', 5, struct erase_info_user) -#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) +#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) +#define MEMERASE _IOW('M', 2, struct erase_info_user) +#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) +#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf) +#define MEMLOCK _IOW('M', 5, struct erase_info_user) +#define MEMUNLOCK _IOW('M', 6, struct erase_info_user) #define MEMGETREGIONCOUNT _IOR('M', 7, int) #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) #define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) @@ -109,8 +99,15 @@ #define MEMSETBADBLOCK _IOW('M', 12, lo #define OTPSELECT _IOR('M', 13, int) #define OTPGETREGIONCOUNT _IOW('M', 14, int) #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) -#define OTPLOCK _IOR('M', 16, struct otp_info) +#define OTPLOCK _IOR('M', 16, struct otp_info) +#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) +#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) +#define MTDFILEMODE _IO('M', 19) +/* + * Obsolete legacy interface. Keep it in order not to break userspace + * interfaces + */ struct nand_oobinfo { uint32_t useecc; uint32_t eccbytes; @@ -118,4 +115,46 @@ struct nand_oobinfo { uint32_t eccpos[32]; }; +struct nand_oobfree { + uint32_t offset; + uint32_t length; +}; + +#define MTD_MAX_OOBFREE_ENTRIES 8 +/* + * ECC layout control structure. Exported to userspace for + * diagnosis and to allow creation of raw images + */ +struct nand_ecclayout { + uint32_t eccbytes; + uint32_t eccpos[64]; + uint32_t oobavail; + struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; +}; + +/** + * struct mtd_ecc_stats - error correction stats + * + * @corrected: number of corrected bits + * @failed: number of uncorrectable errors + * @badblocks: number of bad blocks in this partition + * @bbtblocks: number of blocks reserved for bad block tables + */ +struct mtd_ecc_stats { + uint32_t corrected; + uint32_t failed; + uint32_t badblocks; + uint32_t bbtblocks; +}; + +/* + * Read/write file modes for access to MTD + */ +enum mtd_file_modes { + MTD_MODE_NORMAL = MTD_OTP_OFF, + MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY, + MTD_MODE_OTP_USER = MTD_OTP_USER, + MTD_MODE_RAW, +}; + #endif /* __MTD_ABI_H__ */ diff --git a/include/mtd/mtd-user.h b/include/mtd/mtd-user.h index 1c13fc7..713f34d 100644 --- a/include/mtd/mtd-user.h +++ b/include/mtd/mtd-user.h @@ -16,5 +16,6 @@ typedef struct mtd_info_user mtd_info_t; typedef struct erase_info_user erase_info_t; typedef struct region_info_user region_info_t; typedef struct nand_oobinfo nand_oobinfo_t; +typedef struct nand_ecclayout nand_ecclayout_t; #endif /* __MTD_USER_H__ */ diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 750e250..3d71251 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -45,7 +45,6 @@ #endif #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 427dac9..2fec827 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -1,7 +1,6 @@ #ifndef __LINUX_NET_AFUNIX_H #define __LINUX_NET_AFUNIX_H -#include #include #include #include @@ -54,14 +53,23 @@ struct unix_address { struct unix_skb_parms { struct ucred creds; /* Skb credentials */ struct scm_fp_list *fp; /* Passed files */ +#ifdef CONFIG_SECURITY_NETWORK + char *secdata; /* Security context */ + u32 seclen; /* Security length */ +#endif }; #define UNIXCB(skb) (*(struct unix_skb_parms*)&((skb)->cb)) #define UNIXCREDS(skb) (&UNIXCB((skb)).creds) +#define UNIXSECDATA(skb) (&UNIXCB((skb)).secdata) +#define UNIXSECLEN(skb) (&UNIXCB((skb)).seclen) #define unix_state_rlock(s) spin_lock(&unix_sk(s)->lock) #define unix_state_runlock(s) spin_unlock(&unix_sk(s)->lock) #define unix_state_wlock(s) spin_lock(&unix_sk(s)->lock) +#define unix_state_wlock_nested(s) \ + spin_lock_nested(&unix_sk(s)->lock, \ + SINGLE_DEPTH_NESTING) #define unix_state_wunlock(s) spin_unlock(&unix_sk(s)->lock) #ifdef __KERNEL__ diff --git a/include/net/ax25.h b/include/net/ax25.h index 5bd9974..69374cd 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -6,7 +6,6 @@ #ifndef _AX25_H #define _AX25_H -#include #include #include #include @@ -183,14 +182,26 @@ typedef struct { typedef struct ax25_route { struct ax25_route *next; - atomic_t ref; + atomic_t refcount; ax25_address callsign; struct net_device *dev; ax25_digi *digipeat; char ip_mode; - struct timer_list timer; } ax25_route; +static inline void ax25_hold_route(ax25_route *ax25_rt) +{ + atomic_inc(&ax25_rt->refcount); +} + +extern void __ax25_put_route(ax25_route *ax25_rt); + +static inline void ax25_put_route(ax25_route *ax25_rt) +{ + if (atomic_dec_and_test(&ax25_rt->refcount)) + __ax25_put_route(ax25_rt); +} + typedef struct { char slave; /* slave_mode? */ struct timer_list slave_timer; /* timeout timer */ @@ -349,17 +360,11 @@ extern int ax25_check_iframes_acked(ax2 extern void ax25_rt_device_down(struct net_device *); extern int ax25_rt_ioctl(unsigned int, void __user *); extern struct file_operations ax25_route_fops; +extern ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev); extern int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern ax25_route *ax25_rt_find_route(ax25_route *, ax25_address *, - struct net_device *); extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); extern void ax25_rt_free(void); -static inline void ax25_put_route(ax25_route *ax25_rt) -{ - atomic_dec(&ax25_rt->ref); -} - /* ax25_std_in.c */ extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 911ceb5..771d177 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -175,6 +175,6 @@ extern int hci_sock_cleanup(void); extern int bt_sysfs_init(void); extern void bt_sysfs_cleanup(void); -extern struct class bt_class; +extern struct class *bt_class; #endif /* __BLUETOOTH_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index b06a2d2..b2bdb1a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -54,7 +54,8 @@ #define HCI_PCI 5 /* HCI device quirks */ enum { HCI_QUIRK_RESET_ON_INIT, - HCI_QUIRK_RAW_DEVICE + HCI_QUIRK_RAW_DEVICE, + HCI_QUIRK_FIXUP_BUFFER_SIZE }; /* HCI device flags */ @@ -100,9 +101,10 @@ #define HCISETSECMGR _IOW('H', 230, int) #define HCIINQUIRY _IOR('H', 240, int) /* HCI timeouts */ -#define HCI_CONN_TIMEOUT (HZ * 40) -#define HCI_DISCONN_TIMEOUT (HZ * 2) -#define HCI_CONN_IDLE_TIMEOUT (HZ * 60) +#define HCI_CONNECT_TIMEOUT (40000) /* 40 seconds */ +#define HCI_DISCONN_TIMEOUT (2000) /* 2 seconds */ +#define HCI_IDLE_TIMEOUT (6000) /* 6 seconds */ +#define HCI_INIT_TIMEOUT (10000) /* 10 seconds */ /* HCI Packet types */ #define HCI_COMMAND_PKT 0x01 @@ -144,7 +146,7 @@ #define LMP_SOFFSET 0x08 #define LMP_TACCURACY 0x10 #define LMP_RSWITCH 0x20 #define LMP_HOLD 0x40 -#define LMP_SNIF 0x80 +#define LMP_SNIFF 0x80 #define LMP_PARK 0x01 #define LMP_RSSI 0x02 @@ -159,13 +161,21 @@ #define LMP_CVSD 0x01 #define LMP_PSCHEME 0x02 #define LMP_PCONTROL 0x04 +#define LMP_SNIFF_SUBR 0x02 + +/* Connection modes */ +#define HCI_CM_ACTIVE 0x0000 +#define HCI_CM_HOLD 0x0001 +#define HCI_CM_SNIFF 0x0002 +#define HCI_CM_PARK 0x0003 + /* Link policies */ #define HCI_LP_RSWITCH 0x0001 #define HCI_LP_HOLD 0x0002 #define HCI_LP_SNIFF 0x0004 #define HCI_LP_PARK 0x0008 -/* Link mode */ +/* Link modes */ #define HCI_LM_ACCEPT 0x8000 #define HCI_LM_MASTER 0x0001 #define HCI_LM_AUTH 0x0002 @@ -191,7 +201,7 @@ struct hci_rp_read_loc_version { } __attribute__ ((packed)); #define OCF_READ_LOCAL_FEATURES 0x0003 -struct hci_rp_read_loc_features { +struct hci_rp_read_local_features { __u8 status; __u8 features[8]; } __attribute__ ((packed)); @@ -375,17 +385,32 @@ struct hci_cp_change_conn_link_key { } __attribute__ ((packed)); #define OCF_READ_REMOTE_FEATURES 0x001B -struct hci_cp_read_rmt_features { +struct hci_cp_read_remote_features { __le16 handle; } __attribute__ ((packed)); #define OCF_READ_REMOTE_VERSION 0x001D -struct hci_cp_read_rmt_version { +struct hci_cp_read_remote_version { __le16 handle; } __attribute__ ((packed)); /* Link Policy */ -#define OGF_LINK_POLICY 0x02 +#define OGF_LINK_POLICY 0x02 + +#define OCF_SNIFF_MODE 0x0003 +struct hci_cp_sniff_mode { + __le16 handle; + __le16 max_interval; + __le16 min_interval; + __le16 attempt; + __le16 timeout; +} __attribute__ ((packed)); + +#define OCF_EXIT_SNIFF_MODE 0x0004 +struct hci_cp_exit_sniff_mode { + __le16 handle; +} __attribute__ ((packed)); + #define OCF_ROLE_DISCOVERY 0x0009 struct hci_cp_role_discovery { __le16 handle; @@ -406,7 +431,7 @@ struct hci_rp_read_link_policy { __le16 policy; } __attribute__ ((packed)); -#define OCF_SWITCH_ROLE 0x000B +#define OCF_SWITCH_ROLE 0x000B struct hci_cp_switch_role { bdaddr_t bdaddr; __u8 role; @@ -422,6 +447,14 @@ struct hci_rp_write_link_policy { __le16 handle; } __attribute__ ((packed)); +#define OCF_SNIFF_SUBRATE 0x0011 +struct hci_cp_sniff_subrate { + __le16 handle; + __le16 max_latency; + __le16 min_remote_timeout; + __le16 min_local_timeout; +} __attribute__ ((packed)); + /* Status params */ #define OGF_STATUS_PARAM 0x05 @@ -581,15 +614,15 @@ struct hci_ev_link_key_notify { __u8 key_type; } __attribute__ ((packed)); -#define HCI_EV_RMT_FEATURES 0x0B -struct hci_ev_rmt_features { +#define HCI_EV_REMOTE_FEATURES 0x0B +struct hci_ev_remote_features { __u8 status; __le16 handle; __u8 features[8]; } __attribute__ ((packed)); -#define HCI_EV_RMT_VERSION 0x0C -struct hci_ev_rmt_version { +#define HCI_EV_REMOTE_VERSION 0x0C +struct hci_ev_remote_version { __u8 status; __le16 handle; __u8 lmp_ver; @@ -610,6 +643,16 @@ struct hci_ev_pscan_rep_mode { __u8 pscan_rep_mode; } __attribute__ ((packed)); +#define HCI_EV_SNIFF_SUBRATE 0x2E +struct hci_ev_sniff_subrate { + __u8 status; + __le16 handle; + __le16 max_tx_latency; + __le16 max_rx_latency; + __le16 max_remote_timeout; + __le16 max_local_timeout; +} __attribute__ ((packed)); + /* Internal events generated by Bluetooth stack */ #define HCI_EV_STACK_INTERNAL 0xFD struct hci_ev_stack_internal { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bb9f81d..d84855f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -31,10 +31,7 @@ #include #define HCI_PROTO_L2CAP 0 #define HCI_PROTO_SCO 1 -#define HCI_INIT_TIMEOUT (HZ * 10) - /* HCI Core structures */ - struct inquiry_data { bdaddr_t bdaddr; __u8 pscan_rep_mode; @@ -81,6 +78,10 @@ struct hci_dev { __u16 link_policy; __u16 link_mode; + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + unsigned long quirks; atomic_t cmd_cnt; @@ -123,7 +124,8 @@ struct hci_dev { atomic_t promisc; - struct class_device class_dev; + struct device *parent; + struct device dev; struct module *owner; @@ -145,18 +147,24 @@ struct hci_conn { bdaddr_t dst; __u16 handle; __u16 state; + __u8 mode; __u8 type; __u8 out; __u8 dev_class[3]; + __u8 features[8]; + __u16 interval; + __u16 link_policy; __u32 link_mode; + __u8 power_save; unsigned long pend; - + unsigned int sent; - + struct sk_buff_head data_q; - struct timer_list timer; - + struct timer_list disc_timer; + struct timer_list idle_timer; + struct hci_dev *hdev; void *l2cap_data; void *sco_data; @@ -211,7 +219,8 @@ void hci_inquiry_cache_update(struct hci enum { HCI_CONN_AUTH_PEND, HCI_CONN_ENCRYPT_PEND, - HCI_CONN_RSWITCH_PEND + HCI_CONN_RSWITCH_PEND, + HCI_CONN_MODE_CHANGE_PEND, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) @@ -286,31 +295,27 @@ int hci_conn_encrypt(struct hci_conn *co int hci_conn_change_link_key(struct hci_conn *conn); int hci_conn_switch_role(struct hci_conn *conn, uint8_t role); -static inline void hci_conn_set_timer(struct hci_conn *conn, unsigned long timeout) -{ - mod_timer(&conn->timer, jiffies + timeout); -} - -static inline void hci_conn_del_timer(struct hci_conn *conn) -{ - del_timer(&conn->timer); -} +void hci_conn_enter_active_mode(struct hci_conn *conn); +void hci_conn_enter_sniff_mode(struct hci_conn *conn); static inline void hci_conn_hold(struct hci_conn *conn) { atomic_inc(&conn->refcnt); - hci_conn_del_timer(conn); + del_timer(&conn->disc_timer); } static inline void hci_conn_put(struct hci_conn *conn) { if (atomic_dec_and_test(&conn->refcnt)) { + unsigned long timeo; if (conn->type == ACL_LINK) { - unsigned long timeo = (conn->out) ? - HCI_DISCONN_TIMEOUT : HCI_DISCONN_TIMEOUT * 2; - hci_conn_set_timer(conn, timeo); + timeo = msecs_to_jiffies(HCI_DISCONN_TIMEOUT); + if (!conn->out) + timeo *= 2; + del_timer(&conn->idle_timer); } else - hci_conn_set_timer(conn, HZ / 100); + timeo = msecs_to_jiffies(10); + mod_timer(&conn->disc_timer, jiffies + timeo); } } @@ -408,11 +413,13 @@ static inline int hci_recv_frame(struct int hci_register_sysfs(struct hci_dev *hdev); void hci_unregister_sysfs(struct hci_dev *hdev); -#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->class_dev.dev = (pdev)) +#define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->parent = (pdev)) /* ----- LMP capabilities ----- */ -#define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH) -#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT) +#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH) +#define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT) +#define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF) +#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) /* ----- HCI protocols ----- */ struct hci_proto { diff --git a/include/net/compat.h b/include/net/compat.h index e65cbed..9859b60 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -1,7 +1,6 @@ #ifndef NET_COMPAT_H #define NET_COMPAT_H -#include struct sock; diff --git a/include/net/dst.h b/include/net/dst.h index 5161e89..36d54fc 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -8,7 +8,6 @@ #ifndef _NET_DST_H #define _NET_DST_H -#include #include #include #include diff --git a/include/net/icmp.h b/include/net/icmp.h index e7c3f20..05f8ff7 100644 --- a/include/net/icmp.h +++ b/include/net/icmp.h @@ -18,7 +18,6 @@ #ifndef _ICMP_H #define _ICMP_H -#include #include #include diff --git a/include/net/ieee80211.h b/include/net/ieee80211.h index d5926bf..ecc4286 100644 --- a/include/net/ieee80211.h +++ b/include/net/ieee80211.h @@ -29,7 +29,7 @@ #include /* ETH_ALEN #include /* ARRAY_SIZE */ #include -#define IEEE80211_VERSION "git-1.1.7" +#define IEEE80211_VERSION "git-1.1.13" #define IEEE80211_DATA_LEN 2304 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section @@ -104,6 +104,9 @@ #define IEEE80211_STYPE_QOS_DATA #define IEEE80211_SCTL_FRAG 0x000F #define IEEE80211_SCTL_SEQ 0xFFF0 +/* QOS control */ +#define IEEE80211_QCTL_TID 0x000F + /* debug macros */ #ifdef CONFIG_IEEE80211_DEBUG @@ -965,6 +968,7 @@ #define IEEE80211_52GHZ_CHANNELS (IEEE80 enum { IEEE80211_CH_PASSIVE_ONLY = (1 << 0), + IEEE80211_CH_80211H_RULES = (1 << 1), IEEE80211_CH_B_ONLY = (1 << 2), IEEE80211_CH_NO_IBSS = (1 << 3), IEEE80211_CH_UNIFORM_SPREADING = (1 << 4), @@ -973,10 +977,10 @@ enum { }; struct ieee80211_channel { - u32 freq; + u32 freq; /* in MHz */ u8 channel; u8 flags; - u8 max_power; + u8 max_power; /* in dBm */ }; struct ieee80211_geo { @@ -1075,6 +1079,7 @@ struct ieee80211_device { int (*handle_management) (struct net_device * dev, struct ieee80211_network * network, u16 type); + int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); /* Typical STA methods */ int (*handle_auth) (struct net_device * dev, @@ -1243,7 +1248,8 @@ extern int ieee80211_set_encryption(stru extern int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev); extern void ieee80211_txb_free(struct ieee80211_txb *); extern int ieee80211_tx_frame(struct ieee80211_device *ieee, - struct ieee80211_hdr *frame, int len); + struct ieee80211_hdr *frame, int hdr_len, + int total_len, int encrypt_mpdu); /* ieee80211_rx.c */ extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h index 052ed59..00ad810 100644 --- a/include/net/ieee80211softmac.h +++ b/include/net/ieee80211softmac.h @@ -86,6 +86,9 @@ struct ieee80211softmac_assoc_info { /* BSSID we're trying to associate to */ char bssid[ETH_ALEN]; + + /* Rates supported by the network */ + struct ieee80211softmac_ratesinfo supported_rates; /* some flags. * static_essid is valid if the essid is constant, @@ -101,6 +104,7 @@ struct ieee80211softmac_assoc_info { */ u8 static_essid:1, associating:1, + assoc_wait:1, bssvalid:1, bssfixed:1; @@ -132,23 +136,26 @@ #define IEEE80211SOFTMAC_ASSOC_SCAN_RETR struct ieee80211softmac_txrates { /* The Bit-Rate to be used for multicast frames. */ u8 mcast_rate; - /* The Bit-Rate to be used for multicast fallback - * (If the device supports fallback and hardware-retry) - */ - u8 mcast_fallback; + + /* The Bit-Rate to be used for multicast management frames. */ + u8 mgt_mcast_rate; + /* The Bit-Rate to be used for any other (normal) data packet. */ u8 default_rate; /* The Bit-Rate to be used for default fallback * (If the device supports fallback and hardware-retry) */ u8 default_fallback; + + /* This is the rate that the user asked for */ + u8 user_rate; }; /* Bits for txrates_change callback. */ #define IEEE80211SOFTMAC_TXRATECHG_DEFAULT (1 << 0) /* default_rate */ #define IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK (1 << 1) /* default_fallback */ #define IEEE80211SOFTMAC_TXRATECHG_MCAST (1 << 2) /* mcast_rate */ -#define IEEE80211SOFTMAC_TXRATECHG_MCAST_FBACK (1 << 3) /* mcast_fallback */ +#define IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST (1 << 3) /* mgt_mcast_rate */ struct ieee80211softmac_device { /* 802.11 structure for data stuff */ @@ -250,6 +257,28 @@ extern void ieee80211softmac_fragment_lo * Note that the rates need to be sorted. */ extern void ieee80211softmac_set_rates(struct net_device *dev, u8 count, u8 *rates); +/* Helper function which advises you the rate at which a frame should be + * transmitted at. */ +static inline u8 ieee80211softmac_suggest_txrate(struct ieee80211softmac_device *mac, + int is_multicast, + int is_mgt) +{ + struct ieee80211softmac_txrates *txrates = &mac->txrates; + + if (!mac->associated) + return txrates->mgt_mcast_rate; + + /* We are associated, sending unicast frame */ + if (!is_multicast) + return txrates->default_rate; + + /* We are associated, sending multicast frame */ + if (is_mgt) + return txrates->mgt_mcast_rate; + else + return txrates->mcast_rate; +} + /* Start the SoftMAC. Call this after you initialized the device * and it is ready to run. */ @@ -282,7 +311,7 @@ #define IEEE80211SOFTMAC_EVENT_LAST 8 * - context set to the context data you want passed * The return value is 0, or an error. */ -typedef void (*notify_function_ptr)(struct net_device *dev, void *context); +typedef void (*notify_function_ptr)(struct net_device *dev, int event_type, void *context); #define ieee80211softmac_notify(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_KERNEL); #define ieee80211softmac_notify_atomic(dev, event, fun, context) ieee80211softmac_notify_gfp(dev, event, fun, context, GFP_ATOMIC); diff --git a/include/net/ieee80211softmac_wx.h b/include/net/ieee80211softmac_wx.h index 3e0be45..4ee3ad5 100644 --- a/include/net/ieee80211softmac_wx.h +++ b/include/net/ieee80211softmac_wx.h @@ -91,4 +91,9 @@ ieee80211softmac_wx_get_genie(struct net struct iw_request_info *info, union iwreq_data *wrqu, char *extra); +extern int +ieee80211softmac_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra); #endif /* _IEEE80211SOFTMAC_WX */ diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index 59f0c83..bc6a71d 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -14,7 +14,6 @@ #ifndef _INET6_HASHTABLES_H #define _INET6_HASHTABLES_H -#include #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 135d80f..98e0bb3 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -14,7 +14,6 @@ #ifndef _INET_HASHTABLES_H #define _INET_HASHTABLES_H -#include #include #include diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 883eb52..1f4a9a6 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -16,7 +16,6 @@ #ifndef _INET_SOCK_H #define _INET_SOCK_H -#include #include #include diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index e837f98..600cb54 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -15,7 +15,6 @@ #ifndef _INET_TIMEWAIT_SOCK_ #define _INET_TIMEWAIT_SOCK_ -#include #include #include diff --git a/include/net/ip.h b/include/net/ip.h index 3d2e5ca..98f9084 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -22,7 +22,6 @@ #ifndef _IP_H #define _IP_H -#include #include #include #include @@ -147,7 +146,6 @@ void ip_send_reply(struct sock *sk, stru struct ipv4_config { int log_martians; - int autoconfig; int no_pmtu_disc; }; diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index a398ae5..ab29daf 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -146,7 +146,7 @@ static inline void ip6_dst_store(struct struct rt6_info *rt = (struct rt6_info *) dst; write_lock(&sk->sk_dst_lock); - __sk_dst_set(sk, dst); + sk_setup_caps(sk, dst); np->daddr_cache = daddr; np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; write_unlock(&sk->sk_dst_lock); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index e000fa2..a095d1d 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -16,7 +16,6 @@ #ifndef _NET_IP_FIB_H #define _NET_IP_FIB_H -#include #include #include diff --git a/include/net/ip_mp_alg.h b/include/net/ip_mp_alg.h index 7722573..ac747b6 100644 --- a/include/net/ip_mp_alg.h +++ b/include/net/ip_mp_alg.h @@ -7,7 +7,6 @@ #ifndef _NET_IP_MP_ALG_H #define _NET_IP_MP_ALG_H -#include #include #include #include diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 7d2674f..3b57b15 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -248,7 +248,6 @@ struct ip_vs_daemon_user { #ifdef __KERNEL__ -#include #include /* for struct list_head */ #include /* for struct rwlock_t */ #include /* for struct atomic_t */ diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4abedb8..a8fdf79 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -104,7 +104,6 @@ #define IP6_MF 0x0001 #ifdef __KERNEL__ -#include #include /* sysctls */ diff --git a/include/net/irda/irda.h b/include/net/irda/irda.h index 1880e46..1cb0607 100644 --- a/include/net/irda/irda.h +++ b/include/net/irda/irda.h @@ -26,7 +26,6 @@ #ifndef NET_IRDA_H #define NET_IRDA_H -#include #include /* struct sk_buff */ #include #include /* sa_family_t in */ diff --git a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h index 92c8280..bca19ca 100644 --- a/include/net/irda/irda_device.h +++ b/include/net/irda/irda_device.h @@ -39,7 +39,6 @@ #ifndef IRDA_DEVICE_H #define IRDA_DEVICE_H -#include #include #include #include @@ -161,7 +160,7 @@ typedef struct { int irq, irq2; /* Interrupts used */ int dma, dma2; /* DMA channel(s) used */ int fifo_size; /* FIFO size */ - int irqflags; /* interrupt flags (ie, SA_SHIRQ|SA_INTERRUPT) */ + int irqflags; /* interrupt flags (ie, IRQF_SHARED|IRQF_DISABLED) */ int direction; /* Link direction, used by some FIR drivers */ int enabled; /* Powered on? */ int suspended; /* Suspended by APM */ diff --git a/include/net/irda/irlap.h b/include/net/irda/irlap.h index 2127cae..e77eb88 100644 --- a/include/net/irda/irlap.h +++ b/include/net/irda/irlap.h @@ -27,7 +27,6 @@ #ifndef IRLAP_H #define IRLAP_H -#include #include #include #include diff --git a/include/net/irda/irlmp.h b/include/net/irda/irlmp.h index c0c895d..11ecfa5 100644 --- a/include/net/irda/irlmp.h +++ b/include/net/irda/irlmp.h @@ -29,7 +29,6 @@ #define IRLMP_H #include /* for HZ */ -#include #include #include diff --git a/include/net/irda/irlmp_frame.h b/include/net/irda/irlmp_frame.h index eb3ad15..c463f8b 100644 --- a/include/net/irda/irlmp_frame.h +++ b/include/net/irda/irlmp_frame.h @@ -26,7 +26,6 @@ #ifndef IRMLP_FRAME_H #define IRMLP_FRAME_H -#include #include #include diff --git a/include/net/irda/qos.h b/include/net/irda/qos.h index 9ae3d6b..cc577dc 100644 --- a/include/net/irda/qos.h +++ b/include/net/irda/qos.h @@ -31,7 +31,6 @@ #ifndef IRDA_QOS_H #define IRDA_QOS_H -#include #include #include diff --git a/include/net/llc_if.h b/include/net/llc_if.h index 090eaa0..c608812 100644 --- a/include/net/llc_if.h +++ b/include/net/llc_if.h @@ -16,6 +16,7 @@ #define LLC_IF_H #include #include #include +#include #include #define LLC_DATAUNIT_PRIM 1 @@ -61,8 +62,6 @@ #define LLC_STATUS_REFUSE 6 /* data conn #define LLC_STATUS_CONFLICT 7 /* disconnect conn */ #define LLC_STATUS_RESET_DONE 8 /* */ -extern u8 llc_mac_null_var[IFHWADDRLEN]; - /** * llc_mac_null - determines if a address is a null mac address * @mac: Mac address to test if null. @@ -70,16 +69,20 @@ extern u8 llc_mac_null_var[IFHWADDRLEN]; * Determines if a given address is a null mac address. Returns 0 if the * address is not a null mac, 1 if the address is a null mac. */ -static __inline__ int llc_mac_null(u8 *mac) +static inline int llc_mac_null(const u8 *mac) { - return !memcmp(mac, llc_mac_null_var, IFHWADDRLEN); + return is_zero_ether_addr(mac); } -static __inline__ int llc_addrany(struct llc_addr *addr) +static inline int llc_addrany(const struct llc_addr *addr) { return llc_mac_null(addr->mac) && !addr->lsap; } +static inline int llc_mac_multicast(const u8 *mac) +{ + return is_multicast_ether_addr(mac); +} /** * llc_mac_match - determines if two mac addresses are the same * @mac1: First mac address to compare. @@ -89,9 +92,9 @@ static __inline__ int llc_addrany(struct * is not a complete match up to len, 1 if a complete match up to len is * found. */ -static __inline__ int llc_mac_match(u8 *mac1, u8 *mac2) +static inline int llc_mac_match(const u8 *mac1, const u8 *mac2) { - return !memcmp(mac1, mac2, IFHWADDRLEN); + return !compare_ether_addr(mac1, mac2); } extern int llc_establish_connection(struct sock *sk, u8 *lmac, diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 91fa271..d3915da 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -37,7 +37,6 @@ #define ND_MAX_RANDOM_FACTOR (3/2) #ifdef __KERNEL__ -#include #include #include #include diff --git a/include/net/netdma.h b/include/net/netdma.h new file mode 100644 index 0000000..19760eb --- /dev/null +++ b/include/net/netdma.h @@ -0,0 +1,44 @@ +/* + * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + */ +#ifndef NETDMA_H +#define NETDMA_H +#include +#ifdef CONFIG_NET_DMA +#include +#include + +static inline struct dma_chan *get_softnet_dma(void) +{ + struct dma_chan *chan; + rcu_read_lock(); + chan = rcu_dereference(__get_cpu_var(softnet_data.net_dma)); + if (chan) + dma_chan_get(chan); + rcu_read_unlock(); + return chan; +} + +int dma_skb_copy_datagram_iovec(struct dma_chan* chan, + const struct sk_buff *skb, int offset, struct iovec *to, + size_t len, struct dma_pinned_list *pinned_list); + +#endif /* CONFIG_NET_DMA */ +#endif /* NETDMA_H */ diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 916013c..1fbd819 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -15,7 +15,6 @@ #define _NF_CONNTRACK_H #include #ifdef __KERNEL__ -#include #include #include #include @@ -114,6 +113,10 @@ #if defined(CONFIG_NF_CONNTRACK_MARK) u_int32_t mark; #endif +#ifdef CONFIG_NF_CONNTRACK_SECMARK + u_int32_t secmark; +#endif + /* Storage reserved for other modules: */ union nf_conntrack_proto proto; @@ -285,6 +288,7 @@ static inline int nf_ct_is_dying(struct } extern unsigned int nf_conntrack_htable_size; +extern int nf_conntrack_checksum; #define NF_CT_STAT_INC(count) (__get_cpu_var(nf_conntrack_stat).count++) diff --git a/include/net/netfilter/nf_conntrack_compat.h b/include/net/netfilter/nf_conntrack_compat.h index 3cac19f..f1b1482 100644 --- a/include/net/netfilter/nf_conntrack_compat.h +++ b/include/net/netfilter/nf_conntrack_compat.h @@ -20,6 +20,19 @@ static inline u_int32_t *nf_ct_get_mark( } #endif /* CONFIG_IP_NF_CONNTRACK_MARK */ +#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK +static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb, + u_int32_t *ctinfo) +{ + struct ip_conntrack *ct = ip_conntrack_get(skb, ctinfo); + + if (ct) + return &ct->secmark; + else + return NULL; +} +#endif /* CONFIG_IP_NF_CONNTRACK_SECMARK */ + #ifdef CONFIG_IP_NF_CT_ACCT static inline struct ip_conntrack_counter * nf_ct_get_counters(const struct sk_buff *skb) @@ -70,6 +83,19 @@ static inline u_int32_t *nf_ct_get_mark( } #endif /* CONFIG_NF_CONNTRACK_MARK */ +#ifdef CONFIG_NF_CONNTRACK_SECMARK +static inline u_int32_t *nf_ct_get_secmark(const struct sk_buff *skb, + u_int32_t *ctinfo) +{ + struct nf_conn *ct = nf_ct_get(skb, ctinfo); + + if (ct) + return &ct->secmark; + else + return NULL; +} +#endif /* CONFIG_NF_CONNTRACK_MARK */ + #ifdef CONFIG_NF_CT_ACCT static inline struct ip_conntrack_counter * nf_ct_get_counters(const struct sk_buff *skb) diff --git a/include/net/pkt_act.h b/include/net/pkt_act.h index b225d84..cf5e4d2 100644 --- a/include/net/pkt_act.h +++ b/include/net/pkt_act.h @@ -4,7 +4,6 @@ #define __NET_PKT_ACT_H #include #include #include -#include #include #include #include diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index b94d1ad..1925c65 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -169,17 +169,23 @@ #define PSCHED_TLESS(tv1, tv2) (((tv1).t #define PSCHED_TADD2(tv, delta, tv_res) \ ({ \ - int __delta = (tv).tv_usec + (delta); \ - (tv_res).tv_sec = (tv).tv_sec; \ - if (__delta > USEC_PER_SEC) { (tv_res).tv_sec++; __delta -= USEC_PER_SEC; } \ + int __delta = (delta); \ + (tv_res) = (tv); \ + while(__delta >= USEC_PER_SEC){ \ + (tv_res).tv_sec++; \ + __delta -= USEC_PER_SEC; \ + } \ (tv_res).tv_usec = __delta; \ }) #define PSCHED_TADD(tv, delta) \ ({ \ - (tv).tv_usec += (delta); \ - if ((tv).tv_usec > USEC_PER_SEC) { (tv).tv_sec++; \ - (tv).tv_usec -= USEC_PER_SEC; } \ + int __delta = (delta); \ + while(__delta >= USEC_PER_SEC){ \ + (tv).tv_sec++; \ + __delta -= USEC_PER_SEC; \ + } \ + (tv).tv_usec = __delta; \ }) /* Set/check that time is in the "past perfect"; @@ -218,12 +224,13 @@ extern struct qdisc_rate_table *qdisc_ge struct rtattr *tab); extern void qdisc_put_rtab(struct qdisc_rate_table *tab); -extern int qdisc_restart(struct net_device *dev); +extern void __qdisc_run(struct net_device *dev); static inline void qdisc_run(struct net_device *dev) { - while (!netif_queue_stopped(dev) && qdisc_restart(dev) < 0) - /* NOTHING */; + if (!netif_queue_stopped(dev) && + !test_and_set_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) + __qdisc_run(dev); } extern int tc_classify(struct sk_buff *skb, struct tcf_proto *tp, diff --git a/include/net/protocol.h b/include/net/protocol.h index 6dc5970..a225d63 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -24,7 +24,6 @@ #ifndef _PROTOCOL_H #define _PROTOCOL_H -#include #include #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include @@ -37,6 +36,8 @@ #define MAX_INET_PROTOS 256 /* Must be struct net_protocol { int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); + struct sk_buff *(*gso_segment)(struct sk_buff *skb, + int features); int no_policy; }; @@ -49,11 +50,17 @@ struct inet6_protocol struct inet6_skb_parm *opt, int type, int code, int offset, __u32 info); + + struct sk_buff *(*gso_segment)(struct sk_buff *skb, + int features); + unsigned int flags; /* INET6_PROTO_xxx */ }; #define INET6_PROTO_NOPOLICY 0x1 #define INET6_PROTO_FINAL 0x2 +/* This should be set for any extension header which is compatible with GSO. */ +#define INET6_PROTO_GSO_EXTHDR 0x4 #endif /* This is used to register socket interfaces for IP protocols. */ diff --git a/include/net/raw.h b/include/net/raw.h index e67b28a..e4af597 100644 --- a/include/net/raw.h +++ b/include/net/raw.h @@ -17,7 +17,6 @@ #ifndef _RAW_H #define _RAW_H -#include #include @@ -36,7 +35,7 @@ extern rwlock_t raw_v4_lock; extern struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num, - unsigned long raddr, unsigned long laddr, + __be32 raddr, __be32 laddr, int dif); extern int raw_v4_input(struct sk_buff *skb, struct iphdr *iph, int hash); diff --git a/include/net/red.h b/include/net/red.h index 2ed4358..5ccdbb3 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -1,7 +1,6 @@ #ifndef __NET_SCHED_RED_H #define __NET_SCHED_RED_H -#include #include #include #include diff --git a/include/net/route.h b/include/net/route.h index 98c915a..c4a0686 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -24,7 +24,6 @@ #ifndef _ROUTE_H #define _ROUTE_H -#include #include #include #include diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 7b6ec99..b0e9108 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -1,7 +1,6 @@ #ifndef __NET_SCHED_GENERIC_H #define __NET_SCHED_GENERIC_H -#include #include #include #include diff --git a/include/net/scm.h b/include/net/scm.h index 540619c..02daa09 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -19,6 +19,10 @@ struct scm_cookie { struct ucred creds; /* Skb credentials */ struct scm_fp_list *fp; /* Passed files */ +#ifdef CONFIG_SECURITY_NETWORK + char *secdata; /* Security context */ + u32 seclen; /* Security length */ +#endif unsigned long seq; /* Connection seqno */ }; @@ -48,6 +52,17 @@ static __inline__ int scm_send(struct so return __scm_send(sock, msg, scm); } +#ifdef CONFIG_SECURITY_NETWORK +static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm) +{ + if (test_bit(SOCK_PASSSEC, &sock->flags) && scm->secdata != NULL) + put_cmsg(msg, SOL_SOCKET, SCM_SECURITY, scm->seclen, scm->secdata); +} +#else +static inline void scm_passec(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm) +{ } +#endif /* CONFIG_SECURITY_NETWORK */ + static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm, int flags) { @@ -62,6 +77,8 @@ static __inline__ void scm_recv(struct s if (test_bit(SOCK_PASSCRED, &sock->flags)) put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds); + scm_passec(sock, msg, scm); + if (!scm->fp) return; diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index aa6033c..a9663b4 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -63,7 +63,6 @@ #define __net_sctp_h__ */ -#include #ifdef TEST_FRAME #undef CONFIG_PROC_FS diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 7f4fea1..5f69158 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -555,7 +555,8 @@ struct sctp_af { int (*to_addr_param) (const union sctp_addr *, union sctp_addr_param *); int (*addr_valid) (union sctp_addr *, - struct sctp_sock *); + struct sctp_sock *, + const struct sk_buff *); sctp_scope_t (*scope) (union sctp_addr *); void (*inaddr_any) (union sctp_addr *, unsigned short); int (*is_any) (const union sctp_addr *); diff --git a/include/net/sock.h b/include/net/sock.h index c9fad6f..324b3ea 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -40,11 +40,11 @@ #ifndef _SOCK_H #define _SOCK_H -#include #include #include #include #include +#include #include #include /* struct sk_buff */ #include @@ -79,14 +79,17 @@ typedef struct { spinlock_t slock; struct sock_iocb *owner; wait_queue_head_t wq; + /* + * We express the mutex-alike socket_lock semantics + * to the lock validator by explicitly managing + * the slock as a lock variant (in addition to + * the slock itself): + */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif } socket_lock_t; -#define sock_lock_init(__sk) \ -do { spin_lock_init(&((__sk)->sk_lock.slock)); \ - (__sk)->sk_lock.owner = NULL; \ - init_waitqueue_head(&((__sk)->sk_lock.wq)); \ -} while(0) - struct sock; struct proto; @@ -132,6 +135,7 @@ struct sock_common { * @sk_receive_queue: incoming packets * @sk_wmem_alloc: transmit queue bytes committed * @sk_write_queue: Packet sending queue + * @sk_async_wait_queue: DMA copied packets * @sk_omem_alloc: "o" is "option" or "other" * @sk_wmem_queued: persistent queue size * @sk_forward_alloc: space allocated forward @@ -140,6 +144,7 @@ struct sock_common { * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, %SO_OOBINLINE settings * @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) + * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) * @sk_lingertime: %SO_LINGER l_linger setting * @sk_backlog: always used with the per-socket spinlock held * @sk_callback_lock: used with the callbacks in the end of this struct @@ -205,11 +210,13 @@ #define sk_prot __sk_common.skc_prot atomic_t sk_omem_alloc; struct sk_buff_head sk_receive_queue; struct sk_buff_head sk_write_queue; + struct sk_buff_head sk_async_wait_queue; int sk_wmem_queued; int sk_forward_alloc; gfp_t sk_allocation; int sk_sndbuf; int sk_route_caps; + int sk_gso_type; int sk_rcvlowat; unsigned long sk_flags; unsigned long sk_lingertime; @@ -382,7 +389,6 @@ enum sock_flags { SOCK_USE_WRITE_QUEUE, /* whether to call sk->sk_write_space in sock_wfree */ SOCK_DBG, /* %SO_DEBUG setting */ SOCK_RCVTSTAMP, /* %SO_TIMESTAMP setting */ - SOCK_NO_LARGESEND, /* whether to sent large segments or not */ SOCK_LOCALROUTE, /* route locally only, %SO_DONTROUTE setting */ SOCK_QUEUE_SHRUNK, /* write queue has been shrunk recently */ }; @@ -745,6 +751,9 @@ extern void FASTCALL(release_sock(struct /* BH context may only use the following locking interface. */ #define bh_lock_sock(__sk) spin_lock(&((__sk)->sk_lock.slock)) +#define bh_lock_sock_nested(__sk) \ + spin_lock_nested(&((__sk)->sk_lock.slock), \ + SINGLE_DEPTH_NESTING) #define bh_unlock_sock(__sk) spin_unlock(&((__sk)->sk_lock.slock)) extern struct sock *sk_alloc(int family, @@ -871,10 +880,7 @@ static inline int sk_filter(struct sock if (filter) { unsigned int pkt_len = sk_run_filter(skb, filter->insns, filter->len); - if (!pkt_len) - err = -EPERM; - else - skb_trim(skb, pkt_len); + err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM; } if (needlock) @@ -1028,13 +1034,22 @@ extern struct dst_entry *__sk_dst_check( extern struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie); +static inline int sk_can_gso(const struct sock *sk) +{ + return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type); +} + static inline void sk_setup_caps(struct sock *sk, struct dst_entry *dst) { __sk_dst_set(sk, dst); sk->sk_route_caps = dst->dev->features; - if (sk->sk_route_caps & NETIF_F_TSO) { - if (sock_flag(sk, SOCK_NO_LARGESEND) || dst->header_len) - sk->sk_route_caps &= ~NETIF_F_TSO; + if (sk->sk_route_caps & NETIF_F_GSO) + sk->sk_route_caps |= NETIF_F_GSO_MASK; + if (sk_can_gso(sk)) { + if (dst->header_len) + sk->sk_route_caps &= ~NETIF_F_GSO_MASK; + else + sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; } } @@ -1267,15 +1282,27 @@ sock_recv_timestamp(struct msghdr *msg, * sk_eat_skb - Release a skb if it is no longer needed * @sk: socket to eat this skb from * @skb: socket buffer to eat + * @copied_early: flag indicating whether DMA operations copied this data early * * This routine must be called with interrupts disabled or with the socket * locked so that the sk_buff queue operation is ok. */ -static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb) +#ifdef CONFIG_NET_DMA +static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb, int copied_early) +{ + __skb_unlink(skb, &sk->sk_receive_queue); + if (!copied_early) + __kfree_skb(skb); + else + __skb_queue_tail(&sk->sk_async_wait_queue, skb); +} +#else +static inline void sk_eat_skb(struct sock *sk, struct sk_buff *skb, int copied_early) { __skb_unlink(skb, &sk->sk_receive_queue); __kfree_skb(skb); } +#endif extern void sock_enable_timestamp(struct sock *sk); extern int sock_get_timestamp(struct sock *, struct timeval __user *); diff --git a/include/net/tcp.h b/include/net/tcp.h index 3c989db..3cd803b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -21,13 +21,13 @@ #define _TCP_H #define TCP_DEBUG 1 #define FASTRETRANS_DEBUG 1 -#include #include #include #include #include #include #include +#include #include #include @@ -218,6 +218,7 @@ extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_tw_reuse; extern int sysctl_tcp_frto; extern int sysctl_tcp_low_latency; +extern int sysctl_tcp_dma_copybreak; extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; @@ -225,6 +226,7 @@ extern int sysctl_tcp_abc; extern int sysctl_tcp_mtu_probing; extern int sysctl_tcp_base_mss; extern int sysctl_tcp_workaround_signed_windows; +extern int sysctl_tcp_slow_start_after_idle; extern atomic_t tcp_memory_allocated; extern atomic_t tcp_sockets_allocated; @@ -293,6 +295,8 @@ extern int tcp_rcv_established(struct extern void tcp_rcv_space_adjust(struct sock *sk); +extern void tcp_cleanup_rbuf(struct sock *sk, int copied); + extern int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp); @@ -565,13 +569,13 @@ #include */ static inline int tcp_skb_pcount(const struct sk_buff *skb) { - return skb_shinfo(skb)->tso_segs; + return skb_shinfo(skb)->gso_segs; } /* This is valid iff tcp_skb_pcount() > 1. */ static inline int tcp_skb_mss(const struct sk_buff *skb) { - return skb_shinfo(skb)->tso_size; + return skb_shinfo(skb)->gso_size; } static inline void tcp_dec_pcount_approx(__u32 *count, @@ -628,7 +632,7 @@ struct tcp_congestion_ops { /* return slow start threshold (required) */ u32 (*ssthresh)(struct sock *sk); /* lower bound for congestion window (optional) */ - u32 (*min_cwnd)(struct sock *sk); + u32 (*min_cwnd)(const struct sock *sk); /* do new cwnd calculation (required) */ void (*cong_avoid)(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, int good_ack); @@ -663,7 +667,7 @@ extern struct tcp_congestion_ops tcp_ini extern u32 tcp_reno_ssthresh(struct sock *sk); extern void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, int flag); -extern u32 tcp_reno_min_cwnd(struct sock *sk); +extern u32 tcp_reno_min_cwnd(const struct sock *sk); extern struct tcp_congestion_ops tcp_reno; static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state) @@ -747,7 +751,7 @@ static inline int tcp_is_cwnd_limited(co if (in_flight >= tp->snd_cwnd) return 1; - if (!(sk->sk_route_caps & NETIF_F_TSO)) + if (!sk_can_gso(sk)) return 0; left = tp->snd_cwnd - in_flight; @@ -817,6 +821,12 @@ static inline void tcp_prequeue_init(str tp->ucopy.len = 0; tp->ucopy.memory = 0; skb_queue_head_init(&tp->ucopy.prequeue); +#ifdef CONFIG_NET_DMA + tp->ucopy.dma_chan = NULL; + tp->ucopy.wakeup = 0; + tp->ucopy.pinned_list = NULL; + tp->ucopy.dma_cookie = 0; +#endif } /* Packet is added to VJ-style prequeue for processing in process @@ -1076,6 +1086,8 @@ extern struct request_sock_ops tcp_reque extern int tcp_v4_destroy_sock(struct sock *sk); +extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); + #ifdef CONFIG_PROC_FS extern int tcp4_proc_init(void); extern void tcp4_proc_exit(void); diff --git a/include/net/tcp_ecn.h b/include/net/tcp_ecn.h index c6b8439..4629d77 100644 --- a/include/net/tcp_ecn.h +++ b/include/net/tcp_ecn.h @@ -31,10 +31,9 @@ static inline void TCP_ECN_send_syn(stru struct sk_buff *skb) { tp->ecn_flags = 0; - if (sysctl_tcp_ecn && !(sk->sk_route_caps & NETIF_F_TSO)) { + if (sysctl_tcp_ecn) { TCP_SKB_CB(skb)->flags |= TCPCB_FLAG_ECE|TCPCB_FLAG_CWR; tp->ecn_flags = TCP_ECN_OK; - sock_set_flag(sk, SOCK_NO_LARGESEND); } } @@ -56,6 +55,7 @@ static inline void TCP_ECN_send(struct s if (tp->ecn_flags&TCP_ECN_QUEUE_CWR) { tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; skb->h.th->cwr = 1; + skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; } } else { /* ACK or retransmitted segment: clear ECT|CE */ diff --git a/include/net/tipc/tipc_bearer.h b/include/net/tipc/tipc_bearer.h index 098607c..e07136d 100644 --- a/include/net/tipc/tipc_bearer.h +++ b/include/net/tipc/tipc_bearer.h @@ -49,10 +49,18 @@ #include #define TIPC_MEDIA_TYPE_ETH 1 +/* + * Destination address structure used by TIPC bearers when sending messages + * + * IMPORTANT: The fields of this structure MUST be stored using the specified + * byte order indicated below, as the structure is exchanged between nodes + * as part of a link setup process. + */ + struct tipc_media_addr { - __u32 type; + __u32 type; /* bearer type (network byte order) */ union { - __u8 eth_addr[6]; /* Ethernet bearer */ + __u8 eth_addr[6]; /* 48 bit Ethernet addr (byte array) */ #if 0 /* Prototypes for other possible bearer types */ diff --git a/include/net/xfrm.h b/include/net/xfrm.h index afa508d..9c5ee9f 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -20,6 +20,8 @@ #include #include #define XFRM_ALIGN8(len) (((len) + 7) & ~7) +#define MODULE_ALIAS_XFRM_MODE(family, encap) \ + MODULE_ALIAS("xfrm-mode-" __stringify(family) "-" __stringify(encap)) extern struct sock *xfrm_nl; extern u32 sysctl_xfrm_aevent_etime; @@ -164,6 +166,7 @@ struct xfrm_state /* Reference to data common to all the instances of this * transformer. */ struct xfrm_type *type; + struct xfrm_mode *mode; /* Security context */ struct xfrm_sec_ctx *security; @@ -204,8 +207,8 @@ struct xfrm_type; struct xfrm_dst; struct xfrm_policy_afinfo { unsigned short family; - rwlock_t lock; - struct xfrm_type_map *type_map; + struct xfrm_type *type_map[IPPROTO_MAX]; + struct xfrm_mode *mode_map[XFRM_MODE_MAX]; struct dst_ops *dst_ops; void (*garbage_collect)(void); int (*dst_lookup)(struct xfrm_dst **dst, struct flowi *fl); @@ -232,7 +235,6 @@ extern int __xfrm_state_delete(struct xf struct xfrm_state_afinfo { unsigned short family; - rwlock_t lock; struct list_head *state_bydst; struct list_head *state_byspi; int (*init_flags)(struct xfrm_state *x); @@ -264,16 +266,24 @@ struct xfrm_type u32 (*get_max_size)(struct xfrm_state *, int size); }; -struct xfrm_type_map { - rwlock_t lock; - struct xfrm_type *map[256]; -}; - extern int xfrm_register_type(struct xfrm_type *type, unsigned short family); extern int xfrm_unregister_type(struct xfrm_type *type, unsigned short family); extern struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family); extern void xfrm_put_type(struct xfrm_type *type); +struct xfrm_mode { + int (*input)(struct xfrm_state *x, struct sk_buff *skb); + int (*output)(struct sk_buff *skb); + + struct module *owner; + unsigned int encap; +}; + +extern int xfrm_register_mode(struct xfrm_mode *mode, int family); +extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family); +extern struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family); +extern void xfrm_put_mode(struct xfrm_mode *mode); + struct xfrm_tmpl { /* id in template is interpreted as: diff --git a/include/pcmcia/ss.h b/include/pcmcia/ss.h index 5e0a01a..ede6398 100644 --- a/include/pcmcia/ss.h +++ b/include/pcmcia/ss.h @@ -15,7 +15,6 @@ #ifndef _LINUX_SS_H #define _LINUX_SS_H -#include #include #include /* task_struct, completion */ #include diff --git a/include/rdma/Kbuild b/include/rdma/Kbuild new file mode 100644 index 0000000..eb710ba --- /dev/null +++ b/include/rdma/Kbuild @@ -0,0 +1 @@ +header-y := ib_user_mad.h diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h new file mode 100644 index 0000000..fcb5ba8 --- /dev/null +++ b/include/rdma/ib_addr.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2005 Voltaire Inc. All rights reserved. + * Copyright (c) 2005 Intel Corporation. All rights reserved. + * + * This Software is licensed under one of the following licenses: + * + * 1) under the terms of the "Common Public License 1.0" a copy of which is + * available from the Open Source Initiative, see + * http://www.opensource.org/licenses/cpl.php. + * + * 2) under the terms of the "The BSD License" a copy of which is + * available from the Open Source Initiative, see + * http://www.opensource.org/licenses/bsd-license.php. + * + * 3) under the terms of the "GNU General Public License (GPL) Version 2" a + * copy of which is available from the Open Source Initiative, see + * http://www.opensource.org/licenses/gpl-license.php. + * + * Licensee has the right to choose one of the above licenses. + * + * Redistributions of source code must retain the above copyright + * notice and one of the license notices. + * + * Redistributions in binary form must reproduce both the above copyright + * notice, one of the license notices in the documentation + * and/or other materials provided with the distribution. + * + */ + +#if !defined(IB_ADDR_H) +#define IB_ADDR_H + +#include +#include +#include +#include +#include + +struct rdma_dev_addr { + unsigned char src_dev_addr[MAX_ADDR_LEN]; + unsigned char dst_dev_addr[MAX_ADDR_LEN]; + unsigned char broadcast[MAX_ADDR_LEN]; + enum ib_node_type dev_type; +}; + +/** + * rdma_translate_ip - Translate a local IP address to an RDMA hardware + * address. + */ +int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr); + +/** + * rdma_resolve_ip - Resolve source and destination IP addresses to + * RDMA hardware addresses. + * @src_addr: An optional source address to use in the resolution. If a + * source address is not provided, a usable address will be returned via + * the callback. + * @dst_addr: The destination address to resolve. + * @addr: A reference to a data location that will receive the resolved + * addresses. The data location must remain valid until the callback has + * been invoked. + * @timeout_ms: Amount of time to wait for the address resolution to complete. + * @callback: Call invoked once address resolution has completed, timed out, + * or been canceled. A status of 0 indicates success. + * @context: User-specified context associated with the call. + */ +int rdma_resolve_ip(struct sockaddr *src_addr, struct sockaddr *dst_addr, + struct rdma_dev_addr *addr, int timeout_ms, + void (*callback)(int status, struct sockaddr *src_addr, + struct rdma_dev_addr *addr, void *context), + void *context); + +void rdma_addr_cancel(struct rdma_dev_addr *addr); + +static inline int ip_addr_size(struct sockaddr *addr) +{ + return addr->sa_family == AF_INET6 ? + sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); +} + +static inline u16 ib_addr_get_pkey(struct rdma_dev_addr *dev_addr) +{ + return ((u16)dev_addr->broadcast[8] << 8) | (u16)dev_addr->broadcast[9]; +} + +static inline void ib_addr_set_pkey(struct rdma_dev_addr *dev_addr, u16 pkey) +{ + dev_addr->broadcast[8] = pkey >> 8; + dev_addr->broadcast[9] = (unsigned char) pkey; +} + +static inline union ib_gid *ib_addr_get_sgid(struct rdma_dev_addr *dev_addr) +{ + return (union ib_gid *) (dev_addr->src_dev_addr + 4); +} + +static inline void ib_addr_set_sgid(struct rdma_dev_addr *dev_addr, + union ib_gid *gid) +{ + memcpy(dev_addr->src_dev_addr + 4, gid, sizeof *gid); +} + +static inline union ib_gid *ib_addr_get_dgid(struct rdma_dev_addr *dev_addr) +{ + return (union ib_gid *) (dev_addr->dst_dev_addr + 4); +} + +static inline void ib_addr_set_dgid(struct rdma_dev_addr *dev_addr, + union ib_gid *gid) +{ + memcpy(dev_addr->dst_dev_addr + 4, gid, sizeof *gid); +} + +#endif /* IB_ADDR_H */ diff --git a/include/rdma/ib_cache.h b/include/rdma/ib_cache.h index 5bf9834..f179d23 100644 --- a/include/rdma/ib_cache.h +++ b/include/rdma/ib_cache.h @@ -102,4 +102,17 @@ int ib_find_cached_pkey(struct ib_device u16 pkey, u16 *index); +/** + * ib_get_cached_lmc - Returns a cached lmc table entry + * @device: The device to query. + * @port_num: The port number of the device to query. + * @lmc: The lmc value for the specified port for that device. + * + * ib_get_cached_lmc() fetches the specified lmc table entry stored in + * the local software cache. + */ +int ib_get_cached_lmc(struct ib_device *device, + u8 port_num, + u8 *lmc); + #endif /* _IB_CACHE_H */ diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h index 0a9fcd5..c9b4738 100644 --- a/include/rdma/ib_cm.h +++ b/include/rdma/ib_cm.h @@ -32,7 +32,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: ib_cm.h 2730 2005-06-28 16:43:03Z sean.hefty $ + * $Id: ib_cm.h 4311 2005-12-05 18:42:01Z sean.hefty $ */ #if !defined(IB_CM_H) #define IB_CM_H @@ -102,7 +102,8 @@ enum ib_cm_data_size { IB_CM_APR_INFO_LENGTH = 72, IB_CM_SIDR_REQ_PRIVATE_DATA_SIZE = 216, IB_CM_SIDR_REP_PRIVATE_DATA_SIZE = 136, - IB_CM_SIDR_REP_INFO_LENGTH = 72 + IB_CM_SIDR_REP_INFO_LENGTH = 72, + IB_CM_COMPARE_SIZE = 64 }; struct ib_cm_id; @@ -238,7 +239,6 @@ struct ib_cm_sidr_rep_event_param { u32 qpn; void *info; u8 info_len; - }; struct ib_cm_event { @@ -317,6 +317,15 @@ void ib_destroy_cm_id(struct ib_cm_id *c #define IB_SERVICE_ID_AGN_MASK __constant_cpu_to_be64(0xFF00000000000000ULL) #define IB_CM_ASSIGN_SERVICE_ID __constant_cpu_to_be64(0x0200000000000000ULL) +#define IB_CMA_SERVICE_ID __constant_cpu_to_be64(0x0000000001000000ULL) +#define IB_CMA_SERVICE_ID_MASK __constant_cpu_to_be64(0xFFFFFFFFFF000000ULL) +#define IB_SDP_SERVICE_ID __constant_cpu_to_be64(0x0000000000010000ULL) +#define IB_SDP_SERVICE_ID_MASK __constant_cpu_to_be64(0xFFFFFFFFFFFF0000ULL) + +struct ib_cm_compare_data { + u8 data[IB_CM_COMPARE_SIZE]; + u8 mask[IB_CM_COMPARE_SIZE]; +}; /** * ib_cm_listen - Initiates listening on the specified service ID for @@ -330,10 +339,12 @@ #define IB_CM_ASSIGN_SERVICE_ID __consta * range of service IDs. If set to 0, the service ID is matched * exactly. This parameter is ignored if %service_id is set to * IB_CM_ASSIGN_SERVICE_ID. + * @compare_data: This parameter is optional. It specifies data that must + * appear in the private data of a connection request for the specified + * listen request. */ -int ib_cm_listen(struct ib_cm_id *cm_id, - __be64 service_id, - __be64 service_mask); +int ib_cm_listen(struct ib_cm_id *cm_id, __be64 service_id, __be64 service_mask, + struct ib_cm_compare_data *compare_data); struct ib_cm_req_param { struct ib_sa_path_rec *primary_path; @@ -535,7 +546,6 @@ struct ib_cm_sidr_req_param { const void *private_data; u8 private_data_len; u8 max_cm_retries; - u16 pkey; }; /** @@ -559,7 +569,7 @@ struct ib_cm_sidr_rep_param { }; /** - * ib_send_cm_sidr_rep - Sends a service ID resolution request to the + * ib_send_cm_sidr_rep - Sends a service ID resolution reply to the * remote node. * @cm_id: Communication identifier associated with the received service ID * resolution request. diff --git a/include/rdma/ib_marshall.h b/include/rdma/ib_marshall.h new file mode 100644 index 0000000..66bf4d7 --- /dev/null +++ b/include/rdma/ib_marshall.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2005 Intel Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(IB_USER_MARSHALL_H) +#define IB_USER_MARSHALL_H + +#include +#include +#include +#include + +void ib_copy_qp_attr_to_user(struct ib_uverbs_qp_attr *dst, + struct ib_qp_attr *src); + +void ib_copy_path_rec_to_user(struct ib_user_path_rec *dst, + struct ib_sa_path_rec *src); + +void ib_copy_path_rec_from_user(struct ib_sa_path_rec *dst, + struct ib_user_path_rec *src); + +#endif /* IB_USER_MARSHALL_H */ diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h index ad63c21..c99e442 100644 --- a/include/rdma/ib_sa.h +++ b/include/rdma/ib_sa.h @@ -370,5 +370,12 @@ ib_sa_mcmember_rec_delete(struct ib_devi context, query); } +/** + * ib_init_ah_from_path - Initialize address handle attributes based on an SA + * path record. + */ +int ib_init_ah_from_path(struct ib_device *device, u8 port_num, + struct ib_sa_path_rec *rec, + struct ib_ah_attr *ah_attr); #endif /* IB_SA_H */ diff --git a/include/rdma/ib_smi.h b/include/rdma/ib_smi.h index 87f6073..f29af13 100644 --- a/include/rdma/ib_smi.h +++ b/include/rdma/ib_smi.h @@ -85,6 +85,42 @@ #define IB_SMP_ATTR_VENDOR_DIAG __cons #define IB_SMP_ATTR_LED_INFO __constant_htons(0x0031) #define IB_SMP_ATTR_VENDOR_MASK __constant_htons(0xFF00) +struct ib_port_info { + __be64 mkey; + __be64 gid_prefix; + __be16 lid; + __be16 sm_lid; + __be32 cap_mask; + __be16 diag_code; + __be16 mkey_lease_period; + u8 local_port_num; + u8 link_width_enabled; + u8 link_width_supported; + u8 link_width_active; + u8 linkspeed_portstate; /* 4 bits, 4 bits */ + u8 portphysstate_linkdown; /* 4 bits, 4 bits */ + u8 mkeyprot_resv_lmc; /* 2 bits, 3, 3 */ + u8 linkspeedactive_enabled; /* 4 bits, 4 bits */ + u8 neighbormtu_mastersmsl; /* 4 bits, 4 bits */ + u8 vlcap_inittype; /* 4 bits, 4 bits */ + u8 vl_high_limit; + u8 vl_arb_high_cap; + u8 vl_arb_low_cap; + u8 inittypereply_mtucap; /* 4 bits, 4 bits */ + u8 vlstallcnt_hoqlife; /* 3 bits, 5 bits */ + u8 operationalvl_pei_peo_fpi_fpo; /* 4 bits, 1, 1, 1, 1 */ + __be16 mkey_violations; + __be16 pkey_violations; + __be16 qkey_violations; + u8 guid_cap; + u8 clientrereg_resv_subnetto; /* 1 bit, 2 bits, 5 */ + u8 resv_resptimevalue; /* 3 bits, 5 bits */ + u8 localphyerrors_overrunerrors; /* 4 bits, 4 bits */ + __be16 max_credit_hint; + u8 resv; + u8 link_roundtrip_latency[3]; +}; + static inline u8 ib_get_smp_direction(struct ib_smp *smp) { diff --git a/include/rdma/ib_user_cm.h b/include/rdma/ib_user_cm.h index 19be116..066c20b 100644 --- a/include/rdma/ib_user_cm.h +++ b/include/rdma/ib_user_cm.h @@ -30,13 +30,13 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: ib_user_cm.h 2576 2005-06-09 17:00:30Z libor $ + * $Id: ib_user_cm.h 4019 2005-11-11 00:33:09Z sean.hefty $ */ #ifndef IB_USER_CM_H #define IB_USER_CM_H -#include +#include #define IB_USER_CM_ABI_VERSION 4 @@ -110,58 +110,6 @@ struct ib_ucm_init_qp_attr { __u32 qp_state; }; -struct ib_ucm_ah_attr { - __u8 grh_dgid[16]; - __u32 grh_flow_label; - __u16 dlid; - __u16 reserved; - __u8 grh_sgid_index; - __u8 grh_hop_limit; - __u8 grh_traffic_class; - __u8 sl; - __u8 src_path_bits; - __u8 static_rate; - __u8 is_global; - __u8 port_num; -}; - -struct ib_ucm_init_qp_attr_resp { - __u32 qp_attr_mask; - __u32 qp_state; - __u32 cur_qp_state; - __u32 path_mtu; - __u32 path_mig_state; - __u32 qkey; - __u32 rq_psn; - __u32 sq_psn; - __u32 dest_qp_num; - __u32 qp_access_flags; - - struct ib_ucm_ah_attr ah_attr; - struct ib_ucm_ah_attr alt_ah_attr; - - /* ib_qp_cap */ - __u32 max_send_wr; - __u32 max_recv_wr; - __u32 max_send_sge; - __u32 max_recv_sge; - __u32 max_inline_data; - - __u16 pkey_index; - __u16 alt_pkey_index; - __u8 en_sqd_async_notify; - __u8 sq_draining; - __u8 max_rd_atomic; - __u8 max_dest_rd_atomic; - __u8 min_rnr_timer; - __u8 port_num; - __u8 timeout; - __u8 retry_cnt; - __u8 rnr_retry; - __u8 alt_port_num; - __u8 alt_timeout; -}; - struct ib_ucm_listen { __be64 service_id; __be64 service_mask; @@ -180,28 +128,6 @@ struct ib_ucm_private_data { __u8 reserved[3]; }; -struct ib_ucm_path_rec { - __u8 dgid[16]; - __u8 sgid[16]; - __be16 dlid; - __be16 slid; - __u32 raw_traffic; - __be32 flow_label; - __u32 reversible; - __u32 mtu; - __be16 pkey; - __u8 hop_limit; - __u8 traffic_class; - __u8 numb_path; - __u8 sl; - __u8 mtu_selector; - __u8 rate_selector; - __u8 rate; - __u8 packet_life_time_selector; - __u8 packet_life_time; - __u8 preference; -}; - struct ib_ucm_req { __u32 id; __u32 qpn; @@ -274,7 +200,7 @@ struct ib_ucm_sidr_req { __be64 sid; __u64 data; __u64 path; - __u16 pkey; + __u16 reserved_pkey; __u8 len; __u8 max_cm_retries; __u8 reserved[4]; @@ -304,8 +230,8 @@ struct ib_ucm_event_get { }; struct ib_ucm_req_event_resp { - struct ib_ucm_path_rec primary_path; - struct ib_ucm_path_rec alternate_path; + struct ib_user_path_rec primary_path; + struct ib_user_path_rec alternate_path; __be64 remote_ca_guid; __u32 remote_qkey; __u32 remote_qpn; @@ -349,7 +275,7 @@ struct ib_ucm_mra_event_resp { }; struct ib_ucm_lap_event_resp { - struct ib_ucm_path_rec path; + struct ib_user_path_rec path; }; struct ib_ucm_apr_event_resp { diff --git a/include/rdma/ib_user_sa.h b/include/rdma/ib_user_sa.h new file mode 100644 index 0000000..6591201 --- /dev/null +++ b/include/rdma/ib_user_sa.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2005 Intel Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IB_USER_SA_H +#define IB_USER_SA_H + +#include + +struct ib_user_path_rec { + __u8 dgid[16]; + __u8 sgid[16]; + __be16 dlid; + __be16 slid; + __u32 raw_traffic; + __be32 flow_label; + __u32 reversible; + __u32 mtu; + __be16 pkey; + __u8 hop_limit; + __u8 traffic_class; + __u8 numb_path; + __u8 sl; + __u8 mtu_selector; + __u8 rate_selector; + __u8 rate; + __u8 packet_life_time_selector; + __u8 packet_life_time; + __u8 preference; +}; + +#endif /* IB_USER_SA_H */ diff --git a/include/rdma/ib_user_verbs.h b/include/rdma/ib_user_verbs.h index 338ed43..7b53720 100644 --- a/include/rdma/ib_user_verbs.h +++ b/include/rdma/ib_user_verbs.h @@ -32,7 +32,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: ib_user_verbs.h 2708 2005-06-24 17:27:21Z roland $ + * $Id: ib_user_verbs.h 4019 2005-11-11 00:33:09Z sean.hefty $ */ #ifndef IB_USER_VERBS_H @@ -323,6 +323,64 @@ struct ib_uverbs_destroy_cq_resp { __u32 async_events_reported; }; +struct ib_uverbs_global_route { + __u8 dgid[16]; + __u32 flow_label; + __u8 sgid_index; + __u8 hop_limit; + __u8 traffic_class; + __u8 reserved; +}; + +struct ib_uverbs_ah_attr { + struct ib_uverbs_global_route grh; + __u16 dlid; + __u8 sl; + __u8 src_path_bits; + __u8 static_rate; + __u8 is_global; + __u8 port_num; + __u8 reserved; +}; + +struct ib_uverbs_qp_attr { + __u32 qp_attr_mask; + __u32 qp_state; + __u32 cur_qp_state; + __u32 path_mtu; + __u32 path_mig_state; + __u32 qkey; + __u32 rq_psn; + __u32 sq_psn; + __u32 dest_qp_num; + __u32 qp_access_flags; + + struct ib_uverbs_ah_attr ah_attr; + struct ib_uverbs_ah_attr alt_ah_attr; + + /* ib_qp_cap */ + __u32 max_send_wr; + __u32 max_recv_wr; + __u32 max_send_sge; + __u32 max_recv_sge; + __u32 max_inline_data; + + __u16 pkey_index; + __u16 alt_pkey_index; + __u8 en_sqd_async_notify; + __u8 sq_draining; + __u8 max_rd_atomic; + __u8 max_dest_rd_atomic; + __u8 min_rnr_timer; + __u8 port_num; + __u8 timeout; + __u8 retry_cnt; + __u8 rnr_retry; + __u8 alt_port_num; + __u8 alt_timeout; + __u8 reserved[5]; +}; + struct ib_uverbs_create_qp { __u64 response; __u64 user_handle; @@ -541,26 +599,6 @@ struct ib_uverbs_post_srq_recv_resp { __u32 bad_wr; }; -struct ib_uverbs_global_route { - __u8 dgid[16]; - __u32 flow_label; - __u8 sgid_index; - __u8 hop_limit; - __u8 traffic_class; - __u8 reserved; -}; - -struct ib_uverbs_ah_attr { - struct ib_uverbs_global_route grh; - __u16 dlid; - __u8 sl; - __u8 src_path_bits; - __u8 static_rate; - __u8 is_global; - __u8 port_num; - __u8 reserved; -}; - struct ib_uverbs_create_ah { __u64 response; __u64 user_handle; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 6bbf1b3..ee1f3a3 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -260,7 +260,8 @@ enum ib_event_type { IB_EVENT_SM_CHANGE, IB_EVENT_SRQ_ERR, IB_EVENT_SRQ_LIMIT_REACHED, - IB_EVENT_QP_LAST_WQE_REACHED + IB_EVENT_QP_LAST_WQE_REACHED, + IB_EVENT_CLIENT_REREGISTER }; struct ib_event { @@ -696,8 +697,12 @@ struct ib_ucontext { struct ib_uobject { u64 user_handle; /* handle given to us by userspace */ struct ib_ucontext *context; /* associated user context */ + void *object; /* containing object */ struct list_head list; /* link to context's list */ u32 id; /* index into kernel idr */ + struct kref ref; + struct rw_semaphore mutex; /* protects .live */ + int live; }; struct ib_umem { @@ -827,6 +832,7 @@ struct ib_cache { struct ib_event_handler event_handler; struct ib_pkey_cache **pkey_cache; struct ib_gid_cache **gid_cache; + u8 *lmc_cache; }; struct ib_device { @@ -1086,6 +1092,20 @@ int ib_dealloc_pd(struct ib_pd *pd); struct ib_ah *ib_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr); /** + * ib_init_ah_from_wc - Initializes address handle attributes from a + * work completion. + * @device: Device on which the received message arrived. + * @port_num: Port on which the received message arrived. + * @wc: Work completion associated with the received message. + * @grh: References the received global route header. This parameter is + * ignored unless the work completion indicates that the GRH is valid. + * @ah_attr: Returned attributes that can be used when creating an address + * handle for replying to the message. + */ +int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc, + struct ib_grh *grh, struct ib_ah_attr *ah_attr); + +/** * ib_create_ah_from_wc - Creates an address handle associated with the * sender of the specified work completion. * @pd: The protection domain associated with the address handle. diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h new file mode 100644 index 0000000..402c63d --- /dev/null +++ b/include/rdma/rdma_cm.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2005 Voltaire Inc. All rights reserved. + * Copyright (c) 2005 Intel Corporation. All rights reserved. + * + * This Software is licensed under one of the following licenses: + * + * 1) under the terms of the "Common Public License 1.0" a copy of which is + * available from the Open Source Initiative, see + * http://www.opensource.org/licenses/cpl.php. + * + * 2) under the terms of the "The BSD License" a copy of which is + * available from the Open Source Initiative, see + * http://www.opensource.org/licenses/bsd-license.php. + * + * 3) under the terms of the "GNU General Public License (GPL) Version 2" a + * copy of which is available from the Open Source Initiative, see + * http://www.opensource.org/licenses/gpl-license.php. + * + * Licensee has the right to choose one of the above licenses. + * + * Redistributions of source code must retain the above copyright + * notice and one of the license notices. + * + * Redistributions in binary form must reproduce both the above copyright + * notice, one of the license notices in the documentation + * and/or other materials provided with the distribution. + * + */ + +#if !defined(RDMA_CM_H) +#define RDMA_CM_H + +#include +#include +#include +#include + +/* + * Upon receiving a device removal event, users must destroy the associated + * RDMA identifier and release all resources allocated with the device. + */ +enum rdma_cm_event_type { + RDMA_CM_EVENT_ADDR_RESOLVED, + RDMA_CM_EVENT_ADDR_ERROR, + RDMA_CM_EVENT_ROUTE_RESOLVED, + RDMA_CM_EVENT_ROUTE_ERROR, + RDMA_CM_EVENT_CONNECT_REQUEST, + RDMA_CM_EVENT_CONNECT_RESPONSE, + RDMA_CM_EVENT_CONNECT_ERROR, + RDMA_CM_EVENT_UNREACHABLE, + RDMA_CM_EVENT_REJECTED, + RDMA_CM_EVENT_ESTABLISHED, + RDMA_CM_EVENT_DISCONNECTED, + RDMA_CM_EVENT_DEVICE_REMOVAL, +}; + +enum rdma_port_space { + RDMA_PS_SDP = 0x0001, + RDMA_PS_TCP = 0x0106, + RDMA_PS_UDP = 0x0111, + RDMA_PS_SCTP = 0x0183 +}; + +struct rdma_addr { + struct sockaddr src_addr; + u8 src_pad[sizeof(struct sockaddr_in6) - + sizeof(struct sockaddr)]; + struct sockaddr dst_addr; + u8 dst_pad[sizeof(struct sockaddr_in6) - + sizeof(struct sockaddr)]; + struct rdma_dev_addr dev_addr; +}; + +struct rdma_route { + struct rdma_addr addr; + struct ib_sa_path_rec *path_rec; + int num_paths; +}; + +struct rdma_cm_event { + enum rdma_cm_event_type event; + int status; + void *private_data; + u8 private_data_len; +}; + +struct rdma_cm_id; + +/** + * rdma_cm_event_handler - Callback used to report user events. + * + * Notes: Users may not call rdma_destroy_id from this callback to destroy + * the passed in id, or a corresponding listen id. Returning a + * non-zero value from the callback will destroy the passed in id. + */ +typedef int (*rdma_cm_event_handler)(struct rdma_cm_id *id, + struct rdma_cm_event *event); + +struct rdma_cm_id { + struct ib_device *device; + void *context; + struct ib_qp *qp; + rdma_cm_event_handler event_handler; + struct rdma_route route; + enum rdma_port_space ps; + u8 port_num; +}; + +/** + * rdma_create_id - Create an RDMA identifier. + * + * @event_handler: User callback invoked to report events associated with the + * returned rdma_id. + * @context: User specified context associated with the id. + * @ps: RDMA port space. + */ +struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler, + void *context, enum rdma_port_space ps); + +void rdma_destroy_id(struct rdma_cm_id *id); + +/** + * rdma_bind_addr - Bind an RDMA identifier to a source address and + * associated RDMA device, if needed. + * + * @id: RDMA identifier. + * @addr: Local address information. Wildcard values are permitted. + * + * This associates a source address with the RDMA identifier before calling + * rdma_listen. If a specific local address is given, the RDMA identifier will + * be bound to a local RDMA device. + */ +int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr); + +/** + * rdma_resolve_addr - Resolve destination and optional source addresses + * from IP addresses to an RDMA address. If successful, the specified + * rdma_cm_id will be bound to a local device. + * + * @id: RDMA identifier. + * @src_addr: Source address information. This parameter may be NULL. + * @dst_addr: Destination address information. + * @timeout_ms: Time to wait for resolution to complete. + */ +int rdma_resolve_addr(struct rdma_cm_id *id, struct sockaddr *src_addr, + struct sockaddr *dst_addr, int timeout_ms); + +/** + * rdma_resolve_route - Resolve the RDMA address bound to the RDMA identifier + * into route information needed to establish a connection. + * + * This is called on the client side of a connection. + * Users must have first called rdma_resolve_addr to resolve a dst_addr + * into an RDMA address before calling this routine. + */ +int rdma_resolve_route(struct rdma_cm_id *id, int timeout_ms); + +/** + * rdma_create_qp - Allocate a QP and associate it with the specified RDMA + * identifier. + * + * QPs allocated to an rdma_cm_id will automatically be transitioned by the CMA + * through their states. + */ +int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd, + struct ib_qp_init_attr *qp_init_attr); + +/** + * rdma_destroy_qp - Deallocate the QP associated with the specified RDMA + * identifier. + * + * Users must destroy any QP associated with an RDMA identifier before + * destroying the RDMA ID. + */ +void rdma_destroy_qp(struct rdma_cm_id *id); + +/** + * rdma_init_qp_attr - Initializes the QP attributes for use in transitioning + * to a specified QP state. + * @id: Communication identifier associated with the QP attributes to + * initialize. + * @qp_attr: On input, specifies the desired QP state. On output, the + * mandatory and desired optional attributes will be set in order to + * modify the QP to the specified state. + * @qp_attr_mask: The QP attribute mask that may be used to transition the + * QP to the specified state. + * + * Users must set the @qp_attr->qp_state to the desired QP state. This call + * will set all required attributes for the given transition, along with + * known optional attributes. Users may override the attributes returned from + * this call before calling ib_modify_qp. + * + * Users that wish to have their QP automatically transitioned through its + * states can associate a QP with the rdma_cm_id by calling rdma_create_qp(). + */ +int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, + int *qp_attr_mask); + +struct rdma_conn_param { + const void *private_data; + u8 private_data_len; + u8 responder_resources; + u8 initiator_depth; + u8 flow_control; + u8 retry_count; /* ignored when accepting */ + u8 rnr_retry_count; + /* Fields below ignored if a QP is created on the rdma_cm_id. */ + u8 srq; + u32 qp_num; + enum ib_qp_type qp_type; +}; + +/** + * rdma_connect - Initiate an active connection request. + * + * Users must have resolved a route for the rdma_cm_id to connect with + * by having called rdma_resolve_route before calling this routine. + */ +int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param); + +/** + * rdma_listen - This function is called by the passive side to + * listen for incoming connection requests. + * + * Users must have bound the rdma_cm_id to a local address by calling + * rdma_bind_addr before calling this routine. + */ +int rdma_listen(struct rdma_cm_id *id, int backlog); + +/** + * rdma_accept - Called to accept a connection request or response. + * @id: Connection identifier associated with the request. + * @conn_param: Information needed to establish the connection. This must be + * provided if accepting a connection request. If accepting a connection + * response, this parameter must be NULL. + * + * Typically, this routine is only called by the listener to accept a connection + * request. It must also be called on the active side of a connection if the + * user is performing their own QP transitions. + */ +int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param); + +/** + * rdma_reject - Called to reject a connection request or response. + */ +int rdma_reject(struct rdma_cm_id *id, const void *private_data, + u8 private_data_len); + +/** + * rdma_disconnect - This function disconnects the associated QP and + * transitions it into the error state. + */ +int rdma_disconnect(struct rdma_cm_id *id); + +#endif /* RDMA_CM_H */ + diff --git a/include/rdma/rdma_cm_ib.h b/include/rdma/rdma_cm_ib.h new file mode 100644 index 0000000..e8c3af1 --- /dev/null +++ b/include/rdma/rdma_cm_ib.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006 Intel Corporation. All rights reserved. + * + * This Software is licensed under one of the following licenses: + * + * 1) under the terms of the "Common Public License 1.0" a copy of which is + * available from the Open Source Initiative, see + * http://www.opensource.org/licenses/cpl.php. + * + * 2) under the terms of the "The BSD License" a copy of which is + * available from the Open Source Initiative, see + * http://www.opensource.org/licenses/bsd-license.php. + * + * 3) under the terms of the "GNU General Public License (GPL) Version 2" a + * copy of which is available from the Open Source Initiative, see + * http://www.opensource.org/licenses/gpl-license.php. + * + * Licensee has the right to choose one of the above licenses. + * + * Redistributions of source code must retain the above copyright + * notice and one of the license notices. + * + * Redistributions in binary form must reproduce both the above copyright + * notice, one of the license notices in the documentation + * and/or other materials provided with the distribution. + * + */ + +#if !defined(RDMA_CM_IB_H) +#define RDMA_CM_IB_H + +#include + +/** + * rdma_set_ib_paths - Manually sets the path records used to establish a + * connection. + * @id: Connection identifier associated with the request. + * @path_rec: Reference to the path record + * + * This call permits a user to specify routing information for rdma_cm_id's + * bound to Infiniband devices. It is called on the client side of a + * connection and replaces the call to rdma_resolve_route. + */ +int rdma_set_ib_paths(struct rdma_cm_id *id, + struct ib_sa_path_rec *path_rec, int num_paths); + +#endif /* RDMA_CM_IB_H */ diff --git a/include/scsi/Kbuild b/include/scsi/Kbuild new file mode 100644 index 0000000..14a033d --- /dev/null +++ b/include/scsi/Kbuild @@ -0,0 +1,2 @@ +header-y += scsi.h +unifdef-y := scsi_ioctl.h sg.h diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index e5618b9..55ebf03 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -43,10 +43,23 @@ enum iscsi_uevent_e { ISCSI_UEVENT_GET_STATS = UEVENT_BASE + 10, ISCSI_UEVENT_GET_PARAM = UEVENT_BASE + 11, + ISCSI_UEVENT_TRANSPORT_EP_CONNECT = UEVENT_BASE + 12, + ISCSI_UEVENT_TRANSPORT_EP_POLL = UEVENT_BASE + 13, + ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT = UEVENT_BASE + 14, + + ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15, + /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3, + ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, +}; + +enum iscsi_tgt_dscvr { + ISCSI_TGT_DSCVR_SEND_TARGETS = 1, + ISCSI_TGT_DSCVR_ISNS = 2, + ISCSI_TGT_DSCVR_SLP = 3, }; struct iscsi_uevent { @@ -60,61 +73,98 @@ struct iscsi_uevent { uint32_t initial_cmdsn; } c_session; struct msg_destroy_session { - uint64_t session_handle; uint32_t sid; } d_session; struct msg_create_conn { - uint64_t session_handle; - uint32_t cid; uint32_t sid; + uint32_t cid; } c_conn; struct msg_bind_conn { - uint64_t session_handle; - uint64_t conn_handle; - uint32_t transport_fd; + uint32_t sid; + uint32_t cid; + uint64_t transport_eph; uint32_t is_leading; } b_conn; struct msg_destroy_conn { - uint64_t conn_handle; + uint32_t sid; uint32_t cid; } d_conn; struct msg_send_pdu { + uint32_t sid; + uint32_t cid; uint32_t hdr_size; uint32_t data_size; - uint64_t conn_handle; } send_pdu; struct msg_set_param { - uint64_t conn_handle; + uint32_t sid; + uint32_t cid; uint32_t param; /* enum iscsi_param */ - uint32_t value; + uint32_t len; } set_param; struct msg_start_conn { - uint64_t conn_handle; + uint32_t sid; + uint32_t cid; } start_conn; struct msg_stop_conn { + uint32_t sid; + uint32_t cid; uint64_t conn_handle; uint32_t flag; } stop_conn; struct msg_get_stats { - uint64_t conn_handle; + uint32_t sid; + uint32_t cid; } get_stats; + struct msg_transport_connect { + uint32_t non_blocking; + } ep_connect; + struct msg_transport_poll { + uint64_t ep_handle; + uint32_t timeout_ms; + } ep_poll; + struct msg_transport_disconnect { + uint64_t ep_handle; + } ep_disconnect; + struct msg_tgt_dscvr { + enum iscsi_tgt_dscvr type; + uint32_t host_no; + /* + * enable = 1 to establish a new connection + * with the server. enable = 0 to disconnect + * from the server. Used primarily to switch + * from one iSNS server to another. + */ + uint32_t enable; + } tgt_dscvr; } u; union { /* messages k -> u */ - uint64_t handle; int retcode; struct msg_create_session_ret { - uint64_t session_handle; uint32_t sid; + uint32_t host_no; } c_session_ret; + struct msg_create_conn_ret { + uint32_t sid; + uint32_t cid; + } c_conn_ret; struct msg_recv_req { + uint32_t sid; + uint32_t cid; uint64_t recv_handle; - uint64_t conn_handle; } recv_req; struct msg_conn_error { - uint64_t conn_handle; + uint32_t sid; + uint32_t cid; uint32_t error; /* enum iscsi_err */ } connerror; + struct msg_session_destroyed { + uint32_t host_no; + uint32_t sid; + } d_session; + struct msg_transport_connect_ret { + uint64_t handle; + } ep_connect_ret; } r; } __attribute__ ((aligned (sizeof(uint64_t)))); @@ -139,29 +189,66 @@ enum iscsi_err { ISCSI_ERR_SESSION_FAILED = ISCSI_ERR_BASE + 13, ISCSI_ERR_HDR_DGST = ISCSI_ERR_BASE + 14, ISCSI_ERR_DATA_DGST = ISCSI_ERR_BASE + 15, - ISCSI_ERR_PARAM_NOT_FOUND = ISCSI_ERR_BASE + 16 + ISCSI_ERR_PARAM_NOT_FOUND = ISCSI_ERR_BASE + 16, + ISCSI_ERR_NO_SCSI_CMD = ISCSI_ERR_BASE + 17, }; /* * iSCSI Parameters (RFC3720) */ enum iscsi_param { - ISCSI_PARAM_MAX_RECV_DLENGTH = 0, - ISCSI_PARAM_MAX_XMIT_DLENGTH = 1, - ISCSI_PARAM_HDRDGST_EN = 2, - ISCSI_PARAM_DATADGST_EN = 3, - ISCSI_PARAM_INITIAL_R2T_EN = 4, - ISCSI_PARAM_MAX_R2T = 5, - ISCSI_PARAM_IMM_DATA_EN = 6, - ISCSI_PARAM_FIRST_BURST = 7, - ISCSI_PARAM_MAX_BURST = 8, - ISCSI_PARAM_PDU_INORDER_EN = 9, - ISCSI_PARAM_DATASEQ_INORDER_EN = 10, - ISCSI_PARAM_ERL = 11, - ISCSI_PARAM_IFMARKER_EN = 12, - ISCSI_PARAM_OFMARKER_EN = 13, + /* passed in using netlink set param */ + ISCSI_PARAM_MAX_RECV_DLENGTH, + ISCSI_PARAM_MAX_XMIT_DLENGTH, + ISCSI_PARAM_HDRDGST_EN, + ISCSI_PARAM_DATADGST_EN, + ISCSI_PARAM_INITIAL_R2T_EN, + ISCSI_PARAM_MAX_R2T, + ISCSI_PARAM_IMM_DATA_EN, + ISCSI_PARAM_FIRST_BURST, + ISCSI_PARAM_MAX_BURST, + ISCSI_PARAM_PDU_INORDER_EN, + ISCSI_PARAM_DATASEQ_INORDER_EN, + ISCSI_PARAM_ERL, + ISCSI_PARAM_IFMARKER_EN, + ISCSI_PARAM_OFMARKER_EN, + ISCSI_PARAM_EXP_STATSN, + ISCSI_PARAM_TARGET_NAME, + ISCSI_PARAM_TPGT, + ISCSI_PARAM_PERSISTENT_ADDRESS, + ISCSI_PARAM_PERSISTENT_PORT, + ISCSI_PARAM_SESS_RECOVERY_TMO, + + /* pased in through bind conn using transport_fd */ + ISCSI_PARAM_CONN_PORT, + ISCSI_PARAM_CONN_ADDRESS, + + /* must always be last */ + ISCSI_PARAM_MAX, }; -#define ISCSI_PARAM_MAX 14 + +#define ISCSI_MAX_RECV_DLENGTH (1 << ISCSI_PARAM_MAX_RECV_DLENGTH) +#define ISCSI_MAX_XMIT_DLENGTH (1 << ISCSI_PARAM_MAX_XMIT_DLENGTH) +#define ISCSI_HDRDGST_EN (1 << ISCSI_PARAM_HDRDGST_EN) +#define ISCSI_DATADGST_EN (1 << ISCSI_PARAM_DATADGST_EN) +#define ISCSI_INITIAL_R2T_EN (1 << ISCSI_PARAM_INITIAL_R2T_EN) +#define ISCSI_MAX_R2T (1 << ISCSI_PARAM_MAX_R2T) +#define ISCSI_IMM_DATA_EN (1 << ISCSI_PARAM_IMM_DATA_EN) +#define ISCSI_FIRST_BURST (1 << ISCSI_PARAM_FIRST_BURST) +#define ISCSI_MAX_BURST (1 << ISCSI_PARAM_MAX_BURST) +#define ISCSI_PDU_INORDER_EN (1 << ISCSI_PARAM_PDU_INORDER_EN) +#define ISCSI_DATASEQ_INORDER_EN (1 << ISCSI_PARAM_DATASEQ_INORDER_EN) +#define ISCSI_ERL (1 << ISCSI_PARAM_ERL) +#define ISCSI_IFMARKER_EN (1 << ISCSI_PARAM_IFMARKER_EN) +#define ISCSI_OFMARKER_EN (1 << ISCSI_PARAM_OFMARKER_EN) +#define ISCSI_EXP_STATSN (1 << ISCSI_PARAM_EXP_STATSN) +#define ISCSI_TARGET_NAME (1 << ISCSI_PARAM_TARGET_NAME) +#define ISCSI_TPGT (1 << ISCSI_PARAM_TPGT) +#define ISCSI_PERSISTENT_ADDRESS (1 << ISCSI_PARAM_PERSISTENT_ADDRESS) +#define ISCSI_PERSISTENT_PORT (1 << ISCSI_PARAM_PERSISTENT_PORT) +#define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO) +#define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT) +#define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS) #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle) #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr) diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h index 4feda05..02f6e4b 100644 --- a/include/scsi/iscsi_proto.h +++ b/include/scsi/iscsi_proto.h @@ -21,8 +21,6 @@ #ifndef ISCSI_PROTO_H #define ISCSI_PROTO_H -#define ISCSI_VERSION_STR "0.3" -#define ISCSI_DATE_STR "22-Apr-2005" #define ISCSI_DRAFT20_VERSION 0x00 /* default iSCSI listen port for incoming connections */ diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h new file mode 100644 index 0000000..ba27608 --- /dev/null +++ b/include/scsi/libiscsi.h @@ -0,0 +1,293 @@ +/* + * iSCSI lib definitions + * + * Copyright (C) 2006 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004 - 2006 Mike Christie + * Copyright (C) 2004 - 2005 Dmitry Yusupov + * Copyright (C) 2004 - 2005 Alex Aizman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef LIBISCSI_H +#define LIBISCSI_H + +#include +#include +#include +#include + +struct scsi_transport_template; +struct scsi_device; +struct Scsi_Host; +struct scsi_cmnd; +struct socket; +struct iscsi_transport; +struct iscsi_cls_session; +struct iscsi_cls_conn; +struct iscsi_session; +struct iscsi_nopin; + +/* #define DEBUG_SCSI */ +#ifdef DEBUG_SCSI +#define debug_scsi(fmt...) printk(KERN_INFO "iscsi: " fmt) +#else +#define debug_scsi(fmt...) +#endif + +#define ISCSI_XMIT_CMDS_MAX 128 /* must be power of 2 */ +#define ISCSI_MGMT_CMDS_MAX 32 /* must be power of 2 */ +#define ISCSI_CONN_MAX 1 + +#define ISCSI_MGMT_ITT_OFFSET 0xa00 + +#define ISCSI_DEF_CMD_PER_LUN 32 +#define ISCSI_MAX_CMD_PER_LUN 128 + +/* Task Mgmt states */ +#define TMABORT_INITIAL 0x0 +#define TMABORT_SUCCESS 0x1 +#define TMABORT_FAILED 0x2 +#define TMABORT_TIMEDOUT 0x3 + +/* Connection suspend "bit" */ +#define ISCSI_SUSPEND_BIT 1 + +#define ISCSI_ITT_MASK (0xfff) +#define ISCSI_CID_SHIFT 12 +#define ISCSI_CID_MASK (0xffff << ISCSI_CID_SHIFT) +#define ISCSI_AGE_SHIFT 28 +#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT) + +struct iscsi_mgmt_task { + /* + * Becuae LLDs allocate their hdr differently, this is a pointer to + * that storage. It must be setup at session creation time. + */ + struct iscsi_hdr *hdr; + char *data; /* mgmt payload */ + int data_count; /* counts data to be sent */ + uint32_t itt; /* this ITT */ + void *dd_data; /* driver/transport data */ + struct list_head running; +}; + +struct iscsi_cmd_task { + /* + * Becuae LLDs allocate their hdr differently, this is a pointer to + * that storage. It must be setup at session creation time. + */ + struct iscsi_cmd *hdr; + int itt; /* this ITT */ + int datasn; /* DataSN */ + + uint32_t unsol_datasn; + int imm_count; /* imm-data (bytes) */ + int unsol_count; /* unsolicited (bytes)*/ + int data_count; /* remaining Data-Out */ + struct scsi_cmnd *sc; /* associated SCSI cmd*/ + int total_length; + struct iscsi_conn *conn; /* used connection */ + struct iscsi_mgmt_task *mtask; /* tmf mtask in progr */ + + struct list_head running; /* running cmd list */ + void *dd_data; /* driver/transport data */ +}; + +struct iscsi_conn { + struct iscsi_cls_conn *cls_conn; /* ptr to class connection */ + void *dd_data; /* iscsi_transport data */ + struct iscsi_session *session; /* parent session */ + /* + * LLDs should set this lock. It protects the transport recv + * code + */ + rwlock_t *recv_lock; + /* + * conn_stop() flag: stop to recover, stop to terminate + */ + int stop_stage; + + /* iSCSI connection-wide sequencing */ + uint32_t exp_statsn; + + /* control data */ + int id; /* CID */ + struct list_head item; /* maintains list of conns */ + int c_stage; /* connection state */ + struct iscsi_mgmt_task *login_mtask; /* mtask used for login/text */ + struct iscsi_mgmt_task *mtask; /* xmit mtask in progress */ + struct iscsi_cmd_task *ctask; /* xmit ctask in progress */ + + /* xmit */ + struct kfifo *immqueue; /* immediate xmit queue */ + struct kfifo *mgmtqueue; /* mgmt (control) xmit queue */ + struct list_head mgmt_run_list; /* list of control tasks */ + struct kfifo *xmitqueue; /* data-path cmd queue */ + struct list_head run_list; /* list of cmds in progress */ + struct work_struct xmitwork; /* per-conn. xmit workqueue */ + /* + * serializes connection xmit, access to kfifos: + * xmitqueue, immqueue, mgmtqueue + */ + struct mutex xmitmutex; + + unsigned long suspend_tx; /* suspend Tx */ + unsigned long suspend_rx; /* suspend Rx */ + + /* abort */ + wait_queue_head_t ehwait; /* used in eh_abort() */ + struct iscsi_tm tmhdr; + struct timer_list tmabort_timer; + int tmabort_state; /* see TMABORT_INITIAL, etc.*/ + + /* negotiated params */ + int max_recv_dlength; /* initiator_max_recv_dsl*/ + int max_xmit_dlength; /* target_max_recv_dsl */ + int hdrdgst_en; + int datadgst_en; + int ifmarker_en; + int ofmarker_en; + /* values userspace uses to id a conn */ + int persistent_port; + char *persistent_address; + + /* MIB-statistics */ + uint64_t txdata_octets; + uint64_t rxdata_octets; + uint32_t scsicmd_pdus_cnt; + uint32_t dataout_pdus_cnt; + uint32_t scsirsp_pdus_cnt; + uint32_t datain_pdus_cnt; + uint32_t r2t_pdus_cnt; + uint32_t tmfcmd_pdus_cnt; + int32_t tmfrsp_pdus_cnt; + + /* custom statistics */ + uint32_t eh_abort_cnt; +}; + +struct iscsi_queue { + struct kfifo *queue; /* FIFO Queue */ + void **pool; /* Pool of elements */ + int max; /* Max number of elements */ +}; + +struct iscsi_session { + /* iSCSI session-wide sequencing */ + uint32_t cmdsn; + uint32_t exp_cmdsn; + uint32_t max_cmdsn; + + /* configuration */ + int initial_r2t_en; + int max_r2t; + int imm_data_en; + int first_burst; + int max_burst; + int time2wait; + int time2retain; + int pdu_inorder_en; + int dataseq_inorder_en; + int erl; + int tpgt; + char *targetname; + + /* control data */ + struct iscsi_transport *tt; + struct Scsi_Host *host; + struct iscsi_conn *leadconn; /* leading connection */ + spinlock_t lock; /* protects session state, * + * sequence numbers, * + * session resources: * + * - cmdpool, * + * - mgmtpool, * + * - r2tpool */ + int state; /* session state */ + struct list_head item; + int age; /* counts session re-opens */ + + struct list_head connections; /* list of connections */ + int cmds_max; /* size of cmds array */ + struct iscsi_cmd_task **cmds; /* Original Cmds arr */ + struct iscsi_queue cmdpool; /* PDU's pool */ + int mgmtpool_max; /* size of mgmt array */ + struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */ + struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */ +}; + +/* + * scsi host template + */ +extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth); +extern int iscsi_eh_abort(struct scsi_cmnd *sc); +extern int iscsi_eh_host_reset(struct scsi_cmnd *sc); +extern int iscsi_queuecommand(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)); + +/* + * session management + */ +extern struct iscsi_cls_session * +iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *, + int, int, uint32_t, uint32_t *); +extern void iscsi_session_teardown(struct iscsi_cls_session *); +extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *); +extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *); +extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf, int buflen); +extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session, + enum iscsi_param param, char *buf); + +#define session_to_cls(_sess) \ + hostdata_session(_sess->host->hostdata) + +/* + * connection management + */ +extern struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *, + uint32_t); +extern void iscsi_conn_teardown(struct iscsi_cls_conn *); +extern int iscsi_conn_start(struct iscsi_cls_conn *); +extern void iscsi_conn_stop(struct iscsi_cls_conn *, int); +extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *, + int); +extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err); +extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, + enum iscsi_param param, char *buf); + +/* + * pdu and task processing + */ +extern int iscsi_check_assign_cmdsn(struct iscsi_session *, + struct iscsi_nopin *); +extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *, + struct iscsi_data *hdr, + int transport_data_cnt); +extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *, + char *, uint32_t); +extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, + char *, int); +extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, + char *, int); +extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *, + uint32_t *); + +/* + * generic helpers + */ +extern void iscsi_pool_free(struct iscsi_queue *, void **); +extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int); + +#endif diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 1ace1b9..371f70d 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -9,7 +9,6 @@ #include struct request; struct scatterlist; struct scsi_device; -struct scsi_request; /* embedded in scsi_cmnd */ @@ -29,13 +28,8 @@ struct scsi_pointer { }; struct scsi_cmnd { - int sc_magic; - struct scsi_device *device; - struct scsi_request *sc_request; - struct list_head list; /* scsi_cmnd participates in queue lists */ - struct list_head eh_entry; /* entry for the host eh_cmd_q */ int eh_eflags; /* Used by error handlr */ void (*done) (struct scsi_cmnd *); /* Mid-level done function */ @@ -149,7 +143,12 @@ #define SCSI_STATE_MLQUEUE 0x100 extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); extern void scsi_put_command(struct scsi_cmnd *); -extern void scsi_io_completion(struct scsi_cmnd *, unsigned int, unsigned int); +extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); extern void scsi_finish_command(struct scsi_cmnd *cmd); +extern void scsi_req_abort_cmd(struct scsi_cmnd *cmd); + +extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, + size_t *offset, size_t *len); +extern void scsi_kunmap_atomic_sg(void *virt); #endif /* _SCSI_SCSI_CMND_H */ diff --git a/include/scsi/scsi_dbg.h b/include/scsi/scsi_dbg.h index 4d69dee..3bbbfbe 100644 --- a/include/scsi/scsi_dbg.h +++ b/include/scsi/scsi_dbg.h @@ -2,14 +2,12 @@ #ifndef _SCSI_SCSI_DBG_H #define _SCSI_SCSI_DBG_H struct scsi_cmnd; -struct scsi_request; struct scsi_sense_hdr; extern void scsi_print_command(struct scsi_cmnd *); extern void scsi_print_sense_hdr(const char *, struct scsi_sense_hdr *); extern void __scsi_print_command(unsigned char *); extern void scsi_print_sense(const char *, struct scsi_cmnd *); -extern void scsi_print_req_sense(const char *, struct scsi_request *); extern void __scsi_print_sense(const char *name, const unsigned char *sense_buffer, int sense_len); diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index d31b16d..b4ddd3b 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -29,4 +29,5 @@ #define BLIST_NO_ULD_ATTACH 0x100000 /* #define BLIST_SELECT_NO_ATN 0x200000 /* select without ATN */ #define BLIST_RETRY_HWERROR 0x400000 /* retry HARDWARE_ERROR */ #define BLIST_MAX_512 0x800000 /* maximum 512 sector cdb length */ +#define BLIST_ATTACH_PQ3 0x1000000 /* Scan: Attach to PQ3 devices */ #endif diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h index d160880..c5c0f67 100644 --- a/include/scsi/scsi_eh.h +++ b/include/scsi/scsi_eh.h @@ -3,7 +3,6 @@ #define _SCSI_SCSI_EH_H struct scsi_cmnd; struct scsi_device; -struct scsi_request; struct Scsi_Host; /* @@ -43,8 +42,6 @@ extern void scsi_report_device_reset(str extern int scsi_block_when_processing_errors(struct scsi_device *); extern int scsi_normalize_sense(const u8 *sense_buffer, int sb_len, struct scsi_sense_hdr *sshdr); -extern int scsi_request_normalize_sense(struct scsi_request *sreq, - struct scsi_sense_hdr *sshdr); extern int scsi_command_normalize_sense(struct scsi_cmnd *cmd, struct scsi_sense_hdr *sshdr); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index de6ce54..b3dd90f 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -472,6 +472,7 @@ struct Scsi_Host { */ unsigned int host_busy; /* commands actually active on low-level */ unsigned int host_failed; /* commands that failed. */ + unsigned int host_eh_scheduled; /* EH scheduled without command */ unsigned short host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */ int resetting; /* if set, it means that last_reset is a valid value */ @@ -541,6 +542,9 @@ struct Scsi_Host { */ unsigned ordered_tag:1; + /* task mgmt function in progress */ + unsigned tmf_in_progress:1; + /* * Optional work queue to be utilized by the transport */ @@ -618,7 +622,8 @@ static inline int scsi_host_in_recovery( { return shost->shost_state == SHOST_RECOVERY || shost->shost_state == SHOST_CANCEL_RECOVERY || - shost->shost_state == SHOST_DEL_RECOVERY; + shost->shost_state == SHOST_DEL_RECOVERY || + shost->tmf_in_progress; } extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *); diff --git a/include/scsi/scsi_request.h b/include/scsi/scsi_request.h deleted file mode 100644 index 98d69fd..0000000 --- a/include/scsi/scsi_request.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef _SCSI_SCSI_REQUEST_H -#define _SCSI_SCSI_REQUEST_H - -#include - -struct request; -struct scsi_cmnd; -struct scsi_device; -struct Scsi_Host; - - -/* - * This is essentially a slimmed down version of Scsi_Cmnd. The point of - * having this is that requests that are injected into the queue as result - * of things like ioctls and character devices shouldn't be using a - * Scsi_Cmnd until such a time that the command is actually at the head - * of the queue and being sent to the driver. - */ -struct scsi_request { - int sr_magic; - int sr_result; /* Status code from lower level driver */ - unsigned char sr_sense_buffer[SCSI_SENSE_BUFFERSIZE]; /* obtained by REQUEST SENSE - * when CHECK CONDITION is - * received on original command - * (auto-sense) */ - - struct Scsi_Host *sr_host; - struct scsi_device *sr_device; - struct scsi_cmnd *sr_command; - struct request *sr_request; /* A copy of the command we are - working on */ - unsigned sr_bufflen; /* Size of data buffer */ - void *sr_buffer; /* Data buffer */ - int sr_allowed; - enum dma_data_direction sr_data_direction; - unsigned char sr_cmd_len; - unsigned char sr_cmnd[MAX_COMMAND_SIZE]; - void (*sr_done) (struct scsi_cmnd *); /* Mid-level done function */ - int sr_timeout_per_command; - unsigned short sr_use_sg; /* Number of pieces of scatter-gather */ - unsigned short sr_sglist_len; /* size of malloc'd scatter-gather list */ - unsigned sr_underflow; /* Return error if less than - this amount is transferred */ - void *upper_private_data; /* reserved for owner (usually upper - level driver) of this request */ -}; - -extern struct scsi_request *scsi_allocate_request(struct scsi_device *, gfp_t); -extern void scsi_release_request(struct scsi_request *); -extern void scsi_do_req(struct scsi_request *, const void *cmnd, - void *buffer, unsigned bufflen, - void (*done) (struct scsi_cmnd *), - int timeout, int retries); -#endif /* _SCSI_SCSI_REQUEST_H */ diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index 5626225..6d28b03 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -27,7 +27,6 @@ #ifndef SCSI_TRANSPORT_FC_H #define SCSI_TRANSPORT_FC_H -#include #include #include diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index b41cf07..5a3df1d 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -2,7 +2,7 @@ * iSCSI transport class definitions * * Copyright (C) IBM Corporation, 2004 - * Copyright (C) Mike Christie, 2004 - 2005 + * Copyright (C) Mike Christie, 2004 - 2006 * Copyright (C) Dmitry Yusupov, 2004 - 2005 * Copyright (C) Alex Aizman, 2004 - 2005 * @@ -27,9 +27,14 @@ #include #include struct scsi_transport_template; +struct iscsi_transport; struct Scsi_Host; struct mempool_zone; struct iscsi_cls_conn; +struct iscsi_conn; +struct iscsi_cmd_task; +struct iscsi_mgmt_task; +struct sockaddr; /** * struct iscsi_transport - iSCSI Transport template @@ -42,10 +47,33 @@ struct iscsi_cls_conn; * @bind_conn: associate this connection with existing iSCSI session * and specified transport descriptor * @destroy_conn: destroy inactive iSCSI connection - * @set_param: set iSCSI Data-Path operational parameter + * @set_param: set iSCSI parameter. Return 0 on success, -ENODATA + * when param is not supported, and a -Exx value on other + * error. + * @get_param get iSCSI parameter. Must return number of bytes + * copied to buffer on success, -ENODATA when param + * is not supported, and a -Exx value on other error * @start_conn: set connection to be operational * @stop_conn: suspend/recover/terminate connection * @send_pdu: send iSCSI PDU, Login, Logout, NOP-Out, Reject, Text. + * @session_recovery_timedout: notify LLD a block during recovery timed out + * @suspend_conn_recv: susepend the recv side of the connection + * @termincate_conn: destroy socket connection. Called with mutex lock. + * @init_cmd_task: Initialize a iscsi_cmd_task and any internal structs. + * Called from queuecommand with session lock held. + * @init_mgmt_task: Initialize a iscsi_mgmt_task and any internal structs. + * Called from iscsi_conn_send_generic with xmitmutex. + * @xmit_cmd_task: Requests LLD to transfer cmd task. Returns 0 or the + * the number of bytes transferred on success, and -Exyz + * value on error. + * @xmit_mgmt_task: Requests LLD to transfer mgmt task. Returns 0 or the + * the number of bytes transferred on success, and -Exyz + * value on error. + * @cleanup_cmd_task: requests LLD to fail cmd task. Called with xmitmutex + * and session->lock after the connection has been + * suspended and terminated during recovery. If called + * from abort task then connection is not suspended + * or terminated but sk_callback_lock is held * * Template API provided by iSCSI Transport */ @@ -53,38 +81,56 @@ struct iscsi_transport { struct module *owner; char *name; unsigned int caps; + /* LLD sets this to indicate what values it can export to sysfs */ + unsigned int param_mask; struct scsi_host_template *host_template; - /* LLD session/scsi_host data size */ - int hostdata_size; - /* LLD iscsi_host data size */ - int ihostdata_size; /* LLD connection data size */ int conndata_size; + /* LLD session data size */ + int sessiondata_size; int max_lun; unsigned int max_conn; unsigned int max_cmd_len; - struct iscsi_cls_session *(*create_session) - (struct scsi_transport_template *t, uint32_t sn, uint32_t *sid); + struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it, + struct scsi_transport_template *t, uint32_t sn, uint32_t *hn); void (*destroy_session) (struct iscsi_cls_session *session); struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess, uint32_t cid); int (*bind_conn) (struct iscsi_cls_session *session, struct iscsi_cls_conn *cls_conn, - uint32_t transport_fd, int is_leading); + uint64_t transport_eph, int is_leading); int (*start_conn) (struct iscsi_cls_conn *conn); void (*stop_conn) (struct iscsi_cls_conn *conn, int flag); void (*destroy_conn) (struct iscsi_cls_conn *conn); int (*set_param) (struct iscsi_cls_conn *conn, enum iscsi_param param, - uint32_t value); + char *buf, int buflen); int (*get_conn_param) (struct iscsi_cls_conn *conn, - enum iscsi_param param, - uint32_t *value); + enum iscsi_param param, char *buf); int (*get_session_param) (struct iscsi_cls_session *session, - enum iscsi_param param, uint32_t *value); + enum iscsi_param param, char *buf); int (*send_pdu) (struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size); void (*get_stats) (struct iscsi_cls_conn *conn, struct iscsi_stats *stats); + void (*suspend_conn_recv) (struct iscsi_conn *conn); + void (*terminate_conn) (struct iscsi_conn *conn); + void (*init_cmd_task) (struct iscsi_cmd_task *ctask); + void (*init_mgmt_task) (struct iscsi_conn *conn, + struct iscsi_mgmt_task *mtask, + char *data, uint32_t data_size); + int (*xmit_cmd_task) (struct iscsi_conn *conn, + struct iscsi_cmd_task *ctask); + void (*cleanup_cmd_task) (struct iscsi_conn *conn, + struct iscsi_cmd_task *ctask); + int (*xmit_mgmt_task) (struct iscsi_conn *conn, + struct iscsi_mgmt_task *mtask); + void (*session_recovery_timedout) (struct iscsi_cls_session *session); + int (*ep_connect) (struct sockaddr *dst_addr, int non_blocking, + uint64_t *ep_handle); + int (*ep_poll) (uint64_t ep_handle, int timeout_ms); + void (*ep_disconnect) (uint64_t ep_handle); + int (*tgt_dscvr) (enum iscsi_tgt_dscvr type, uint32_t host_no, + uint32_t enable, struct sockaddr *dst_addr); }; /* @@ -100,10 +146,19 @@ extern void iscsi_conn_error(struct iscs extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size); + +/* Connection's states */ +#define ISCSI_CONN_INITIAL_STAGE 0 +#define ISCSI_CONN_STARTED 1 +#define ISCSI_CONN_STOPPED 2 +#define ISCSI_CONN_CLEANUP_WAIT 3 + struct iscsi_cls_conn { struct list_head conn_list; /* item in connlist */ void *dd_data; /* LLD private data */ struct iscsi_transport *transport; + uint32_t cid; /* connection id */ + int active; /* must be accessed with the connlock */ struct device dev; /* sysfs transport/container device */ struct mempool_zone *z_error; @@ -114,9 +169,27 @@ struct iscsi_cls_conn { #define iscsi_dev_to_conn(_dev) \ container_of(_dev, struct iscsi_cls_conn, dev) +/* Session's states */ +#define ISCSI_STATE_FREE 1 +#define ISCSI_STATE_LOGGED_IN 2 +#define ISCSI_STATE_FAILED 3 +#define ISCSI_STATE_TERMINATE 4 +#define ISCSI_STATE_IN_RECOVERY 5 +#define ISCSI_STATE_RECOVERY_FAILED 6 + struct iscsi_cls_session { struct list_head sess_list; /* item in session_list */ + struct list_head host_list; struct iscsi_transport *transport; + + /* recovery fields */ + int recovery_tmo; + struct work_struct recovery_work; + + int target_id; + + int sid; /* session id */ + void *dd_data; /* LLD private data */ struct device dev; /* sysfs transport/container device */ }; @@ -126,22 +199,34 @@ #define iscsi_dev_to_session(_dev) \ #define iscsi_session_to_shost(_session) \ dev_to_shost(_session->dev.parent) +#define starget_to_session(_stgt) \ + iscsi_dev_to_session(_stgt->dev.parent) + +struct iscsi_host { + struct list_head sessions; + struct mutex mutex; +}; + /* * session and connection functions that can be used by HW iSCSI LLDs */ +extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost, + struct iscsi_transport *transport); +extern int iscsi_add_session(struct iscsi_cls_session *session, + unsigned int target_id); +extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn); +extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn); extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, - struct iscsi_transport *t); + struct iscsi_transport *t, + unsigned int target_id); +extern void iscsi_remove_session(struct iscsi_cls_session *session); +extern void iscsi_free_session(struct iscsi_cls_session *session); extern int iscsi_destroy_session(struct iscsi_cls_session *session); extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess, uint32_t cid); extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn); +extern void iscsi_unblock_session(struct iscsi_cls_session *session); +extern void iscsi_block_session(struct iscsi_cls_session *session); -/* - * session functions used by software iscsi - */ -extern struct Scsi_Host * -iscsi_transport_create_session(struct scsi_transport_template *scsit, - struct iscsi_transport *transport); -extern int iscsi_transport_destroy_session(struct Scsi_Host *shost); #endif diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 93cfb4b..e3c503c 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -3,6 +3,7 @@ #define SCSI_TRANSPORT_SAS_H #include #include +#include struct scsi_transport_template; struct sas_rphy; @@ -55,7 +56,6 @@ struct sas_phy { enum sas_linkrate minimum_linkrate; enum sas_linkrate maximum_linkrate_hw; enum sas_linkrate maximum_linkrate; - u8 port_identifier; /* internal state */ unsigned int local_attached : 1; @@ -66,8 +66,8 @@ struct sas_phy { u32 loss_of_dword_sync_count; u32 phy_reset_problem_count; - /* the other end of the link */ - struct sas_rphy *rphy; + /* for the list of phys belonging to a port */ + struct list_head port_siblings; }; #define dev_to_phy(d) \ @@ -124,6 +124,24 @@ struct sas_expander_device { #define rphy_to_expander_device(r) \ container_of((r), struct sas_expander_device, rphy) +struct sas_port { + struct device dev; + + u8 port_identifier; + int num_phys; + + /* the other end of the link */ + struct sas_rphy *rphy; + + struct mutex phy_list_mutex; + struct list_head phy_list; +}; + +#define dev_to_sas_port(d) \ + container_of((d), struct sas_port, dev) +#define transport_class_to_sas_port(cdev) \ + dev_to_sas_port((cdev)->dev) + /* The functions by which the transport class and the driver communicate */ struct sas_function_template { int (*get_linkerrors)(struct sas_phy *); @@ -133,6 +151,7 @@ struct sas_function_template { }; +void sas_remove_children(struct device *); extern void sas_remove_host(struct Scsi_Host *); extern struct sas_phy *sas_phy_alloc(struct device *, int); @@ -141,13 +160,21 @@ extern int sas_phy_add(struct sas_phy *) extern void sas_phy_delete(struct sas_phy *); extern int scsi_is_sas_phy(const struct device *); -extern struct sas_rphy *sas_end_device_alloc(struct sas_phy *); -extern struct sas_rphy *sas_expander_alloc(struct sas_phy *, enum sas_device_type); +extern struct sas_rphy *sas_end_device_alloc(struct sas_port *); +extern struct sas_rphy *sas_expander_alloc(struct sas_port *, enum sas_device_type); void sas_rphy_free(struct sas_rphy *); extern int sas_rphy_add(struct sas_rphy *); extern void sas_rphy_delete(struct sas_rphy *); extern int scsi_is_sas_rphy(const struct device *); +struct sas_port *sas_port_alloc(struct device *, int); +int sas_port_add(struct sas_port *); +void sas_port_free(struct sas_port *); +void sas_port_delete(struct sas_port *); +void sas_port_add_phy(struct sas_port *, struct sas_phy *); +void sas_port_delete_phy(struct sas_port *, struct sas_phy *); +int scsi_is_sas_port(const struct device *); + extern struct scsi_transport_template * sas_attach_transport(struct sas_function_template *); extern void sas_release_transport(struct scsi_transport_template *); diff --git a/include/scsi/scsi_transport_spi.h b/include/scsi/scsi_transport_spi.h index 5e1d619..302680c 100644 --- a/include/scsi/scsi_transport_spi.h +++ b/include/scsi/scsi_transport_spi.h @@ -20,7 +20,6 @@ #ifndef SCSI_TRANSPORT_SPI_H #define SCSI_TRANSPORT_SPI_H -#include #include #include diff --git a/include/scsi/sg_request.h b/include/scsi/sg_request.h deleted file mode 100644 index 57ff525..0000000 --- a/include/scsi/sg_request.h +++ /dev/null @@ -1,26 +0,0 @@ -typedef struct scsi_request Scsi_Request; - -static Scsi_Request *dummy_cmdp; /* only used for sizeof */ - -typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ - unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ - unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */ - unsigned bufflen; /* Size of (aggregate) data buffer */ - unsigned b_malloc_len; /* actual len malloc'ed in buffer */ - void *buffer; /* Data buffer or scatter list (k_use_sg>0) */ - char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ - unsigned char cmd_opcode; /* first byte of command */ -} Sg_scatter_hold; - -typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ - Scsi_Request *my_cmdp; /* != 0 when request with lower levels */ - struct sg_request *nextrp; /* NULL -> tail request (slist) */ - struct sg_fd *parentfp; /* NULL -> not in use */ - Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ - sg_io_hdr_t header; /* scsi command+info, see */ - unsigned char sense_b[sizeof (dummy_cmdp->sr_sense_buffer)]; - char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ - char orphan; /* 1 -> drop on sight, 0 -> normal */ - char sg_io_owned; /* 1 -> packet belongs to SG_IO */ - volatile char done; /* 0->before bh, 1->before read, 2->read */ -} Sg_request; diff --git a/include/scsi/srp.h b/include/scsi/srp.h index 637f77e..ad178fa 100644 --- a/include/scsi/srp.h +++ b/include/scsi/srp.h @@ -87,6 +87,11 @@ enum srp_login_rej_reason { SRP_LOGIN_REJ_CHANNEL_LIMIT_REACHED = 0x00010006 }; +enum { + SRP_REV10_IB_IO_CLASS = 0xff00, + SRP_REV16A_IB_IO_CLASS = 0x0100 +}; + struct srp_direct_buf { __be64 va; __be32 key; diff --git a/include/sound/Kbuild b/include/sound/Kbuild new file mode 100644 index 0000000..3a5a3df --- /dev/null +++ b/include/sound/Kbuild @@ -0,0 +1,2 @@ +header-y := asound_fm.h hdsp.h hdspm.h sfnt_info.h sscape_ioctl.h +unifdef-y := asequencer.h asound.h emu10k1.h sb16_csp.h diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index b45a737..758f8bf 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -265,6 +265,7 @@ #define AC97_SIGMATEL_CIC2 0x78 /* specific - Analog Devices */ #define AC97_AD_TEST 0x5a /* test register */ +#define AC97_AD_TEST2 0x5c /* undocumented test register 2 */ #define AC97_AD_CODEC_CFG 0x70 /* codec configuration */ #define AC97_AD_JACK_SPDIF 0x72 /* Jack Sense & S/PDIF */ #define AC97_AD_SERIAL_CFG 0x74 /* Serial Configuration */ @@ -378,6 +379,7 @@ #define AC97_HAS_NO_CD (1<<14) /* no CD #define AC97_HAS_NO_MIC (1<<15) /* no MIC volume */ #define AC97_HAS_NO_TONE (1<<16) /* no Tone volume */ #define AC97_HAS_NO_STD_PCM (1<<17) /* no standard AC97 PCM volume and mute */ +#define AC97_HAS_NO_AUX (1<<18) /* no standard AC97 AUX volume and mute */ /* rates indexes */ #define AC97_RATES_FRONT_DAC 0 diff --git a/include/sound/ak4xxx-adda.h b/include/sound/ak4xxx-adda.h index 3bf5911..3d98884 100644 --- a/include/sound/ak4xxx-adda.h +++ b/include/sound/ak4xxx-adda.h @@ -32,8 +32,8 @@ struct snd_akm4xxx; struct snd_ak4xxx_ops { void (*lock)(struct snd_akm4xxx *ak, int chip); void (*unlock)(struct snd_akm4xxx *ak, int chip); - void (*write)(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsigned char val); - // unsigned char (*read)(struct snd_akm4xxx *ak, int chip, unsigned char reg); + void (*write)(struct snd_akm4xxx *ak, int chip, unsigned char reg, + unsigned char val); void (*set_rate_val)(struct snd_akm4xxx *ak, unsigned int rate); }; @@ -41,29 +41,40 @@ #define AK4XXX_IMAGE_SIZE (AK4XXX_MAX_CH struct snd_akm4xxx { struct snd_card *card; - unsigned int num_adcs; /* AK4524 or AK4528 ADCs */ - unsigned int num_dacs; /* AK4524 or AK4528 DACs */ - unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */ - unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2]; /* saved register image for IPGA (AK4528) */ + unsigned int num_adcs; /* AK4524 or AK4528 ADCs */ + unsigned int num_dacs; /* AK4524 or AK4528 DACs */ + unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */ + unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2]; /* saved register image + * for IPGA (AK4528) + */ unsigned long private_value[AK4XXX_MAX_CHIPS]; /* helper for driver */ void *private_data[AK4XXX_MAX_CHIPS]; /* helper for driver */ /* template should fill the following fields */ - unsigned int idx_offset; /* control index offset */ + unsigned int idx_offset; /* control index offset */ enum { SND_AK4524, SND_AK4528, SND_AK4529, SND_AK4355, SND_AK4358, SND_AK4381 } type; + unsigned int *num_stereo; /* array of combined counts + * for the mixer + */ + char **channel_names; /* array of mixer channel names */ struct snd_ak4xxx_ops ops; }; -void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, unsigned char val); +void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg, + unsigned char val); void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state); void snd_akm4xxx_init(struct snd_akm4xxx *ak); int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak); -#define snd_akm4xxx_get(ak,chip,reg) (ak)->images[(chip) * 16 + (reg)] -#define snd_akm4xxx_set(ak,chip,reg,val) ((ak)->images[(chip) * 16 + (reg)] = (val)) -#define snd_akm4xxx_get_ipga(ak,chip,reg) (ak)->ipga_gain[chip][(reg)-4] -#define snd_akm4xxx_set_ipga(ak,chip,reg,val) ((ak)->ipga_gain[chip][(reg)-4] = (val)) +#define snd_akm4xxx_get(ak,chip,reg) \ + (ak)->images[(chip) * 16 + (reg)] +#define snd_akm4xxx_set(ak,chip,reg,val) \ + ((ak)->images[(chip) * 16 + (reg)] = (val)) +#define snd_akm4xxx_get_ipga(ak,chip,reg) \ + (ak)->ipga_gain[chip][(reg)-4] +#define snd_akm4xxx_set_ipga(ak,chip,reg,val) \ + ((ak)->ipga_gain[chip][(reg)-4] = (val)) #endif /* __SOUND_AK4XXX_ADDA_H */ diff --git a/include/sound/asequencer.h b/include/sound/asequencer.h index 6691e4a..3f2f404 100644 --- a/include/sound/asequencer.h +++ b/include/sound/asequencer.h @@ -605,6 +605,10 @@ #define SNDRV_SEQ_PORT_TYPE_SYNTH (1<<10 #define SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE (1<<11) /* Sampling device (support sample download) */ #define SNDRV_SEQ_PORT_TYPE_SAMPLE (1<<12) /* Sampling device (sample can be downloaded at any time) */ /*...*/ +#define SNDRV_SEQ_PORT_TYPE_HARDWARE (1<<16) /* driver for a hardware device */ +#define SNDRV_SEQ_PORT_TYPE_SOFTWARE (1<<17) /* implemented in software */ +#define SNDRV_SEQ_PORT_TYPE_SYNTHESIZER (1<<18) /* generates sound */ +#define SNDRV_SEQ_PORT_TYPE_PORT (1<<19) /* connects to other device(s) */ #define SNDRV_SEQ_PORT_TYPE_APPLICATION (1<<20) /* application (sequencer/editor) */ /* misc. conditioning flags */ diff --git a/include/sound/asound.h b/include/sound/asound.h index 9cc021c..41885f4 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -137,7 +137,7 @@ enum { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 8) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; diff --git a/include/sound/core.h b/include/sound/core.h index 5135147..5d184be 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -233,9 +233,8 @@ int copy_from_user_toio(volatile void __ /* init.c */ -extern unsigned int snd_cards_lock; extern struct snd_card *snd_cards[SNDRV_CARDS]; -extern rwlock_t snd_card_rwlock; +int snd_card_locked(int card); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) #define SND_MIXER_OSS_NOTIFY_REGISTER 0 #define SND_MIXER_OSS_NOTIFY_DISCONNECT 1 diff --git a/include/sound/driver.h b/include/sound/driver.h index 89c6a73..3c522e5 100644 --- a/include/sound/driver.h +++ b/include/sound/driver.h @@ -26,7 +26,6 @@ #ifdef ALSA_BUILD #include "config.h" #endif -#include /* number of supported soundcards */ #ifdef CONFIG_SND_DYNAMIC_MINORS diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 186e00a..884bbf5 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -245,6 +245,7 @@ #define A_GPOUTPUT_MASK 0x00ff #define A_IOCFG_GPOUT0 0x0044 /* analog/digital */ #define A_IOCFG_DISABLE_ANALOG 0x0040 /* = 'enable' for Audigy2 (chiprev=4) */ #define A_IOCFG_ENABLE_DIGITAL 0x0004 +#define A_IOCFG_ENABLE_DIGITAL_AUDIGY4 0x0080 #define A_IOCFG_UNKNOWN_20 0x0020 #define A_IOCFG_DISABLE_AC97_FRONT 0x0080 /* turn off ac97 front -> front (10k2.1) */ #define A_IOCFG_GPOUT1 0x0002 /* IR? drive's internal bypass (?) */ @@ -1065,6 +1066,7 @@ struct snd_emu_chip_details { unsigned char emu1212m; /* EMU 1212m card */ unsigned char spi_dac; /* SPI interface for DAC */ unsigned char i2c_adc; /* I2C interface for ADC */ + unsigned char adc_1361t; /* Use Philips 1361T ADC */ const char *driver; const char *name; const char *id; /* for backward compatibility - can be NULL if not needed */ diff --git a/include/sound/hdsp.h b/include/sound/hdsp.h index 25e1951..dec6b1d 100644 --- a/include/sound/hdsp.h +++ b/include/sound/hdsp.h @@ -30,13 +30,13 @@ enum HDSP_IO_Type { }; struct hdsp_peak_rms { - u32 input_peaks[26]; - u32 playback_peaks[26]; - u32 output_peaks[28]; - u64 input_rms[26]; - u64 playback_rms[26]; + __u32 input_peaks[26]; + __u32 playback_peaks[26]; + __u32 output_peaks[28]; + __u64 input_rms[26]; + __u64 playback_rms[26]; /* These are only used for H96xx cards */ - u64 output_rms[26]; + __u64 output_rms[26]; }; #define SNDRV_HDSP_IOCTL_GET_PEAK_RMS _IOR('H', 0x40, struct hdsp_peak_rms) diff --git a/include/sound/info.h b/include/sound/info.h index f23d838..74f6996 100644 --- a/include/sound/info.h +++ b/include/sound/info.h @@ -27,9 +27,9 @@ #include /* buffer for information */ struct snd_info_buffer { char *buffer; /* pointer to begin of buffer */ - char *curr; /* current position in buffer */ - unsigned long size; /* current size */ - unsigned long len; /* total length of buffer */ + unsigned int curr; /* current position in buffer */ + unsigned int size; /* current size */ + unsigned int len; /* total length of buffer */ int stop; /* stop flag */ int error; /* error code */ }; @@ -40,8 +40,6 @@ #define SNDRV_INFO_CONTENT_DATA 1 struct snd_info_entry; struct snd_info_entry_text { - unsigned long read_size; - unsigned long write_size; void (*read) (struct snd_info_entry *entry, struct snd_info_buffer *buffer); void (*write) (struct snd_info_entry *entry, struct snd_info_buffer *buffer); }; @@ -132,11 +130,9 @@ int snd_card_proc_new(struct snd_card *c static inline void snd_info_set_text_ops(struct snd_info_entry *entry, void *private_data, - long read_size, void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) { entry->private_data = private_data; - entry->c.text.read_size = read_size; entry->c.text.read = read; } @@ -167,7 +163,6 @@ static inline int snd_card_proc_new(stru struct snd_info_entry **entryp) { return -EINVAL; } static inline void snd_info_set_text_ops(struct snd_info_entry *entry __attribute__((unused)), void *private_data, - long read_size, void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) {} static inline int snd_info_check_reserved_words(const char *str) { return 1; } diff --git a/include/sound/initval.h b/include/sound/initval.h index d29e3d3..2ae76ef 100644 --- a/include/sound/initval.h +++ b/include/sound/initval.h @@ -62,7 +62,8 @@ static int snd_legacy_find_free_irq(int { while (*irq_table != -1) { if (!request_irq(*irq_table, snd_legacy_empty_irq_handler, - SA_INTERRUPT, "ALSA Test IRQ", (void *) irq_table)) { + IRQF_DISABLED | IRQF_PROBE_SHARED, "ALSA Test IRQ", + (void *) irq_table)) { free_irq(*irq_table, (void *) irq_table); return *irq_table; } diff --git a/include/sound/mpu401.h b/include/sound/mpu401.h index 8e97ace..ac50432 100644 --- a/include/sound/mpu401.h +++ b/include/sound/mpu401.h @@ -45,6 +45,12 @@ #define MPU401_HW_INTEL8X0 17 /* Intel8 #define MPU401_HW_PC98II 18 /* Roland PC98II */ #define MPU401_HW_AUREAL 19 /* Aureal Vortex */ +#define MPU401_INFO_INPUT (1 << 0) /* input stream */ +#define MPU401_INFO_OUTPUT (1 << 1) /* output stream */ +#define MPU401_INFO_INTEGRATED (1 << 2) /* integrated h/w port */ +#define MPU401_INFO_MMIO (1 << 3) /* MMIO access */ +#define MPU401_INFO_TX_IRQ (1 << 4) /* independent TX irq */ + #define MPU401_MODE_BIT_INPUT 0 #define MPU401_MODE_BIT_OUTPUT 1 #define MPU401_MODE_BIT_INPUT_TRIGGER 2 @@ -62,6 +68,7 @@ struct snd_mpu401 { struct snd_rawmidi *rmidi; unsigned short hardware; /* MPU401_HW_XXXX */ + unsigned int info_flags; /* MPU401_INFO_XXX */ unsigned long port; /* base port of MPU-401 chip */ unsigned long cport; /* port + 1 (usually) */ struct resource *res; /* port resource */ @@ -99,13 +106,16 @@ #define MPU401D(mpu) (mpu)->port */ -irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs); +irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, + struct pt_regs *regs); +irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id, + struct pt_regs *regs); int snd_mpu401_uart_new(struct snd_card *card, int device, unsigned short hardware, unsigned long port, - int integrated, + unsigned int info_flags, int irq, int irq_flags, struct snd_rawmidi ** rrawmidi); diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 3734258..f84d849 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -300,7 +300,6 @@ struct snd_pcm_runtime { /* -- mmap -- */ volatile struct snd_pcm_mmap_status *status; volatile struct snd_pcm_mmap_control *control; - atomic_t mmap_count; /* -- locking / scheduling -- */ wait_queue_head_t sleep; @@ -368,7 +367,9 @@ struct snd_pcm_substream { struct snd_pcm_group *group; /* pointer to current group */ /* -- assigned files -- */ void *file; - struct file *ffile; + int ref_count; + atomic_t mmap_count; + unsigned int f_flags; void (*pcm_release)(struct snd_pcm_substream *); #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ @@ -387,7 +388,7 @@ #endif unsigned int hw_opened: 1; }; -#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL) +#define SUBSTREAM_BUSY(substream) ((substream)->ref_count > 0) struct snd_pcm_str { @@ -825,14 +826,6 @@ int snd_interval_ratnum(struct snd_inter void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var); -int snd_pcm_hw_param_near(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, - unsigned int val, int *dir); -int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, - unsigned int val, int dir); int snd_pcm_hw_params_choose(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); int snd_pcm_hw_refine(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); @@ -979,13 +972,13 @@ struct page *snd_pcm_sgbuf_ops_page(stru static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area) { struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; - atomic_inc(&substream->runtime->mmap_count); + atomic_inc(&substream->mmap_count); } static inline void snd_pcm_mmap_data_close(struct vm_area_struct *area) { struct snd_pcm_substream *substream = (struct snd_pcm_substream *)area->vm_private_data; - atomic_dec(&substream->runtime->mmap_count); + atomic_dec(&substream->mmap_count); } /* mmap for io-memory area */ diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index fb18aef..85cf1cf 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -22,29 +22,21 @@ #define __SOUND_PCM_PARAMS_H * */ -extern int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, const struct snd_mask *val); -extern unsigned int snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir); -extern unsigned int snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir); -extern int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, int dir); -extern int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var); -extern int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, int dir); - -/* To share the same code we have alsa-lib */ -#define INLINE static inline -#define assert(a) (void)(a) +int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir); +int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir); #define SNDRV_MASK_BITS 64 /* we use so far 64bits only */ #define SNDRV_MASK_SIZE (SNDRV_MASK_BITS / 32) #define MASK_OFS(i) ((i) >> 5) #define MASK_BIT(i) (1U << ((i) & 31)) -INLINE unsigned int ld2(u_int32_t v) +static inline unsigned int ld2(u_int32_t v) { unsigned r = 0; @@ -69,22 +61,22 @@ INLINE unsigned int ld2(u_int32_t v) return r; } -INLINE size_t snd_mask_sizeof(void) +static inline size_t snd_mask_sizeof(void) { return sizeof(struct snd_mask); } -INLINE void snd_mask_none(struct snd_mask *mask) +static inline void snd_mask_none(struct snd_mask *mask) { memset(mask, 0, sizeof(*mask)); } -INLINE void snd_mask_any(struct snd_mask *mask) +static inline void snd_mask_any(struct snd_mask *mask) { memset(mask, 0xff, SNDRV_MASK_SIZE * sizeof(u_int32_t)); } -INLINE int snd_mask_empty(const struct snd_mask *mask) +static inline int snd_mask_empty(const struct snd_mask *mask) { int i; for (i = 0; i < SNDRV_MASK_SIZE; i++) @@ -93,10 +85,9 @@ INLINE int snd_mask_empty(const struct s return 1; } -INLINE unsigned int snd_mask_min(const struct snd_mask *mask) +static inline unsigned int snd_mask_min(const struct snd_mask *mask) { int i; - assert(!snd_mask_empty(mask)); for (i = 0; i < SNDRV_MASK_SIZE; i++) { if (mask->bits[i]) return ffs(mask->bits[i]) - 1 + (i << 5); @@ -104,10 +95,9 @@ INLINE unsigned int snd_mask_min(const s return 0; } -INLINE unsigned int snd_mask_max(const struct snd_mask *mask) +static inline unsigned int snd_mask_max(const struct snd_mask *mask) { int i; - assert(!snd_mask_empty(mask)); for (i = SNDRV_MASK_SIZE - 1; i >= 0; i--) { if (mask->bits[i]) return ld2(mask->bits[i]) + (i << 5); @@ -115,70 +105,68 @@ INLINE unsigned int snd_mask_max(const s return 0; } -INLINE void snd_mask_set(struct snd_mask *mask, unsigned int val) +static inline void snd_mask_set(struct snd_mask *mask, unsigned int val) { - assert(val <= SNDRV_MASK_BITS); mask->bits[MASK_OFS(val)] |= MASK_BIT(val); } -INLINE void snd_mask_reset(struct snd_mask *mask, unsigned int val) +static inline void snd_mask_reset(struct snd_mask *mask, unsigned int val) { - assert(val <= SNDRV_MASK_BITS); mask->bits[MASK_OFS(val)] &= ~MASK_BIT(val); } -INLINE void snd_mask_set_range(struct snd_mask *mask, unsigned int from, unsigned int to) +static inline void snd_mask_set_range(struct snd_mask *mask, + unsigned int from, unsigned int to) { unsigned int i; - assert(to <= SNDRV_MASK_BITS && from <= to); for (i = from; i <= to; i++) mask->bits[MASK_OFS(i)] |= MASK_BIT(i); } -INLINE void snd_mask_reset_range(struct snd_mask *mask, unsigned int from, unsigned int to) +static inline void snd_mask_reset_range(struct snd_mask *mask, + unsigned int from, unsigned int to) { unsigned int i; - assert(to <= SNDRV_MASK_BITS && from <= to); for (i = from; i <= to; i++) mask->bits[MASK_OFS(i)] &= ~MASK_BIT(i); } -INLINE void snd_mask_leave(struct snd_mask *mask, unsigned int val) +static inline void snd_mask_leave(struct snd_mask *mask, unsigned int val) { unsigned int v; - assert(val <= SNDRV_MASK_BITS); v = mask->bits[MASK_OFS(val)] & MASK_BIT(val); snd_mask_none(mask); mask->bits[MASK_OFS(val)] = v; } -INLINE void snd_mask_intersect(struct snd_mask *mask, const struct snd_mask *v) +static inline void snd_mask_intersect(struct snd_mask *mask, + const struct snd_mask *v) { int i; for (i = 0; i < SNDRV_MASK_SIZE; i++) mask->bits[i] &= v->bits[i]; } -INLINE int snd_mask_eq(const struct snd_mask *mask, const struct snd_mask *v) +static inline int snd_mask_eq(const struct snd_mask *mask, + const struct snd_mask *v) { return ! memcmp(mask, v, SNDRV_MASK_SIZE * sizeof(u_int32_t)); } -INLINE void snd_mask_copy(struct snd_mask *mask, const struct snd_mask *v) +static inline void snd_mask_copy(struct snd_mask *mask, + const struct snd_mask *v) { *mask = *v; } -INLINE int snd_mask_test(const struct snd_mask *mask, unsigned int val) +static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val) { - assert(val <= SNDRV_MASK_BITS); return mask->bits[MASK_OFS(val)] & MASK_BIT(val); } -INLINE int snd_mask_single(const struct snd_mask *mask) +static inline int snd_mask_single(const struct snd_mask *mask) { int i, c = 0; - assert(!snd_mask_empty(mask)); for (i = 0; i < SNDRV_MASK_SIZE; i++) { if (! mask->bits[i]) continue; @@ -191,10 +179,10 @@ INLINE int snd_mask_single(const struct return 1; } -INLINE int snd_mask_refine(struct snd_mask *mask, const struct snd_mask *v) +static inline int snd_mask_refine(struct snd_mask *mask, + const struct snd_mask *v) { struct snd_mask old; - assert(!snd_mask_empty(mask)); snd_mask_copy(&old, mask); snd_mask_intersect(mask, v); if (snd_mask_empty(mask)) @@ -202,27 +190,24 @@ INLINE int snd_mask_refine(struct snd_ma return !snd_mask_eq(mask, &old); } -INLINE int snd_mask_refine_first(struct snd_mask *mask) +static inline int snd_mask_refine_first(struct snd_mask *mask) { - assert(!snd_mask_empty(mask)); if (snd_mask_single(mask)) return 0; snd_mask_leave(mask, snd_mask_min(mask)); return 1; } -INLINE int snd_mask_refine_last(struct snd_mask *mask) +static inline int snd_mask_refine_last(struct snd_mask *mask) { - assert(!snd_mask_empty(mask)); if (snd_mask_single(mask)) return 0; snd_mask_leave(mask, snd_mask_max(mask)); return 1; } -INLINE int snd_mask_refine_min(struct snd_mask *mask, unsigned int val) +static inline int snd_mask_refine_min(struct snd_mask *mask, unsigned int val) { - assert(!snd_mask_empty(mask)); if (snd_mask_min(mask) >= val) return 0; snd_mask_reset_range(mask, 0, val - 1); @@ -231,9 +216,8 @@ INLINE int snd_mask_refine_min(struct sn return 1; } -INLINE int snd_mask_refine_max(struct snd_mask *mask, unsigned int val) +static inline int snd_mask_refine_max(struct snd_mask *mask, unsigned int val) { - assert(!snd_mask_empty(mask)); if (snd_mask_max(mask) <= val) return 0; snd_mask_reset_range(mask, val + 1, SNDRV_MASK_BITS); @@ -242,10 +226,9 @@ INLINE int snd_mask_refine_max(struct sn return 1; } -INLINE int snd_mask_refine_set(struct snd_mask *mask, unsigned int val) +static inline int snd_mask_refine_set(struct snd_mask *mask, unsigned int val) { int changed; - assert(!snd_mask_empty(mask)); changed = !snd_mask_single(mask); snd_mask_leave(mask, val); if (snd_mask_empty(mask)) @@ -253,13 +236,12 @@ INLINE int snd_mask_refine_set(struct sn return changed; } -INLINE int snd_mask_value(const struct snd_mask *mask) +static inline int snd_mask_value(const struct snd_mask *mask) { - assert(!snd_mask_empty(mask)); return snd_mask_min(mask); } -INLINE void snd_interval_any(struct snd_interval *i) +static inline void snd_interval_any(struct snd_interval *i) { i->min = 0; i->openmin = 0; @@ -269,63 +251,59 @@ INLINE void snd_interval_any(struct snd_ i->empty = 0; } -INLINE void snd_interval_none(struct snd_interval *i) +static inline void snd_interval_none(struct snd_interval *i) { i->empty = 1; } -INLINE int snd_interval_checkempty(const struct snd_interval *i) +static inline int snd_interval_checkempty(const struct snd_interval *i) { return (i->min > i->max || (i->min == i->max && (i->openmin || i->openmax))); } -INLINE int snd_interval_empty(const struct snd_interval *i) +static inline int snd_interval_empty(const struct snd_interval *i) { return i->empty; } -INLINE int snd_interval_single(const struct snd_interval *i) +static inline int snd_interval_single(const struct snd_interval *i) { - assert(!snd_interval_empty(i)); return (i->min == i->max || (i->min + 1 == i->max && i->openmax)); } -INLINE int snd_interval_value(const struct snd_interval *i) +static inline int snd_interval_value(const struct snd_interval *i) { - assert(snd_interval_single(i)); return i->min; } -INLINE int snd_interval_min(const struct snd_interval *i) +static inline int snd_interval_min(const struct snd_interval *i) { - assert(!snd_interval_empty(i)); return i->min; } -INLINE int snd_interval_max(const struct snd_interval *i) +static inline int snd_interval_max(const struct snd_interval *i) { unsigned int v; - assert(!snd_interval_empty(i)); v = i->max; if (i->openmax) v--; return v; } -INLINE int snd_interval_test(const struct snd_interval *i, unsigned int val) +static inline int snd_interval_test(const struct snd_interval *i, unsigned int val) { return !((i->min > val || (i->min == val && i->openmin) || i->max < val || (i->max == val && i->openmax))); } -INLINE void snd_interval_copy(struct snd_interval *d, const struct snd_interval *s) +static inline void snd_interval_copy(struct snd_interval *d, const struct snd_interval *s) { *d = *s; } -INLINE int snd_interval_setinteger(struct snd_interval *i) +static inline int snd_interval_setinteger(struct snd_interval *i) { if (i->integer) return 0; @@ -335,7 +313,7 @@ INLINE int snd_interval_setinteger(struc return 1; } -INLINE int snd_interval_eq(const struct snd_interval *i1, const struct snd_interval *i2) +static inline int snd_interval_eq(const struct snd_interval *i1, const struct snd_interval *i2) { if (i1->empty) return i2->empty; @@ -359,8 +337,5 @@ static inline unsigned int sub(unsigned return 0; } -#undef INLINE -#undef assert - #endif /* __SOUND_PCM_PARAMS_H */ diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 584e73d..7dbcd10 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -46,6 +46,7 @@ #define SNDRV_RAWMIDI_LFLG_NOOPENLOCK (1 struct snd_rawmidi; struct snd_rawmidi_substream; +struct snd_seq_port_info; struct snd_rawmidi_ops { int (*open) (struct snd_rawmidi_substream * substream); @@ -57,6 +58,8 @@ struct snd_rawmidi_ops { struct snd_rawmidi_global_ops { int (*dev_register) (struct snd_rawmidi * rmidi); int (*dev_unregister) (struct snd_rawmidi * rmidi); + void (*get_port_info)(struct snd_rawmidi *rmidi, int number, + struct snd_seq_port_info *info); }; struct snd_rawmidi_runtime { diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h index a4f5545..b5067d3 100644 --- a/include/sound/tea575x-tuner.h +++ b/include/sound/tea575x-tuner.h @@ -20,9 +20,10 @@ #define __SOUND_TEA575X_TUNER_H * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - */ + */ #include +#include struct snd_tea575x; diff --git a/include/sound/version.h b/include/sound/version.h index 4f0e658..2ee849d 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h. Generated by configure. */ -#define CONFIG_SND_VERSION "1.0.11rc4" -#define CONFIG_SND_DATE " (Wed Mar 22 10:27:24 2006 UTC)" +#define CONFIG_SND_VERSION "1.0.12rc1" +#define CONFIG_SND_DATE " (Thu Jun 22 13:55:50 2006 UTC)" diff --git a/include/video/Kbuild b/include/video/Kbuild new file mode 100644 index 0000000..76a6073 --- /dev/null +++ b/include/video/Kbuild @@ -0,0 +1 @@ +unifdef-y := sisfb.h diff --git a/include/video/edid.h b/include/video/edid.h index b913f19..f6a42d6 100644 --- a/include/video/edid.h +++ b/include/video/edid.h @@ -3,7 +3,6 @@ #define __linux_video_edid_h__ #ifdef __KERNEL__ -#include #ifdef CONFIG_X86 struct edid_info { diff --git a/include/video/vga.h b/include/video/vga.h index 700d6c8..b49a512 100644 --- a/include/video/vga.h +++ b/include/video/vga.h @@ -17,7 +17,6 @@ #ifndef __linux_video_vga_h__ #define __linux_video_vga_h__ -#include #include #include #ifndef CONFIG_AMIGA diff --git a/init/Kconfig b/init/Kconfig index 3b36a1d..a5b073a 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1,3 +1,11 @@ +config DEFCONFIG_LIST + string + option defconfig_list + default "/lib/modules/$UNAME_RELEASE/.config" + default "/etc/kernel-config" + default "/boot/config-$UNAME_RELEASE" + default "arch/$ARCH/defconfig" + menu "Code maturity level options" config EXPERIMENTAL @@ -46,8 +54,8 @@ config LOCK_KERNEL config INIT_ENV_ARG_LIMIT int - default 32 if !USERMODE - default 128 if USERMODE + default 32 if !UML + default 128 if UML help Maximum of each of the number of arguments and environment variables passed to init from the kernel command line. @@ -151,7 +159,8 @@ config BSD_PROCESS_ACCT_V3 at . config SYSCTL - bool "Sysctl support" + bool "Sysctl support" if EMBEDDED + default y ---help--- The sysctl interface provides a means of dynamically changing certain kernel parameters and variables on the fly without requiring @@ -182,7 +191,8 @@ config AUDITSYSCALL help Enable low-overhead system-call auditing infrastructure that can be used independently or with another kernel subsystem, - such as SELinux. + such as SELinux. To use audit's filesystem watch feature, please + ensure that INOTIFY is configured. config IKCONFIG bool "Kernel .config support" @@ -234,16 +244,6 @@ config UID16 help This enables the legacy 16-bit UID syscall wrappers. -config VM86 - depends X86 - default y - bool "Enable VM86 support" if EMBEDDED - help - This option is required by programs like DOSEMU to run 16-bit legacy - code on X86 processors. It also may be needed by software like - XFree86 to initialize some video cards via BIOS. Disabling this - option saves about 6k. - config CC_OPTIMIZE_FOR_SIZE bool "Optimize for size (Look out for broken compilers!)" default y @@ -339,9 +339,14 @@ config BASE_FULL kernel data structures. This saves memory on small machines, but may reduce performance. +config RT_MUTEXES + boolean + select PLIST + config FUTEX bool "Enable futex support" if EMBEDDED default y + select RT_MUTEXES help Disabling this option will cause the kernel to be built without support for "fast userspace mutexes". The resulting kernel may not @@ -374,6 +379,15 @@ config SLAB SLOB is more space efficient but does not scale well and is more susceptible to fragmentation. +config VM_EVENT_COUNTERS + default y + bool "Enable VM event counters for /proc/vmstat" if EMBEDDED + help + VM event counters are only needed to for event counts to be + shown. They have no function for the kernel itself. This + option allows the disabling of the VM event counters. + /proc/vmstat will only show page counts. + endmenu # General setup config TINY_SHMEM @@ -389,9 +403,6 @@ config SLOB default !SLAB bool -config OBSOLETE_INTERMODULE - tristate - menu "Loadable module support" config MODULES diff --git a/init/Makefile b/init/Makefile index a230007..633a268 100644 --- a/init/Makefile +++ b/init/Makefile @@ -6,7 +6,6 @@ obj-y := main.o version.o mounts.o in obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o mounts-y := do_mounts.o -mounts-$(CONFIG_DEVFS_FS) += do_mounts_devfs.o mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o mounts-$(CONFIG_BLK_DEV_INITRD) += do_mounts_initrd.o mounts-$(CONFIG_BLK_DEV_MD) += do_mounts_md.o diff --git a/init/do_mounts.c b/init/do_mounts.c index f4b7b9d..94aeec7 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -325,7 +325,7 @@ static int __init mount_nfs_root(void) { void *data = nfs_root_data(); - create_dev("/dev/root", ROOT_DEV, NULL); + create_dev("/dev/root", ROOT_DEV); if (data && do_mount_root("/dev/root", "nfs", root_mountflags, data) == 0) return 1; @@ -386,7 +386,7 @@ #ifdef CONFIG_BLK_DEV_FD change_floppy("root floppy"); } #endif - create_dev("/dev/root", ROOT_DEV, root_device_name); + create_dev("/dev/root", ROOT_DEV); mount_block_root("/dev/root", root_mountflags); } @@ -397,8 +397,6 @@ void __init prepare_namespace(void) { int is_floppy; - mount_devfs(); - if (root_delay) { printk(KERN_INFO "Waiting %dsec before mounting root device...\n", root_delay); @@ -409,6 +407,10 @@ void __init prepare_namespace(void) if (saved_root_name[0]) { root_device_name = saved_root_name; + if (!strncmp(root_device_name, "mtd", 3)) { + mount_block_root(root_device_name, root_mountflags); + goto out; + } ROOT_DEV = name_to_dev_t(root_device_name); if (strncmp(root_device_name, "/dev/", 5) == 0) root_device_name += 5; @@ -424,10 +426,8 @@ void __init prepare_namespace(void) mount_root(); out: - umount_devfs("/dev"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); security_sb_post_mountroot(); - mount_devfs_fs (); } diff --git a/init/do_mounts.h b/init/do_mounts.h index e0a7ac9..e7f2e7f 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -15,25 +14,12 @@ void mount_root(void); extern int root_mountflags; extern char *root_device_name; -#ifdef CONFIG_DEVFS_FS - -void mount_devfs(void); -void umount_devfs(char *path); -int create_dev(char *name, dev_t dev, char *devfs_name); - -#else - -static inline void mount_devfs(void) {} -static inline void umount_devfs(const char *path) {} - -static inline int create_dev(char *name, dev_t dev, char *devfs_name) +static inline int create_dev(char *name, dev_t dev) { sys_unlink(name); return sys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); } -#endif - #if BITS_PER_LONG == 32 static inline u32 bstat(char *name) { diff --git a/init/do_mounts_devfs.c b/init/do_mounts_devfs.c deleted file mode 100644 index cc52647..0000000 --- a/init/do_mounts_devfs.c +++ /dev/null @@ -1,137 +0,0 @@ - -#include -#include -#include - -#include "do_mounts.h" - -void __init mount_devfs(void) -{ - sys_mount("devfs", "/dev", "devfs", 0, NULL); -} - -void __init umount_devfs(char *path) -{ - sys_umount(path, 0); -} - -/* - * If the dir will fit in *buf, return its length. If it won't fit, return - * zero. Return -ve on error. - */ -static int __init do_read_dir(int fd, void *buf, int len) -{ - long bytes, n; - char *p = buf; - sys_lseek(fd, 0, 0); - - for (bytes = 0; bytes < len; bytes += n) { - n = sys_getdents64(fd, (struct linux_dirent64 *)(p + bytes), - len - bytes); - if (n < 0) - return n; - if (n == 0) - return bytes; - } - return 0; -} - -/* - * Try to read all of a directory. Returns the contents at *p, which - * is kmalloced memory. Returns the number of bytes read at *len. Returns - * NULL on error. - */ -static void * __init read_dir(char *path, int *len) -{ - int size; - int fd = sys_open(path, 0, 0); - - *len = 0; - if (fd < 0) - return NULL; - - for (size = 1 << 9; size <= (PAGE_SIZE << MAX_ORDER); size <<= 1) { - void *p = kmalloc(size, GFP_KERNEL); - int n; - if (!p) - break; - n = do_read_dir(fd, p, size); - if (n > 0) { - sys_close(fd); - *len = n; - return p; - } - kfree(p); - if (n == -EINVAL) - continue; /* Try a larger buffer */ - if (n < 0) - break; - } - sys_close(fd); - return NULL; -} - -/* - * recursively scan , looking for a device node of type - */ -static int __init find_in_devfs(char *path, unsigned dev) -{ - char *end = path + strlen(path); - int rest = path + 64 - end; - int size; - char *p = read_dir(path, &size); - char *s; - - if (!p) - return -1; - for (s = p; s < p + size; s += ((struct linux_dirent64 *)s)->d_reclen) { - struct linux_dirent64 *d = (struct linux_dirent64 *)s; - if (strlen(d->d_name) + 2 > rest) - continue; - switch (d->d_type) { - case DT_BLK: - sprintf(end, "/%s", d->d_name); - if (bstat(path) != dev) - break; - kfree(p); - return 0; - case DT_DIR: - if (strcmp(d->d_name, ".") == 0) - break; - if (strcmp(d->d_name, "..") == 0) - break; - sprintf(end, "/%s", d->d_name); - if (find_in_devfs(path, dev) < 0) - break; - kfree(p); - return 0; - } - } - kfree(p); - return -1; -} - -/* - * create a device node called which points to - * if possible, otherwise find a device node - * which matches and make a symlink pointing to it. - */ -int __init create_dev(char *name, dev_t dev, char *devfs_name) -{ - char path[64]; - - sys_unlink(name); - if (devfs_name && devfs_name[0]) { - if (strncmp(devfs_name, "/dev/", 5) == 0) - devfs_name += 5; - sprintf(path, "/dev/%s", devfs_name); - if (sys_access(path, 0) == 0) - return sys_symlink(devfs_name, name); - } - if (!dev) - return -1; - strcpy(path, "/dev"); - if (find_in_devfs(path, new_encode_dev(dev)) < 0) - return -1; - return sys_symlink(path + 5, name); -} diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 405f903..a06f037 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -44,7 +44,7 @@ static void __init handle_initrd(void) int pid; real_root_dev = new_encode_dev(ROOT_DEV); - create_dev("/dev/root.old", Root_RAM0, NULL); + create_dev("/dev/root.old", Root_RAM0); /* mount initrd on rootfs' /root */ mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); sys_mkdir("/old", 0700); @@ -54,7 +54,6 @@ static void __init handle_initrd(void) sys_chdir("/root"); sys_mount(".", "/", NULL, MS_MOVE, NULL); sys_chroot("."); - mount_devfs_fs (); current->flags |= PF_NOFREEZE; pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); @@ -71,7 +70,6 @@ static void __init handle_initrd(void) sys_chroot("."); sys_close(old_fd); sys_close(root_fd); - umount_devfs("/old/dev"); if (new_decode_dev(real_root_dev) == Root_RAM0) { sys_chdir("/old"); @@ -107,7 +105,7 @@ static void __init handle_initrd(void) int __init initrd_load(void) { if (mount_initrd) { - create_dev("/dev/ram", Root_RAM0, NULL); + create_dev("/dev/ram", Root_RAM0); /* * Load the initrd data into /dev/ram0. Execute it as initrd * unless /dev/ram0 is supposed to be our actual root device, diff --git a/init/do_mounts_md.c b/init/do_mounts_md.c index f6f3680..2429e1b 100644 --- a/init/do_mounts_md.c +++ b/init/do_mounts_md.c @@ -125,19 +125,18 @@ static void __init md_setup_drive(void) int err = 0; char *devname; mdu_disk_info_t dinfo; - char name[16], devfs_name[16]; + char name[16]; minor = md_setup_args[ent].minor; partitioned = md_setup_args[ent].partitioned; devname = md_setup_args[ent].device_names; sprintf(name, "/dev/md%s%d", partitioned?"_d":"", minor); - sprintf(devfs_name, "/dev/md/%s%d", partitioned?"d":"", minor); if (partitioned) dev = MKDEV(mdp_major, minor << MdpMinorShift); else dev = MKDEV(MD_MAJOR, minor); - create_dev(name, dev, devfs_name); + create_dev(name, dev); for (i = 0; i < MD_SB_DISKS && devname != 0; i++) { char *p; char comp_name[64]; @@ -272,7 +271,7 @@ __setup("md=", md_setup); void __init md_run_setup(void) { - create_dev("/dev/md0", MKDEV(MD_MAJOR, 0), "md/0"); + create_dev("/dev/md0", MKDEV(MD_MAJOR, 0)); if (raid_noautodetect) printk(KERN_INFO "md: Skipping autodetection of RAID arrays. (raid=noautodetect)\n"); else { diff --git a/init/do_mounts_rd.c b/init/do_mounts_rd.c index c2683fc..ed652f4 100644 --- a/init/do_mounts_rd.c +++ b/init/do_mounts_rd.c @@ -262,8 +262,8 @@ int __init rd_load_disk(int n) { if (rd_prompt) change_floppy("root floppy disk to be loaded into RAM disk"); - create_dev("/dev/root", ROOT_DEV, root_device_name); - create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n), NULL); + create_dev("/dev/root", ROOT_DEV); + create_dev("/dev/ram", MKDEV(RAMDISK_MAJOR, n)); return rd_load_image("/dev/root"); } diff --git a/init/initramfs.c b/init/initramfs.c index f81cfa4..d28c109 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -30,6 +30,7 @@ #define N_ALIGN(len) ((((len) + 1) & ~3) static __initdata struct hash { int ino, minor, major; + mode_t mode; struct hash *next; char name[N_ALIGN(PATH_MAX)]; } *head[32]; @@ -41,7 +42,8 @@ static inline int hash(int major, int mi return tmp & 31; } -static char __init *find_link(int major, int minor, int ino, char *name) +static char __init *find_link(int major, int minor, int ino, + mode_t mode, char *name) { struct hash **p, *q; for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { @@ -51,14 +53,17 @@ static char __init *find_link(int major, continue; if ((*p)->major != major) continue; + if (((*p)->mode ^ mode) & S_IFMT) + continue; return (*p)->name; } q = (struct hash *)malloc(sizeof(struct hash)); if (!q) panic("can't allocate link hash entry"); - q->ino = ino; - q->minor = minor; q->major = major; + q->minor = minor; + q->ino = ino; + q->mode = mode; strcpy(q->name, name); q->next = NULL; *p = q; @@ -229,13 +234,25 @@ static int __init do_reset(void) static int __init maybe_link(void) { if (nlink >= 2) { - char *old = find_link(major, minor, ino, collected); + char *old = find_link(major, minor, ino, mode, collected); if (old) return (sys_link(old, collected) < 0) ? -1 : 1; } return 0; } +static void __init clean_path(char *path, mode_t mode) +{ + struct stat st; + + if (!sys_newlstat(path, &st) && (st.st_mode^mode) & S_IFMT) { + if (S_ISDIR(st.st_mode)) + sys_rmdir(path); + else + sys_unlink(path); + } +} + static __initdata int wfd; static int __init do_name(void) @@ -248,9 +265,15 @@ static int __init do_name(void) } if (dry_run) return 0; + clean_path(collected, mode); if (S_ISREG(mode)) { - if (maybe_link() >= 0) { - wfd = sys_open(collected, O_WRONLY|O_CREAT, mode); + int ml = maybe_link(); + if (ml >= 0) { + int openflags = O_WRONLY|O_CREAT; + if (ml != 1) + openflags |= O_TRUNC; + wfd = sys_open(collected, openflags, mode); + if (wfd >= 0) { sys_fchown(wfd, uid, gid); sys_fchmod(wfd, mode); @@ -291,6 +314,7 @@ static int __init do_copy(void) static int __init do_symlink(void) { collected[N_ALIGN(name_len) + body_len] = '\0'; + clean_path(collected, 0); sys_symlink(collected + N_ALIGN(name_len), collected); sys_lchown(collected, uid, gid); state = SkipIt; diff --git a/init/main.c b/init/main.c index f715b9b..628b8e9 100644 --- a/init/main.c +++ b/init/main.c @@ -11,11 +11,9 @@ #define __KERNEL_SYSCALLS__ -#include #include #include #include -#include #include #include #include @@ -47,6 +45,10 @@ #include #include #include #include +#include +#include +#include +#include #include #include @@ -79,7 +81,6 @@ extern void mca_init(void); extern void sbus_init(void); extern void sysctl_init(void); extern void signals_init(void); -extern void buffer_init(void); extern void pidhash_init(void); extern void pidmap_init(void); extern void prio_tree_init(void); @@ -446,10 +447,27 @@ static void __init boot_cpu_init(void) cpu_set(cpu, cpu_possible_map); } +void __init __attribute__((weak)) smp_setup_processor_id(void) +{ +} + asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start___param[], __stop___param[]; + + smp_setup_processor_id(); + + /* + * Need to run as early as possible, to initialize the + * lockdep hash: + */ + lockdep_init(); + + local_irq_disable(); + early_boot_irqs_off(); + early_init_irq_lock_class(); + /* * Interrupts are still disabled. Do necessary setups, then * enable them @@ -482,6 +500,7 @@ asmlinkage void __init start_kernel(void __stop___param - __start___param, &unknown_bootoption); sort_main_extable(); + unwind_init(); trap_init(); rcu_init(); init_IRQ(); @@ -489,7 +508,13 @@ asmlinkage void __init start_kernel(void init_timers(); hrtimers_init(); softirq_init(); + timekeeping_init(); time_init(); + profile_init(); + if (!irqs_disabled()) + printk("start_kernel(): bug: interrupts were enabled early\n"); + early_boot_irqs_on(); + local_irq_enable(); /* * HACK ALERT! This is early. We're enabling the console before @@ -499,8 +524,16 @@ asmlinkage void __init start_kernel(void console_init(); if (panic_later) panic(panic_later, panic_param); - profile_init(); - local_irq_enable(); + + lockdep_info(); + + /* + * Need to run this when irqs are enabled, because it wants + * to self-test [hard/soft]-irqs on/off lock inversion bugs + * too: + */ + locking_selftest(); + #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) { diff --git a/init/version.c b/init/version.c index 3ddc3ce..e290802 100644 --- a/init/version.c +++ b/init/version.c @@ -10,6 +10,7 @@ #include #include #include #include +#include #include #define version(a) Version_ ## a diff --git a/ipc/compat.c b/ipc/compat.c index a544dfb..4d20cfd 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -21,7 +21,6 @@ * */ #include -#include #include #include #include diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 41ecbd4..02e6f67 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -8,6 +8,8 @@ * Lockless receive & send, fd based notify: * Manfred Spraul (manfred@colorfullife.com) * + * Audit: George Wilson (ltcgcw@us.ibm.com) + * * This file is released under the GPL. */ @@ -24,6 +26,7 @@ #include #include #include #include +#include #include #include @@ -202,11 +205,11 @@ static int mqueue_fill_super(struct supe return 0; } -static struct super_block *mqueue_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int mqueue_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, mqueue_fill_super); + return get_sb_single(fs_type, flags, data, mqueue_fill_super, mnt); } static void init_once(void *foo, kmem_cache_t * cachep, unsigned long flags) @@ -356,7 +359,7 @@ static ssize_t mqueue_read_file(struct f return count; } -static int mqueue_flush_file(struct file *filp) +static int mqueue_flush_file(struct file *filp, fl_owner_t id) { struct mqueue_inode_info *info = MQUEUE_I(filp->f_dentry->d_inode); @@ -657,6 +660,10 @@ asmlinkage long sys_mq_open(const char _ char *name; int fd, error; + error = audit_mq_open(oflag, mode, u_attr); + if (error != 0) + return error; + if (IS_ERR(name = getname(u_name))) return PTR_ERR(name); @@ -814,6 +821,10 @@ asmlinkage long sys_mq_timedsend(mqd_t m long timeout; int ret; + ret = audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout); + if (ret != 0) + return ret; + if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) return -EINVAL; @@ -896,6 +907,10 @@ asmlinkage ssize_t sys_mq_timedreceive(m struct mqueue_inode_info *info; struct ext_wait_queue wait; + ret = audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout); + if (ret != 0) + return ret; + timeout = prepare_timeout(u_abs_timeout); ret = -EBADF; @@ -975,6 +990,10 @@ asmlinkage long sys_mq_notify(mqd_t mqde struct mqueue_inode_info *info; struct sk_buff *nc; + ret = audit_mq_notify(mqdes, u_notification); + if (ret != 0) + return ret; + nc = NULL; sock = NULL; if (u_notification != NULL) { @@ -1115,6 +1134,9 @@ asmlinkage long sys_mq_getsetattr(mqd_t omqstat = info->attr; omqstat.mq_flags = filp->f_flags & O_NONBLOCK; if (u_mqstat) { + ret = audit_mq_getsetattr(mqdes, &mqstat); + if (ret != 0) + goto out; if (mqstat.mq_flags & O_NONBLOCK) filp->f_flags |= O_NONBLOCK; else diff --git a/ipc/msg.c b/ipc/msg.c index 7d1340c..cd92d34 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -19,7 +19,6 @@ */ #include -#include #include #include #include @@ -454,6 +453,11 @@ asmlinkage long sys_msgctl (int msqid, i err = audit_ipc_obj(ipcp); if (err) goto out_unlock_up; + if (cmd==IPC_SET) { + err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode); + if (err) + goto out_unlock_up; + } err = -EPERM; if (current->euid != ipcp->cuid && @@ -468,10 +472,6 @@ asmlinkage long sys_msgctl (int msqid, i switch (cmd) { case IPC_SET: { - err = audit_ipc_set_perm(setbuf.qbytes, setbuf.uid, setbuf.gid, setbuf.mode, ipcp); - if (err) - goto out_unlock_up; - err = -EPERM; if (setbuf.qbytes > msg_ctlmnb && !capable(CAP_SYS_RESOURCE)) goto out_unlock_up; diff --git a/ipc/sem.c b/ipc/sem.c index 7919f8e..6013c75 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -66,7 +66,6 @@ * Dustin Kirkland */ -#include #include #include #include @@ -828,6 +827,11 @@ static int semctl_down(int semid, int se if (err) goto out_unlock; + if (cmd == IPC_SET) { + err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode); + if (err) + goto out_unlock; + } if (current->euid != ipcp->cuid && current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) { err=-EPERM; @@ -844,9 +848,6 @@ static int semctl_down(int semid, int se err = 0; break; case IPC_SET: - err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, ipcp); - if (err) - goto out_unlock; ipcp->uid = setbuf.uid; ipcp->gid = setbuf.gid; ipcp->mode = (ipcp->mode & ~S_IRWXUGO) diff --git a/ipc/shm.c b/ipc/shm.c index 8098968..940b0c9 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -17,7 +17,6 @@ * Dustin Kirkland */ -#include #include #include #include @@ -643,7 +642,7 @@ asmlinkage long sys_shmctl (int shmid, i err = audit_ipc_obj(&(shp->shm_perm)); if (err) goto out_unlock_up; - err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode, &(shp->shm_perm)); + err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode); if (err) goto out_unlock_up; err=-EPERM; @@ -698,7 +697,6 @@ long do_shmat(int shmid, char __user *sh int err; unsigned long flags; unsigned long prot; - unsigned long o_flags; int acc_mode; void *user_addr; @@ -725,11 +723,9 @@ #endif if (shmflg & SHM_RDONLY) { prot = PROT_READ; - o_flags = O_RDONLY; acc_mode = S_IRUGO; } else { prot = PROT_READ | PROT_WRITE; - o_flags = O_RDWR; acc_mode = S_IRUGO | S_IWUGO; } if (shmflg & SHM_EXEC) { diff --git a/ipc/util.c b/ipc/util.c index 8193299..67b6d17 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -14,7 +14,6 @@ * Dustin Kirkland */ -#include #include #include #include diff --git a/kernel/Makefile b/kernel/Makefile index 58908f9..47dbcd5 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -8,20 +8,30 @@ obj-y = sched.o fork.o exec_domain.o signal.o sys.o kmod.o workqueue.o pid.o \ rcupdate.o extable.o params.o posix-timers.o \ kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ - hrtimer.o + hrtimer.o rwsem.o +obj-$(CONFIG_STACKTRACE) += stacktrace.o +obj-y += time/ obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o +obj-$(CONFIG_LOCKDEP) += lockdep.o +ifeq ($(CONFIG_PROC_FS),y) +obj-$(CONFIG_LOCKDEP) += lockdep_proc.o +endif obj-$(CONFIG_FUTEX) += futex.o ifeq ($(CONFIG_COMPAT),y) obj-$(CONFIG_FUTEX) += futex_compat.o endif +obj-$(CONFIG_RT_MUTEXES) += rtmutex.o +obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o +obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o obj-$(CONFIG_SMP) += cpu.o spinlock.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o +obj-$(CONFIG_PROVE_LOCKING) += spinlock.o obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o -obj-$(CONFIG_OBSOLETE_INTERMODULE) += intermodule.o obj-$(CONFIG_KALLSYMS) += kallsyms.o +obj-$(CONFIG_STACK_UNWIND) += unwind.o obj-$(CONFIG_PM) += power/ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o diff --git a/kernel/acct.c b/kernel/acct.c index b327f4d..f18e0b8 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -43,7 +43,6 @@ * a struct file opened for write. Fixed. 2/6/2000, AV. */ -#include #include #include #include @@ -75,7 +74,7 @@ #define ACCT_TIMEOUT (acct_parm[2]) /* f /* * External references and all of the globals. */ -static void do_acct_process(long, struct file *); +static void do_acct_process(struct file *); /* * This structure is used so that all the data protected by lock @@ -118,7 +117,7 @@ static int check_free_space(struct file spin_unlock(&acct_globals.lock); /* May block */ - if (vfs_statfs(file->f_dentry->d_inode->i_sb, &sbuf)) + if (vfs_statfs(file->f_dentry, &sbuf)) return res; suspend = sbuf.f_blocks * SUSPEND; resume = sbuf.f_blocks * RESUME; @@ -196,7 +195,7 @@ static void acct_file_reopen(struct file if (old_acct) { mnt_unpin(old_acct->f_vfsmnt); spin_unlock(&acct_globals.lock); - do_acct_process(0, old_acct); + do_acct_process(old_acct); filp_close(old_acct, NULL); spin_lock(&acct_globals.lock); } @@ -419,16 +418,15 @@ #endif /* * do_acct_process does all actual work. Caller holds the reference to file. */ -static void do_acct_process(long exitcode, struct file *file) +static void do_acct_process(struct file *file) { + struct pacct_struct *pacct = ¤t->signal->pacct; acct_t ac; mm_segment_t fs; - unsigned long vsize; unsigned long flim; u64 elapsed; u64 run_time; struct timespec uptime; - unsigned long jiffies; /* * First check to see if there is enough free_space to continue @@ -469,12 +467,6 @@ #if ACCT_VERSION==1 || ACCT_VERSION==2 #endif do_div(elapsed, AHZ); ac.ac_btime = xtime.tv_sec - elapsed; - jiffies = cputime_to_jiffies(cputime_add(current->utime, - current->signal->utime)); - ac.ac_utime = encode_comp_t(jiffies_to_AHZ(jiffies)); - jiffies = cputime_to_jiffies(cputime_add(current->stime, - current->signal->stime)); - ac.ac_stime = encode_comp_t(jiffies_to_AHZ(jiffies)); /* we really need to bite the bullet and change layout */ ac.ac_uid = current->uid; ac.ac_gid = current->gid; @@ -496,37 +488,18 @@ #endif old_encode_dev(tty_devnum(current->signal->tty)) : 0; read_unlock(&tasklist_lock); - ac.ac_flag = 0; - if (current->flags & PF_FORKNOEXEC) - ac.ac_flag |= AFORK; - if (current->flags & PF_SUPERPRIV) - ac.ac_flag |= ASU; - if (current->flags & PF_DUMPCORE) - ac.ac_flag |= ACORE; - if (current->flags & PF_SIGNALED) - ac.ac_flag |= AXSIG; - - vsize = 0; - if (current->mm) { - struct vm_area_struct *vma; - down_read(¤t->mm->mmap_sem); - vma = current->mm->mmap; - while (vma) { - vsize += vma->vm_end - vma->vm_start; - vma = vma->vm_next; - } - up_read(¤t->mm->mmap_sem); - } - vsize = vsize / 1024; - ac.ac_mem = encode_comp_t(vsize); + spin_lock(¤t->sighand->siglock); + ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime))); + ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime))); + ac.ac_flag = pacct->ac_flag; + ac.ac_mem = encode_comp_t(pacct->ac_mem); + ac.ac_minflt = encode_comp_t(pacct->ac_minflt); + ac.ac_majflt = encode_comp_t(pacct->ac_majflt); + ac.ac_exitcode = pacct->ac_exitcode; + spin_unlock(¤t->sighand->siglock); ac.ac_io = encode_comp_t(0 /* current->io_usage */); /* %% */ ac.ac_rw = encode_comp_t(ac.ac_io / 1024); - ac.ac_minflt = encode_comp_t(current->signal->min_flt + - current->min_flt); - ac.ac_majflt = encode_comp_t(current->signal->maj_flt + - current->maj_flt); ac.ac_swaps = encode_comp_t(0); - ac.ac_exitcode = exitcode; /* * Kernel segment override to datasegment and write it @@ -546,12 +519,64 @@ #endif } /** + * acct_init_pacct - initialize a new pacct_struct + * @pacct: per-process accounting info struct to initialize + */ +void acct_init_pacct(struct pacct_struct *pacct) +{ + memset(pacct, 0, sizeof(struct pacct_struct)); + pacct->ac_utime = pacct->ac_stime = cputime_zero; +} + +/** + * acct_collect - collect accounting information into pacct_struct + * @exitcode: task exit code + * @group_dead: not 0, if this thread is the last one in the process. + */ +void acct_collect(long exitcode, int group_dead) +{ + struct pacct_struct *pacct = ¤t->signal->pacct; + unsigned long vsize = 0; + + if (group_dead && current->mm) { + struct vm_area_struct *vma; + down_read(¤t->mm->mmap_sem); + vma = current->mm->mmap; + while (vma) { + vsize += vma->vm_end - vma->vm_start; + vma = vma->vm_next; + } + up_read(¤t->mm->mmap_sem); + } + + spin_lock_irq(¤t->sighand->siglock); + if (group_dead) + pacct->ac_mem = vsize / 1024; + if (thread_group_leader(current)) { + pacct->ac_exitcode = exitcode; + if (current->flags & PF_FORKNOEXEC) + pacct->ac_flag |= AFORK; + } + if (current->flags & PF_SUPERPRIV) + pacct->ac_flag |= ASU; + if (current->flags & PF_DUMPCORE) + pacct->ac_flag |= ACORE; + if (current->flags & PF_SIGNALED) + pacct->ac_flag |= AXSIG; + pacct->ac_utime = cputime_add(pacct->ac_utime, current->utime); + pacct->ac_stime = cputime_add(pacct->ac_stime, current->stime); + pacct->ac_minflt += current->min_flt; + pacct->ac_majflt += current->maj_flt; + spin_unlock_irq(¤t->sighand->siglock); +} + +/** * acct_process - now just a wrapper around do_acct_process * @exitcode: task exit code * * handles process accounting for an exiting task */ -void acct_process(long exitcode) +void acct_process(void) { struct file *file = NULL; @@ -570,7 +595,7 @@ void acct_process(long exitcode) get_file(file); spin_unlock(&acct_globals.lock); - do_acct_process(exitcode, file); + do_acct_process(file); fput(file); } @@ -599,9 +624,7 @@ void acct_update_integrals(struct task_s */ void acct_clear_integrals(struct task_struct *tsk) { - if (tsk) { - tsk->acct_stimexpd = 0; - tsk->acct_rss_mem1 = 0; - tsk->acct_vm_mem1 = 0; - } + tsk->acct_stimexpd = 0; + tsk->acct_rss_mem1 = 0; + tsk->acct_vm_mem1 = 0; } diff --git a/kernel/audit.c b/kernel/audit.c index df57b49..d417ca1 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -56,6 +56,7 @@ #include #include #include #include +#include #include "audit.h" @@ -89,6 +90,7 @@ static int audit_backlog_wait_overflow = /* The identity of the user shutting down the audit system. */ uid_t audit_sig_uid = -1; pid_t audit_sig_pid = -1; +u32 audit_sig_sid = 0; /* Records can be lost in several ways: 0) [suppressed in audit_alloc] @@ -102,6 +104,12 @@ static atomic_t audit_lost = ATOMIC_I /* The netlink socket. */ static struct sock *audit_sock; +/* Inotify handle. */ +struct inotify_handle *audit_ih; + +/* Hash for inode-based rules */ +struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; + /* The audit_freelist is a list of pre-allocated audit buffers (if more * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of * being placed on the freelist). */ @@ -114,10 +122,8 @@ static struct task_struct *kauditd_task; static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait); -/* The netlink socket is only to be read by 1 CPU, which lets us assume - * that list additions and deletions never happen simultaneously in - * auditsc.c */ -DEFINE_MUTEX(audit_netlink_mutex); +/* Serialize requests from userspace. */ +static DEFINE_MUTEX(audit_cmd_mutex); /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting * audit records. Since printk uses a 1024 byte buffer, this buffer @@ -250,7 +256,7 @@ static int audit_set_rate_limit(int limi "audit_rate_limit=%d old=%d by auid=%u", limit, old, loginuid); audit_rate_limit = limit; - return old; + return 0; } static int audit_set_backlog_limit(int limit, uid_t loginuid, u32 sid) @@ -273,7 +279,7 @@ static int audit_set_backlog_limit(int l "audit_backlog_limit=%d old=%d by auid=%u", limit, old, loginuid); audit_backlog_limit = limit; - return old; + return 0; } static int audit_set_enabled(int state, uid_t loginuid, u32 sid) @@ -299,7 +305,7 @@ static int audit_set_enabled(int state, "audit_enabled=%d old=%d by auid=%u", state, old, loginuid); audit_enabled = state; - return old; + return 0; } static int audit_set_failure(int state, uid_t loginuid, u32 sid) @@ -327,7 +333,7 @@ static int audit_set_failure(int state, "audit_failure=%d old=%d by auid=%u", state, old, loginuid); audit_failure = state; - return old; + return 0; } static int kauditd_thread(void *dummy) @@ -363,9 +369,52 @@ static int kauditd_thread(void *dummy) remove_wait_queue(&kauditd_wait, &wait); } } +} + +int audit_send_list(void *_dest) +{ + struct audit_netlink_list *dest = _dest; + int pid = dest->pid; + struct sk_buff *skb; + + /* wait for parent to finish and send an ACK */ + mutex_lock(&audit_cmd_mutex); + mutex_unlock(&audit_cmd_mutex); + + while ((skb = __skb_dequeue(&dest->q)) != NULL) + netlink_unicast(audit_sock, skb, pid, 0); + + kfree(dest); + return 0; } +struct sk_buff *audit_make_reply(int pid, int seq, int type, int done, + int multi, void *payload, int size) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + int len = NLMSG_SPACE(size); + void *data; + int flags = multi ? NLM_F_MULTI : 0; + int t = done ? NLMSG_DONE : type; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + + nlh = NLMSG_PUT(skb, pid, seq, t, size); + nlh->nlmsg_flags = flags; + data = NLMSG_DATA(nlh); + memcpy(data, payload, size); + return skb; + +nlmsg_failure: /* Used by NLMSG_PUT */ + if (skb) + kfree_skb(skb); + return NULL; +} + /** * audit_send_reply - send an audit reply message via netlink * @pid: process id to send reply to @@ -383,36 +432,20 @@ void audit_send_reply(int pid, int seq, void *payload, int size) { struct sk_buff *skb; - struct nlmsghdr *nlh; - int len = NLMSG_SPACE(size); - void *data; - int flags = multi ? NLM_F_MULTI : 0; - int t = done ? NLMSG_DONE : type; - - skb = alloc_skb(len, GFP_KERNEL); + skb = audit_make_reply(pid, seq, type, done, multi, payload, size); if (!skb) return; - - nlh = NLMSG_PUT(skb, pid, seq, t, size); - nlh->nlmsg_flags = flags; - data = NLMSG_DATA(nlh); - memcpy(data, payload, size); - /* Ignore failure. It'll only happen if the sender goes away, because our timeout is set to infinite. */ netlink_unicast(audit_sock, skb, pid, 0); return; - -nlmsg_failure: /* Used by NLMSG_PUT */ - if (skb) - kfree_skb(skb); } /* * Check for appropriate CAP_AUDIT_ capabilities on incoming audit * control messages. */ -static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) +static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) { int err = 0; @@ -426,13 +459,13 @@ static int audit_netlink_ok(kernel_cap_t case AUDIT_DEL: case AUDIT_DEL_RULE: case AUDIT_SIGNAL_INFO: - if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) + if (security_netlink_recv(skb, CAP_AUDIT_CONTROL)) err = -EPERM; break; case AUDIT_USER: case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: case AUDIT_FIRST_USER_MSG2...AUDIT_LAST_USER_MSG2: - if (!cap_raised(eff_cap, CAP_AUDIT_WRITE)) + if (security_netlink_recv(skb, CAP_AUDIT_WRITE)) err = -EPERM; break; default: /* bad msg */ @@ -451,9 +484,11 @@ static int audit_receive_msg(struct sk_b struct audit_buffer *ab; u16 msg_type = nlh->nlmsg_type; uid_t loginuid; /* loginuid of sender */ - struct audit_sig_info sig_data; + struct audit_sig_info *sig_data; + char *ctx; + u32 len; - err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); + err = audit_netlink_ok(skb, msg_type); if (err) return err; @@ -503,12 +538,9 @@ static int audit_receive_msg(struct sk_b if (status_get->mask & AUDIT_STATUS_PID) { int old = audit_pid; if (sid) { - char *ctx = NULL; - u32 len; - int rc; - if ((rc = selinux_ctxid_to_string( + if ((err = selinux_ctxid_to_string( sid, &ctx, &len))) - return rc; + return err; else audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, @@ -523,10 +555,10 @@ static int audit_receive_msg(struct sk_b audit_pid = status_get->pid; } if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) - audit_set_rate_limit(status_get->rate_limit, + err = audit_set_rate_limit(status_get->rate_limit, loginuid, sid); if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT) - audit_set_backlog_limit(status_get->backlog_limit, + err = audit_set_backlog_limit(status_get->backlog_limit, loginuid, sid); break; case AUDIT_USER: @@ -544,8 +576,6 @@ static int audit_receive_msg(struct sk_b "user pid=%d uid=%u auid=%u", pid, uid, loginuid); if (sid) { - char *ctx = NULL; - u32 len; if (selinux_ctxid_to_string( sid, &ctx, &len)) { audit_log_format(ab, @@ -584,10 +614,21 @@ static int audit_receive_msg(struct sk_b loginuid, sid); break; case AUDIT_SIGNAL_INFO: - sig_data.uid = audit_sig_uid; - sig_data.pid = audit_sig_pid; + err = selinux_ctxid_to_string(audit_sig_sid, &ctx, &len); + if (err) + return err; + sig_data = kmalloc(sizeof(*sig_data) + len, GFP_KERNEL); + if (!sig_data) { + kfree(ctx); + return -ENOMEM; + } + sig_data->uid = audit_sig_uid; + sig_data->pid = audit_sig_pid; + memcpy(sig_data->ctx, ctx, len); + kfree(ctx); audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO, - 0, 0, &sig_data, sizeof(sig_data)); + 0, 0, sig_data, sizeof(*sig_data) + len); + kfree(sig_data); break; default: err = -EINVAL; @@ -629,20 +670,30 @@ static void audit_receive(struct sock *s struct sk_buff *skb; unsigned int qlen; - mutex_lock(&audit_netlink_mutex); + mutex_lock(&audit_cmd_mutex); for (qlen = skb_queue_len(&sk->sk_receive_queue); qlen; qlen--) { skb = skb_dequeue(&sk->sk_receive_queue); audit_receive_skb(skb); kfree_skb(skb); } - mutex_unlock(&audit_netlink_mutex); + mutex_unlock(&audit_cmd_mutex); } +#ifdef CONFIG_AUDITSYSCALL +static const struct inotify_operations audit_inotify_ops = { + .handle_event = audit_handle_ievent, + .destroy_watch = audit_free_parent, +}; +#endif /* Initialize audit support at boot time. */ static int __init audit_init(void) { +#ifdef CONFIG_AUDITSYSCALL + int i; +#endif + printk(KERN_INFO "audit: initializing netlink socket (%s)\n", audit_default ? "enabled" : "disabled"); audit_sock = netlink_kernel_create(NETLINK_AUDIT, 0, audit_receive, @@ -661,6 +712,16 @@ static int __init audit_init(void) selinux_audit_set_callback(&selinux_audit_rule_update); audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); + +#ifdef CONFIG_AUDITSYSCALL + audit_ih = inotify_init(&audit_inotify_ops); + if (IS_ERR(audit_ih)) + audit_panic("cannot initialize inotify handle"); + + for (i = 0; i < AUDIT_INODE_BUCKETS; i++) + INIT_LIST_HEAD(&audit_inode_hash[i]); +#endif + return 0; } __initcall(audit_init); @@ -690,10 +751,12 @@ static void audit_buffer_free(struct aud kfree_skb(ab->skb); spin_lock_irqsave(&audit_freelist_lock, flags); - if (++audit_freelist_count > AUDIT_MAXFREE) + if (audit_freelist_count > AUDIT_MAXFREE) kfree(ab); - else + else { + audit_freelist_count++; list_add(&ab->list, &audit_freelist); + } spin_unlock_irqrestore(&audit_freelist_lock, flags); } @@ -755,7 +818,7 @@ err: */ unsigned int audit_serial(void) { - static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(serial_lock); static unsigned int serial = 0; unsigned long flags; @@ -988,28 +1051,76 @@ void audit_log_hex(struct audit_buffer * skb_put(skb, len << 1); /* new string is twice the old string */ } +/* + * Format a string of no more than slen characters into the audit buffer, + * enclosed in quote marks. + */ +static void audit_log_n_string(struct audit_buffer *ab, size_t slen, + const char *string) +{ + int avail, new_len; + unsigned char *ptr; + struct sk_buff *skb; + + BUG_ON(!ab->skb); + skb = ab->skb; + avail = skb_tailroom(skb); + new_len = slen + 3; /* enclosing quotes + null terminator */ + if (new_len > avail) { + avail = audit_expand(ab, new_len); + if (!avail) + return; + } + ptr = skb->tail; + *ptr++ = '"'; + memcpy(ptr, string, slen); + ptr += slen; + *ptr++ = '"'; + *ptr = 0; + skb_put(skb, slen + 2); /* don't include null terminator */ +} + /** - * audit_log_unstrustedstring - log a string that may contain random characters + * audit_log_n_unstrustedstring - log a string that may contain random characters * @ab: audit_buffer + * @len: lenth of string (not including trailing null) * @string: string to be logged * * This code will escape a string that is passed to it if the string * contains a control character, unprintable character, double quote mark, * or a space. Unescaped strings will start and end with a double quote mark. * Strings that are escaped are printed in hex (2 digits per char). + * + * The caller specifies the number of characters in the string to log, which may + * or may not be the entire string. */ -void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) +const char *audit_log_n_untrustedstring(struct audit_buffer *ab, size_t len, + const char *string) { const unsigned char *p = string; while (*p) { if (*p == '"' || *p < 0x21 || *p > 0x7f) { - audit_log_hex(ab, string, strlen(string)); - return; + audit_log_hex(ab, string, len); + return string + len + 1; } p++; } - audit_log_format(ab, "\"%s\"", string); + audit_log_n_string(ab, len, string); + return p + 1; +} + +/** + * audit_log_unstrustedstring - log a string that may contain random characters + * @ab: audit_buffer + * @string: string to be logged + * + * Same as audit_log_n_unstrustedstring(), except that strlen is used to + * determine string length. + */ +const char *audit_log_untrustedstring(struct audit_buffer *ab, const char *string) +{ + return audit_log_n_untrustedstring(ab, strlen(string), string); } /* This is a helper-function to print the escaped d_path */ diff --git a/kernel/audit.h b/kernel/audit.h index 6f73392..6aa33b8 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -19,9 +19,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include +#include /* 0 = no checking 1 = put_count checking @@ -53,6 +53,18 @@ enum audit_state { }; /* Rule lists */ +struct audit_parent; + +struct audit_watch { + atomic_t count; /* reference count */ + char *path; /* insertion path */ + dev_t dev; /* associated superblock device */ + unsigned long ino; /* associated inode number */ + struct audit_parent *parent; /* associated parent */ + struct list_head wlist; /* entry in parent->watches list */ + struct list_head rules; /* associated rules */ +}; + struct audit_field { u32 type; u32 val; @@ -69,7 +81,11 @@ struct audit_krule { u32 mask[AUDIT_BITMASK_SIZE]; u32 buflen; /* for data alloc on list rules */ u32 field_count; + char *filterkey; /* ties events to rules */ struct audit_field *fields; + struct audit_field *inode_f; /* quick access to an inode field */ + struct audit_watch *watch; /* associated watch */ + struct list_head rlist; /* entry in audit_watch.rules list */ }; struct audit_entry { @@ -78,15 +94,53 @@ struct audit_entry { struct audit_krule rule; }; - extern int audit_pid; -extern int audit_comparator(const u32 left, const u32 op, const u32 right); +#define AUDIT_INODE_BUCKETS 32 +extern struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; + +static inline int audit_hash_ino(u32 ino) +{ + return (ino & (AUDIT_INODE_BUCKETS-1)); +} + +extern int audit_comparator(const u32 left, const u32 op, const u32 right); +extern int audit_compare_dname_path(const char *dname, const char *path, + int *dirlen); +extern struct sk_buff * audit_make_reply(int pid, int seq, int type, + int done, int multi, + void *payload, int size); extern void audit_send_reply(int pid, int seq, int type, int done, int multi, void *payload, int size); extern void audit_log_lost(const char *message); extern void audit_panic(const char *message); -extern struct mutex audit_netlink_mutex; +struct audit_netlink_list { + int pid; + struct sk_buff_head q; +}; + +int audit_send_list(void *); + +struct inotify_watch; +extern void audit_free_parent(struct inotify_watch *); +extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32, + const char *, struct inode *); extern int selinux_audit_rule_update(void); + +#ifdef CONFIG_AUDITSYSCALL +extern void __audit_signal_info(int sig, struct task_struct *t); +static inline void audit_signal_info(int sig, struct task_struct *t) +{ + if (unlikely(audit_pid && t->tgid == audit_pid)) + __audit_signal_info(sig, t); +} +extern enum audit_state audit_filter_inodes(struct task_struct *, + struct audit_context *); +extern void audit_set_auditable(struct audit_context *); +#else +#define audit_signal_info(s,t) +#define audit_filter_inodes(t,c) AUDIT_DISABLED +#define audit_set_auditable(c) +#endif diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 7c13490..5b4e162 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -22,13 +22,59 @@ #include #include #include +#include +#include +#include #include +#include +#include #include #include "audit.h" -/* There are three lists of rules -- one to search at task creation - * time, one to search at syscall entry time, and another to search at - * syscall exit time. */ +/* + * Locking model: + * + * audit_filter_mutex: + * Synchronizes writes and blocking reads of audit's filterlist + * data. Rcu is used to traverse the filterlist and access + * contents of structs audit_entry, audit_watch and opaque + * selinux rules during filtering. If modified, these structures + * must be copied and replace their counterparts in the filterlist. + * An audit_parent struct is not accessed during filtering, so may + * be written directly provided audit_filter_mutex is held. + */ + +/* + * Reference counting: + * + * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED + * event. Each audit_watch holds a reference to its associated parent. + * + * audit_watch: if added to lists, lifetime is from audit_init_watch() to + * audit_remove_watch(). Additionally, an audit_watch may exist + * temporarily to assist in searching existing filter data. Each + * audit_krule holds a reference to its associated watch. + */ + +struct audit_parent { + struct list_head ilist; /* entry in inotify registration list */ + struct list_head watches; /* associated watches */ + struct inotify_watch wdata; /* inotify watch data */ + unsigned flags; /* status flags */ +}; + +/* + * audit_parent status flags: + * + * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to + * a filesystem event to ensure we're adding audit watches to a valid parent. + * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot + * receive them while we have nameidata, but must be used for IN_MOVE_SELF which + * we can receive while holding nameidata. + */ +#define AUDIT_PARENT_INVALID 0x001 + +/* Audit filter lists, defined in */ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { LIST_HEAD_INIT(audit_filter_list[0]), LIST_HEAD_INIT(audit_filter_list[1]), @@ -41,9 +87,53 @@ #error Fix audit_filter_list initialiser #endif }; +static DEFINE_MUTEX(audit_filter_mutex); + +/* Inotify handle */ +extern struct inotify_handle *audit_ih; + +/* Inotify events we care about. */ +#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF + +void audit_free_parent(struct inotify_watch *i_watch) +{ + struct audit_parent *parent; + + parent = container_of(i_watch, struct audit_parent, wdata); + WARN_ON(!list_empty(&parent->watches)); + kfree(parent); +} + +static inline void audit_get_watch(struct audit_watch *watch) +{ + atomic_inc(&watch->count); +} + +static void audit_put_watch(struct audit_watch *watch) +{ + if (atomic_dec_and_test(&watch->count)) { + WARN_ON(watch->parent); + WARN_ON(!list_empty(&watch->rules)); + kfree(watch->path); + kfree(watch); + } +} + +static void audit_remove_watch(struct audit_watch *watch) +{ + list_del(&watch->wlist); + put_inotify_watch(&watch->parent->wdata); + watch->parent = NULL; + audit_put_watch(watch); /* match initial get */ +} + static inline void audit_free_rule(struct audit_entry *e) { int i; + + /* some rules don't have associated watches */ + if (e->rule.watch) + audit_put_watch(e->rule.watch); if (e->rule.fields) for (i = 0; i < e->rule.field_count; i++) { struct audit_field *f = &e->rule.fields[i]; @@ -51,6 +141,7 @@ static inline void audit_free_rule(struc selinux_audit_rule_free(f->se_rule); } kfree(e->rule.fields); + kfree(e->rule.filterkey); kfree(e); } @@ -60,6 +151,50 @@ static inline void audit_free_rule_rcu(s audit_free_rule(e); } +/* Initialize a parent watch entry. */ +static struct audit_parent *audit_init_parent(struct nameidata *ndp) +{ + struct audit_parent *parent; + s32 wd; + + parent = kzalloc(sizeof(*parent), GFP_KERNEL); + if (unlikely(!parent)) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&parent->watches); + parent->flags = 0; + + inotify_init_watch(&parent->wdata); + /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */ + get_inotify_watch(&parent->wdata); + wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode, + AUDIT_IN_WATCH); + if (wd < 0) { + audit_free_parent(&parent->wdata); + return ERR_PTR(wd); + } + + return parent; +} + +/* Initialize a watch entry. */ +static struct audit_watch *audit_init_watch(char *path) +{ + struct audit_watch *watch; + + watch = kzalloc(sizeof(*watch), GFP_KERNEL); + if (unlikely(!watch)) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&watch->rules); + atomic_set(&watch->count, 1); + watch->path = path; + watch->dev = (dev_t)-1; + watch->ino = (unsigned long)-1; + + return watch; +} + /* Initialize an audit filterlist entry. */ static inline struct audit_entry *audit_init_entry(u32 field_count) { @@ -107,6 +242,66 @@ static char *audit_unpack_string(void ** return str; } +/* Translate an inode field to kernel respresentation. */ +static inline int audit_to_inode(struct audit_krule *krule, + struct audit_field *f) +{ + if (krule->listnr != AUDIT_FILTER_EXIT || + krule->watch || krule->inode_f) + return -EINVAL; + + krule->inode_f = f; + return 0; +} + +/* Translate a watch string to kernel respresentation. */ +static int audit_to_watch(struct audit_krule *krule, char *path, int len, + u32 op) +{ + struct audit_watch *watch; + + if (!audit_ih) + return -EOPNOTSUPP; + + if (path[0] != '/' || path[len-1] == '/' || + krule->listnr != AUDIT_FILTER_EXIT || + op & ~AUDIT_EQUAL || + krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */ + return -EINVAL; + + watch = audit_init_watch(path); + if (unlikely(IS_ERR(watch))) + return PTR_ERR(watch); + + audit_get_watch(watch); + krule->watch = watch; + + return 0; +} + +static __u32 *classes[AUDIT_SYSCALL_CLASSES]; + +int __init audit_register_class(int class, unsigned *list) +{ + __u32 *p = kzalloc(AUDIT_BITMASK_SIZE * sizeof(__u32), GFP_KERNEL); + if (!p) + return -ENOMEM; + while (*list != ~0U) { + unsigned n = *list++; + if (n >= AUDIT_BITMASK_SIZE * 32 - AUDIT_SYSCALL_CLASSES) { + kfree(p); + return -EINVAL; + } + p[AUDIT_WORD(n)] |= AUDIT_BIT(n); + } + if (class >= AUDIT_SYSCALL_CLASSES || classes[class]) { + kfree(p); + return -EINVAL; + } + classes[class] = p; + return 0; +} + /* Common user-space to kernel rule translation. */ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule) { @@ -128,8 +323,11 @@ #ifdef CONFIG_AUDITSYSCALL #endif ; } - if (rule->action != AUDIT_NEVER && rule->action != AUDIT_POSSIBLE && - rule->action != AUDIT_ALWAYS) + if (unlikely(rule->action == AUDIT_POSSIBLE)) { + printk(KERN_ERR "AUDIT_POSSIBLE is deprecated\n"); + goto exit_err; + } + if (rule->action != AUDIT_NEVER && rule->action != AUDIT_ALWAYS) goto exit_err; if (rule->field_count > AUDIT_MAX_FIELDS) goto exit_err; @@ -147,6 +345,22 @@ #endif for (i = 0; i < AUDIT_BITMASK_SIZE; i++) entry->rule.mask[i] = rule->mask[i]; + for (i = 0; i < AUDIT_SYSCALL_CLASSES; i++) { + int bit = AUDIT_BITMASK_SIZE * 32 - i - 1; + __u32 *p = &entry->rule.mask[AUDIT_WORD(bit)]; + __u32 *class; + + if (!(*p & AUDIT_BIT(bit))) + continue; + *p &= ~AUDIT_BIT(bit); + class = classes[i]; + if (class) { + int j; + for (j = 0; j < AUDIT_BITMASK_SIZE; j++) + entry->rule.mask[j] |= class[j]; + } + } + return entry; exit_err: @@ -158,6 +372,7 @@ exit_err: static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule) { struct audit_entry *entry; + struct audit_field *f; int err = 0; int i; @@ -172,14 +387,37 @@ static struct audit_entry *audit_rule_to f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); f->val = rule->values[i]; - if (f->type & AUDIT_UNUSED_BITS || - f->type == AUDIT_SE_USER || - f->type == AUDIT_SE_ROLE || - f->type == AUDIT_SE_TYPE || - f->type == AUDIT_SE_SEN || - f->type == AUDIT_SE_CLR) { - err = -EINVAL; + err = -EINVAL; + switch(f->type) { + default: goto exit_free; + case AUDIT_PID: + case AUDIT_UID: + case AUDIT_EUID: + case AUDIT_SUID: + case AUDIT_FSUID: + case AUDIT_GID: + case AUDIT_EGID: + case AUDIT_SGID: + case AUDIT_FSGID: + case AUDIT_LOGINUID: + case AUDIT_PERS: + case AUDIT_ARCH: + case AUDIT_MSGTYPE: + case AUDIT_DEVMAJOR: + case AUDIT_DEVMINOR: + case AUDIT_EXIT: + case AUDIT_SUCCESS: + case AUDIT_ARG0: + case AUDIT_ARG1: + case AUDIT_ARG2: + case AUDIT_ARG3: + break; + case AUDIT_INODE: + err = audit_to_inode(&entry->rule, f); + if (err) + goto exit_free; + break; } entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; @@ -196,6 +434,18 @@ static struct audit_entry *audit_rule_to } } + f = entry->rule.inode_f; + if (f) { + switch(f->op) { + case AUDIT_NOT_EQUAL: + entry->rule.inode_f = NULL; + case AUDIT_EQUAL: + break; + default: + goto exit_free; + } + } + exit_nofree: return entry; @@ -210,6 +460,7 @@ static struct audit_entry *audit_data_to { int err = 0; struct audit_entry *entry; + struct audit_field *f; void *bufp; size_t remain = datasz - sizeof(struct audit_rule_data); int i; @@ -235,11 +486,39 @@ static struct audit_entry *audit_data_to f->se_str = NULL; f->se_rule = NULL; switch(f->type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_PID: + case AUDIT_UID: + case AUDIT_EUID: + case AUDIT_SUID: + case AUDIT_FSUID: + case AUDIT_GID: + case AUDIT_EGID: + case AUDIT_SGID: + case AUDIT_FSGID: + case AUDIT_LOGINUID: + case AUDIT_PERS: + case AUDIT_ARCH: + case AUDIT_MSGTYPE: + case AUDIT_PPID: + case AUDIT_DEVMAJOR: + case AUDIT_DEVMINOR: + case AUDIT_EXIT: + case AUDIT_SUCCESS: + case AUDIT_ARG0: + case AUDIT_ARG1: + case AUDIT_ARG2: + case AUDIT_ARG3: + break; + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: str = audit_unpack_string(&bufp, &remain, f->val); if (IS_ERR(str)) goto exit_free; @@ -260,6 +539,47 @@ static struct audit_entry *audit_data_to } else f->se_str = str; break; + case AUDIT_WATCH: + str = audit_unpack_string(&bufp, &remain, f->val); + if (IS_ERR(str)) + goto exit_free; + entry->rule.buflen += f->val; + + err = audit_to_watch(&entry->rule, str, f->val, f->op); + if (err) { + kfree(str); + goto exit_free; + } + break; + case AUDIT_INODE: + err = audit_to_inode(&entry->rule, f); + if (err) + goto exit_free; + break; + case AUDIT_FILTERKEY: + err = -EINVAL; + if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN) + goto exit_free; + str = audit_unpack_string(&bufp, &remain, f->val); + if (IS_ERR(str)) + goto exit_free; + entry->rule.buflen += f->val; + entry->rule.filterkey = str; + break; + default: + goto exit_free; + } + } + + f = entry->rule.inode_f; + if (f) { + switch(f->op) { + case AUDIT_NOT_EQUAL: + entry->rule.inode_f = NULL; + case AUDIT_EQUAL: + break; + default: + goto exit_free; } } @@ -291,7 +611,7 @@ static struct audit_rule *audit_krule_to rule = kmalloc(sizeof(*rule), GFP_KERNEL); if (unlikely(!rule)) - return ERR_PTR(-ENOMEM); + return NULL; memset(rule, 0, sizeof(*rule)); rule->flags = krule->flags | krule->listnr; @@ -322,7 +642,7 @@ static struct audit_rule_data *audit_kru data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL); if (unlikely(!data)) - return ERR_PTR(-ENOMEM); + return NULL; memset(data, 0, sizeof(*data)); data->flags = krule->flags | krule->listnr; @@ -335,14 +655,27 @@ static struct audit_rule_data *audit_kru data->fields[i] = f->type; data->fieldflags[i] = f->op; switch(f->type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: data->buflen += data->values[i] = audit_pack_string(&bufp, f->se_str); break; + case AUDIT_WATCH: + data->buflen += data->values[i] = + audit_pack_string(&bufp, krule->watch->path); + break; + case AUDIT_FILTERKEY: + data->buflen += data->values[i] = + audit_pack_string(&bufp, krule->filterkey); + break; default: data->values[i] = f->val; } @@ -370,14 +703,28 @@ static int audit_compare_rule(struct aud return 1; switch(a->fields[i].type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: if (strcmp(a->fields[i].se_str, b->fields[i].se_str)) return 1; break; + case AUDIT_WATCH: + if (strcmp(a->watch->path, b->watch->path)) + return 1; + break; + case AUDIT_FILTERKEY: + /* both filterkeys exist based on above type compare */ + if (strcmp(a->filterkey, b->filterkey)) + return 1; + break; default: if (a->fields[i].val != b->fields[i].val) return 1; @@ -391,6 +738,32 @@ static int audit_compare_rule(struct aud return 0; } +/* Duplicate the given audit watch. The new watch's rules list is initialized + * to an empty list and wlist is undefined. */ +static struct audit_watch *audit_dupe_watch(struct audit_watch *old) +{ + char *path; + struct audit_watch *new; + + path = kstrdup(old->path, GFP_KERNEL); + if (unlikely(!path)) + return ERR_PTR(-ENOMEM); + + new = audit_init_watch(path); + if (unlikely(IS_ERR(new))) { + kfree(path); + goto out; + } + + new->dev = old->dev; + new->ino = old->ino; + get_inotify_watch(&old->parent->wdata); + new->parent = old->parent; + +out: + return new; +} + /* Duplicate selinux field information. The se_rule is opaque, so must be * re-initialized. */ static inline int audit_dupe_selinux_field(struct audit_field *df, @@ -422,12 +795,16 @@ static inline int audit_dupe_selinux_fie /* Duplicate an audit rule. This will be a deep copy with the exception * of the watch - that pointer is carried over. The selinux specific fields * will be updated in the copy. The point is to be able to replace the old - * rule with the new rule in the filterlist, then free the old rule. */ -static struct audit_entry *audit_dupe_rule(struct audit_krule *old) + * rule with the new rule in the filterlist, then free the old rule. + * The rlist element is undefined; list manipulations are handled apart from + * the initial copy. */ +static struct audit_entry *audit_dupe_rule(struct audit_krule *old, + struct audit_watch *watch) { u32 fcount = old->field_count; struct audit_entry *entry; struct audit_krule *new; + char *fk; int i, err = 0; entry = audit_init_entry(fcount); @@ -442,6 +819,8 @@ static struct audit_entry *audit_dupe_ru for (i = 0; i < AUDIT_BITMASK_SIZE; i++) new->mask[i] = old->mask[i]; new->buflen = old->buflen; + new->inode_f = old->inode_f; + new->watch = NULL; new->field_count = old->field_count; memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); @@ -449,13 +828,25 @@ static struct audit_entry *audit_dupe_ru * the originals will all be freed when the old rule is freed. */ for (i = 0; i < fcount; i++) { switch (new->fields[i].type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: err = audit_dupe_selinux_field(&new->fields[i], &old->fields[i]); + break; + case AUDIT_FILTERKEY: + fk = kstrdup(old->filterkey, GFP_KERNEL); + if (unlikely(!fk)) + err = -ENOMEM; + else + new->filterkey = fk; } if (err) { audit_free_rule(entry); @@ -463,68 +854,409 @@ static struct audit_entry *audit_dupe_ru } } + if (watch) { + audit_get_watch(watch); + new->watch = watch; + } + return entry; } -/* Add rule to given filterlist if not a duplicate. Protected by - * audit_netlink_mutex. */ +/* Update inode info in audit rules based on filesystem event. */ +static void audit_update_watch(struct audit_parent *parent, + const char *dname, dev_t dev, + unsigned long ino, unsigned invalidating) +{ + struct audit_watch *owatch, *nwatch, *nextw; + struct audit_krule *r, *nextr; + struct audit_entry *oentry, *nentry; + struct audit_buffer *ab; + + mutex_lock(&audit_filter_mutex); + list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) { + if (audit_compare_dname_path(dname, owatch->path, NULL)) + continue; + + /* If the update involves invalidating rules, do the inode-based + * filtering now, so we don't omit records. */ + if (invalidating && + audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT) + audit_set_auditable(current->audit_context); + + nwatch = audit_dupe_watch(owatch); + if (unlikely(IS_ERR(nwatch))) { + mutex_unlock(&audit_filter_mutex); + audit_panic("error updating watch, skipping"); + return; + } + nwatch->dev = dev; + nwatch->ino = ino; + + list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) { + + oentry = container_of(r, struct audit_entry, rule); + list_del(&oentry->rule.rlist); + list_del_rcu(&oentry->list); + + nentry = audit_dupe_rule(&oentry->rule, nwatch); + if (unlikely(IS_ERR(nentry))) + audit_panic("error updating watch, removing"); + else { + int h = audit_hash_ino((u32)ino); + list_add(&nentry->rule.rlist, &nwatch->rules); + list_add_rcu(&nentry->list, &audit_inode_hash[h]); + } + + call_rcu(&oentry->rcu, audit_free_rule_rcu); + } + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + audit_log_format(ab, "audit updated rules specifying watch="); + audit_log_untrustedstring(ab, owatch->path); + audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino); + audit_log_end(ab); + + audit_remove_watch(owatch); + goto add_watch_to_parent; /* event applies to a single watch */ + } + mutex_unlock(&audit_filter_mutex); + return; + +add_watch_to_parent: + list_add(&nwatch->wlist, &parent->watches); + mutex_unlock(&audit_filter_mutex); + return; +} + +/* Remove all watches & rules associated with a parent that is going away. */ +static void audit_remove_parent_watches(struct audit_parent *parent) +{ + struct audit_watch *w, *nextw; + struct audit_krule *r, *nextr; + struct audit_entry *e; + + mutex_lock(&audit_filter_mutex); + parent->flags |= AUDIT_PARENT_INVALID; + list_for_each_entry_safe(w, nextw, &parent->watches, wlist) { + list_for_each_entry_safe(r, nextr, &w->rules, rlist) { + e = container_of(r, struct audit_entry, rule); + list_del(&r->rlist); + list_del_rcu(&e->list); + call_rcu(&e->rcu, audit_free_rule_rcu); + + audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, + "audit implicitly removed rule from list=%d\n", + AUDIT_FILTER_EXIT); + } + audit_remove_watch(w); + } + mutex_unlock(&audit_filter_mutex); +} + +/* Unregister inotify watches for parents on in_list. + * Generates an IN_IGNORED event. */ +static void audit_inotify_unregister(struct list_head *in_list) +{ + struct audit_parent *p, *n; + + list_for_each_entry_safe(p, n, in_list, ilist) { + list_del(&p->ilist); + inotify_rm_watch(audit_ih, &p->wdata); + /* the put matching the get in audit_do_del_rule() */ + put_inotify_watch(&p->wdata); + } +} + +/* Find an existing audit rule. + * Caller must hold audit_filter_mutex to prevent stale rule data. */ +static struct audit_entry *audit_find_rule(struct audit_entry *entry, + struct list_head *list) +{ + struct audit_entry *e, *found = NULL; + int h; + + if (entry->rule.watch) { + /* we don't know the inode number, so must walk entire hash */ + for (h = 0; h < AUDIT_INODE_BUCKETS; h++) { + list = &audit_inode_hash[h]; + list_for_each_entry(e, list, list) + if (!audit_compare_rule(&entry->rule, &e->rule)) { + found = e; + goto out; + } + } + goto out; + } + + list_for_each_entry(e, list, list) + if (!audit_compare_rule(&entry->rule, &e->rule)) { + found = e; + goto out; + } + +out: + return found; +} + +/* Get path information necessary for adding watches. */ +static int audit_get_nd(char *path, struct nameidata **ndp, + struct nameidata **ndw) +{ + struct nameidata *ndparent, *ndwatch; + int err; + + ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL); + if (unlikely(!ndparent)) + return -ENOMEM; + + ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL); + if (unlikely(!ndwatch)) { + kfree(ndparent); + return -ENOMEM; + } + + err = path_lookup(path, LOOKUP_PARENT, ndparent); + if (err) { + kfree(ndparent); + kfree(ndwatch); + return err; + } + + err = path_lookup(path, 0, ndwatch); + if (err) { + kfree(ndwatch); + ndwatch = NULL; + } + + *ndp = ndparent; + *ndw = ndwatch; + + return 0; +} + +/* Release resources used for watch path information. */ +static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw) +{ + if (ndp) { + path_release(ndp); + kfree(ndp); + } + if (ndw) { + path_release(ndw); + kfree(ndw); + } +} + +/* Associate the given rule with an existing parent inotify_watch. + * Caller must hold audit_filter_mutex. */ +static void audit_add_to_parent(struct audit_krule *krule, + struct audit_parent *parent) +{ + struct audit_watch *w, *watch = krule->watch; + int watch_found = 0; + + list_for_each_entry(w, &parent->watches, wlist) { + if (strcmp(watch->path, w->path)) + continue; + + watch_found = 1; + + /* put krule's and initial refs to temporary watch */ + audit_put_watch(watch); + audit_put_watch(watch); + + audit_get_watch(w); + krule->watch = watch = w; + break; + } + + if (!watch_found) { + get_inotify_watch(&parent->wdata); + watch->parent = parent; + + list_add(&watch->wlist, &parent->watches); + } + list_add(&krule->rlist, &watch->rules); +} + +/* Find a matching watch entry, or add this one. + * Caller must hold audit_filter_mutex. */ +static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp, + struct nameidata *ndw) +{ + struct audit_watch *watch = krule->watch; + struct inotify_watch *i_watch; + struct audit_parent *parent; + int ret = 0; + + /* update watch filter fields */ + if (ndw) { + watch->dev = ndw->dentry->d_inode->i_sb->s_dev; + watch->ino = ndw->dentry->d_inode->i_ino; + } + + /* The audit_filter_mutex must not be held during inotify calls because + * we hold it during inotify event callback processing. If an existing + * inotify watch is found, inotify_find_watch() grabs a reference before + * returning. + */ + mutex_unlock(&audit_filter_mutex); + + if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &i_watch) < 0) { + parent = audit_init_parent(ndp); + if (IS_ERR(parent)) { + /* caller expects mutex locked */ + mutex_lock(&audit_filter_mutex); + return PTR_ERR(parent); + } + } else + parent = container_of(i_watch, struct audit_parent, wdata); + + mutex_lock(&audit_filter_mutex); + + /* parent was moved before we took audit_filter_mutex */ + if (parent->flags & AUDIT_PARENT_INVALID) + ret = -ENOENT; + else + audit_add_to_parent(krule, parent); + + /* match get in audit_init_parent or inotify_find_watch */ + put_inotify_watch(&parent->wdata); + return ret; +} + +/* Add rule to given filterlist if not a duplicate. */ static inline int audit_add_rule(struct audit_entry *entry, - struct list_head *list) + struct list_head *list) { struct audit_entry *e; + struct audit_field *inode_f = entry->rule.inode_f; + struct audit_watch *watch = entry->rule.watch; + struct nameidata *ndp, *ndw; + int h, err, putnd_needed = 0; + + if (inode_f) { + h = audit_hash_ino(inode_f->val); + list = &audit_inode_hash[h]; + } - /* Do not use the _rcu iterator here, since this is the only - * addition routine. */ - list_for_each_entry(e, list, list) { - if (!audit_compare_rule(&entry->rule, &e->rule)) - return -EEXIST; + mutex_lock(&audit_filter_mutex); + e = audit_find_rule(entry, list); + mutex_unlock(&audit_filter_mutex); + if (e) { + err = -EEXIST; + goto error; + } + + /* Avoid calling path_lookup under audit_filter_mutex. */ + if (watch) { + err = audit_get_nd(watch->path, &ndp, &ndw); + if (err) + goto error; + putnd_needed = 1; + } + + mutex_lock(&audit_filter_mutex); + if (watch) { + /* audit_filter_mutex is dropped and re-taken during this call */ + err = audit_add_watch(&entry->rule, ndp, ndw); + if (err) { + mutex_unlock(&audit_filter_mutex); + goto error; + } + h = audit_hash_ino((u32)watch->ino); + list = &audit_inode_hash[h]; } if (entry->rule.flags & AUDIT_FILTER_PREPEND) { list_add_rcu(&entry->list, list); + entry->rule.flags &= ~AUDIT_FILTER_PREPEND; } else { list_add_tail_rcu(&entry->list, list); } + mutex_unlock(&audit_filter_mutex); - return 0; + if (putnd_needed) + audit_put_nd(ndp, ndw); + + return 0; + +error: + if (putnd_needed) + audit_put_nd(ndp, ndw); + if (watch) + audit_put_watch(watch); /* tmp watch, matches initial get */ + return err; } -/* Remove an existing rule from filterlist. Protected by - * audit_netlink_mutex. */ +/* Remove an existing rule from filterlist. */ static inline int audit_del_rule(struct audit_entry *entry, struct list_head *list) { struct audit_entry *e; + struct audit_field *inode_f = entry->rule.inode_f; + struct audit_watch *watch, *tmp_watch = entry->rule.watch; + LIST_HEAD(inotify_list); + int h, ret = 0; + + if (inode_f) { + h = audit_hash_ino(inode_f->val); + list = &audit_inode_hash[h]; + } - /* Do not use the _rcu iterator here, since this is the only - * deletion routine. */ - list_for_each_entry(e, list, list) { - if (!audit_compare_rule(&entry->rule, &e->rule)) { - list_del_rcu(&e->list); - call_rcu(&e->rcu, audit_free_rule_rcu); - return 0; + mutex_lock(&audit_filter_mutex); + e = audit_find_rule(entry, list); + if (!e) { + mutex_unlock(&audit_filter_mutex); + ret = -ENOENT; + goto out; + } + + watch = e->rule.watch; + if (watch) { + struct audit_parent *parent = watch->parent; + + list_del(&e->rule.rlist); + + if (list_empty(&watch->rules)) { + audit_remove_watch(watch); + + if (list_empty(&parent->watches)) { + /* Put parent on the inotify un-registration + * list. Grab a reference before releasing + * audit_filter_mutex, to be released in + * audit_inotify_unregister(). */ + list_add(&parent->ilist, &inotify_list); + get_inotify_watch(&parent->wdata); + } } } - return -ENOENT; /* No matching rule */ + + list_del_rcu(&e->list); + call_rcu(&e->rcu, audit_free_rule_rcu); + + mutex_unlock(&audit_filter_mutex); + + if (!list_empty(&inotify_list)) + audit_inotify_unregister(&inotify_list); + +out: + if (tmp_watch) + audit_put_watch(tmp_watch); /* match initial get */ + + return ret; } /* List rules using struct audit_rule. Exists for backward * compatibility with userspace. */ -static int audit_list(void *_dest) +static void audit_list(int pid, int seq, struct sk_buff_head *q) { - int pid, seq; - int *dest = _dest; + struct sk_buff *skb; struct audit_entry *entry; int i; - pid = dest[0]; - seq = dest[1]; - kfree(dest); - - mutex_lock(&audit_netlink_mutex); - - /* The *_rcu iterators not needed here because we are - always called with audit_netlink_mutex held. */ + /* This is a blocking read, so use audit_filter_mutex instead of rcu + * iterator to sync with list writers. */ for (i=0; irule); if (unlikely(!rule)) break; - audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, + skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, rule, sizeof(*rule)); + if (skb) + skb_queue_tail(q, skb); kfree(rule); } } - audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); - - mutex_unlock(&audit_netlink_mutex); - return 0; + for (i = 0; i < AUDIT_INODE_BUCKETS; i++) { + list_for_each_entry(entry, &audit_inode_hash[i], list) { + struct audit_rule *rule; + + rule = audit_krule_to_rule(&entry->rule); + if (unlikely(!rule)) + break; + skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1, + rule, sizeof(*rule)); + if (skb) + skb_queue_tail(q, skb); + kfree(rule); + } + } + skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); + if (skb) + skb_queue_tail(q, skb); } /* List rules using struct audit_rule_data. */ -static int audit_list_rules(void *_dest) +static void audit_list_rules(int pid, int seq, struct sk_buff_head *q) { - int pid, seq; - int *dest = _dest; + struct sk_buff *skb; struct audit_entry *e; int i; - pid = dest[0]; - seq = dest[1]; - kfree(dest); - - mutex_lock(&audit_netlink_mutex); - - /* The *_rcu iterators not needed here because we are - always called with audit_netlink_mutex held. */ + /* This is a blocking read, so use audit_filter_mutex instead of rcu + * iterator to sync with list writers. */ for (i=0; irule); if (unlikely(!data)) break; - audit_send_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, - data, sizeof(*data)); + skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, + data, sizeof(*data) + data->buflen); + if (skb) + skb_queue_tail(q, skb); kfree(data); } } - audit_send_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); + for (i=0; i< AUDIT_INODE_BUCKETS; i++) { + list_for_each_entry(e, &audit_inode_hash[i], list) { + struct audit_rule_data *data; - mutex_unlock(&audit_netlink_mutex); - return 0; + data = audit_krule_to_data(&e->rule); + if (unlikely(!data)) + break; + skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1, + data, sizeof(*data) + data->buflen); + if (skb) + skb_queue_tail(q, skb); + kfree(data); + } + } + skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0); + if (skb) + skb_queue_tail(q, skb); +} + +/* Log rule additions and removals */ +static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action, + struct audit_krule *rule, int res) +{ + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); + if (!ab) + return; + audit_log_format(ab, "auid=%u", loginuid); + if (sid) { + char *ctx = NULL; + u32 len; + if (selinux_ctxid_to_string(sid, &ctx, &len)) + audit_log_format(ab, " ssid=%u", sid); + else + audit_log_format(ab, " subj=%s", ctx); + kfree(ctx); + } + audit_log_format(ab, " %s rule key=", action); + if (rule->filterkey) + audit_log_untrustedstring(ab, rule->filterkey); + else + audit_log_format(ab, "(null)"); + audit_log_format(ab, " list=%d res=%d", rule->listnr, res); + audit_log_end(ab); } /** @@ -592,7 +1375,7 @@ int audit_receive_filter(int type, int p size_t datasz, uid_t loginuid, u32 sid) { struct task_struct *tsk; - int *dest; + struct audit_netlink_list *dest; int err = 0; struct audit_entry *entry; @@ -605,18 +1388,22 @@ int audit_receive_filter(int type, int p * happen if we're actually running in the context of auditctl * trying to _send_ the stuff */ - dest = kmalloc(2 * sizeof(int), GFP_KERNEL); + dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL); if (!dest) return -ENOMEM; - dest[0] = pid; - dest[1] = seq; + dest->pid = pid; + skb_queue_head_init(&dest->q); + mutex_lock(&audit_filter_mutex); if (type == AUDIT_LIST) - tsk = kthread_run(audit_list, dest, "audit_list"); + audit_list(pid, seq, &dest->q); else - tsk = kthread_run(audit_list_rules, dest, - "audit_list_rules"); + audit_list_rules(pid, seq, &dest->q); + mutex_unlock(&audit_filter_mutex); + + tsk = kthread_run(audit_send_list, dest, "audit_send_list"); if (IS_ERR(tsk)) { + skb_queue_purge(&dest->q); kfree(dest); err = PTR_ERR(tsk); } @@ -632,23 +1419,7 @@ int audit_receive_filter(int type, int p err = audit_add_rule(entry, &audit_filter_list[entry->rule.listnr]); - if (sid) { - char *ctx = NULL; - u32 len; - if (selinux_ctxid_to_string(sid, &ctx, &len)) { - /* Maybe call audit_panic? */ - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u ssid=%u add rule to list=%d res=%d", - loginuid, sid, entry->rule.listnr, !err); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u subj=%s add rule to list=%d res=%d", - loginuid, ctx, entry->rule.listnr, !err); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u add rule to list=%d res=%d", - loginuid, entry->rule.listnr, !err); + audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err); if (err) audit_free_rule(entry); @@ -664,24 +1435,8 @@ int audit_receive_filter(int type, int p err = audit_del_rule(entry, &audit_filter_list[entry->rule.listnr]); - - if (sid) { - char *ctx = NULL; - u32 len; - if (selinux_ctxid_to_string(sid, &ctx, &len)) { - /* Maybe call audit_panic? */ - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u ssid=%u remove rule from list=%d res=%d", - loginuid, sid, entry->rule.listnr, !err); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u subj=%s remove rule from list=%d res=%d", - loginuid, ctx, entry->rule.listnr, !err); - kfree(ctx); - } else - audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, - "auid=%u remove rule from list=%d res=%d", - loginuid, entry->rule.listnr, !err); + audit_log_rule_change(loginuid, sid, "remove", &entry->rule, + !err); audit_free_rule(entry); break; @@ -712,7 +1467,43 @@ int audit_comparator(const u32 left, con return 0; } +/* Compare given dentry name with last component in given path, + * return of 0 indicates a match. */ +int audit_compare_dname_path(const char *dname, const char *path, + int *dirlen) +{ + int dlen, plen; + const char *p; + + if (!dname || !path) + return 1; + dlen = strlen(dname); + plen = strlen(path); + if (plen < dlen) + return 1; + + /* disregard trailing slashes */ + p = path + plen - 1; + while ((*p == '/') && (p > path)) + p--; + + /* find last path component */ + p = p - dlen + 1; + if (p < path) + return 1; + else if (p > path) { + if (*--p != '/') + return 1; + else + p++; + } + + /* return length of path's directory component */ + if (dirlen) + *dirlen = p - path; + return strncmp(p, dname, dlen); +} static int audit_filter_user_rules(struct netlink_skb_parms *cb, struct audit_krule *rule, @@ -744,7 +1535,6 @@ static int audit_filter_user_rules(struc } switch (rule->action) { case AUDIT_NEVER: *state = AUDIT_DISABLED; break; - case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break; case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; } return 1; @@ -806,11 +1596,16 @@ static inline int audit_rule_has_selinux for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; switch (f->type) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: return 1; } } @@ -826,32 +1621,65 @@ static inline int audit_rule_has_selinux int selinux_audit_rule_update(void) { struct audit_entry *entry, *n, *nentry; + struct audit_watch *watch; int i, err = 0; - /* audit_netlink_mutex synchronizes the writers */ - mutex_lock(&audit_netlink_mutex); + /* audit_filter_mutex synchronizes the writers */ + mutex_lock(&audit_filter_mutex); for (i = 0; i < AUDIT_NR_FILTERS; i++) { list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) { if (!audit_rule_has_selinux(&entry->rule)) continue; - nentry = audit_dupe_rule(&entry->rule); + watch = entry->rule.watch; + nentry = audit_dupe_rule(&entry->rule, watch); if (unlikely(IS_ERR(nentry))) { /* save the first error encountered for the * return value */ if (!err) err = PTR_ERR(nentry); audit_panic("error updating selinux filters"); + if (watch) + list_del(&entry->rule.rlist); list_del_rcu(&entry->list); } else { + if (watch) { + list_add(&nentry->rule.rlist, + &watch->rules); + list_del(&entry->rule.rlist); + } list_replace_rcu(&entry->list, &nentry->list); } call_rcu(&entry->rcu, audit_free_rule_rcu); } } - mutex_unlock(&audit_netlink_mutex); + mutex_unlock(&audit_filter_mutex); return err; } + +/* Update watch data in audit rules based on inotify events. */ +void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask, + u32 cookie, const char *dname, struct inode *inode) +{ + struct audit_parent *parent; + + parent = container_of(i_watch, struct audit_parent, wdata); + + if (mask & (IN_CREATE|IN_MOVED_TO) && inode) + audit_update_watch(parent, dname, inode->i_sb->s_dev, + inode->i_ino, 0); + else if (mask & (IN_DELETE|IN_MOVED_FROM)) + audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1); + /* inotify automatically removes the watch and sends IN_IGNORED */ + else if (mask & (IN_DELETE_SELF|IN_UNMOUNT)) + audit_remove_parent_watches(parent); + /* inotify does not remove the watch, so remove it manually */ + else if(mask & IN_MOVE_SELF) { + audit_remove_parent_watches(parent); + inotify_remove_watch_locked(audit_ih, i_watch); + } else if (mask & IN_IGNORED) + put_inotify_watch(i_watch); +} diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 1c03a4e..ae40ac8 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -3,7 +3,7 @@ * * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina. * Copyright 2005 Hewlett-Packard Development Company, L.P. - * Copyright (C) 2005 IBM Corporation + * Copyright (C) 2005, 2006 IBM Corporation * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -29,6 +29,9 @@ * this file -- see entry.S) is based on a GPL'd patch written by * okir@suse.de and Copyright 2003 SuSE Linux AG. * + * POSIX message queue support added by George Wilson , + * 2006. + * * The support of additional filter rules compares (>, <, >=, <=) was * added by Dustin Kirkland , 2005. * @@ -49,6 +52,7 @@ #include #include #include #include +#include #include #include #include @@ -59,6 +63,8 @@ #include #include #include #include +#include +#include #include "audit.h" @@ -76,6 +82,9 @@ #define AUDIT_NAMES 20 * path_lookup. */ #define AUDIT_NAMES_RESERVED 7 +/* Indicates that audit should log the full pathname. */ +#define AUDIT_NAME_FULL -1 + /* When fs/namei.c:getname() is called, we store the pointer in name and * we don't let putname() free it (instead we free all of the saved * pointers at syscall exit time). @@ -83,8 +92,9 @@ #define AUDIT_NAMES_RESERVED 7 * Further, in fs/namei.c:path_lookup() we store the inode and device. */ struct audit_names { const char *name; + int name_len; /* number of name's characters to log */ + unsigned name_put; /* call __putname() for this name */ unsigned long ino; - unsigned long pino; dev_t dev; umode_t mode; uid_t uid; @@ -100,6 +110,33 @@ struct audit_aux_data { #define AUDIT_AUX_IPCPERM 0 +struct audit_aux_data_mq_open { + struct audit_aux_data d; + int oflag; + mode_t mode; + struct mq_attr attr; +}; + +struct audit_aux_data_mq_sendrecv { + struct audit_aux_data d; + mqd_t mqdes; + size_t msg_len; + unsigned int msg_prio; + struct timespec abs_timeout; +}; + +struct audit_aux_data_mq_notify { + struct audit_aux_data d; + mqd_t mqdes; + struct sigevent notification; +}; + +struct audit_aux_data_mq_getsetattr { + struct audit_aux_data d; + mqd_t mqdes; + struct mq_attr mqstat; +}; + struct audit_aux_data_ipcctl { struct audit_aux_data d; struct ipc_perm p; @@ -110,6 +147,13 @@ struct audit_aux_data_ipcctl { u32 osid; }; +struct audit_aux_data_execve { + struct audit_aux_data d; + int argc; + int envc; + char mem[0]; +}; + struct audit_aux_data_socketcall { struct audit_aux_data d; int nargs; @@ -142,13 +186,14 @@ struct audit_context { int auditable; /* 1 if record should be written */ int name_count; struct audit_names names[AUDIT_NAMES]; + char * filterkey; /* key for rule that triggered record */ struct dentry * pwd; struct vfsmount * pwdmnt; struct audit_context *previous; /* For nested syscalls */ struct audit_aux_data *aux; /* Save things to print about task_struct */ - pid_t pid; + pid_t pid, ppid; uid_t uid, euid, suid, fsuid; gid_t gid, egid, sgid, fsgid; unsigned long personality; @@ -160,12 +205,13 @@ #if AUDIT_DEBUG #endif }; - +/* Determine if any context name data matches a rule's watch data */ /* Compare a task_struct with an audit_rule. Return 1 on match, 0 * otherwise. */ static int audit_filter_rules(struct task_struct *tsk, struct audit_krule *rule, struct audit_context *ctx, + struct audit_names *name, enum audit_state *state) { int i, j, need_sid = 1; @@ -179,6 +225,10 @@ static int audit_filter_rules(struct tas case AUDIT_PID: result = audit_comparator(tsk->pid, f->op, f->val); break; + case AUDIT_PPID: + if (ctx) + result = audit_comparator(ctx->ppid, f->op, f->val); + break; case AUDIT_UID: result = audit_comparator(tsk->uid, f->op, f->val); break; @@ -224,7 +274,10 @@ static int audit_filter_rules(struct tas } break; case AUDIT_DEVMAJOR: - if (ctx) { + if (name) + result = audit_comparator(MAJOR(name->dev), + f->op, f->val); + else if (ctx) { for (j = 0; j < ctx->name_count; j++) { if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) { ++result; @@ -234,7 +287,10 @@ static int audit_filter_rules(struct tas } break; case AUDIT_DEVMINOR: - if (ctx) { + if (name) + result = audit_comparator(MINOR(name->dev), + f->op, f->val); + else if (ctx) { for (j = 0; j < ctx->name_count; j++) { if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) { ++result; @@ -244,26 +300,32 @@ static int audit_filter_rules(struct tas } break; case AUDIT_INODE: - if (ctx) { + if (name) + result = (name->ino == f->val); + else if (ctx) { for (j = 0; j < ctx->name_count; j++) { - if (audit_comparator(ctx->names[j].ino, f->op, f->val) || - audit_comparator(ctx->names[j].pino, f->op, f->val)) { + if (audit_comparator(ctx->names[j].ino, f->op, f->val)) { ++result; break; } } } break; + case AUDIT_WATCH: + if (name && rule->watch->ino != (unsigned long)-1) + result = (name->dev == rule->watch->dev && + name->ino == rule->watch->ino); + break; case AUDIT_LOGINUID: result = 0; if (ctx) result = audit_comparator(ctx->loginuid, f->op, f->val); break; - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: /* NOTE: this may return negative values indicating a temporary error. We simply treat this as a match for now to avoid losing information that @@ -280,6 +342,46 @@ static int audit_filter_rules(struct tas ctx); } break; + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + /* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR + also applies here */ + if (f->se_rule) { + /* Find files that match */ + if (name) { + result = selinux_audit_rule_match( + name->osid, f->type, f->op, + f->se_rule, ctx); + } else if (ctx) { + for (j = 0; j < ctx->name_count; j++) { + if (selinux_audit_rule_match( + ctx->names[j].osid, + f->type, f->op, + f->se_rule, ctx)) { + ++result; + break; + } + } + } + /* Find ipc objects that match */ + if (ctx) { + struct audit_aux_data *aux; + for (aux = ctx->aux; aux; + aux = aux->next) { + if (aux->type == AUDIT_IPC) { + struct audit_aux_data_ipcctl *axi = (void *)aux; + if (selinux_audit_rule_match(axi->osid, f->type, f->op, f->se_rule, ctx)) { + ++result; + break; + } + } + } + } + } + break; case AUDIT_ARG0: case AUDIT_ARG1: case AUDIT_ARG2: @@ -287,14 +389,19 @@ static int audit_filter_rules(struct tas if (ctx) result = audit_comparator(ctx->argv[f->type-AUDIT_ARG0], f->op, f->val); break; + case AUDIT_FILTERKEY: + /* ignore this field for filtering */ + result = 1; + break; } if (!result) return 0; } + if (rule->filterkey) + ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC); switch (rule->action) { case AUDIT_NEVER: *state = AUDIT_DISABLED; break; - case AUDIT_POSSIBLE: *state = AUDIT_BUILD_CONTEXT; break; case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break; } return 1; @@ -311,7 +418,7 @@ static enum audit_state audit_filter_tas rcu_read_lock(); list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) { - if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { + if (audit_filter_rules(tsk, &e->rule, NULL, NULL, &state)) { rcu_read_unlock(); return state; } @@ -341,8 +448,47 @@ static enum audit_state audit_filter_sys int bit = AUDIT_BIT(ctx->major); list_for_each_entry_rcu(e, list, list) { - if ((e->rule.mask[word] & bit) == bit - && audit_filter_rules(tsk, &e->rule, ctx, &state)) { + if ((e->rule.mask[word] & bit) == bit && + audit_filter_rules(tsk, &e->rule, ctx, NULL, + &state)) { + rcu_read_unlock(); + return state; + } + } + } + rcu_read_unlock(); + return AUDIT_BUILD_CONTEXT; +} + +/* At syscall exit time, this filter is called if any audit_names[] have been + * collected during syscall processing. We only check rules in sublists at hash + * buckets applicable to the inode numbers in audit_names[]. + * Regarding audit_state, same rules apply as for audit_filter_syscall(). + */ +enum audit_state audit_filter_inodes(struct task_struct *tsk, + struct audit_context *ctx) +{ + int i; + struct audit_entry *e; + enum audit_state state; + + if (audit_pid && tsk->tgid == audit_pid) + return AUDIT_DISABLED; + + rcu_read_lock(); + for (i = 0; i < ctx->name_count; i++) { + int word = AUDIT_WORD(ctx->major); + int bit = AUDIT_BIT(ctx->major); + struct audit_names *n = &ctx->names[i]; + int h = audit_hash_ino((u32)n->ino); + struct list_head *list = &audit_inode_hash[h]; + + if (list_empty(list)) + continue; + + list_for_each_entry_rcu(e, list, list) { + if ((e->rule.mask[word] & bit) == bit && + audit_filter_rules(tsk, &e->rule, ctx, n, &state)) { rcu_read_unlock(); return state; } @@ -352,6 +498,11 @@ static enum audit_state audit_filter_sys return AUDIT_BUILD_CONTEXT; } +void audit_set_auditable(struct audit_context *ctx) +{ + ctx->auditable = 1; +} + static inline struct audit_context *audit_get_context(struct task_struct *tsk, int return_valid, int return_code) @@ -365,12 +516,22 @@ static inline struct audit_context *audi if (context->in_syscall && !context->auditable) { enum audit_state state; + state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); + if (state == AUDIT_RECORD_CONTEXT) { + context->auditable = 1; + goto get_context; + } + + state = audit_filter_inodes(tsk, context); if (state == AUDIT_RECORD_CONTEXT) context->auditable = 1; + } +get_context: context->pid = tsk->pid; + context->ppid = sys_getppid(); /* sic. tsk == current in all cases */ context->uid = tsk->uid; context->gid = tsk->gid; context->euid = tsk->euid; @@ -413,7 +574,7 @@ #if AUDIT_DEBUG #endif for (i = 0; i < context->name_count; i++) { - if (context->names[i].name) + if (context->names[i].name && context->names[i].name_put) __putname(context->names[i].name); } context->name_count = 0; @@ -513,6 +674,7 @@ static inline void audit_free_context(st } audit_free_names(context); audit_free_aux(context); + kfree(context->filterkey); kfree(context); context = previous; } while (context); @@ -544,8 +706,7 @@ static void audit_log_task_context(struc return; error_path: - if (ctx) - kfree(ctx); + kfree(ctx); audit_panic("error in audit_log_task_context"); return; } @@ -606,7 +767,7 @@ static void audit_log_exit(struct audit_ tty = "(none)"; audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" - " pid=%d auid=%u uid=%u gid=%u" + " ppid=%d pid=%d auid=%u uid=%u gid=%u" " euid=%u suid=%u fsuid=%u" " egid=%u sgid=%u fsgid=%u tty=%s", context->argv[0], @@ -614,6 +775,7 @@ static void audit_log_exit(struct audit_ context->argv[2], context->argv[3], context->name_count, + context->ppid, context->pid, context->loginuid, context->uid, @@ -621,6 +783,11 @@ static void audit_log_exit(struct audit_ context->euid, context->suid, context->fsuid, context->egid, context->sgid, context->fsgid, tty); audit_log_task_info(ab, tsk); + if (context->filterkey) { + audit_log_format(ab, " key="); + audit_log_untrustedstring(ab, context->filterkey); + } else + audit_log_format(ab, " key=(null)"); audit_log_end(ab); for (aux = context->aux; aux; aux = aux->next) { @@ -630,11 +797,48 @@ static void audit_log_exit(struct audit_ continue; /* audit_panic has been called */ switch (aux->type) { + case AUDIT_MQ_OPEN: { + struct audit_aux_data_mq_open *axi = (void *)aux; + audit_log_format(ab, + "oflag=0x%x mode=%#o mq_flags=0x%lx mq_maxmsg=%ld " + "mq_msgsize=%ld mq_curmsgs=%ld", + axi->oflag, axi->mode, axi->attr.mq_flags, + axi->attr.mq_maxmsg, axi->attr.mq_msgsize, + axi->attr.mq_curmsgs); + break; } + + case AUDIT_MQ_SENDRECV: { + struct audit_aux_data_mq_sendrecv *axi = (void *)aux; + audit_log_format(ab, + "mqdes=%d msg_len=%zd msg_prio=%u " + "abs_timeout_sec=%ld abs_timeout_nsec=%ld", + axi->mqdes, axi->msg_len, axi->msg_prio, + axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec); + break; } + + case AUDIT_MQ_NOTIFY: { + struct audit_aux_data_mq_notify *axi = (void *)aux; + audit_log_format(ab, + "mqdes=%d sigev_signo=%d", + axi->mqdes, + axi->notification.sigev_signo); + break; } + + case AUDIT_MQ_GETSETATTR: { + struct audit_aux_data_mq_getsetattr *axi = (void *)aux; + audit_log_format(ab, + "mqdes=%d mq_flags=0x%lx mq_maxmsg=%ld mq_msgsize=%ld " + "mq_curmsgs=%ld ", + axi->mqdes, + axi->mqstat.mq_flags, axi->mqstat.mq_maxmsg, + axi->mqstat.mq_msgsize, axi->mqstat.mq_curmsgs); + break; } + case AUDIT_IPC: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - " qbytes=%lx iuid=%u igid=%u mode=%x", - axi->qbytes, axi->uid, axi->gid, axi->mode); + "ouid=%u ogid=%u mode=%x", + axi->uid, axi->gid, axi->mode); if (axi->osid != 0) { char *ctx = NULL; u32 len; @@ -652,19 +856,18 @@ static void audit_log_exit(struct audit_ case AUDIT_IPC_SET_PERM: { struct audit_aux_data_ipcctl *axi = (void *)aux; audit_log_format(ab, - " new qbytes=%lx new iuid=%u new igid=%u new mode=%x", + "qbytes=%lx ouid=%u ogid=%u mode=%x", axi->qbytes, axi->uid, axi->gid, axi->mode); - if (axi->osid != 0) { - char *ctx = NULL; - u32 len; - if (selinux_ctxid_to_string( - axi->osid, &ctx, &len)) { - audit_log_format(ab, " osid=%u", - axi->osid); - call_panic = 1; - } else - audit_log_format(ab, " obj=%s", ctx); - kfree(ctx); + break; } + + case AUDIT_EXECVE: { + struct audit_aux_data_execve *axi = (void *)aux; + int i; + const char *p; + for (i = 0, p = axi->mem; i < axi->argc; i++) { + audit_log_format(ab, "a%d=", i); + p = audit_log_untrustedstring(ab, p); + audit_log_format(ab, "\n"); } break; } @@ -700,8 +903,7 @@ static void audit_log_exit(struct audit_ } } for (i = 0; i < context->name_count; i++) { - unsigned long ino = context->names[i].ino; - unsigned long pino = context->names[i].pino; + struct audit_names *n = &context->names[i]; ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH); if (!ab) @@ -709,33 +911,47 @@ static void audit_log_exit(struct audit_ audit_log_format(ab, "item=%d", i); - audit_log_format(ab, " name="); - if (context->names[i].name) - audit_log_untrustedstring(ab, context->names[i].name); - else - audit_log_format(ab, "(null)"); - - if (pino != (unsigned long)-1) - audit_log_format(ab, " parent=%lu", pino); - if (ino != (unsigned long)-1) - audit_log_format(ab, " inode=%lu", ino); - if ((pino != (unsigned long)-1) || (ino != (unsigned long)-1)) - audit_log_format(ab, " dev=%02x:%02x mode=%#o" - " ouid=%u ogid=%u rdev=%02x:%02x", - MAJOR(context->names[i].dev), - MINOR(context->names[i].dev), - context->names[i].mode, - context->names[i].uid, - context->names[i].gid, - MAJOR(context->names[i].rdev), - MINOR(context->names[i].rdev)); - if (context->names[i].osid != 0) { + if (n->name) { + switch(n->name_len) { + case AUDIT_NAME_FULL: + /* log the full path */ + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, n->name); + break; + case 0: + /* name was specified as a relative path and the + * directory component is the cwd */ + audit_log_d_path(ab, " name=", context->pwd, + context->pwdmnt); + break; + default: + /* log the name's directory component */ + audit_log_format(ab, " name="); + audit_log_n_untrustedstring(ab, n->name_len, + n->name); + } + } else + audit_log_format(ab, " name=(null)"); + + if (n->ino != (unsigned long)-1) { + audit_log_format(ab, " inode=%lu" + " dev=%02x:%02x mode=%#o" + " ouid=%u ogid=%u rdev=%02x:%02x", + n->ino, + MAJOR(n->dev), + MINOR(n->dev), + n->mode, + n->uid, + n->gid, + MAJOR(n->rdev), + MINOR(n->rdev)); + } + if (n->osid != 0) { char *ctx = NULL; u32 len; if (selinux_ctxid_to_string( - context->names[i].osid, &ctx, &len)) { - audit_log_format(ab, " osid=%u", - context->names[i].osid); + n->osid, &ctx, &len)) { + audit_log_format(ab, " osid=%u", n->osid); call_panic = 2; } else audit_log_format(ab, " obj=%s", ctx); @@ -897,6 +1113,8 @@ void audit_syscall_exit(int valid, long } else { audit_free_names(context); audit_free_aux(context); + kfree(context->filterkey); + context->filterkey = NULL; tsk->audit_context = context; } } @@ -908,11 +1126,11 @@ void audit_syscall_exit(int valid, long * Add a name to the list of audit names for this context. * Called from fs/namei.c:getname(). */ -void audit_getname(const char *name) +void __audit_getname(const char *name) { struct audit_context *context = current->audit_context; - if (!context || IS_ERR(name) || !name) + if (IS_ERR(name) || !name) return; if (!context->in_syscall) { @@ -925,6 +1143,8 @@ #endif } BUG_ON(context->name_count >= AUDIT_NAMES); context->names[context->name_count].name = name; + context->names[context->name_count].name_len = AUDIT_NAME_FULL; + context->names[context->name_count].name_put = 1; context->names[context->name_count].ino = (unsigned long)-1; ++context->name_count; if (!context->pwd) { @@ -991,11 +1211,10 @@ static void audit_inode_context(int idx, * audit_inode - store the inode and device from a lookup * @name: name being audited * @inode: inode being audited - * @flags: lookup flags (as used in path_lookup()) * * Called from fs/namei.c:path_lookup(). */ -void __audit_inode(const char *name, const struct inode *inode, unsigned flags) +void __audit_inode(const char *name, const struct inode *inode) { int idx; struct audit_context *context = current->audit_context; @@ -1021,20 +1240,13 @@ #if AUDIT_DEBUG ++context->ino_count; #endif } + context->names[idx].ino = inode->i_ino; context->names[idx].dev = inode->i_sb->s_dev; context->names[idx].mode = inode->i_mode; context->names[idx].uid = inode->i_uid; context->names[idx].gid = inode->i_gid; context->names[idx].rdev = inode->i_rdev; audit_inode_context(idx, inode); - if ((flags & LOOKUP_PARENT) && (strcmp(name, "/") != 0) && - (strcmp(name, ".") != 0)) { - context->names[idx].ino = (unsigned long)-1; - context->names[idx].pino = inode->i_ino; - } else { - context->names[idx].ino = inode->i_ino; - context->names[idx].pino = (unsigned long)-1; - } } /** @@ -1056,51 +1268,40 @@ void __audit_inode_child(const char *dna { int idx; struct audit_context *context = current->audit_context; + const char *found_name = NULL; + int dirlen = 0; if (!context->in_syscall) return; /* determine matching parent */ - if (dname) - for (idx = 0; idx < context->name_count; idx++) - if (context->names[idx].pino == pino) { - const char *n; - const char *name = context->names[idx].name; - int dlen = strlen(dname); - int nlen = name ? strlen(name) : 0; - - if (nlen < dlen) - continue; - - /* disregard trailing slashes */ - n = name + nlen - 1; - while ((*n == '/') && (n > name)) - n--; - - /* find last path component */ - n = n - dlen + 1; - if (n < name) - continue; - else if (n > name) { - if (*--n != '/') - continue; - else - n++; - } - - if (strncmp(n, dname, dlen) == 0) - goto update_context; + if (!dname) + goto update_context; + for (idx = 0; idx < context->name_count; idx++) + if (context->names[idx].ino == pino) { + const char *name = context->names[idx].name; + + if (!name) + continue; + + if (audit_compare_dname_path(dname, name, &dirlen) == 0) { + context->names[idx].name_len = dirlen; + found_name = name; + break; } + } - /* catch-all in case match not found */ +update_context: idx = context->name_count++; - context->names[idx].name = NULL; - context->names[idx].pino = pino; #if AUDIT_DEBUG context->ino_count++; #endif + /* Re-use the name belonging to the slot for a matching parent directory. + * All names for this context are relinquished in audit_free_names() */ + context->names[idx].name = found_name; + context->names[idx].name_len = AUDIT_NAME_FULL; + context->names[idx].name_put = 0; /* don't call __putname() */ -update_context: if (inode) { context->names[idx].ino = inode->i_ino; context->names[idx].dev = inode->i_sb->s_dev; @@ -1109,7 +1310,8 @@ update_context: context->names[idx].gid = inode->i_gid; context->names[idx].rdev = inode->i_rdev; audit_inode_context(idx, inode); - } + } else + context->names[idx].ino = (unsigned long)-1; } /** @@ -1142,18 +1344,23 @@ void auditsc_get_stamp(struct audit_cont */ int audit_set_loginuid(struct task_struct *task, uid_t loginuid) { - if (task->audit_context) { - struct audit_buffer *ab; - - ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); - if (ab) { - audit_log_format(ab, "login pid=%d uid=%u " - "old auid=%u new auid=%u", - task->pid, task->uid, - task->audit_context->loginuid, loginuid); - audit_log_end(ab); + struct audit_context *context = task->audit_context; + + if (context) { + /* Only log if audit is enabled */ + if (context->in_syscall) { + struct audit_buffer *ab; + + ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_LOGIN); + if (ab) { + audit_log_format(ab, "login pid=%d uid=%u " + "old auid=%u new auid=%u", + task->pid, task->uid, + context->loginuid, loginuid); + audit_log_end(ab); + } } - task->audit_context->loginuid = loginuid; + context->loginuid = loginuid; } return 0; } @@ -1170,16 +1377,193 @@ uid_t audit_get_loginuid(struct audit_co } /** - * audit_ipc_obj - record audit data for ipc object - * @ipcp: ipc permissions + * __audit_mq_open - record audit data for a POSIX MQ open + * @oflag: open flag + * @mode: mode bits + * @u_attr: queue attributes * * Returns 0 for success or NULL context or < 0 on error. */ -int audit_ipc_obj(struct kern_ipc_perm *ipcp) +int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr) { - struct audit_aux_data_ipcctl *ax; + struct audit_aux_data_mq_open *ax; + struct audit_context *context = current->audit_context; + + if (!audit_enabled) + return 0; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + + if (u_attr != NULL) { + if (copy_from_user(&ax->attr, u_attr, sizeof(ax->attr))) { + kfree(ax); + return -EFAULT; + } + } else + memset(&ax->attr, 0, sizeof(ax->attr)); + + ax->oflag = oflag; + ax->mode = mode; + + ax->d.type = AUDIT_MQ_OPEN; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +/** + * __audit_mq_timedsend - record audit data for a POSIX MQ timed send + * @mqdes: MQ descriptor + * @msg_len: Message length + * @msg_prio: Message priority + * @u_abs_timeout: Message timeout in absolute time + * + * Returns 0 for success or NULL context or < 0 on error. + */ +int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, + const struct timespec __user *u_abs_timeout) +{ + struct audit_aux_data_mq_sendrecv *ax; + struct audit_context *context = current->audit_context; + + if (!audit_enabled) + return 0; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + + if (u_abs_timeout != NULL) { + if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) { + kfree(ax); + return -EFAULT; + } + } else + memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout)); + + ax->mqdes = mqdes; + ax->msg_len = msg_len; + ax->msg_prio = msg_prio; + + ax->d.type = AUDIT_MQ_SENDRECV; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +/** + * __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive + * @mqdes: MQ descriptor + * @msg_len: Message length + * @u_msg_prio: Message priority + * @u_abs_timeout: Message timeout in absolute time + * + * Returns 0 for success or NULL context or < 0 on error. + */ +int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, + unsigned int __user *u_msg_prio, + const struct timespec __user *u_abs_timeout) +{ + struct audit_aux_data_mq_sendrecv *ax; + struct audit_context *context = current->audit_context; + + if (!audit_enabled) + return 0; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + + if (u_msg_prio != NULL) { + if (get_user(ax->msg_prio, u_msg_prio)) { + kfree(ax); + return -EFAULT; + } + } else + ax->msg_prio = 0; + + if (u_abs_timeout != NULL) { + if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) { + kfree(ax); + return -EFAULT; + } + } else + memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout)); + + ax->mqdes = mqdes; + ax->msg_len = msg_len; + + ax->d.type = AUDIT_MQ_SENDRECV; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +/** + * __audit_mq_notify - record audit data for a POSIX MQ notify + * @mqdes: MQ descriptor + * @u_notification: Notification event + * + * Returns 0 for success or NULL context or < 0 on error. + */ + +int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification) +{ + struct audit_aux_data_mq_notify *ax; + struct audit_context *context = current->audit_context; + + if (!audit_enabled) + return 0; + + if (likely(!context)) + return 0; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + + if (u_notification != NULL) { + if (copy_from_user(&ax->notification, u_notification, sizeof(ax->notification))) { + kfree(ax); + return -EFAULT; + } + } else + memset(&ax->notification, 0, sizeof(ax->notification)); + + ax->mqdes = mqdes; + + ax->d.type = AUDIT_MQ_NOTIFY; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +/** + * __audit_mq_getsetattr - record audit data for a POSIX MQ get/set attribute + * @mqdes: MQ descriptor + * @mqstat: MQ flags + * + * Returns 0 for success or NULL context or < 0 on error. + */ +int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat) +{ + struct audit_aux_data_mq_getsetattr *ax; struct audit_context *context = current->audit_context; + if (!audit_enabled) + return 0; + if (likely(!context)) return 0; @@ -1187,6 +1571,30 @@ int audit_ipc_obj(struct kern_ipc_perm * if (!ax) return -ENOMEM; + ax->mqdes = mqdes; + ax->mqstat = *mqstat; + + ax->d.type = AUDIT_MQ_GETSETATTR; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + +/** + * audit_ipc_obj - record audit data for ipc object + * @ipcp: ipc permissions + * + * Returns 0 for success or NULL context or < 0 on error. + */ +int __audit_ipc_obj(struct kern_ipc_perm *ipcp) +{ + struct audit_aux_data_ipcctl *ax; + struct audit_context *context = current->audit_context; + + ax = kmalloc(sizeof(*ax), GFP_ATOMIC); + if (!ax) + return -ENOMEM; + ax->uid = ipcp->uid; ax->gid = ipcp->gid; ax->mode = ipcp->mode; @@ -1207,14 +1615,11 @@ int audit_ipc_obj(struct kern_ipc_perm * * * Returns 0 for success or NULL context or < 0 on error. */ -int audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode, struct kern_ipc_perm *ipcp) +int __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) { struct audit_aux_data_ipcctl *ax; struct audit_context *context = current->audit_context; - if (likely(!context)) - return 0; - ax = kmalloc(sizeof(*ax), GFP_ATOMIC); if (!ax) return -ENOMEM; @@ -1223,7 +1628,6 @@ int audit_ipc_set_perm(unsigned long qby ax->uid = uid; ax->gid = gid; ax->mode = mode; - selinux_get_ipc_sid(ipcp, &ax->osid); ax->d.type = AUDIT_IPC_SET_PERM; ax->d.next = context->aux; @@ -1231,6 +1635,39 @@ int audit_ipc_set_perm(unsigned long qby return 0; } +int audit_bprm(struct linux_binprm *bprm) +{ + struct audit_aux_data_execve *ax; + struct audit_context *context = current->audit_context; + unsigned long p, next; + void *to; + + if (likely(!audit_enabled || !context)) + return 0; + + ax = kmalloc(sizeof(*ax) + PAGE_SIZE * MAX_ARG_PAGES - bprm->p, + GFP_KERNEL); + if (!ax) + return -ENOMEM; + + ax->argc = bprm->argc; + ax->envc = bprm->envc; + for (p = bprm->p, to = ax->mem; p < MAX_ARG_PAGES*PAGE_SIZE; p = next) { + struct page *page = bprm->page[p / PAGE_SIZE]; + void *kaddr = kmap(page); + next = (p + PAGE_SIZE) & ~(PAGE_SIZE - 1); + memcpy(to, kaddr + (p & (PAGE_SIZE - 1)), next - p); + to += next - p; + kunmap(page); + } + + ax->d.type = AUDIT_EXECVE; + ax->d.next = context->aux; + context->aux = (void *)ax; + return 0; +} + + /** * audit_socketcall - record audit data for sys_socketcall * @nargs: number of args @@ -1325,19 +1762,20 @@ int audit_avc_path(struct dentry *dentry * If the audit subsystem is being terminated, record the task (pid) * and uid that is doing that. */ -void audit_signal_info(int sig, struct task_struct *t) +void __audit_signal_info(int sig, struct task_struct *t) { extern pid_t audit_sig_pid; extern uid_t audit_sig_uid; - - if (unlikely(audit_pid && t->tgid == audit_pid)) { - if (sig == SIGTERM || sig == SIGHUP) { - struct audit_context *ctx = current->audit_context; - audit_sig_pid = current->pid; - if (ctx) - audit_sig_uid = ctx->loginuid; - else - audit_sig_uid = current->uid; - } + extern u32 audit_sig_sid; + + if (sig == SIGTERM || sig == SIGHUP || sig == SIGUSR1) { + struct task_struct *tsk = current; + struct audit_context *ctx = tsk->audit_context; + audit_sig_pid = tsk->pid; + if (ctx) + audit_sig_uid = ctx->loginuid; + else + audit_sig_uid = tsk->uid; + selinux_get_task_sid(tsk, &audit_sig_sid); } } diff --git a/kernel/capability.c b/kernel/capability.c index 1a4d8a4..c7685ad 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -46,7 +46,7 @@ asmlinkage long sys_capget(cap_user_head int ret = 0; pid_t pid; __u32 version; - task_t *target; + struct task_struct *target; struct __user_cap_data_struct data; if (get_user(version, &header->version)) @@ -96,7 +96,7 @@ static inline int cap_set_pg(int pgrp, k kernel_cap_t *inheritable, kernel_cap_t *permitted) { - task_t *g, *target; + struct task_struct *g, *target; int ret = -EPERM; int found = 0; @@ -128,7 +128,7 @@ static inline int cap_set_all(kernel_cap kernel_cap_t *inheritable, kernel_cap_t *permitted) { - task_t *g, *target; + struct task_struct *g, *target; int ret = -EPERM; int found = 0; @@ -172,7 +172,7 @@ asmlinkage long sys_capset(cap_user_head { kernel_cap_t inheritable, permitted, effective; __u32 version; - task_t *target; + struct task_struct *target; int ret; pid_t pid; diff --git a/kernel/compat.c b/kernel/compat.c index c1601a8..126dee9 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -21,6 +21,7 @@ #include #include #include #include +#include #include @@ -729,17 +730,10 @@ void sigset_from_compat (sigset_t *set, compat_sigset_t *compat) { switch (_NSIG_WORDS) { -#if defined (__COMPAT_ENDIAN_SWAP__) - case 4: set->sig[3] = compat->sig[7] | (((long)compat->sig[6]) << 32 ); - case 3: set->sig[2] = compat->sig[5] | (((long)compat->sig[4]) << 32 ); - case 2: set->sig[1] = compat->sig[3] | (((long)compat->sig[2]) << 32 ); - case 1: set->sig[0] = compat->sig[1] | (((long)compat->sig[0]) << 32 ); -#else case 4: set->sig[3] = compat->sig[6] | (((long)compat->sig[7]) << 32 ); case 3: set->sig[2] = compat->sig[4] | (((long)compat->sig[5]) << 32 ); case 2: set->sig[1] = compat->sig[2] | (((long)compat->sig[3]) << 32 ); case 1: set->sig[0] = compat->sig[0] | (((long)compat->sig[1]) << 32 ); -#endif } } @@ -934,3 +928,25 @@ asmlinkage long compat_sys_adjtimex(stru return ret; } + +#ifdef CONFIG_NUMA +asmlinkage long compat_sys_move_pages(pid_t pid, unsigned long nr_pages, + compat_uptr_t __user *pages32, + const int __user *nodes, + int __user *status, + int flags) +{ + const void __user * __user *pages; + int i; + + pages = compat_alloc_user_space(nr_pages * sizeof(void *)); + for (i = 0; i < nr_pages; i++) { + compat_uptr_t p; + + if (get_user(p, pages32 + i) || + put_user(compat_ptr(p), pages + i)) + return -EFAULT; + } + return sys_move_pages(pid, nr_pages, pages, nodes, status, flags); +} +#endif diff --git a/kernel/configs.c b/kernel/configs.c index 009e1eb..f9e3197 100644 --- a/kernel/configs.c +++ b/kernel/configs.c @@ -23,7 +23,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include diff --git a/kernel/cpu.c b/kernel/cpu.c index fe2b8d0..70fbf2e 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -13,12 +13,12 @@ #include #include #include #include -#include +#include /* This protects CPUs going up and down... */ -static DECLARE_MUTEX(cpucontrol); +static DEFINE_MUTEX(cpucontrol); -static BLOCKING_NOTIFIER_HEAD(cpu_chain); +static __cpuinitdata BLOCKING_NOTIFIER_HEAD(cpu_chain); #ifdef CONFIG_HOTPLUG_CPU static struct task_struct *lock_cpu_hotplug_owner; @@ -30,9 +30,9 @@ static int __lock_cpu_hotplug(int interr if (lock_cpu_hotplug_owner != current) { if (interruptible) - ret = down_interruptible(&cpucontrol); + ret = mutex_lock_interruptible(&cpucontrol); else - down(&cpucontrol); + mutex_lock(&cpucontrol); } /* @@ -56,7 +56,7 @@ void unlock_cpu_hotplug(void) { if (--lock_cpu_hotplug_depth == 0) { lock_cpu_hotplug_owner = NULL; - up(&cpucontrol); + mutex_unlock(&cpucontrol); } } EXPORT_SYMBOL_GPL(unlock_cpu_hotplug); @@ -69,10 +69,13 @@ EXPORT_SYMBOL_GPL(lock_cpu_hotplug_inter #endif /* CONFIG_HOTPLUG_CPU */ /* Need to know about CPUs going up/down? */ -int register_cpu_notifier(struct notifier_block *nb) +int __cpuinit register_cpu_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&cpu_chain, nb); } + +#ifdef CONFIG_HOTPLUG_CPU + EXPORT_SYMBOL(register_cpu_notifier); void unregister_cpu_notifier(struct notifier_block *nb) @@ -81,7 +84,6 @@ void unregister_cpu_notifier(struct noti } EXPORT_SYMBOL(unregister_cpu_notifier); -#ifdef CONFIG_HOTPLUG_CPU static inline void check_for_tasks(int cpu) { struct task_struct *p; diff --git a/kernel/cpuset.c b/kernel/cpuset.c index ab81fdd..c232dc0 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -18,7 +18,6 @@ * distribution for more details. */ -#include #include #include #include @@ -41,6 +40,7 @@ #include #include #include #include +#include #include #include #include @@ -392,11 +392,11 @@ static int cpuset_fill_super(struct supe return 0; } -static struct super_block *cpuset_get_sb(struct file_system_type *fs_type, - int flags, const char *unused_dev_name, - void *data) +static int cpuset_get_sb(struct file_system_type *fs_type, + int flags, const char *unused_dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, cpuset_fill_super); + return get_sb_single(fs_type, flags, data, cpuset_fill_super, mnt); } static struct file_system_type cpuset_fs_type = { @@ -1063,7 +1063,7 @@ static int update_flag(cpuset_flagbits_t } /* - * Frequency meter - How fast is some event occuring? + * Frequency meter - How fast is some event occurring? * * These routines manage a digitally filtered, constant time based, * event frequency meter. There are four routines: @@ -1177,6 +1177,7 @@ static int attach_task(struct cpuset *cs cpumask_t cpus; nodemask_t from, to; struct mm_struct *mm; + int retval; if (sscanf(pidbuf, "%d", &pid) != 1) return -EIO; @@ -1205,6 +1206,12 @@ static int attach_task(struct cpuset *cs get_task_struct(tsk); } + retval = security_task_setscheduler(tsk, 0, NULL); + if (retval) { + put_task_struct(tsk); + return retval; + } + mutex_lock(&callback_mutex); task_lock(tsk); @@ -2434,31 +2441,43 @@ void __cpuset_memory_pressure_bump(void) */ static int proc_cpuset_show(struct seq_file *m, void *v) { + struct pid *pid; struct task_struct *tsk; char *buf; - int retval = 0; + int retval; + retval = -ENOMEM; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) - return -ENOMEM; + goto out; - tsk = m->private; + retval = -ESRCH; + pid = m->private; + tsk = get_pid_task(pid, PIDTYPE_PID); + if (!tsk) + goto out_free; + + retval = -EINVAL; mutex_lock(&manage_mutex); + retval = cpuset_path(tsk->cpuset, buf, PAGE_SIZE); if (retval < 0) - goto out; + goto out_unlock; seq_puts(m, buf); seq_putc(m, '\n'); -out: +out_unlock: mutex_unlock(&manage_mutex); + put_task_struct(tsk); +out_free: kfree(buf); +out: return retval; } static int cpuset_open(struct inode *inode, struct file *file) { - struct task_struct *tsk = PROC_I(inode)->task; - return single_open(file, proc_cpuset_show, tsk); + struct pid *pid = PROC_I(inode)->pid; + return single_open(file, proc_cpuset_show, pid); } struct file_operations proc_cpuset_operations = { diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index c01cead..3c2eaea 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -7,7 +7,6 @@ * 2001-05-06 Complete rewrite, Christoph Hellwig (hch@infradead.org) */ -#include #include #include #include diff --git a/kernel/exit.c b/kernel/exit.c index e06d0c1..6664c08 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include #include /* for audit_free() */ +#include #include #include @@ -45,8 +45,6 @@ #include extern void sem_exit (void); extern struct task_struct *child_reaper; -int getrusage(struct task_struct *, int, struct rusage __user *); - static void exit_mm(struct task_struct * tsk); static void __unhash_process(struct task_struct *p) @@ -136,14 +134,10 @@ static void delayed_put_task_struct(stru void release_task(struct task_struct * p) { + struct task_struct *leader; int zap_leader; - task_t *leader; - struct dentry *proc_dentry; - repeat: atomic_dec(&p->user->processes); - spin_lock(&p->proc_lock); - proc_dentry = proc_pid_unhash(p); write_lock_irq(&tasklist_lock); ptrace_unlink(p); BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); @@ -172,8 +166,7 @@ repeat: sched_exit(p); write_unlock_irq(&tasklist_lock); - spin_unlock(&p->proc_lock); - proc_pid_flush(proc_dentry); + proc_flush_task(p); release_thread(p); call_rcu(&p->rcu, delayed_put_task_struct); @@ -216,7 +209,7 @@ out: * * "I ask you, have you ever known what it is to be an orphan?" */ -static int will_become_orphaned_pgrp(int pgrp, task_t *ignored_task) +static int will_become_orphaned_pgrp(int pgrp, struct task_struct *ignored_task) { struct task_struct *p; int ret = 1; @@ -579,7 +572,7 @@ static void exit_mm(struct task_struct * down_read(&mm->mmap_sem); } atomic_inc(&mm->mm_count); - if (mm != tsk->active_mm) BUG(); + BUG_ON(mm != tsk->active_mm); /* more a memory barrier than a real lock */ task_lock(tsk); tsk->mm = NULL; @@ -589,7 +582,8 @@ static void exit_mm(struct task_struct * mmput(mm); } -static inline void choose_new_parent(task_t *p, task_t *reaper) +static inline void +choose_new_parent(struct task_struct *p, struct task_struct *reaper) { /* * Make sure we're not reparenting to ourselves and that @@ -599,7 +593,8 @@ static inline void choose_new_parent(tas p->real_parent = reaper; } -static void reparent_thread(task_t *p, task_t *father, int traced) +static void +reparent_thread(struct task_struct *p, struct task_struct *father, int traced) { /* We don't want people slaying init. */ if (p->exit_signal != -1) @@ -663,8 +658,8 @@ static void reparent_thread(task_t *p, t * group, and if no such member exists, give it to * the global child reaper process (ie "init") */ -static void forget_original_parent(struct task_struct * father, - struct list_head *to_release) +static void +forget_original_parent(struct task_struct *father, struct list_head *to_release) { struct task_struct *p, *reaper = father; struct list_head *_p, *_n; @@ -687,7 +682,7 @@ static void forget_original_parent(struc */ list_for_each_safe(_p, _n, &father->children) { int ptrace; - p = list_entry(_p,struct task_struct,sibling); + p = list_entry(_p, struct task_struct, sibling); ptrace = p->ptrace; @@ -716,7 +711,7 @@ static void forget_original_parent(struc list_add(&p->ptrace_list, to_release); } list_for_each_safe(_p, _n, &father->ptrace_children) { - p = list_entry(_p,struct task_struct,ptrace_list); + p = list_entry(_p, struct task_struct, ptrace_list); choose_new_parent(p, reaper); reparent_thread(p, father, 1); } @@ -836,7 +831,7 @@ static void exit_notify(struct task_stru list_for_each_safe(_p, _n, &ptrace_dead) { list_del_init(_p); - t = list_entry(_p,struct task_struct,ptrace_list); + t = list_entry(_p, struct task_struct, ptrace_list); release_task(t); } @@ -895,11 +890,11 @@ fastcall NORET_TYPE void do_exit(long co if (group_dead) { hrtimer_cancel(&tsk->signal->real_timer); exit_itimers(tsk->signal); - acct_process(code); } + acct_collect(code, group_dead); if (unlikely(tsk->robust_list)) exit_robust_list(tsk); -#ifdef CONFIG_COMPAT +#if defined(CONFIG_FUTEX) && defined(CONFIG_COMPAT) if (unlikely(tsk->compat_robust_list)) compat_exit_robust_list(tsk); #endif @@ -907,6 +902,8 @@ #endif audit_free(tsk); exit_mm(tsk); + if (group_dead) + acct_process(); exit_sem(tsk); __exit_files(tsk); __exit_fs(tsk); @@ -930,9 +927,17 @@ #ifdef CONFIG_NUMA tsk->mempolicy = NULL; #endif /* - * If DEBUG_MUTEXES is on, make sure we are holding no locks: + * This must happen late, after the PID is not + * hashed anymore: */ - mutex_debug_check_no_locks_held(tsk); + if (unlikely(!list_empty(&tsk->pi_state_list))) + exit_pi_state_list(tsk); + if (unlikely(current->pi_state_cache)) + kfree(current->pi_state_cache); + /* + * Make sure we are holding no locks: + */ + debug_check_no_locks_held(tsk); if (tsk->io_context) exit_io_context(); @@ -1007,7 +1012,7 @@ asmlinkage void sys_exit_group(int error do_group_exit((error_code & 0xff) << 8); } -static int eligible_child(pid_t pid, int options, task_t *p) +static int eligible_child(pid_t pid, int options, struct task_struct *p) { if (pid > 0) { if (p->pid != pid) @@ -1048,12 +1053,13 @@ static int eligible_child(pid_t pid, int return 1; } -static int wait_noreap_copyout(task_t *p, pid_t pid, uid_t uid, +static int wait_noreap_copyout(struct task_struct *p, pid_t pid, uid_t uid, int why, int status, struct siginfo __user *infop, struct rusage __user *rusagep) { int retval = rusagep ? getrusage(p, RUSAGE_BOTH, rusagep) : 0; + put_task_struct(p); if (!retval) retval = put_user(SIGCHLD, &infop->si_signo); @@ -1078,7 +1084,7 @@ static int wait_noreap_copyout(task_t *p * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_zombie(task_t *p, int noreap, +static int wait_task_zombie(struct task_struct *p, int noreap, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { @@ -1240,8 +1246,8 @@ static int wait_task_zombie(task_t *p, i * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_stopped(task_t *p, int delayed_group_leader, int noreap, - struct siginfo __user *infop, +static int wait_task_stopped(struct task_struct *p, int delayed_group_leader, + int noreap, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { int retval, exit_code; @@ -1355,7 +1361,7 @@ bail_ref: * the lock and this task is uninteresting. If we return nonzero, we have * released the lock and the system call should return. */ -static int wait_task_continued(task_t *p, int noreap, +static int wait_task_continued(struct task_struct *p, int noreap, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { @@ -1441,7 +1447,7 @@ repeat: int ret; list_for_each(_p,&tsk->children) { - p = list_entry(_p,struct task_struct,sibling); + p = list_entry(_p, struct task_struct, sibling); ret = eligible_child(pid, options, p); if (!ret) @@ -1530,8 +1536,7 @@ check_continued: if (options & __WNOTHREAD) break; tsk = next_thread(tsk); - if (tsk->signal != current->signal) - BUG(); + BUG_ON(tsk->signal != current->signal); } while (tsk != current); read_unlock(&tasklist_lock); diff --git a/kernel/fork.c b/kernel/fork.c index ac8100e..56e4e07 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -11,7 +11,6 @@ * management can be a bitch. See 'mm/memory.c': 'copy_page_range()' */ -#include #include #include #include @@ -104,6 +103,7 @@ static kmem_cache_t *mm_cachep; void free_task(struct task_struct *tsk) { free_thread_info(tsk->thread_info); + rt_mutex_debug_task_free(tsk); free_task_struct(tsk); } EXPORT_SYMBOL(free_task); @@ -193,7 +193,10 @@ static inline int dup_mmap(struct mm_str down_write(&oldmm->mmap_sem); flush_cache_mm(oldmm); - down_write(&mm->mmap_sem); + /* + * Not linked in yet - no deadlock potential: + */ + down_write_nested(&mm->mmap_sem, SINGLE_DEPTH_NESTING); mm->locked_vm = 0; mm->mmap = NULL; @@ -368,6 +371,8 @@ void fastcall __mmdrop(struct mm_struct */ void mmput(struct mm_struct *mm) { + might_sleep(); + if (atomic_dec_and_test(&mm->mm_users)) { exit_aio(mm); exit_mmap(mm); @@ -623,6 +628,7 @@ out: /* * Allocate a new files structure and copy contents from the * passed in files structure. + * errorp will be valid only when the returned files_struct is NULL. */ static struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) { @@ -631,6 +637,7 @@ static struct files_struct *dup_fd(struc int open_files, size, i, expand; struct fdtable *old_fdt, *new_fdt; + *errorp = -ENOMEM; newf = alloc_files(); if (!newf) goto out; @@ -744,7 +751,6 @@ static int copy_files(unsigned long clon * break this. */ tsk->files = NULL; - error = -ENOMEM; newf = dup_fd(oldf, &error); if (!newf) goto out; @@ -871,6 +877,7 @@ static inline int copy_signal(unsigned l tsk->it_prof_expires = secs_to_cputime(sig->rlim[RLIMIT_CPU].rlim_cur); } + acct_init_pacct(&sig->pacct); return 0; } @@ -909,6 +916,15 @@ asmlinkage long sys_set_tid_address(int return current->pid; } +static inline void rt_mutex_init_task(struct task_struct *p) +{ +#ifdef CONFIG_RT_MUTEXES + spin_lock_init(&p->pi_lock); + plist_head_init(&p->pi_waiters, &p->pi_lock); + p->pi_blocked_on = NULL; +#endif +} + /* * This creates a new process as a copy of the old one, * but does not actually start it yet. @@ -917,13 +933,13 @@ asmlinkage long sys_set_tid_address(int * parts of the process environment (as per the clone * flags). The actual kick-off is left to the caller. */ -static task_t *copy_process(unsigned long clone_flags, - unsigned long stack_start, - struct pt_regs *regs, - unsigned long stack_size, - int __user *parent_tidptr, - int __user *child_tidptr, - int pid) +static struct task_struct *copy_process(unsigned long clone_flags, + unsigned long stack_start, + struct pt_regs *regs, + unsigned long stack_size, + int __user *parent_tidptr, + int __user *child_tidptr, + int pid) { int retval; struct task_struct *p = NULL; @@ -955,6 +971,10 @@ static task_t *copy_process(unsigned lon if (!p) goto fork_out; +#ifdef CONFIG_TRACE_IRQFLAGS + DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); + DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); +#endif retval = -EAGAIN; if (atomic_read(&p->user->processes) >= p->signal->rlim[RLIMIT_NPROC].rlim_cur) { @@ -989,13 +1009,10 @@ static task_t *copy_process(unsigned lon if (put_user(p->pid, parent_tidptr)) goto bad_fork_cleanup; - p->proc_dentry = NULL; - INIT_LIST_HEAD(&p->children); INIT_LIST_HEAD(&p->sibling); p->vfork_done = NULL; spin_lock_init(&p->alloc_lock); - spin_lock_init(&p->proc_lock); clear_tsk_thread_flag(p, TIF_SIGPENDING); init_sigpending(&p->pending); @@ -1032,6 +1049,28 @@ #ifdef CONFIG_NUMA } mpol_fix_fork_child_flag(p); #endif +#ifdef CONFIG_TRACE_IRQFLAGS + p->irq_events = 0; + p->hardirqs_enabled = 0; + p->hardirq_enable_ip = 0; + p->hardirq_enable_event = 0; + p->hardirq_disable_ip = _THIS_IP_; + p->hardirq_disable_event = 0; + p->softirqs_enabled = 1; + p->softirq_enable_ip = _THIS_IP_; + p->softirq_enable_event = 0; + p->softirq_disable_ip = 0; + p->softirq_disable_event = 0; + p->hardirq_context = 0; + p->softirq_context = 0; +#endif +#ifdef CONFIG_LOCKDEP + p->lockdep_depth = 0; /* no locks held yet */ + p->curr_chain_key = 0; + p->lockdep_recursion = 0; +#endif + + rt_mutex_init_task(p); #ifdef CONFIG_DEBUG_MUTEXES p->blocked_on = NULL; /* not blocked yet */ @@ -1075,6 +1114,9 @@ #endif #ifdef CONFIG_COMPAT p->compat_robust_list = NULL; #endif + INIT_LIST_HEAD(&p->pi_state_list); + p->pi_state_cache = NULL; + /* * sigaltstack should be cleared when sharing the same VM */ @@ -1155,18 +1197,6 @@ #endif } if (clone_flags & CLONE_THREAD) { - /* - * Important: if an exit-all has been started then - * do not create this new thread - the whole thread - * group is supposed to exit anyway. - */ - if (current->signal->flags & SIGNAL_GROUP_EXIT) { - spin_unlock(¤t->sighand->siglock); - write_unlock_irq(&tasklist_lock); - retval = -EAGAIN; - goto bad_fork_cleanup_namespace; - } - p->group_leader = current->group_leader; list_add_tail_rcu(&p->thread_group, &p->group_leader->thread_group); @@ -1264,9 +1294,9 @@ struct pt_regs * __devinit __attribute__ return regs; } -task_t * __devinit fork_idle(int cpu) +struct task_struct * __devinit fork_idle(int cpu) { - task_t *task; + struct task_struct *task; struct pt_regs regs; task = copy_process(CLONE_VM, 0, idle_regs(®s), 0, NULL, NULL, 0); diff --git a/kernel/futex.c b/kernel/futex.c index 5699c51..1dc98e4 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -12,6 +12,10 @@ * (C) Copyright 2006 Red Hat Inc, All Rights Reserved * Thanks to Thomas Gleixner for suggestions, analysis and fixes. * + * PI-futex support started by Ingo Molnar and Thomas Gleixner + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2006 Timesys Corp., Thomas Gleixner + * * Thanks to Ben LaHaise for yelling "hashed waitqueues" loudly * enough at me, Linus for the original (flawed) idea, Matthew * Kirkwood for proof-of-concept implementation. @@ -46,6 +50,8 @@ #include #include #include +#include "rtmutex_common.h" + #define FUTEX_HASHBITS (CONFIG_BASE_SMALL ? 4 : 8) /* @@ -63,7 +69,7 @@ union futex_key { int offset; } shared; struct { - unsigned long uaddr; + unsigned long address; struct mm_struct *mm; int offset; } private; @@ -75,6 +81,27 @@ union futex_key { }; /* + * Priority Inheritance state: + */ +struct futex_pi_state { + /* + * list of 'owned' pi_state instances - these have to be + * cleaned up in do_exit() if the task exits prematurely: + */ + struct list_head list; + + /* + * The PI object: + */ + struct rt_mutex pi_mutex; + + struct task_struct *owner; + atomic_t refcount; + + union futex_key key; +}; + +/* * We use this hashed waitqueue instead of a normal wait_queue_t, so * we can wake only the relevant ones (hashed queues may be shared). * @@ -87,15 +114,19 @@ struct futex_q { struct list_head list; wait_queue_head_t waiters; - /* Which hash list lock to use. */ + /* Which hash list lock to use: */ spinlock_t *lock_ptr; - /* Key which the futex is hashed on. */ + /* Key which the futex is hashed on: */ union futex_key key; - /* For fd, sigio sent using these. */ + /* For fd, sigio sent using these: */ int fd; struct file *filp; + + /* Optional priority inheritance state: */ + struct futex_pi_state *pi_state; + struct task_struct *task; }; /* @@ -144,8 +175,9 @@ static inline int match_futex(union fute * * Should be called with ¤t->mm->mmap_sem but NOT any spinlocks. */ -static int get_futex_key(unsigned long uaddr, union futex_key *key) +static int get_futex_key(u32 __user *uaddr, union futex_key *key) { + unsigned long address = (unsigned long)uaddr; struct mm_struct *mm = current->mm; struct vm_area_struct *vma; struct page *page; @@ -154,16 +186,16 @@ static int get_futex_key(unsigned long u /* * The futex address must be "naturally" aligned. */ - key->both.offset = uaddr % PAGE_SIZE; + key->both.offset = address % PAGE_SIZE; if (unlikely((key->both.offset % sizeof(u32)) != 0)) return -EINVAL; - uaddr -= key->both.offset; + address -= key->both.offset; /* * The futex is hashed differently depending on whether * it's in a shared or private mapping. So check vma first. */ - vma = find_extend_vma(mm, uaddr); + vma = find_extend_vma(mm, address); if (unlikely(!vma)) return -EFAULT; @@ -184,7 +216,7 @@ static int get_futex_key(unsigned long u */ if (likely(!(vma->vm_flags & VM_MAYSHARE))) { key->private.mm = mm; - key->private.uaddr = uaddr; + key->private.address = address; return 0; } @@ -194,7 +226,7 @@ static int get_futex_key(unsigned long u key->shared.inode = vma->vm_file->f_dentry->d_inode; key->both.offset++; /* Bit 0 of offset indicates inode-based key. */ if (likely(!(vma->vm_flags & VM_NONLINEAR))) { - key->shared.pgoff = (((uaddr - vma->vm_start) >> PAGE_SHIFT) + key->shared.pgoff = (((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff); return 0; } @@ -205,7 +237,7 @@ static int get_futex_key(unsigned long u * from swap. But that's a lot of code to duplicate here * for a rare case, so we simply fetch the page. */ - err = get_user_pages(current, mm, uaddr, 1, 0, 0, &page, NULL); + err = get_user_pages(current, mm, address, 1, 0, 0, &page, NULL); if (err >= 0) { key->shared.pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); @@ -246,18 +278,244 @@ static void drop_key_refs(union futex_ke } } -static inline int get_futex_value_locked(int *dest, int __user *from) +static inline int get_futex_value_locked(u32 *dest, u32 __user *from) { int ret; inc_preempt_count(); - ret = __copy_from_user_inatomic(dest, from, sizeof(int)); + ret = __copy_from_user_inatomic(dest, from, sizeof(u32)); dec_preempt_count(); return ret ? -EFAULT : 0; } /* + * Fault handling. Called with current->mm->mmap_sem held. + */ +static int futex_handle_fault(unsigned long address, int attempt) +{ + struct vm_area_struct * vma; + struct mm_struct *mm = current->mm; + + if (attempt >= 2 || !(vma = find_vma(mm, address)) || + vma->vm_start > address || !(vma->vm_flags & VM_WRITE)) + return -EFAULT; + + switch (handle_mm_fault(mm, vma, address, 1)) { + case VM_FAULT_MINOR: + current->min_flt++; + break; + case VM_FAULT_MAJOR: + current->maj_flt++; + break; + default: + return -EFAULT; + } + return 0; +} + +/* + * PI code: + */ +static int refill_pi_state_cache(void) +{ + struct futex_pi_state *pi_state; + + if (likely(current->pi_state_cache)) + return 0; + + pi_state = kmalloc(sizeof(*pi_state), GFP_KERNEL); + + if (!pi_state) + return -ENOMEM; + + memset(pi_state, 0, sizeof(*pi_state)); + INIT_LIST_HEAD(&pi_state->list); + /* pi_mutex gets initialized later */ + pi_state->owner = NULL; + atomic_set(&pi_state->refcount, 1); + + current->pi_state_cache = pi_state; + + return 0; +} + +static struct futex_pi_state * alloc_pi_state(void) +{ + struct futex_pi_state *pi_state = current->pi_state_cache; + + WARN_ON(!pi_state); + current->pi_state_cache = NULL; + + return pi_state; +} + +static void free_pi_state(struct futex_pi_state *pi_state) +{ + if (!atomic_dec_and_test(&pi_state->refcount)) + return; + + /* + * If pi_state->owner is NULL, the owner is most probably dying + * and has cleaned up the pi_state already + */ + if (pi_state->owner) { + spin_lock_irq(&pi_state->owner->pi_lock); + list_del_init(&pi_state->list); + spin_unlock_irq(&pi_state->owner->pi_lock); + + rt_mutex_proxy_unlock(&pi_state->pi_mutex, pi_state->owner); + } + + if (current->pi_state_cache) + kfree(pi_state); + else { + /* + * pi_state->list is already empty. + * clear pi_state->owner. + * refcount is at 0 - put it back to 1. + */ + pi_state->owner = NULL; + atomic_set(&pi_state->refcount, 1); + current->pi_state_cache = pi_state; + } +} + +/* + * Look up the task based on what TID userspace gave us. + * We dont trust it. + */ +static struct task_struct * futex_find_get_task(pid_t pid) +{ + struct task_struct *p; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + if (!p) + goto out_unlock; + if ((current->euid != p->euid) && (current->euid != p->uid)) { + p = NULL; + goto out_unlock; + } + if (p->state == EXIT_ZOMBIE || p->exit_state == EXIT_ZOMBIE) { + p = NULL; + goto out_unlock; + } + get_task_struct(p); +out_unlock: + read_unlock(&tasklist_lock); + + return p; +} + +/* + * This task is holding PI mutexes at exit time => bad. + * Kernel cleans up PI-state, but userspace is likely hosed. + * (Robust-futex cleanup is separate and might save the day for userspace.) + */ +void exit_pi_state_list(struct task_struct *curr) +{ + struct futex_hash_bucket *hb; + struct list_head *next, *head = &curr->pi_state_list; + struct futex_pi_state *pi_state; + union futex_key key; + + /* + * We are a ZOMBIE and nobody can enqueue itself on + * pi_state_list anymore, but we have to be careful + * versus waiters unqueueing themselfs + */ + spin_lock_irq(&curr->pi_lock); + while (!list_empty(head)) { + + next = head->next; + pi_state = list_entry(next, struct futex_pi_state, list); + key = pi_state->key; + spin_unlock_irq(&curr->pi_lock); + + hb = hash_futex(&key); + spin_lock(&hb->lock); + + spin_lock_irq(&curr->pi_lock); + if (head->next != next) { + spin_unlock(&hb->lock); + continue; + } + + list_del_init(&pi_state->list); + + WARN_ON(pi_state->owner != curr); + + pi_state->owner = NULL; + spin_unlock_irq(&curr->pi_lock); + + rt_mutex_unlock(&pi_state->pi_mutex); + + spin_unlock(&hb->lock); + + spin_lock_irq(&curr->pi_lock); + } + spin_unlock_irq(&curr->pi_lock); +} + +static int +lookup_pi_state(u32 uval, struct futex_hash_bucket *hb, struct futex_q *me) +{ + struct futex_pi_state *pi_state = NULL; + struct futex_q *this, *next; + struct list_head *head; + struct task_struct *p; + pid_t pid; + + head = &hb->chain; + + list_for_each_entry_safe(this, next, head, list) { + if (match_futex (&this->key, &me->key)) { + /* + * Another waiter already exists - bump up + * the refcount and return its pi_state: + */ + pi_state = this->pi_state; + atomic_inc(&pi_state->refcount); + me->pi_state = pi_state; + + return 0; + } + } + + /* + * We are the first waiter - try to look up the real owner and + * attach the new pi_state to it: + */ + pid = uval & FUTEX_TID_MASK; + p = futex_find_get_task(pid); + if (!p) + return -ESRCH; + + pi_state = alloc_pi_state(); + + /* + * Initialize the pi_mutex in locked state and make 'p' + * the owner of it: + */ + rt_mutex_init_proxy_locked(&pi_state->pi_mutex, p); + + /* Store the key for possible exit cleanups: */ + pi_state->key = me->key; + + spin_lock_irq(&p->pi_lock); + list_add(&pi_state->list, &p->pi_state_list); + pi_state->owner = p; + spin_unlock_irq(&p->pi_lock); + + put_task_struct(p); + + me->pi_state = pi_state; + + return 0; +} + +/* * The hash bucket lock must be held when this is called. * Afterwards, the futex_q must not be accessed. */ @@ -284,16 +542,96 @@ static void wake_futex(struct futex_q *q q->lock_ptr = NULL; } +static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this) +{ + struct task_struct *new_owner; + struct futex_pi_state *pi_state = this->pi_state; + u32 curval, newval; + + if (!pi_state) + return -EINVAL; + + new_owner = rt_mutex_next_owner(&pi_state->pi_mutex); + + /* + * This happens when we have stolen the lock and the original + * pending owner did not enqueue itself back on the rt_mutex. + * Thats not a tragedy. We know that way, that a lock waiter + * is on the fly. We make the futex_q waiter the pending owner. + */ + if (!new_owner) + new_owner = this->task; + + /* + * We pass it to the next owner. (The WAITERS bit is always + * kept enabled while there is PI state around. We must also + * preserve the owner died bit.) + */ + newval = (uval & FUTEX_OWNER_DIED) | FUTEX_WAITERS | new_owner->pid; + + inc_preempt_count(); + curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); + dec_preempt_count(); + + if (curval == -EFAULT) + return -EFAULT; + if (curval != uval) + return -EINVAL; + + list_del_init(&pi_state->owner->pi_state_list); + list_add(&pi_state->list, &new_owner->pi_state_list); + pi_state->owner = new_owner; + rt_mutex_unlock(&pi_state->pi_mutex); + + return 0; +} + +static int unlock_futex_pi(u32 __user *uaddr, u32 uval) +{ + u32 oldval; + + /* + * There is no waiter, so we unlock the futex. The owner died + * bit has not to be preserved here. We are the owner: + */ + inc_preempt_count(); + oldval = futex_atomic_cmpxchg_inatomic(uaddr, uval, 0); + dec_preempt_count(); + + if (oldval == -EFAULT) + return oldval; + if (oldval != uval) + return -EAGAIN; + + return 0; +} + +/* + * Express the locking dependencies for lockdep: + */ +static inline void +double_lock_hb(struct futex_hash_bucket *hb1, struct futex_hash_bucket *hb2) +{ + if (hb1 <= hb2) { + spin_lock(&hb1->lock); + if (hb1 < hb2) + spin_lock_nested(&hb2->lock, SINGLE_DEPTH_NESTING); + } else { /* hb1 > hb2 */ + spin_lock(&hb2->lock); + spin_lock_nested(&hb1->lock, SINGLE_DEPTH_NESTING); + } +} + /* * Wake up all waiters hashed on the physical page that is mapped * to this virtual address: */ -static int futex_wake(unsigned long uaddr, int nr_wake) +static int futex_wake(u32 __user *uaddr, int nr_wake) { - union futex_key key; - struct futex_hash_bucket *bh; - struct list_head *head; + struct futex_hash_bucket *hb; struct futex_q *this, *next; + struct list_head *head; + union futex_key key; int ret; down_read(¤t->mm->mmap_sem); @@ -302,19 +640,23 @@ static int futex_wake(unsigned long uadd if (unlikely(ret != 0)) goto out; - bh = hash_futex(&key); - spin_lock(&bh->lock); - head = &bh->chain; + hb = hash_futex(&key); + spin_lock(&hb->lock); + head = &hb->chain; list_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key)) { + if (this->pi_state) { + ret = -EINVAL; + break; + } wake_futex(this); if (++ret >= nr_wake) break; } } - spin_unlock(&bh->lock); + spin_unlock(&hb->lock); out: up_read(¤t->mm->mmap_sem); return ret; @@ -324,10 +666,12 @@ out: * Wake up all waiters hashed on the physical page that is mapped * to this virtual address: */ -static int futex_wake_op(unsigned long uaddr1, unsigned long uaddr2, int nr_wake, int nr_wake2, int op) +static int +futex_wake_op(u32 __user *uaddr1, u32 __user *uaddr2, + int nr_wake, int nr_wake2, int op) { union futex_key key1, key2; - struct futex_hash_bucket *bh1, *bh2; + struct futex_hash_bucket *hb1, *hb2; struct list_head *head; struct futex_q *this, *next; int ret, op_ret, attempt = 0; @@ -342,27 +686,25 @@ retryfull: if (unlikely(ret != 0)) goto out; - bh1 = hash_futex(&key1); - bh2 = hash_futex(&key2); + hb1 = hash_futex(&key1); + hb2 = hash_futex(&key2); retry: - if (bh1 < bh2) - spin_lock(&bh1->lock); - spin_lock(&bh2->lock); - if (bh1 > bh2) - spin_lock(&bh1->lock); + double_lock_hb(hb1, hb2); - op_ret = futex_atomic_op_inuser(op, (int __user *)uaddr2); + op_ret = futex_atomic_op_inuser(op, uaddr2); if (unlikely(op_ret < 0)) { - int dummy; + u32 dummy; - spin_unlock(&bh1->lock); - if (bh1 != bh2) - spin_unlock(&bh2->lock); + spin_unlock(&hb1->lock); + if (hb1 != hb2) + spin_unlock(&hb2->lock); #ifndef CONFIG_MMU - /* we don't get EFAULT from MMU faults if we don't have an MMU, - * but we might get them from range checking */ + /* + * we don't get EFAULT from MMU faults if we don't have an MMU, + * but we might get them from range checking + */ ret = op_ret; goto out; #endif @@ -372,47 +714,34 @@ #endif goto out; } - /* futex_atomic_op_inuser needs to both read and write + /* + * futex_atomic_op_inuser needs to both read and write * *(int __user *)uaddr2, but we can't modify it * non-atomically. Therefore, if get_user below is not * enough, we need to handle the fault ourselves, while - * still holding the mmap_sem. */ + * still holding the mmap_sem. + */ if (attempt++) { - struct vm_area_struct * vma; - struct mm_struct *mm = current->mm; - - ret = -EFAULT; - if (attempt >= 2 || - !(vma = find_vma(mm, uaddr2)) || - vma->vm_start > uaddr2 || - !(vma->vm_flags & VM_WRITE)) - goto out; - - switch (handle_mm_fault(mm, vma, uaddr2, 1)) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - default: + if (futex_handle_fault((unsigned long)uaddr2, + attempt)) goto out; - } goto retry; } - /* If we would have faulted, release mmap_sem, - * fault it in and start all over again. */ + /* + * If we would have faulted, release mmap_sem, + * fault it in and start all over again. + */ up_read(¤t->mm->mmap_sem); - ret = get_user(dummy, (int __user *)uaddr2); + ret = get_user(dummy, uaddr2); if (ret) return ret; goto retryfull; } - head = &bh1->chain; + head = &hb1->chain; list_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key1)) { @@ -423,7 +752,7 @@ #endif } if (op_ret > 0) { - head = &bh2->chain; + head = &hb2->chain; op_ret = 0; list_for_each_entry_safe(this, next, head, list) { @@ -436,9 +765,9 @@ #endif ret += op_ret; } - spin_unlock(&bh1->lock); - if (bh1 != bh2) - spin_unlock(&bh2->lock); + spin_unlock(&hb1->lock); + if (hb1 != hb2) + spin_unlock(&hb2->lock); out: up_read(¤t->mm->mmap_sem); return ret; @@ -448,11 +777,11 @@ out: * Requeue all waiters hashed on one physical page to another * physical page. */ -static int futex_requeue(unsigned long uaddr1, unsigned long uaddr2, - int nr_wake, int nr_requeue, int *valp) +static int futex_requeue(u32 __user *uaddr1, u32 __user *uaddr2, + int nr_wake, int nr_requeue, u32 *cmpval) { union futex_key key1, key2; - struct futex_hash_bucket *bh1, *bh2; + struct futex_hash_bucket *hb1, *hb2; struct list_head *head1; struct futex_q *this, *next; int ret, drop_count = 0; @@ -467,68 +796,68 @@ static int futex_requeue(unsigned long u if (unlikely(ret != 0)) goto out; - bh1 = hash_futex(&key1); - bh2 = hash_futex(&key2); + hb1 = hash_futex(&key1); + hb2 = hash_futex(&key2); - if (bh1 < bh2) - spin_lock(&bh1->lock); - spin_lock(&bh2->lock); - if (bh1 > bh2) - spin_lock(&bh1->lock); + double_lock_hb(hb1, hb2); - if (likely(valp != NULL)) { - int curval; + if (likely(cmpval != NULL)) { + u32 curval; - ret = get_futex_value_locked(&curval, (int __user *)uaddr1); + ret = get_futex_value_locked(&curval, uaddr1); if (unlikely(ret)) { - spin_unlock(&bh1->lock); - if (bh1 != bh2) - spin_unlock(&bh2->lock); + spin_unlock(&hb1->lock); + if (hb1 != hb2) + spin_unlock(&hb2->lock); - /* If we would have faulted, release mmap_sem, fault + /* + * If we would have faulted, release mmap_sem, fault * it in and start all over again. */ up_read(¤t->mm->mmap_sem); - ret = get_user(curval, (int __user *)uaddr1); + ret = get_user(curval, uaddr1); if (!ret) goto retry; return ret; } - if (curval != *valp) { + if (curval != *cmpval) { ret = -EAGAIN; goto out_unlock; } } - head1 = &bh1->chain; + head1 = &hb1->chain; list_for_each_entry_safe(this, next, head1, list) { if (!match_futex (&this->key, &key1)) continue; if (++ret <= nr_wake) { wake_futex(this); } else { - list_move_tail(&this->list, &bh2->chain); - this->lock_ptr = &bh2->lock; + /* + * If key1 and key2 hash to the same bucket, no need to + * requeue. + */ + if (likely(head1 != &hb2->chain)) { + list_move_tail(&this->list, &hb2->chain); + this->lock_ptr = &hb2->lock; + } this->key = key2; get_key_refs(&key2); drop_count++; if (ret - nr_wake >= nr_requeue) break; - /* Make sure to stop if key1 == key2 */ - if (head1 == &bh2->chain && head1 != &next->list) - head1 = &this->list; } } out_unlock: - spin_unlock(&bh1->lock); - if (bh1 != bh2) - spin_unlock(&bh2->lock); + spin_unlock(&hb1->lock); + if (hb1 != hb2) + spin_unlock(&hb2->lock); /* drop_key_refs() must be called outside the spinlocks. */ while (--drop_count >= 0) @@ -543,7 +872,7 @@ out: static inline struct futex_hash_bucket * queue_lock(struct futex_q *q, int fd, struct file *filp) { - struct futex_hash_bucket *bh; + struct futex_hash_bucket *hb; q->fd = fd; q->filp = filp; @@ -551,23 +880,24 @@ queue_lock(struct futex_q *q, int fd, st init_waitqueue_head(&q->waiters); get_key_refs(&q->key); - bh = hash_futex(&q->key); - q->lock_ptr = &bh->lock; + hb = hash_futex(&q->key); + q->lock_ptr = &hb->lock; - spin_lock(&bh->lock); - return bh; + spin_lock(&hb->lock); + return hb; } -static inline void __queue_me(struct futex_q *q, struct futex_hash_bucket *bh) +static inline void __queue_me(struct futex_q *q, struct futex_hash_bucket *hb) { - list_add_tail(&q->list, &bh->chain); - spin_unlock(&bh->lock); + list_add_tail(&q->list, &hb->chain); + q->task = current; + spin_unlock(&hb->lock); } static inline void -queue_unlock(struct futex_q *q, struct futex_hash_bucket *bh) +queue_unlock(struct futex_q *q, struct futex_hash_bucket *hb) { - spin_unlock(&bh->lock); + spin_unlock(&hb->lock); drop_key_refs(&q->key); } @@ -579,16 +909,17 @@ queue_unlock(struct futex_q *q, struct f /* The key must be already stored in q->key. */ static void queue_me(struct futex_q *q, int fd, struct file *filp) { - struct futex_hash_bucket *bh; - bh = queue_lock(q, fd, filp); - __queue_me(q, bh); + struct futex_hash_bucket *hb; + + hb = queue_lock(q, fd, filp); + __queue_me(q, hb); } /* Return 1 if we were still queued (ie. 0 means we were woken) */ static int unqueue_me(struct futex_q *q) { - int ret = 0; spinlock_t *lock_ptr; + int ret = 0; /* In the common case we don't take the spinlock, which is nice. */ retry: @@ -614,6 +945,9 @@ static int unqueue_me(struct futex_q *q) } WARN_ON(list_empty(&q->list)); list_del(&q->list); + + BUG_ON(q->pi_state); + spin_unlock(lock_ptr); ret = 1; } @@ -622,21 +956,42 @@ static int unqueue_me(struct futex_q *q) return ret; } -static int futex_wait(unsigned long uaddr, int val, unsigned long time) +/* + * PI futexes can not be requeued and must remove themself from the + * hash bucket. The hash bucket lock is held on entry and dropped here. + */ +static void unqueue_me_pi(struct futex_q *q, struct futex_hash_bucket *hb) { - DECLARE_WAITQUEUE(wait, current); - int ret, curval; + WARN_ON(list_empty(&q->list)); + list_del(&q->list); + + BUG_ON(!q->pi_state); + free_pi_state(q->pi_state); + q->pi_state = NULL; + + spin_unlock(&hb->lock); + + drop_key_refs(&q->key); +} + +static int futex_wait(u32 __user *uaddr, u32 val, unsigned long time) +{ + struct task_struct *curr = current; + DECLARE_WAITQUEUE(wait, curr); + struct futex_hash_bucket *hb; struct futex_q q; - struct futex_hash_bucket *bh; + u32 uval; + int ret; + q.pi_state = NULL; retry: - down_read(¤t->mm->mmap_sem); + down_read(&curr->mm->mmap_sem); ret = get_futex_key(uaddr, &q.key); if (unlikely(ret != 0)) goto out_release_sem; - bh = queue_lock(&q, -1, NULL); + hb = queue_lock(&q, -1, NULL); /* * Access the page AFTER the futex is queued. @@ -658,37 +1013,35 @@ static int futex_wait(unsigned long uadd * We hold the mmap semaphore, so the mapping cannot have changed * since we looked it up in get_futex_key. */ - - ret = get_futex_value_locked(&curval, (int __user *)uaddr); + ret = get_futex_value_locked(&uval, uaddr); if (unlikely(ret)) { - queue_unlock(&q, bh); + queue_unlock(&q, hb); - /* If we would have faulted, release mmap_sem, fault it in and + /* + * If we would have faulted, release mmap_sem, fault it in and * start all over again. */ - up_read(¤t->mm->mmap_sem); + up_read(&curr->mm->mmap_sem); - ret = get_user(curval, (int __user *)uaddr); + ret = get_user(uval, uaddr); if (!ret) goto retry; return ret; } - if (curval != val) { - ret = -EWOULDBLOCK; - queue_unlock(&q, bh); - goto out_release_sem; - } + ret = -EWOULDBLOCK; + if (uval != val) + goto out_unlock_release_sem; /* Only actually queue if *uaddr contained val. */ - __queue_me(&q, bh); + __queue_me(&q, hb); /* * Now the futex is queued and we have checked the data, we * don't want to hold mmap_sem while we sleep. - */ - up_read(¤t->mm->mmap_sem); + */ + up_read(&curr->mm->mmap_sem); /* * There might have been scheduling since the queue_me(), as we @@ -720,12 +1073,421 @@ static int futex_wait(unsigned long uadd return 0; if (time == 0) return -ETIMEDOUT; - /* We expect signal_pending(current), but another thread may - * have handled it for us already. */ + /* + * We expect signal_pending(current), but another thread may + * have handled it for us already. + */ return -EINTR; + out_unlock_release_sem: + queue_unlock(&q, hb); + out_release_sem: + up_read(&curr->mm->mmap_sem); + return ret; +} + +/* + * Userspace tried a 0 -> TID atomic transition of the futex value + * and failed. The kernel side here does the whole locking operation: + * if there are waiters then it will block, it does PI, etc. (Due to + * races the kernel might see a 0 value of the futex too.) + */ +static int do_futex_lock_pi(u32 __user *uaddr, int detect, int trylock, + struct hrtimer_sleeper *to) +{ + struct task_struct *curr = current; + struct futex_hash_bucket *hb; + u32 uval, newval, curval; + struct futex_q q; + int ret, attempt = 0; + + if (refill_pi_state_cache()) + return -ENOMEM; + + q.pi_state = NULL; + retry: + down_read(&curr->mm->mmap_sem); + + ret = get_futex_key(uaddr, &q.key); + if (unlikely(ret != 0)) + goto out_release_sem; + + hb = queue_lock(&q, -1, NULL); + + retry_locked: + /* + * To avoid races, we attempt to take the lock here again + * (by doing a 0 -> TID atomic cmpxchg), while holding all + * the locks. It will most likely not succeed. + */ + newval = current->pid; + + inc_preempt_count(); + curval = futex_atomic_cmpxchg_inatomic(uaddr, 0, newval); + dec_preempt_count(); + + if (unlikely(curval == -EFAULT)) + goto uaddr_faulted; + + /* We own the lock already */ + if (unlikely((curval & FUTEX_TID_MASK) == current->pid)) { + if (!detect && 0) + force_sig(SIGKILL, current); + ret = -EDEADLK; + goto out_unlock_release_sem; + } + + /* + * Surprise - we got the lock. Just return + * to userspace: + */ + if (unlikely(!curval)) + goto out_unlock_release_sem; + + uval = curval; + newval = uval | FUTEX_WAITERS; + + inc_preempt_count(); + curval = futex_atomic_cmpxchg_inatomic(uaddr, uval, newval); + dec_preempt_count(); + + if (unlikely(curval == -EFAULT)) + goto uaddr_faulted; + if (unlikely(curval != uval)) + goto retry_locked; + + /* + * We dont have the lock. Look up the PI state (or create it if + * we are the first waiter): + */ + ret = lookup_pi_state(uval, hb, &q); + + if (unlikely(ret)) { + /* + * There were no waiters and the owner task lookup + * failed. When the OWNER_DIED bit is set, then we + * know that this is a robust futex and we actually + * take the lock. This is safe as we are protected by + * the hash bucket lock. We also set the waiters bit + * unconditionally here, to simplify glibc handling of + * multiple tasks racing to acquire the lock and + * cleanup the problems which were left by the dead + * owner. + */ + if (curval & FUTEX_OWNER_DIED) { + uval = newval; + newval = current->pid | + FUTEX_OWNER_DIED | FUTEX_WAITERS; + + inc_preempt_count(); + curval = futex_atomic_cmpxchg_inatomic(uaddr, + uval, newval); + dec_preempt_count(); + + if (unlikely(curval == -EFAULT)) + goto uaddr_faulted; + if (unlikely(curval != uval)) + goto retry_locked; + ret = 0; + } + goto out_unlock_release_sem; + } + + /* + * Only actually queue now that the atomic ops are done: + */ + __queue_me(&q, hb); + + /* + * Now the futex is queued and we have checked the data, we + * don't want to hold mmap_sem while we sleep. + */ + up_read(&curr->mm->mmap_sem); + + WARN_ON(!q.pi_state); + /* + * Block on the PI mutex: + */ + if (!trylock) + ret = rt_mutex_timed_lock(&q.pi_state->pi_mutex, to, 1); + else { + ret = rt_mutex_trylock(&q.pi_state->pi_mutex); + /* Fixup the trylock return value: */ + ret = ret ? 0 : -EWOULDBLOCK; + } + + down_read(&curr->mm->mmap_sem); + spin_lock(q.lock_ptr); + + /* + * Got the lock. We might not be the anticipated owner if we + * did a lock-steal - fix up the PI-state in that case. + */ + if (!ret && q.pi_state->owner != curr) { + u32 newtid = current->pid | FUTEX_WAITERS; + + /* Owner died? */ + if (q.pi_state->owner != NULL) { + spin_lock_irq(&q.pi_state->owner->pi_lock); + list_del_init(&q.pi_state->list); + spin_unlock_irq(&q.pi_state->owner->pi_lock); + } else + newtid |= FUTEX_OWNER_DIED; + + q.pi_state->owner = current; + + spin_lock_irq(¤t->pi_lock); + list_add(&q.pi_state->list, ¤t->pi_state_list); + spin_unlock_irq(¤t->pi_lock); + + /* Unqueue and drop the lock */ + unqueue_me_pi(&q, hb); + up_read(&curr->mm->mmap_sem); + /* + * We own it, so we have to replace the pending owner + * TID. This must be atomic as we have preserve the + * owner died bit here. + */ + ret = get_user(uval, uaddr); + while (!ret) { + newval = (uval & FUTEX_OWNER_DIED) | newtid; + curval = futex_atomic_cmpxchg_inatomic(uaddr, + uval, newval); + if (curval == -EFAULT) + ret = -EFAULT; + if (curval == uval) + break; + uval = curval; + } + } else { + /* + * Catch the rare case, where the lock was released + * when we were on the way back before we locked + * the hash bucket. + */ + if (ret && q.pi_state->owner == curr) { + if (rt_mutex_trylock(&q.pi_state->pi_mutex)) + ret = 0; + } + /* Unqueue and drop the lock */ + unqueue_me_pi(&q, hb); + up_read(&curr->mm->mmap_sem); + } + + if (!detect && ret == -EDEADLK && 0) + force_sig(SIGKILL, current); + + return ret; + + out_unlock_release_sem: + queue_unlock(&q, hb); + + out_release_sem: + up_read(&curr->mm->mmap_sem); + return ret; + + uaddr_faulted: + /* + * We have to r/w *(int __user *)uaddr, but we can't modify it + * non-atomically. Therefore, if get_user below is not + * enough, we need to handle the fault ourselves, while + * still holding the mmap_sem. + */ + if (attempt++) { + if (futex_handle_fault((unsigned long)uaddr, attempt)) + goto out_unlock_release_sem; + + goto retry_locked; + } + + queue_unlock(&q, hb); + up_read(&curr->mm->mmap_sem); + + ret = get_user(uval, uaddr); + if (!ret && (uval != -EFAULT)) + goto retry; + + return ret; +} + +/* + * Restart handler + */ +static long futex_lock_pi_restart(struct restart_block *restart) +{ + struct hrtimer_sleeper timeout, *to = NULL; + int ret; + + restart->fn = do_no_restart_syscall; + + if (restart->arg2 || restart->arg3) { + to = &timeout; + hrtimer_init(&to->timer, CLOCK_REALTIME, HRTIMER_ABS); + hrtimer_init_sleeper(to, current); + to->timer.expires.tv64 = ((u64)restart->arg1 << 32) | + (u64) restart->arg0; + } + + pr_debug("lock_pi restart: %p, %d (%d)\n", + (u32 __user *)restart->arg0, current->pid); + + ret = do_futex_lock_pi((u32 __user *)restart->arg0, restart->arg1, + 0, to); + + if (ret != -EINTR) + return ret; + + restart->fn = futex_lock_pi_restart; + + /* The other values are filled in */ + return -ERESTART_RESTARTBLOCK; +} + +/* + * Called from the syscall entry below. + */ +static int futex_lock_pi(u32 __user *uaddr, int detect, unsigned long sec, + long nsec, int trylock) +{ + struct hrtimer_sleeper timeout, *to = NULL; + struct restart_block *restart; + int ret; + + if (sec != MAX_SCHEDULE_TIMEOUT) { + to = &timeout; + hrtimer_init(&to->timer, CLOCK_REALTIME, HRTIMER_ABS); + hrtimer_init_sleeper(to, current); + to->timer.expires = ktime_set(sec, nsec); + } + + ret = do_futex_lock_pi(uaddr, detect, trylock, to); + + if (ret != -EINTR) + return ret; + + pr_debug("lock_pi interrupted: %p, %d (%d)\n", uaddr, current->pid); + + restart = ¤t_thread_info()->restart_block; + restart->fn = futex_lock_pi_restart; + restart->arg0 = (unsigned long) uaddr; + restart->arg1 = detect; + if (to) { + restart->arg2 = to->timer.expires.tv64 & 0xFFFFFFFF; + restart->arg3 = to->timer.expires.tv64 >> 32; + } else + restart->arg2 = restart->arg3 = 0; + + return -ERESTART_RESTARTBLOCK; +} + +/* + * Userspace attempted a TID -> 0 atomic transition, and failed. + * This is the in-kernel slowpath: we look up the PI state (if any), + * and do the rt-mutex unlock. + */ +static int futex_unlock_pi(u32 __user *uaddr) +{ + struct futex_hash_bucket *hb; + struct futex_q *this, *next; + u32 uval; + struct list_head *head; + union futex_key key; + int ret, attempt = 0; + +retry: + if (get_user(uval, uaddr)) + return -EFAULT; + /* + * We release only a lock we actually own: + */ + if ((uval & FUTEX_TID_MASK) != current->pid) + return -EPERM; + /* + * First take all the futex related locks: + */ + down_read(¤t->mm->mmap_sem); + + ret = get_futex_key(uaddr, &key); + if (unlikely(ret != 0)) + goto out; + + hb = hash_futex(&key); + spin_lock(&hb->lock); + +retry_locked: + /* + * To avoid races, try to do the TID -> 0 atomic transition + * again. If it succeeds then we can return without waking + * anyone else up: + */ + inc_preempt_count(); + uval = futex_atomic_cmpxchg_inatomic(uaddr, current->pid, 0); + dec_preempt_count(); + + if (unlikely(uval == -EFAULT)) + goto pi_faulted; + /* + * Rare case: we managed to release the lock atomically, + * no need to wake anyone else up: + */ + if (unlikely(uval == current->pid)) + goto out_unlock; + + /* + * Ok, other tasks may need to be woken up - check waiters + * and do the wakeup if necessary: + */ + head = &hb->chain; + + list_for_each_entry_safe(this, next, head, list) { + if (!match_futex (&this->key, &key)) + continue; + ret = wake_futex_pi(uaddr, uval, this); + /* + * The atomic access to the futex value + * generated a pagefault, so retry the + * user-access and the wakeup: + */ + if (ret == -EFAULT) + goto pi_faulted; + goto out_unlock; + } + /* + * No waiters - kernel unlocks the futex: + */ + ret = unlock_futex_pi(uaddr, uval); + if (ret == -EFAULT) + goto pi_faulted; + +out_unlock: + spin_unlock(&hb->lock); +out: up_read(¤t->mm->mmap_sem); + + return ret; + +pi_faulted: + /* + * We have to r/w *(int __user *)uaddr, but we can't modify it + * non-atomically. Therefore, if get_user below is not + * enough, we need to handle the fault ourselves, while + * still holding the mmap_sem. + */ + if (attempt++) { + if (futex_handle_fault((unsigned long)uaddr, attempt)) + goto out_unlock; + + goto retry_locked; + } + + spin_unlock(&hb->lock); + up_read(¤t->mm->mmap_sem); + + ret = get_user(uval, uaddr); + if (!ret && (uval != -EFAULT)) + goto retry; + return ret; } @@ -735,6 +1497,7 @@ static int futex_close(struct inode *ino unqueue_me(q); kfree(q); + return 0; } @@ -766,7 +1529,7 @@ static struct file_operations futex_fops * Signal allows caller to avoid the race which would occur if they * set the sigio stuff up afterwards. */ -static int futex_fd(unsigned long uaddr, int signal) +static int futex_fd(u32 __user *uaddr, int signal) { struct futex_q *q; struct file *filp; @@ -803,6 +1566,7 @@ static int futex_fd(unsigned long uaddr, err = -ENOMEM; goto error; } + q->pi_state = NULL; down_read(¤t->mm->mmap_sem); err = get_futex_key(uaddr, &q->key); @@ -840,7 +1604,7 @@ error: * Implementation: user-space maintains a per-thread list of locks it * is holding. Upon do_exit(), the kernel carefully walks this list, * and marks all locks that are owned by this thread with the - * FUTEX_OWNER_DEAD bit, and wakes up a waiter (if any). The list is + * FUTEX_OWNER_DIED bit, and wakes up a waiter (if any). The list is * always manipulated with the lock held, so the list is private and * per-thread. Userspace also maintains a per-thread 'list_op_pending' * field, to allow the kernel to clean up if the thread dies after @@ -915,7 +1679,7 @@ err_unlock: */ int handle_futex_death(u32 __user *uaddr, struct task_struct *curr) { - u32 uval; + u32 uval, nval; retry: if (get_user(uval, uaddr)) @@ -932,12 +1696,16 @@ retry: * thread-death.) The rest of the cleanup is done in * userspace. */ - if (futex_atomic_cmpxchg_inatomic(uaddr, uval, - uval | FUTEX_OWNER_DIED) != uval) + nval = futex_atomic_cmpxchg_inatomic(uaddr, uval, + uval | FUTEX_OWNER_DIED); + if (nval == -EFAULT) + return -1; + + if (nval != uval) goto retry; if (uval & FUTEX_WAITERS) - futex_wake((unsigned long)uaddr, 1); + futex_wake(uaddr, 1); } return 0; } @@ -978,7 +1746,7 @@ void exit_robust_list(struct task_struct while (entry != &head->list) { /* * A pending lock might already be on the list, so - * dont process it twice: + * don't process it twice: */ if (entry != pending) if (handle_futex_death((void *)entry + futex_offset, @@ -999,8 +1767,8 @@ void exit_robust_list(struct task_struct } } -long do_futex(unsigned long uaddr, int op, int val, unsigned long timeout, - unsigned long uaddr2, int val2, int val3) +long do_futex(u32 __user *uaddr, int op, u32 val, unsigned long timeout, + u32 __user *uaddr2, u32 val2, u32 val3) { int ret; @@ -1024,6 +1792,15 @@ long do_futex(unsigned long uaddr, int o case FUTEX_WAKE_OP: ret = futex_wake_op(uaddr, uaddr2, val, val2, val3); break; + case FUTEX_LOCK_PI: + ret = futex_lock_pi(uaddr, val, timeout, val2, 0); + break; + case FUTEX_UNLOCK_PI: + ret = futex_unlock_pi(uaddr); + break; + case FUTEX_TRYLOCK_PI: + ret = futex_lock_pi(uaddr, 0, timeout, val2, 1); + break; default: ret = -ENOSYS; } @@ -1031,36 +1808,40 @@ long do_futex(unsigned long uaddr, int o } -asmlinkage long sys_futex(u32 __user *uaddr, int op, int val, +asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, struct timespec __user *utime, u32 __user *uaddr2, - int val3) + u32 val3) { struct timespec t; unsigned long timeout = MAX_SCHEDULE_TIMEOUT; - int val2 = 0; + u32 val2 = 0; - if (utime && (op == FUTEX_WAIT)) { + if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) { if (copy_from_user(&t, utime, sizeof(t)) != 0) return -EFAULT; if (!timespec_valid(&t)) return -EINVAL; - timeout = timespec_to_jiffies(&t) + 1; + if (op == FUTEX_WAIT) + timeout = timespec_to_jiffies(&t) + 1; + else { + timeout = t.tv_sec; + val2 = t.tv_nsec; + } } /* * requeue parameter in 'utime' if op == FUTEX_REQUEUE. */ - if (op >= FUTEX_REQUEUE) - val2 = (int) (unsigned long) utime; + if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE) + val2 = (u32) (unsigned long) utime; - return do_futex((unsigned long)uaddr, op, val, timeout, - (unsigned long)uaddr2, val2, val3); + return do_futex(uaddr, op, val, timeout, uaddr2, val2, val3); } -static struct super_block * -futexfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int futexfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) { - return get_sb_pseudo(fs_type, "futex", NULL, 0xBAD1DEA); + return get_sb_pseudo(fs_type, "futex", NULL, 0xBAD1DEA, mnt); } static struct file_system_type futex_fs_type = { diff --git a/kernel/futex_compat.c b/kernel/futex_compat.c index 1ab6a0e..d1d92b4 100644 --- a/kernel/futex_compat.c +++ b/kernel/futex_compat.c @@ -129,16 +129,20 @@ asmlinkage long compat_sys_futex(u32 __u unsigned long timeout = MAX_SCHEDULE_TIMEOUT; int val2 = 0; - if (utime && (op == FUTEX_WAIT)) { + if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) { if (get_compat_timespec(&t, utime)) return -EFAULT; if (!timespec_valid(&t)) return -EINVAL; - timeout = timespec_to_jiffies(&t) + 1; + if (op == FUTEX_WAIT) + timeout = timespec_to_jiffies(&t) + 1; + else { + timeout = t.tv_sec; + val2 = t.tv_nsec; + } } - if (op >= FUTEX_REQUEUE) + if (op == FUTEX_REQUEUE || op == FUTEX_CMP_REQUEUE) val2 = (int) (unsigned long) utime; - return do_futex((unsigned long)uaddr, op, val, timeout, - (unsigned long)uaddr2, val2, val3); + return do_futex(uaddr, op, val, timeout, uaddr2, val2, val3); } diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 01fa2ae..d17766d 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -98,7 +98,6 @@ static DEFINE_PER_CPU(struct hrtimer_bas /** * ktime_get_ts - get the monotonic clock in timespec format - * * @ts: pointer to timespec variable * * The function calculates the monotonic clock from the realtime @@ -238,7 +237,6 @@ #if BITS_PER_LONG < 64 # ifndef CONFIG_KTIME_SCALAR /** * ktime_add_ns - Add a scalar nanoseconds value to a ktime_t variable - * * @kt: addend * @nsec: the scalar nsec value to add * @@ -299,7 +297,6 @@ void unlock_hrtimer_base(const struct hr /** * hrtimer_forward - forward the timer expiry - * * @timer: hrtimer to forward * @now: forward past this time * @interval: the interval to forward @@ -393,7 +390,7 @@ static void __remove_hrtimer(struct hrti if (base->first == &timer->node) base->first = rb_next(&timer->node); rb_erase(&timer->node, &base->active); - timer->node.rb_parent = HRTIMER_INACTIVE; + rb_set_parent(&timer->node, &timer->node); } /* @@ -411,7 +408,6 @@ remove_hrtimer(struct hrtimer *timer, st /** * hrtimer_start - (re)start an relative timer on the current CPU - * * @timer: the timer to be added * @tim: expiry time * @mode: expiry mode: absolute (HRTIMER_ABS) or relative (HRTIMER_REL) @@ -460,14 +456,13 @@ EXPORT_SYMBOL_GPL(hrtimer_start); /** * hrtimer_try_to_cancel - try to deactivate a timer - * * @timer: hrtimer to stop * * Returns: * 0 when the timer was not active * 1 when the timer was active * -1 when the timer is currently excuting the callback function and - * can not be stopped + * cannot be stopped */ int hrtimer_try_to_cancel(struct hrtimer *timer) { @@ -489,7 +484,6 @@ EXPORT_SYMBOL_GPL(hrtimer_try_to_cancel) /** * hrtimer_cancel - cancel a timer and wait for the handler to finish. - * * @timer: the timer to be cancelled * * Returns: @@ -510,7 +504,6 @@ EXPORT_SYMBOL_GPL(hrtimer_cancel); /** * hrtimer_get_remaining - get remaining time for the timer - * * @timer: the timer to read */ ktime_t hrtimer_get_remaining(const struct hrtimer *timer) @@ -564,7 +557,6 @@ #endif /** * hrtimer_init - initialize a timer to the given clock - * * @timer: the timer to be initialized * @clock_id: the clock to be used * @mode: timer mode abs/rel @@ -576,19 +568,18 @@ void hrtimer_init(struct hrtimer *timer, memset(timer, 0, sizeof(struct hrtimer)); - bases = per_cpu(hrtimer_bases, raw_smp_processor_id()); + bases = __raw_get_cpu_var(hrtimer_bases); if (clock_id == CLOCK_REALTIME && mode != HRTIMER_ABS) clock_id = CLOCK_MONOTONIC; timer->base = &bases[clock_id]; - timer->node.rb_parent = HRTIMER_INACTIVE; + rb_set_parent(&timer->node, &timer->node); } EXPORT_SYMBOL_GPL(hrtimer_init); /** * hrtimer_get_res - get the timer resolution for a clock - * * @which_clock: which clock to query * @tp: pointer to timespec variable to store the resolution * @@ -599,7 +590,7 @@ int hrtimer_get_res(const clockid_t whic { struct hrtimer_base *bases; - bases = per_cpu(hrtimer_bases, raw_smp_processor_id()); + bases = __raw_get_cpu_var(hrtimer_bases); *tp = ktime_to_timespec(bases[which_clock].resolution); return 0; @@ -678,7 +669,7 @@ static int hrtimer_wakeup(struct hrtimer return HRTIMER_NORESTART; } -void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, task_t *task) +void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) { sl->timer.function = hrtimer_wakeup; sl->task = task; @@ -791,8 +782,10 @@ static void __devinit init_hrtimers_cpu( struct hrtimer_base *base = per_cpu(hrtimer_bases, cpu); int i; - for (i = 0; i < MAX_HRTIMER_BASES; i++, base++) + for (i = 0; i < MAX_HRTIMER_BASES; i++, base++) { spin_lock_init(&base->lock); + lockdep_set_class(&base->lock, &base->lock_key); + } } #ifdef CONFIG_HOTPLUG_CPU @@ -842,7 +835,7 @@ static void migrate_hrtimers(int cpu) } #endif /* CONFIG_HOTPLUG_CPU */ -static int hrtimer_cpu_notify(struct notifier_block *self, +static int __devinit hrtimer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { long cpu = (long)hcpu; @@ -866,7 +859,7 @@ #endif return NOTIFY_OK; } -static struct notifier_block hrtimers_nb = { +static struct notifier_block __devinitdata hrtimers_nb = { .notifier_call = hrtimer_cpu_notify, }; diff --git a/kernel/intermodule.c b/kernel/intermodule.c deleted file mode 100644 index 55b1e5b..0000000 --- a/kernel/intermodule.c +++ /dev/null @@ -1,184 +0,0 @@ -/* Deprecated, do not use. Moved from module.c to here. --RR */ - -/* Written by Keith Owens Oct 2000 */ -#include -#include -#include -#include -#include - -/* inter_module functions are always available, even when the kernel is - * compiled without modules. Consumers of inter_module_xxx routines - * will always work, even when both are built into the kernel, this - * approach removes lots of #ifdefs in mainline code. - */ - -static struct list_head ime_list = LIST_HEAD_INIT(ime_list); -static DEFINE_SPINLOCK(ime_lock); -static int kmalloc_failed; - -struct inter_module_entry { - struct list_head list; - const char *im_name; - struct module *owner; - const void *userdata; -}; - -/** - * inter_module_register - register a new set of inter module data. - * @im_name: an arbitrary string to identify the data, must be unique - * @owner: module that is registering the data, always use THIS_MODULE - * @userdata: pointer to arbitrary userdata to be registered - * - * Description: Check that the im_name has not already been registered, - * complain if it has. For new data, add it to the inter_module_entry - * list. - */ -void inter_module_register(const char *im_name, struct module *owner, const void *userdata) -{ - struct list_head *tmp; - struct inter_module_entry *ime, *ime_new; - - if (!(ime_new = kzalloc(sizeof(*ime), GFP_KERNEL))) { - /* Overloaded kernel, not fatal */ - printk(KERN_ERR - "Aiee, inter_module_register: cannot kmalloc entry for '%s'\n", - im_name); - kmalloc_failed = 1; - return; - } - ime_new->im_name = im_name; - ime_new->owner = owner; - ime_new->userdata = userdata; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - spin_unlock(&ime_lock); - kfree(ime_new); - /* Program logic error, fatal */ - printk(KERN_ERR "inter_module_register: duplicate im_name '%s'", im_name); - BUG(); - } - } - list_add(&(ime_new->list), &ime_list); - spin_unlock(&ime_lock); -} - -/** - * inter_module_unregister - unregister a set of inter module data. - * @im_name: an arbitrary string to identify the data, must be unique - * - * Description: Check that the im_name has been registered, complain if - * it has not. For existing data, remove it from the - * inter_module_entry list. - */ -void inter_module_unregister(const char *im_name) -{ - struct list_head *tmp; - struct inter_module_entry *ime; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - list_del(&(ime->list)); - spin_unlock(&ime_lock); - kfree(ime); - return; - } - } - spin_unlock(&ime_lock); - if (kmalloc_failed) { - printk(KERN_ERR - "inter_module_unregister: no entry for '%s', " - "probably caused by previous kmalloc failure\n", - im_name); - return; - } - else { - /* Program logic error, fatal */ - printk(KERN_ERR "inter_module_unregister: no entry for '%s'", im_name); - BUG(); - } -} - -/** - * inter_module_get - return arbitrary userdata from another module. - * @im_name: an arbitrary string to identify the data, must be unique - * - * Description: If the im_name has not been registered, return NULL. - * Try to increment the use count on the owning module, if that fails - * then return NULL. Otherwise return the userdata. - */ -static const void *inter_module_get(const char *im_name) -{ - struct list_head *tmp; - struct inter_module_entry *ime; - const void *result = NULL; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - if (try_module_get(ime->owner)) - result = ime->userdata; - break; - } - } - spin_unlock(&ime_lock); - return(result); -} - -/** - * inter_module_get_request - im get with automatic request_module. - * @im_name: an arbitrary string to identify the data, must be unique - * @modname: module that is expected to register im_name - * - * Description: If inter_module_get fails, do request_module then retry. - */ -const void *inter_module_get_request(const char *im_name, const char *modname) -{ - const void *result = inter_module_get(im_name); - if (!result) { - request_module("%s", modname); - result = inter_module_get(im_name); - } - return(result); -} - -/** - * inter_module_put - release use of data from another module. - * @im_name: an arbitrary string to identify the data, must be unique - * - * Description: If the im_name has not been registered, complain, - * otherwise decrement the use count on the owning module. - */ -void inter_module_put(const char *im_name) -{ - struct list_head *tmp; - struct inter_module_entry *ime; - - spin_lock(&ime_lock); - list_for_each(tmp, &ime_list) { - ime = list_entry(tmp, struct inter_module_entry, list); - if (strcmp(ime->im_name, im_name) == 0) { - if (ime->owner) - module_put(ime->owner); - spin_unlock(&ime_lock); - return; - } - } - spin_unlock(&ime_lock); - printk(KERN_ERR "inter_module_put: no entry for '%s'", im_name); - BUG(); -} - -EXPORT_SYMBOL(inter_module_register); -EXPORT_SYMBOL(inter_module_unregister); -EXPORT_SYMBOL(inter_module_get_request); -EXPORT_SYMBOL(inter_module_put); - -MODULE_LICENSE("GPL"); - diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile index 9f77f50..1dab0ac 100644 --- a/kernel/irq/Makefile +++ b/kernel/irq/Makefile @@ -1,5 +1,5 @@ -obj-y := handle.o manage.o spurious.o +obj-y := handle.o manage.o spurious.o resend.o chip.o obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o diff --git a/kernel/irq/autoprobe.c b/kernel/irq/autoprobe.c index 3467097..533068c 100644 --- a/kernel/irq/autoprobe.c +++ b/kernel/irq/autoprobe.c @@ -11,12 +11,14 @@ #include #include #include +#include "internals.h" + /* * Autodetection depends on the fact that any interrupt that * comes in on to an unassigned handler will get stuck with * "IRQ_WAITING" cleared and the interrupt disabled. */ -static DECLARE_MUTEX(probe_sem); +static DEFINE_MUTEX(probing_active); /** * probe_irq_on - begin an interrupt autodetect @@ -27,11 +29,11 @@ static DECLARE_MUTEX(probe_sem); */ unsigned long probe_irq_on(void) { - unsigned long val; - irq_desc_t *desc; + struct irq_desc *desc; + unsigned long mask; unsigned int i; - down(&probe_sem); + mutex_lock(&probing_active); /* * something may have generated an irq long ago and we want to * flush such a longstanding irq before considering it as spurious. @@ -40,8 +42,21 @@ unsigned long probe_irq_on(void) desc = irq_desc + i; spin_lock_irq(&desc->lock); - if (!irq_desc[i].action) - irq_desc[i].handler->startup(i); + if (!desc->action && !(desc->status & IRQ_NOPROBE)) { + /* + * An old-style architecture might still have + * the handle_bad_irq handler there: + */ + compat_irq_chip_set_default_handler(desc); + + /* + * Some chips need to know about probing in + * progress: + */ + if (desc->chip->set_type) + desc->chip->set_type(i, IRQ_TYPE_PROBE); + desc->chip->startup(i); + } spin_unlock_irq(&desc->lock); } @@ -57,9 +72,9 @@ unsigned long probe_irq_on(void) desc = irq_desc + i; spin_lock_irq(&desc->lock); - if (!desc->action) { + if (!desc->action && !(desc->status & IRQ_NOPROBE)) { desc->status |= IRQ_AUTODETECT | IRQ_WAITING; - if (desc->handler->startup(i)) + if (desc->chip->startup(i)) desc->status |= IRQ_PENDING; } spin_unlock_irq(&desc->lock); @@ -73,11 +88,11 @@ unsigned long probe_irq_on(void) /* * Now filter out any obviously spurious interrupts */ - val = 0; + mask = 0; for (i = 0; i < NR_IRQS; i++) { - irq_desc_t *desc = irq_desc + i; unsigned int status; + desc = irq_desc + i; spin_lock_irq(&desc->lock); status = desc->status; @@ -85,17 +100,16 @@ unsigned long probe_irq_on(void) /* It triggered already - consider it spurious. */ if (!(status & IRQ_WAITING)) { desc->status = status & ~IRQ_AUTODETECT; - desc->handler->shutdown(i); + desc->chip->shutdown(i); } else if (i < 32) - val |= 1 << i; + mask |= 1 << i; } spin_unlock_irq(&desc->lock); } - return val; + return mask; } - EXPORT_SYMBOL(probe_irq_on); /** @@ -117,7 +131,7 @@ unsigned int probe_irq_mask(unsigned lon mask = 0; for (i = 0; i < NR_IRQS; i++) { - irq_desc_t *desc = irq_desc + i; + struct irq_desc *desc = irq_desc + i; unsigned int status; spin_lock_irq(&desc->lock); @@ -128,11 +142,11 @@ unsigned int probe_irq_mask(unsigned lon mask |= 1 << i; desc->status = status & ~IRQ_AUTODETECT; - desc->handler->shutdown(i); + desc->chip->shutdown(i); } spin_unlock_irq(&desc->lock); } - up(&probe_sem); + mutex_unlock(&probing_active); return mask & val; } @@ -160,7 +174,7 @@ int probe_irq_off(unsigned long val) int i, irq_found = 0, nr_irqs = 0; for (i = 0; i < NR_IRQS; i++) { - irq_desc_t *desc = irq_desc + i; + struct irq_desc *desc = irq_desc + i; unsigned int status; spin_lock_irq(&desc->lock); @@ -173,16 +187,16 @@ int probe_irq_off(unsigned long val) nr_irqs++; } desc->status = status & ~IRQ_AUTODETECT; - desc->handler->shutdown(i); + desc->chip->shutdown(i); } spin_unlock_irq(&desc->lock); } - up(&probe_sem); + mutex_unlock(&probing_active); if (nr_irqs > 1) irq_found = -irq_found; + return irq_found; } - EXPORT_SYMBOL(probe_irq_off); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c new file mode 100644 index 0000000..9336f2e --- /dev/null +++ b/kernel/irq/chip.c @@ -0,0 +1,537 @@ +/* + * linux/kernel/irq/chip.c + * + * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar + * Copyright (C) 2005-2006, Thomas Gleixner, Russell King + * + * This file contains the core interrupt handling code, for irq-chip + * based architectures. + * + * Detailed information is available in Documentation/DocBook/genericirq + */ + +#include +#include +#include +#include + +#include "internals.h" + +/** + * set_irq_chip - set the irq chip for an irq + * @irq: irq number + * @chip: pointer to irq chip description structure + */ +int set_irq_chip(unsigned int irq, struct irq_chip *chip) +{ + struct irq_desc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to install chip for IRQ%d\n", irq); + WARN_ON(1); + return -EINVAL; + } + + if (!chip) + chip = &no_irq_chip; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock, flags); + irq_chip_set_defaults(chip); + desc->chip = chip; + /* + * For compatibility only: + */ + desc->chip = chip; + spin_unlock_irqrestore(&desc->lock, flags); + + return 0; +} +EXPORT_SYMBOL(set_irq_chip); + +/** + * set_irq_type - set the irq type for an irq + * @irq: irq number + * @type: interrupt type - see include/linux/interrupt.h + */ +int set_irq_type(unsigned int irq, unsigned int type) +{ + struct irq_desc *desc; + unsigned long flags; + int ret = -ENXIO; + + if (irq >= NR_IRQS) { + printk(KERN_ERR "Trying to set irq type for IRQ%d\n", irq); + return -ENODEV; + } + + desc = irq_desc + irq; + if (desc->chip->set_type) { + spin_lock_irqsave(&desc->lock, flags); + ret = desc->chip->set_type(irq, type); + spin_unlock_irqrestore(&desc->lock, flags); + } + return ret; +} +EXPORT_SYMBOL(set_irq_type); + +/** + * set_irq_data - set irq type data for an irq + * @irq: Interrupt number + * @data: Pointer to interrupt specific data + * + * Set the hardware irq controller data for an irq + */ +int set_irq_data(unsigned int irq, void *data) +{ + struct irq_desc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR + "Trying to install controller data for IRQ%d\n", irq); + return -EINVAL; + } + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock, flags); + desc->handler_data = data; + spin_unlock_irqrestore(&desc->lock, flags); + return 0; +} +EXPORT_SYMBOL(set_irq_data); + +/** + * set_irq_chip_data - set irq chip data for an irq + * @irq: Interrupt number + * @data: Pointer to chip specific data + * + * Set the hardware irq chip data for an irq + */ +int set_irq_chip_data(unsigned int irq, void *data) +{ + struct irq_desc *desc = irq_desc + irq; + unsigned long flags; + + if (irq >= NR_IRQS || !desc->chip) { + printk(KERN_ERR "BUG: bad set_irq_chip_data(IRQ#%d)\n", irq); + return -EINVAL; + } + + spin_lock_irqsave(&desc->lock, flags); + desc->chip_data = data; + spin_unlock_irqrestore(&desc->lock, flags); + + return 0; +} +EXPORT_SYMBOL(set_irq_chip_data); + +/* + * default enable function + */ +static void default_enable(unsigned int irq) +{ + struct irq_desc *desc = irq_desc + irq; + + desc->chip->unmask(irq); + desc->status &= ~IRQ_MASKED; +} + +/* + * default disable function + */ +static void default_disable(unsigned int irq) +{ + struct irq_desc *desc = irq_desc + irq; + + if (!(desc->status & IRQ_DELAYED_DISABLE)) + irq_desc[irq].chip->mask(irq); +} + +/* + * default startup function + */ +static unsigned int default_startup(unsigned int irq) +{ + irq_desc[irq].chip->enable(irq); + + return 0; +} + +/* + * Fixup enable/disable function pointers + */ +void irq_chip_set_defaults(struct irq_chip *chip) +{ + if (!chip->enable) + chip->enable = default_enable; + if (!chip->disable) + chip->disable = default_disable; + if (!chip->startup) + chip->startup = default_startup; + if (!chip->shutdown) + chip->shutdown = chip->disable; + if (!chip->name) + chip->name = chip->typename; +} + +static inline void mask_ack_irq(struct irq_desc *desc, int irq) +{ + if (desc->chip->mask_ack) + desc->chip->mask_ack(irq); + else { + desc->chip->mask(irq); + desc->chip->ack(irq); + } +} + +/** + * handle_simple_irq - Simple and software-decoded IRQs. + * @irq: the interrupt number + * @desc: the interrupt description structure for this irq + * @regs: pointer to a register structure + * + * Simple interrupts are either sent from a demultiplexing interrupt + * handler or come from hardware, where no interrupt hardware control + * is necessary. + * + * Note: The caller is expected to handle the ack, clear, mask and + * unmask issues if necessary. + */ +void fastcall +handle_simple_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) +{ + struct irqaction *action; + irqreturn_t action_ret; + const unsigned int cpu = smp_processor_id(); + + spin_lock(&desc->lock); + + if (unlikely(desc->status & IRQ_INPROGRESS)) + goto out_unlock; + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + kstat_cpu(cpu).irqs[irq]++; + + action = desc->action; + if (unlikely(!action || (desc->status & IRQ_DISABLED))) + goto out_unlock; + + desc->status |= IRQ_INPROGRESS; + spin_unlock(&desc->lock); + + action_ret = handle_IRQ_event(irq, regs, action); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret, regs); + + spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +out_unlock: + spin_unlock(&desc->lock); +} + +/** + * handle_level_irq - Level type irq handler + * @irq: the interrupt number + * @desc: the interrupt description structure for this irq + * @regs: pointer to a register structure + * + * Level type interrupts are active as long as the hardware line has + * the active level. This may require to mask the interrupt and unmask + * it after the associated handler has acknowledged the device, so the + * interrupt line is back to inactive. + */ +void fastcall +handle_level_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) +{ + unsigned int cpu = smp_processor_id(); + struct irqaction *action; + irqreturn_t action_ret; + + spin_lock(&desc->lock); + mask_ack_irq(desc, irq); + + if (unlikely(desc->status & IRQ_INPROGRESS)) + goto out; + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + kstat_cpu(cpu).irqs[irq]++; + + /* + * If its disabled or no action available + * keep it masked and get out of here + */ + action = desc->action; + if (unlikely(!action || (desc->status & IRQ_DISABLED))) { + desc->status |= IRQ_PENDING; + goto out; + } + + desc->status |= IRQ_INPROGRESS; + desc->status &= ~IRQ_PENDING; + spin_unlock(&desc->lock); + + action_ret = handle_IRQ_event(irq, regs, action); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret, regs); + + spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +out: + if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) + desc->chip->unmask(irq); + spin_unlock(&desc->lock); +} + +/** + * handle_fasteoi_irq - irq handler for transparent controllers + * @irq: the interrupt number + * @desc: the interrupt description structure for this irq + * @regs: pointer to a register structure + * + * Only a single callback will be issued to the chip: an ->eoi() + * call when the interrupt has been serviced. This enables support + * for modern forms of interrupt handlers, which handle the flow + * details in hardware, transparently. + */ +void fastcall +handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc, + struct pt_regs *regs) +{ + unsigned int cpu = smp_processor_id(); + struct irqaction *action; + irqreturn_t action_ret; + + spin_lock(&desc->lock); + + if (unlikely(desc->status & IRQ_INPROGRESS)) + goto out; + + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + kstat_cpu(cpu).irqs[irq]++; + + /* + * If its disabled or no action available + * keep it masked and get out of here + */ + action = desc->action; + if (unlikely(!action || (desc->status & IRQ_DISABLED))) { + desc->status |= IRQ_PENDING; + goto out; + } + + desc->status |= IRQ_INPROGRESS; + desc->status &= ~IRQ_PENDING; + spin_unlock(&desc->lock); + + action_ret = handle_IRQ_event(irq, regs, action); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret, regs); + + spin_lock(&desc->lock); + desc->status &= ~IRQ_INPROGRESS; +out: + desc->chip->eoi(irq); + + spin_unlock(&desc->lock); +} + +/** + * handle_edge_irq - edge type IRQ handler + * @irq: the interrupt number + * @desc: the interrupt description structure for this irq + * @regs: pointer to a register structure + * + * Interrupt occures on the falling and/or rising edge of a hardware + * signal. The occurence is latched into the irq controller hardware + * and must be acked in order to be reenabled. After the ack another + * interrupt can happen on the same source even before the first one + * is handled by the assosiacted event handler. If this happens it + * might be necessary to disable (mask) the interrupt depending on the + * controller hardware. This requires to reenable the interrupt inside + * of the loop which handles the interrupts which have arrived while + * the handler was running. If all pending interrupts are handled, the + * loop is left. + */ +void fastcall +handle_edge_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) +{ + const unsigned int cpu = smp_processor_id(); + + spin_lock(&desc->lock); + + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + + /* + * If we're currently running this IRQ, or its disabled, + * we shouldn't process the IRQ. Mark it pending, handle + * the necessary masking and go out + */ + if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) || + !desc->action)) { + desc->status |= (IRQ_PENDING | IRQ_MASKED); + mask_ack_irq(desc, irq); + goto out_unlock; + } + + kstat_cpu(cpu).irqs[irq]++; + + /* Start handling the irq */ + desc->chip->ack(irq); + + /* Mark the IRQ currently in progress.*/ + desc->status |= IRQ_INPROGRESS; + + do { + struct irqaction *action = desc->action; + irqreturn_t action_ret; + + if (unlikely(!action)) { + desc->chip->mask(irq); + goto out_unlock; + } + + /* + * When another irq arrived while we were handling + * one, we could have masked the irq. + * Renable it, if it was not disabled in meantime. + */ + if (unlikely((desc->status & + (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) == + (IRQ_PENDING | IRQ_MASKED))) { + desc->chip->unmask(irq); + desc->status &= ~IRQ_MASKED; + } + + desc->status &= ~IRQ_PENDING; + spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, regs, action); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret, regs); + spin_lock(&desc->lock); + + } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); + + desc->status &= ~IRQ_INPROGRESS; +out_unlock: + spin_unlock(&desc->lock); +} + +#ifdef CONFIG_SMP +/** + * handle_percpu_IRQ - Per CPU local irq handler + * @irq: the interrupt number + * @desc: the interrupt description structure for this irq + * @regs: pointer to a register structure + * + * Per CPU interrupts on SMP machines without locking requirements + */ +void fastcall +handle_percpu_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) +{ + irqreturn_t action_ret; + + kstat_this_cpu.irqs[irq]++; + + if (desc->chip->ack) + desc->chip->ack(irq); + + action_ret = handle_IRQ_event(irq, regs, desc->action); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret, regs); + + if (desc->chip->eoi) + desc->chip->eoi(irq); +} + +#endif /* CONFIG_SMP */ + +void +__set_irq_handler(unsigned int irq, + void fastcall (*handle)(unsigned int, irq_desc_t *, + struct pt_regs *), + int is_chained) +{ + struct irq_desc *desc; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk(KERN_ERR + "Trying to install type control for IRQ%d\n", irq); + return; + } + + desc = irq_desc + irq; + + if (!handle) + handle = handle_bad_irq; + + if (desc->chip == &no_irq_chip) { + printk(KERN_WARNING "Trying to install %sinterrupt handler " + "for IRQ%d\n", is_chained ? "chained " : " ", irq); + /* + * Some ARM implementations install a handler for really dumb + * interrupt hardware without setting an irq_chip. This worked + * with the ARM no_irq_chip but the check in setup_irq would + * prevent us to setup the interrupt at all. Switch it to + * dummy_irq_chip for easy transition. + */ + desc->chip = &dummy_irq_chip; + } + + spin_lock_irqsave(&desc->lock, flags); + + /* Uninstall? */ + if (handle == handle_bad_irq) { + if (desc->chip != &no_irq_chip) { + desc->chip->mask(irq); + desc->chip->ack(irq); + } + desc->status |= IRQ_DISABLED; + desc->depth = 1; + } + desc->handle_irq = handle; + + if (handle != handle_bad_irq && is_chained) { + desc->status &= ~IRQ_DISABLED; + desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE; + desc->depth = 0; + desc->chip->unmask(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +void +set_irq_chip_and_handler(unsigned int irq, struct irq_chip *chip, + void fastcall (*handle)(unsigned int, + struct irq_desc *, + struct pt_regs *)) +{ + set_irq_chip(irq, chip); + __set_irq_handler(irq, handle, 0); +} + +/* + * Get a descriptive string for the highlevel handler, for + * /proc/interrupts output: + */ +const char * +handle_irq_name(void fastcall (*handle)(unsigned int, struct irq_desc *, + struct pt_regs *)) +{ + if (handle == handle_level_irq) + return "level "; + if (handle == handle_fasteoi_irq) + return "fasteoi"; + if (handle == handle_edge_irq) + return "edge "; + if (handle == handle_simple_irq) + return "simple "; +#ifdef CONFIG_SMP + if (handle == handle_percpu_irq) + return "percpu "; +#endif + if (handle == handle_bad_irq) + return "bad "; + + return NULL; +} diff --git a/kernel/irq/handle.c b/kernel/irq/handle.c index 51df337..fc4e906 100644 --- a/kernel/irq/handle.c +++ b/kernel/irq/handle.c @@ -1,9 +1,13 @@ /* * linux/kernel/irq/handle.c * - * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar + * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar + * Copyright (C) 2005-2006, Thomas Gleixner, Russell King * * This file contains the core interrupt handling code. + * + * Detailed information is available in Documentation/DocBook/genericirq + * */ #include @@ -14,11 +18,22 @@ #include #include "internals.h" +/** + * handle_bad_irq - handle spurious and unhandled irqs + */ +void fastcall +handle_bad_irq(unsigned int irq, struct irq_desc *desc, struct pt_regs *regs) +{ + print_irq_desc(irq, desc); + kstat_this_cpu.irqs[irq]++; + ack_bad_irq(irq); +} + /* * Linux has a controller-independent interrupt architecture. * Every controller has a 'controller-template', that is used * by the main code to do the right thing. Each driver-visible - * interrupt source is transparently wired to the apropriate + * interrupt source is transparently wired to the appropriate * controller. Thus drivers need not be aware of the * interrupt-controller. * @@ -28,41 +43,68 @@ #include "internals.h" * * Controller mappings for all interrupt sources: */ -irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { +struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { .status = IRQ_DISABLED, - .handler = &no_irq_type, - .lock = SPIN_LOCK_UNLOCKED + .chip = &no_irq_chip, + .handle_irq = handle_bad_irq, + .depth = 1, + .lock = SPIN_LOCK_UNLOCKED, +#ifdef CONFIG_SMP + .affinity = CPU_MASK_ALL +#endif } }; /* - * Generic 'no controller' code + * What should we do if we get a hw irq event on an illegal vector? + * Each architecture has to answer this themself. */ -static void end_none(unsigned int irq) { } -static void enable_none(unsigned int irq) { } -static void disable_none(unsigned int irq) { } -static void shutdown_none(unsigned int irq) { } -static unsigned int startup_none(unsigned int irq) { return 0; } - -static void ack_none(unsigned int irq) +static void ack_bad(unsigned int irq) { - /* - * 'what should we do if we get a hw irq event on an illegal vector'. - * each architecture has to answer this themself. - */ + print_irq_desc(irq, irq_desc + irq); ack_bad_irq(irq); } -struct hw_interrupt_type no_irq_type = { - .typename = "none", - .startup = startup_none, - .shutdown = shutdown_none, - .enable = enable_none, - .disable = disable_none, - .ack = ack_none, - .end = end_none, - .set_affinity = NULL +/* + * NOP functions + */ +static void noop(unsigned int irq) +{ +} + +static unsigned int noop_ret(unsigned int irq) +{ + return 0; +} + +/* + * Generic no controller implementation + */ +struct irq_chip no_irq_chip = { + .name = "none", + .startup = noop_ret, + .shutdown = noop, + .enable = noop, + .disable = noop, + .ack = ack_bad, + .end = noop, +}; + +/* + * Generic dummy implementation which can be used for + * real dumb interrupt sources + */ +struct irq_chip dummy_irq_chip = { + .name = "dummy", + .startup = noop_ret, + .shutdown = noop, + .enable = noop, + .disable = noop, + .ack = noop, + .mask = noop, + .unmask = noop, + .end = noop, }; /* @@ -73,16 +115,24 @@ irqreturn_t no_action(int cpl, void *dev return IRQ_NONE; } -/* - * Have got an event to handle: +/** + * handle_IRQ_event - irq action chain handler + * @irq: the interrupt number + * @regs: pointer to a register structure + * @action: the interrupt action chain for this irq + * + * Handles the action chain of an irq event */ -fastcall int handle_IRQ_event(unsigned int irq, struct pt_regs *regs, - struct irqaction *action) +irqreturn_t handle_IRQ_event(unsigned int irq, struct pt_regs *regs, + struct irqaction *action) { - int ret, retval = 0, status = 0; + irqreturn_t ret, retval = IRQ_NONE; + unsigned int status = 0; + + handle_dynamic_tick(action); - if (!(action->flags & SA_INTERRUPT)) - local_irq_enable(); + if (!(action->flags & IRQF_DISABLED)) + local_irq_enable_in_hardirq(); do { ret = action->handler(irq, action->dev_id, regs); @@ -92,22 +142,29 @@ fastcall int handle_IRQ_event(unsigned i action = action->next; } while (action); - if (status & SA_SAMPLE_RANDOM) + if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); return retval; } -/* - * do_IRQ handles all normal device IRQ's (the special +/** + * __do_IRQ - original all in one highlevel IRQ handler + * @irq: the interrupt number + * @regs: pointer to a register structure + * + * __do_IRQ handles all normal device IRQ's (the special * SMP cross-CPU interrupts have their own specific * handlers). + * + * This is the original x86 implementation which is used for every + * interrupt type. */ fastcall unsigned int __do_IRQ(unsigned int irq, struct pt_regs *regs) { - irq_desc_t *desc = irq_desc + irq; - struct irqaction * action; + struct irq_desc *desc = irq_desc + irq; + struct irqaction *action; unsigned int status; kstat_this_cpu.irqs[irq]++; @@ -117,16 +174,16 @@ fastcall unsigned int __do_IRQ(unsigned /* * No locking required for CPU-local interrupts: */ - if (desc->handler->ack) - desc->handler->ack(irq); + if (desc->chip->ack) + desc->chip->ack(irq); action_ret = handle_IRQ_event(irq, regs, desc->action); - desc->handler->end(irq); + desc->chip->end(irq); return 1; } spin_lock(&desc->lock); - if (desc->handler->ack) - desc->handler->ack(irq); + if (desc->chip->ack) + desc->chip->ack(irq); /* * REPLAY is when Linux resends an IRQ that was dropped earlier * WAITING is used by probe to mark irqs that are being tested @@ -186,9 +243,25 @@ out: * The ->end() handler has to deal with interrupts which got * disabled while the handler was running. */ - desc->handler->end(irq); + desc->chip->end(irq); spin_unlock(&desc->lock); return 1; } +#ifdef CONFIG_TRACE_IRQFLAGS + +/* + * lockdep: we want to handle all irq_desc locks as a single lock-class: + */ +static struct lock_class_key irq_desc_lock_class; + +void early_init_irq_lock_class(void) +{ + int i; + + for (i = 0; i < NR_IRQS; i++) + lockdep_set_class(&irq_desc[i].lock, &irq_desc_lock_class); +} + +#endif diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 46feba6..08a849a 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -4,6 +4,12 @@ extern int noirqdebug; +/* Set default functions for irq_chip structures: */ +extern void irq_chip_set_defaults(struct irq_chip *chip); + +/* Set default handler: */ +extern void compat_irq_chip_set_default_handler(struct irq_desc *desc); + #ifdef CONFIG_PROC_FS extern void register_irq_proc(unsigned int irq); extern void register_handler_proc(unsigned int irq, struct irqaction *action); @@ -16,3 +22,43 @@ static inline void unregister_handler_pr struct irqaction *action) { } #endif +/* + * Debugging printout: + */ + +#include + +#define P(f) if (desc->status & f) printk("%14s set\n", #f) + +static inline void print_irq_desc(unsigned int irq, struct irq_desc *desc) +{ + printk("irq %d, desc: %p, depth: %d, count: %d, unhandled: %d\n", + irq, desc, desc->depth, desc->irq_count, desc->irqs_unhandled); + printk("->handle_irq(): %p, ", desc->handle_irq); + print_symbol("%s\n", (unsigned long)desc->handle_irq); + printk("->chip(): %p, ", desc->chip); + print_symbol("%s\n", (unsigned long)desc->chip); + printk("->action(): %p\n", desc->action); + if (desc->action) { + printk("->action->handler(): %p, ", desc->action->handler); + print_symbol("%s\n", (unsigned long)desc->action->handler); + } + + P(IRQ_INPROGRESS); + P(IRQ_DISABLED); + P(IRQ_PENDING); + P(IRQ_REPLAY); + P(IRQ_AUTODETECT); + P(IRQ_WAITING); + P(IRQ_LEVEL); + P(IRQ_MASKED); +#ifdef CONFIG_IRQ_PER_CPU + P(IRQ_PER_CPU); +#endif + P(IRQ_NOPROBE); + P(IRQ_NOREQUEST); + P(IRQ_NOAUTOEN); +} + +#undef P + diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 1279e34..4e46143 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1,12 +1,12 @@ /* * linux/kernel/irq/manage.c * - * Copyright (C) 1992, 1998-2004 Linus Torvalds, Ingo Molnar + * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar + * Copyright (C) 2005-2006 Thomas Gleixner * * This file contains driver APIs to the irq subsystem. */ -#include #include #include #include @@ -16,12 +16,6 @@ #include "internals.h" #ifdef CONFIG_SMP -cpumask_t irq_affinity[NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL }; - -#if defined (CONFIG_GENERIC_PENDING_IRQ) || defined (CONFIG_IRQBALANCE) -cpumask_t __cacheline_aligned pending_irq_cpumask[NR_IRQS]; -#endif - /** * synchronize_irq - wait for pending IRQ handlers (on other CPUs) * @irq: interrupt number to wait for @@ -42,7 +36,6 @@ void synchronize_irq(unsigned int irq) while (desc->status & IRQ_INPROGRESS) cpu_relax(); } - EXPORT_SYMBOL(synchronize_irq); #endif @@ -60,7 +53,7 @@ #endif */ void disable_irq_nosync(unsigned int irq) { - irq_desc_t *desc = irq_desc + irq; + struct irq_desc *desc = irq_desc + irq; unsigned long flags; if (irq >= NR_IRQS) @@ -69,11 +62,10 @@ void disable_irq_nosync(unsigned int irq spin_lock_irqsave(&desc->lock, flags); if (!desc->depth++) { desc->status |= IRQ_DISABLED; - desc->handler->disable(irq); + desc->chip->disable(irq); } spin_unlock_irqrestore(&desc->lock, flags); } - EXPORT_SYMBOL(disable_irq_nosync); /** @@ -90,7 +82,7 @@ EXPORT_SYMBOL(disable_irq_nosync); */ void disable_irq(unsigned int irq) { - irq_desc_t *desc = irq_desc + irq; + struct irq_desc *desc = irq_desc + irq; if (irq >= NR_IRQS) return; @@ -99,7 +91,6 @@ void disable_irq(unsigned int irq) if (desc->action) synchronize_irq(irq); } - EXPORT_SYMBOL(disable_irq); /** @@ -114,7 +105,7 @@ EXPORT_SYMBOL(disable_irq); */ void enable_irq(unsigned int irq) { - irq_desc_t *desc = irq_desc + irq; + struct irq_desc *desc = irq_desc + irq; unsigned long flags; if (irq >= NR_IRQS) @@ -123,17 +114,15 @@ void enable_irq(unsigned int irq) spin_lock_irqsave(&desc->lock, flags); switch (desc->depth) { case 0: + printk(KERN_WARNING "Unbalanced enable for IRQ %d\n", irq); WARN_ON(1); break; case 1: { unsigned int status = desc->status & ~IRQ_DISABLED; - desc->status = status; - if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { - desc->status = status | IRQ_REPLAY; - hw_resend_irq(desc->handler,irq); - } - desc->handler->enable(irq); + /* Prevent probing on this irq: */ + desc->status = status | IRQ_NOPROBE; + check_irq_resend(desc, irq); /* fall-through */ } default: @@ -141,9 +130,29 @@ void enable_irq(unsigned int irq) } spin_unlock_irqrestore(&desc->lock, flags); } - EXPORT_SYMBOL(enable_irq); +/** + * set_irq_wake - control irq power management wakeup + * @irq: interrupt to control + * @on: enable/disable power management wakeup + * + * Enable/disable power management wakeup mode + */ +int set_irq_wake(unsigned int irq, unsigned int on) +{ + struct irq_desc *desc = irq_desc + irq; + unsigned long flags; + int ret = -ENXIO; + + spin_lock_irqsave(&desc->lock, flags); + if (desc->chip->set_wake) + ret = desc->chip->set_wake(irq, on); + spin_unlock_irqrestore(&desc->lock, flags); + return ret; +} +EXPORT_SYMBOL(set_irq_wake); + /* * Internal function that tells the architecture code whether a * particular irq has been exclusively allocated or is available @@ -153,22 +162,33 @@ int can_request_irq(unsigned int irq, un { struct irqaction *action; - if (irq >= NR_IRQS) + if (irq >= NR_IRQS || irq_desc[irq].status & IRQ_NOREQUEST) return 0; action = irq_desc[irq].action; if (action) - if (irqflags & action->flags & SA_SHIRQ) + if (irqflags & action->flags & IRQF_SHARED) action = NULL; return !action; } +void compat_irq_chip_set_default_handler(struct irq_desc *desc) +{ + /* + * If the architecture still has not overriden + * the flow handler then zap the default. This + * should catch incorrect flow-type setting. + */ + if (desc->handle_irq == &handle_bad_irq) + desc->handle_irq = NULL; +} + /* * Internal function to register an irqaction - typically used to * allocate special interrupts that are part of the architecture. */ -int setup_irq(unsigned int irq, struct irqaction * new) +int setup_irq(unsigned int irq, struct irqaction *new) { struct irq_desc *desc = irq_desc + irq; struct irqaction *old, **p; @@ -178,14 +198,14 @@ int setup_irq(unsigned int irq, struct i if (irq >= NR_IRQS) return -EINVAL; - if (desc->handler == &no_irq_type) + if (desc->chip == &no_irq_chip) return -ENOSYS; /* * Some drivers like serial.c use request_irq() heavily, * so we have to be careful not to interfere with a * running system. */ - if (new->flags & SA_SAMPLE_RANDOM) { + if (new->flags & IRQF_SAMPLE_RANDOM) { /* * This function might sleep, we want to call it first, * outside of the atomic block. @@ -200,16 +220,24 @@ int setup_irq(unsigned int irq, struct i /* * The following block of code has to be executed atomically */ - spin_lock_irqsave(&desc->lock,flags); + spin_lock_irqsave(&desc->lock, flags); p = &desc->action; - if ((old = *p) != NULL) { - /* Can't share interrupts unless both agree to */ - if (!(old->flags & new->flags & SA_SHIRQ)) + old = *p; + if (old) { + /* + * Can't share interrupts unless both agree to and are + * the same type (level, edge, polarity). So both flag + * fields must have IRQF_SHARED set and the bits which + * set the trigger type must match. + */ + if (!((old->flags & new->flags) & IRQF_SHARED) || + ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) goto mismatch; -#if defined(ARCH_HAS_IRQ_PER_CPU) && defined(SA_PERCPU_IRQ) +#if defined(CONFIG_IRQ_PER_CPU) /* All handlers must agree on per-cpuness */ - if ((old->flags & IRQ_PER_CPU) != (new->flags & IRQ_PER_CPU)) + if ((old->flags & IRQF_PERCPU) != + (new->flags & IRQF_PERCPU)) goto mismatch; #endif @@ -222,20 +250,45 @@ #endif } *p = new; -#if defined(ARCH_HAS_IRQ_PER_CPU) && defined(SA_PERCPU_IRQ) - if (new->flags & SA_PERCPU_IRQ) +#if defined(CONFIG_IRQ_PER_CPU) + if (new->flags & IRQF_PERCPU) desc->status |= IRQ_PER_CPU; #endif if (!shared) { - desc->depth = 0; - desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | - IRQ_WAITING | IRQ_INPROGRESS); - if (desc->handler->startup) - desc->handler->startup(irq); - else - desc->handler->enable(irq); + irq_chip_set_defaults(desc->chip); + + /* Setup the type (level, edge polarity) if configured: */ + if (new->flags & IRQF_TRIGGER_MASK) { + if (desc->chip && desc->chip->set_type) + desc->chip->set_type(irq, + new->flags & IRQF_TRIGGER_MASK); + else + /* + * IRQF_TRIGGER_* but the PIC does not support + * multiple flow-types? + */ + printk(KERN_WARNING "No IRQF_TRIGGER set_type " + "function for IRQ %d (%s)\n", irq, + desc->chip ? desc->chip->name : + "unknown"); + } else + compat_irq_chip_set_default_handler(desc); + + desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | + IRQ_INPROGRESS); + + if (!(desc->status & IRQ_NOAUTOEN)) { + desc->depth = 0; + desc->status &= ~IRQ_DISABLED; + if (desc->chip->startup) + desc->chip->startup(irq); + else + desc->chip->enable(irq); + } else + /* Undo nested disables: */ + desc->depth = 1; } - spin_unlock_irqrestore(&desc->lock,flags); + spin_unlock_irqrestore(&desc->lock, flags); new->irq = irq; register_irq_proc(irq); @@ -246,8 +299,8 @@ #endif mismatch: spin_unlock_irqrestore(&desc->lock, flags); - if (!(new->flags & SA_PROBEIRQ)) { - printk(KERN_ERR "%s: irq handler mismatch\n", __FUNCTION__); + if (!(new->flags & IRQF_PROBE_SHARED)) { + printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq); dump_stack(); } return -EBUSY; @@ -278,10 +331,10 @@ void free_irq(unsigned int irq, void *de return; desc = irq_desc + irq; - spin_lock_irqsave(&desc->lock,flags); + spin_lock_irqsave(&desc->lock, flags); p = &desc->action; for (;;) { - struct irqaction * action = *p; + struct irqaction *action = *p; if (action) { struct irqaction **pp = p; @@ -295,18 +348,18 @@ void free_irq(unsigned int irq, void *de /* Currently used only by UML, might disappear one day.*/ #ifdef CONFIG_IRQ_RELEASE_METHOD - if (desc->handler->release) - desc->handler->release(irq, dev_id); + if (desc->chip->release) + desc->chip->release(irq, dev_id); #endif if (!desc->action) { desc->status |= IRQ_DISABLED; - if (desc->handler->shutdown) - desc->handler->shutdown(irq); + if (desc->chip->shutdown) + desc->chip->shutdown(irq); else - desc->handler->disable(irq); + desc->chip->disable(irq); } - spin_unlock_irqrestore(&desc->lock,flags); + spin_unlock_irqrestore(&desc->lock, flags); unregister_handler_proc(irq, action); /* Make sure it's not being used on another CPU */ @@ -314,12 +367,11 @@ #endif kfree(action); return; } - printk(KERN_ERR "Trying to free free IRQ%d\n",irq); - spin_unlock_irqrestore(&desc->lock,flags); + printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq); + spin_unlock_irqrestore(&desc->lock, flags); return; } } - EXPORT_SYMBOL(free_irq); /** @@ -346,28 +398,36 @@ EXPORT_SYMBOL(free_irq); * * Flags: * - * SA_SHIRQ Interrupt is shared - * SA_INTERRUPT Disable local interrupts while processing - * SA_SAMPLE_RANDOM The interrupt can be used for entropy + * IRQF_SHARED Interrupt is shared + * IRQF_DISABLED Disable local interrupts while processing + * IRQF_SAMPLE_RANDOM The interrupt can be used for entropy * */ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, const char * devname, void *dev_id) + unsigned long irqflags, const char *devname, void *dev_id) { - struct irqaction * action; + struct irqaction *action; int retval; +#ifdef CONFIG_LOCKDEP + /* + * Lockdep wants atomic interrupt handlers: + */ + irqflags |= SA_INTERRUPT; +#endif /* * Sanity-check: shared interrupts must pass in a real dev-ID, * otherwise we'll have trouble later trying to figure out * which interrupt is which (messes up the interrupt freeing * logic etc). */ - if ((irqflags & SA_SHIRQ) && !dev_id) + if ((irqflags & IRQF_SHARED) && !dev_id) return -EINVAL; if (irq >= NR_IRQS) return -EINVAL; + if (irq_desc[irq].status & IRQ_NOREQUEST) + return -EINVAL; if (!handler) return -EINVAL; @@ -390,6 +450,5 @@ int request_irq(unsigned int irq, return retval; } - EXPORT_SYMBOL(request_irq); diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 134f9f2..a57ebe9 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -3,19 +3,19 @@ #include void set_pending_irq(unsigned int irq, cpumask_t mask) { - irq_desc_t *desc = irq_desc + irq; + struct irq_desc *desc = irq_desc + irq; unsigned long flags; spin_lock_irqsave(&desc->lock, flags); desc->move_irq = 1; - pending_irq_cpumask[irq] = mask; + irq_desc[irq].pending_mask = mask; spin_unlock_irqrestore(&desc->lock, flags); } void move_native_irq(int irq) { + struct irq_desc *desc = irq_desc + irq; cpumask_t tmp; - irq_desc_t *desc = irq_descp(irq); if (likely(!desc->move_irq)) return; @@ -30,15 +30,15 @@ void move_native_irq(int irq) desc->move_irq = 0; - if (likely(cpus_empty(pending_irq_cpumask[irq]))) + if (unlikely(cpus_empty(irq_desc[irq].pending_mask))) return; - if (!desc->handler->set_affinity) + if (!desc->chip->set_affinity) return; assert_spin_locked(&desc->lock); - cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); + cpus_and(tmp, irq_desc[irq].pending_mask, cpu_online_map); /* * If there was a valid mask to work with, please @@ -49,14 +49,14 @@ void move_native_irq(int irq) * cause some ioapics to mal-function. * Being paranoid i guess! */ - if (unlikely(!cpus_empty(tmp))) { + if (likely(!cpus_empty(tmp))) { if (likely(!(desc->status & IRQ_DISABLED))) - desc->handler->disable(irq); + desc->chip->disable(irq); - desc->handler->set_affinity(irq,tmp); + desc->chip->set_affinity(irq,tmp); if (likely(!(desc->status & IRQ_DISABLED))) - desc->handler->enable(irq); + desc->chip->enable(irq); } - cpus_clear(pending_irq_cpumask[irq]); + cpus_clear(irq_desc[irq].pending_mask); } diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index d03b5ee..607c780 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -12,18 +12,15 @@ #include #include "internals.h" -static struct proc_dir_entry *root_irq_dir, *irq_dir[NR_IRQS]; +static struct proc_dir_entry *root_irq_dir; #ifdef CONFIG_SMP -/* - * The /proc/irq//smp_affinity values: - */ -static struct proc_dir_entry *smp_affinity_entry[NR_IRQS]; - #ifdef CONFIG_GENERIC_PENDING_IRQ void proc_set_irq_affinity(unsigned int irq, cpumask_t mask_val) { + set_balance_irq_affinity(irq, mask_val); + /* * Save these away for later use. Re-progam when the * interrupt is pending @@ -33,15 +30,16 @@ void proc_set_irq_affinity(unsigned int #else void proc_set_irq_affinity(unsigned int irq, cpumask_t mask_val) { - irq_affinity[irq] = mask_val; - irq_desc[irq].handler->set_affinity(irq, mask_val); + set_balance_irq_affinity(irq, mask_val); + irq_desc[irq].affinity = mask_val; + irq_desc[irq].chip->set_affinity(irq, mask_val); } #endif static int irq_affinity_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int len = cpumask_scnprintf(page, count, irq_affinity[(long)data]); + int len = cpumask_scnprintf(page, count, irq_desc[(long)data].affinity); if (count - len < 2) return -EINVAL; @@ -56,7 +54,7 @@ static int irq_affinity_write_proc(struc unsigned int irq = (int)(long)data, full_count = count, err; cpumask_t new_value, tmp; - if (!irq_desc[irq].handler->set_affinity || no_irq_affinity) + if (!irq_desc[irq].chip->set_affinity || no_irq_affinity) return -EIO; err = cpumask_parse(buffer, count, new_value); @@ -99,7 +97,7 @@ void register_handler_proc(unsigned int { char name [MAX_NAMELEN]; - if (!irq_dir[irq] || action->dir || !action->name || + if (!irq_desc[irq].dir || action->dir || !action->name || !name_unique(irq, action)) return; @@ -107,7 +105,7 @@ void register_handler_proc(unsigned int snprintf(name, MAX_NAMELEN, "%s", action->name); /* create /proc/irq/1234/handler/ */ - action->dir = proc_mkdir(name, irq_dir[irq]); + action->dir = proc_mkdir(name, irq_desc[irq].dir); } #undef MAX_NAMELEN @@ -119,22 +117,22 @@ void register_irq_proc(unsigned int irq) char name [MAX_NAMELEN]; if (!root_irq_dir || - (irq_desc[irq].handler == &no_irq_type) || - irq_dir[irq]) + (irq_desc[irq].chip == &no_irq_chip) || + irq_desc[irq].dir) return; memset(name, 0, MAX_NAMELEN); sprintf(name, "%d", irq); /* create /proc/irq/1234 */ - irq_dir[irq] = proc_mkdir(name, root_irq_dir); + irq_desc[irq].dir = proc_mkdir(name, root_irq_dir); #ifdef CONFIG_SMP { struct proc_dir_entry *entry; /* create /proc/irq//smp_affinity */ - entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + entry = create_proc_entry("smp_affinity", 0600, irq_desc[irq].dir); if (entry) { entry->nlink = 1; @@ -142,7 +140,6 @@ #ifdef CONFIG_SMP entry->read_proc = irq_affinity_read_proc; entry->write_proc = irq_affinity_write_proc; } - smp_affinity_entry[irq] = entry; } #endif } @@ -152,7 +149,7 @@ #undef MAX_NAMELEN void unregister_handler_proc(unsigned int irq, struct irqaction *action) { if (action->dir) - remove_proc_entry(action->dir->name, irq_dir[irq]); + remove_proc_entry(action->dir->name, irq_desc[irq].dir); } void init_irq_proc(void) diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c new file mode 100644 index 0000000..872f91b --- /dev/null +++ b/kernel/irq/resend.c @@ -0,0 +1,78 @@ +/* + * linux/kernel/irq/resend.c + * + * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar + * Copyright (C) 2005-2006, Thomas Gleixner + * + * This file contains the IRQ-resend code + * + * If the interrupt is waiting to be processed, we try to re-run it. + * We can't directly run it from here since the caller might be in an + * interrupt-protected region. Not all irq controller chips can + * retrigger interrupts at the hardware level, so in those cases + * we allow the resending of IRQs via a tasklet. + */ + +#include +#include +#include +#include + +#include "internals.h" + +#ifdef CONFIG_HARDIRQS_SW_RESEND + +/* Bitmap to handle software resend of interrupts: */ +static DECLARE_BITMAP(irqs_resend, NR_IRQS); + +/* + * Run software resends of IRQ's + */ +static void resend_irqs(unsigned long arg) +{ + struct irq_desc *desc; + int irq; + + while (!bitmap_empty(irqs_resend, NR_IRQS)) { + irq = find_first_bit(irqs_resend, NR_IRQS); + clear_bit(irq, irqs_resend); + desc = irq_desc + irq; + local_irq_disable(); + desc->handle_irq(irq, desc, NULL); + local_irq_enable(); + } +} + +/* Tasklet to handle resend: */ +static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0); + +#endif + +/* + * IRQ resend + * + * Is called with interrupts disabled and desc->lock held. + */ +void check_irq_resend(struct irq_desc *desc, unsigned int irq) +{ + unsigned int status = desc->status; + + /* + * Make sure the interrupt is enabled, before resending it: + */ + desc->chip->enable(irq); + + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status &= ~IRQ_PENDING; + desc->status = status | IRQ_REPLAY; + + if (!desc->chip || !desc->chip->retrigger || + !desc->chip->retrigger(irq)) { +#ifdef CONFIG_HARDIRQS_SW_RESEND + /* Set it pending and activate the softirq: */ + set_bit(irq, irqs_resend); + tasklet_schedule(&resend_tasklet); +#endif + } + } +} diff --git a/kernel/irq/spurious.c b/kernel/irq/spurious.c index 7df9abd..417e980 100644 --- a/kernel/irq/spurious.c +++ b/kernel/irq/spurious.c @@ -11,44 +11,44 @@ #include #include #include -static int irqfixup; +static int irqfixup __read_mostly; /* * Recovery handler for misrouted interrupts. */ - static int misrouted_irq(int irq, struct pt_regs *regs) { int i; - irq_desc_t *desc; int ok = 0; int work = 0; /* Did we do work for a real IRQ */ - for(i = 1; i < NR_IRQS; i++) { + for (i = 1; i < NR_IRQS; i++) { + struct irq_desc *desc = irq_desc + i; struct irqaction *action; if (i == irq) /* Already tried */ continue; - desc = &irq_desc[i]; + spin_lock(&desc->lock); - action = desc->action; /* Already running on another processor */ if (desc->status & IRQ_INPROGRESS) { /* * Already running: If it is shared get the other * CPU to go looking for our mystery interrupt too */ - if (desc->action && (desc->action->flags & SA_SHIRQ)) + if (desc->action && (desc->action->flags & IRQF_SHARED)) desc->status |= IRQ_PENDING; spin_unlock(&desc->lock); continue; } /* Honour the normal IRQ locking */ desc->status |= IRQ_INPROGRESS; + action = desc->action; spin_unlock(&desc->lock); + while (action) { /* Only shared IRQ handlers are safe to call */ - if (action->flags & SA_SHIRQ) { + if (action->flags & IRQF_SHARED) { if (action->handler(i, action->dev_id, regs) == IRQ_HANDLED) ok = 1; @@ -62,9 +62,8 @@ static int misrouted_irq(int irq, struct /* * While we were looking for a fixup someone queued a real - * IRQ clashing with our walk + * IRQ clashing with our walk: */ - while ((desc->status & IRQ_PENDING) && action) { /* * Perform real IRQ processing for the IRQ we deferred @@ -80,8 +79,8 @@ static int misrouted_irq(int irq, struct * If we did actual work for the real IRQ line we must let the * IRQ controller clean up too */ - if(work) - desc->handler->end(i); + if (work && desc->chip && desc->chip->end) + desc->chip->end(i); spin_unlock(&desc->lock); } /* So the caller can adjust the irq error counts */ @@ -100,7 +99,8 @@ static int misrouted_irq(int irq, struct */ static void -__report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) +__report_bad_irq(unsigned int irq, struct irq_desc *desc, + irqreturn_t action_ret) { struct irqaction *action; @@ -113,6 +113,7 @@ __report_bad_irq(unsigned int irq, irq_d } dump_stack(); printk(KERN_ERR "handlers:\n"); + action = desc->action; while (action) { printk(KERN_ERR "[<%p>]", action->handler); @@ -123,7 +124,8 @@ __report_bad_irq(unsigned int irq, irq_d } } -static void report_bad_irq(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret) +static void +report_bad_irq(unsigned int irq, struct irq_desc *desc, irqreturn_t action_ret) { static int count = 100; @@ -133,12 +135,12 @@ static void report_bad_irq(unsigned int } } -void note_interrupt(unsigned int irq, irq_desc_t *desc, irqreturn_t action_ret, - struct pt_regs *regs) +void note_interrupt(unsigned int irq, struct irq_desc *desc, + irqreturn_t action_ret, struct pt_regs *regs) { - if (action_ret != IRQ_HANDLED) { + if (unlikely(action_ret != IRQ_HANDLED)) { desc->irqs_unhandled++; - if (action_ret != IRQ_NONE) + if (unlikely(action_ret != IRQ_NONE)) report_bad_irq(irq, desc, action_ret); } @@ -152,11 +154,11 @@ void note_interrupt(unsigned int irq, ir } desc->irq_count++; - if (desc->irq_count < 100000) + if (likely(desc->irq_count < 100000)) return; desc->irq_count = 0; - if (desc->irqs_unhandled > 99900) { + if (unlikely(desc->irqs_unhandled > 99900)) { /* * The interrupt is stuck */ @@ -166,17 +168,19 @@ void note_interrupt(unsigned int irq, ir */ printk(KERN_EMERG "Disabling IRQ #%d\n", irq); desc->status |= IRQ_DISABLED; - desc->handler->disable(irq); + desc->depth = 1; + desc->chip->disable(irq); } desc->irqs_unhandled = 0; } -int noirqdebug; +int noirqdebug __read_mostly; int __init noirqdebug_setup(char *str) { noirqdebug = 1; printk(KERN_INFO "IRQ lockup detection disabled\n"); + return 1; } @@ -187,6 +191,7 @@ static int __init irqfixup_setup(char *s irqfixup = 1; printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n"); printk(KERN_WARNING "This may impact system performance.\n"); + return 1; } diff --git a/kernel/kexec.c b/kernel/kexec.c index bf39d28..50087ec 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -902,14 +902,14 @@ static int kimage_load_segment(struct ki * kexec does not sync, or unmount filesystems so if you need * that to happen you need to do that yourself. */ -struct kimage *kexec_image = NULL; -static struct kimage *kexec_crash_image = NULL; +struct kimage *kexec_image; +struct kimage *kexec_crash_image; /* * A home grown binary mutex. * Nothing can wait so this mutex is safe to use * in interrupt context :) */ -static int kexec_lock = 0; +static int kexec_lock; asmlinkage long sys_kexec_load(unsigned long entry, unsigned long nr_segments, struct kexec_segment __user *segments, @@ -1042,7 +1042,6 @@ #endif void crash_kexec(struct pt_regs *regs) { - struct kimage *image; int locked; @@ -1056,12 +1055,11 @@ void crash_kexec(struct pt_regs *regs) */ locked = xchg(&kexec_lock, 1); if (!locked) { - image = xchg(&kexec_crash_image, NULL); - if (image) { + if (kexec_crash_image) { struct pt_regs fixed_regs; crash_setup_regs(&fixed_regs, regs); machine_crash_shutdown(&fixed_regs); - machine_kexec(image); + machine_kexec(kexec_crash_image); } xchg(&kexec_lock, 0); } diff --git a/kernel/kmod.c b/kernel/kmod.c index 20a997c..1d32def 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -20,7 +20,6 @@ */ #define __KERNEL_SYSCALLS__ -#include #include #include #include @@ -234,7 +233,7 @@ static void __call_usermodehelper(void * int call_usermodehelper_keys(char *path, char **argv, char **envp, struct key *session_keyring, int wait) { - DECLARE_COMPLETION(done); + DECLARE_COMPLETION_ONSTACK(done); struct subprocess_info sub_info = { .complete = &done, .path = path, diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 1fbf466..64aab08 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -47,11 +47,17 @@ #define KPROBE_TABLE_SIZE (1 << KPROBE_H static struct hlist_head kprobe_table[KPROBE_TABLE_SIZE]; static struct hlist_head kretprobe_inst_table[KPROBE_TABLE_SIZE]; +static atomic_t kprobe_count; DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ DEFINE_SPINLOCK(kretprobe_lock); /* Protects kretprobe_inst_table */ static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; +static struct notifier_block kprobe_page_fault_nb = { + .notifier_call = kprobe_exceptions_notify, + .priority = 0x7fffffff /* we need to notified first */ +}; + #ifdef __ARCH_WANT_KPROBES_INSN_SLOT /* * kprobe->ainsn.insn points to the copy of the instruction to be @@ -368,16 +374,15 @@ static inline void copy_kprobe(struct kp */ static int __kprobes add_new_kprobe(struct kprobe *old_p, struct kprobe *p) { - struct kprobe *kp; - if (p->break_handler) { - list_for_each_entry_rcu(kp, &old_p->list, list) { - if (kp->break_handler) - return -EEXIST; - } + if (old_p->break_handler) + return -EEXIST; list_add_tail_rcu(&p->list, &old_p->list); + old_p->break_handler = aggr_break_handler; } else list_add_rcu(&p->list, &old_p->list); + if (p->post_handler && !old_p->post_handler) + old_p->post_handler = aggr_post_handler; return 0; } @@ -390,9 +395,11 @@ static inline void add_aggr_kprobe(struc copy_kprobe(p, ap); ap->addr = p->addr; ap->pre_handler = aggr_pre_handler; - ap->post_handler = aggr_post_handler; ap->fault_handler = aggr_fault_handler; - ap->break_handler = aggr_break_handler; + if (p->post_handler) + ap->post_handler = aggr_post_handler; + if (p->break_handler) + ap->break_handler = aggr_break_handler; INIT_LIST_HEAD(&ap->list); list_add_rcu(&p->list, &ap->list); @@ -464,6 +471,8 @@ static int __kprobes __register_kprobe(s old_p = get_kprobe(p->addr); if (old_p) { ret = register_aggr_kprobe(old_p, p); + if (!ret) + atomic_inc(&kprobe_count); goto out; } @@ -474,6 +483,10 @@ static int __kprobes __register_kprobe(s hlist_add_head_rcu(&p->hlist, &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]); + if (atomic_add_return(1, &kprobe_count) == \ + (ARCH_INACTIVE_KPROBE_COUNT + 1)) + register_page_fault_notifier(&kprobe_page_fault_nb); + arch_arm_kprobe(p); out: @@ -536,14 +549,40 @@ valid_p: kfree(old_p); } arch_remove_kprobe(p); + } else { + mutex_lock(&kprobe_mutex); + if (p->break_handler) + old_p->break_handler = NULL; + if (p->post_handler){ + list_for_each_entry_rcu(list_p, &old_p->list, list){ + if (list_p->post_handler){ + cleanup_p = 2; + break; + } + } + if (cleanup_p == 0) + old_p->post_handler = NULL; + } + mutex_unlock(&kprobe_mutex); } + + /* Call unregister_page_fault_notifier() + * if no probes are active + */ + mutex_lock(&kprobe_mutex); + if (atomic_add_return(-1, &kprobe_count) == \ + ARCH_INACTIVE_KPROBE_COUNT) + unregister_page_fault_notifier(&kprobe_page_fault_nb); + mutex_unlock(&kprobe_mutex); + return; } static struct notifier_block kprobe_exceptions_nb = { .notifier_call = kprobe_exceptions_notify, - .priority = 0x7fffffff /* we need to notified first */ + .priority = 0x7fffffff /* we need to be notified first */ }; + int __kprobes register_jprobe(struct jprobe *jp) { /* Todo: Verify probepoint is a function entry point */ @@ -652,6 +691,7 @@ static int __init init_kprobes(void) INIT_HLIST_HEAD(&kprobe_table[i]); INIT_HLIST_HEAD(&kretprobe_inst_table[i]); } + atomic_set(&kprobe_count, 0); err = arch_init_kprobes(); if (!err) diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index f119e09..e0ffe4a 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -8,12 +8,12 @@ * */ -#include #include #include #include #include #include +#include #define KERNEL_ATTR_RO(_name) \ static struct subsys_attribute _name##_attr = __ATTR_RO(_name) @@ -48,6 +48,20 @@ static ssize_t uevent_helper_store(struc KERNEL_ATTR_RW(uevent_helper); #endif +#ifdef CONFIG_KEXEC +static ssize_t kexec_loaded_show(struct subsystem *subsys, char *page) +{ + return sprintf(page, "%d\n", !!kexec_image); +} +KERNEL_ATTR_RO(kexec_loaded); + +static ssize_t kexec_crash_loaded_show(struct subsystem *subsys, char *page) +{ + return sprintf(page, "%d\n", !!kexec_crash_image); +} +KERNEL_ATTR_RO(kexec_crash_loaded); +#endif /* CONFIG_KEXEC */ + decl_subsys(kernel, NULL, NULL); EXPORT_SYMBOL_GPL(kernel_subsys); @@ -56,6 +70,10 @@ #if defined(CONFIG_HOTPLUG) && defined(C &uevent_seqnum_attr.attr, &uevent_helper_attr.attr, #endif +#ifdef CONFIG_KEXEC + &kexec_loaded_attr.attr, + &kexec_crash_loaded_attr.attr, +#endif NULL }; diff --git a/kernel/kthread.c b/kernel/kthread.c index c5f3c66..24be714 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -45,6 +45,13 @@ struct kthread_stop_info static DEFINE_MUTEX(kthread_stop_lock); static struct kthread_stop_info kthread_stop_info; +/** + * kthread_should_stop - should this kthread return now? + * + * When someone calls kthread_stop on your kthread, it will be woken + * and this will return true. You should then return, and your return + * value will be passed through to kthread_stop(). + */ int kthread_should_stop(void) { return (kthread_stop_info.k == current); @@ -122,6 +129,25 @@ static void keventd_create_kthread(void complete(&create->done); } +/** + * kthread_create - create a kthread. + * @threadfn: the function to run until signal_pending(current). + * @data: data ptr for @threadfn. + * @namefmt: printf-style name for the thread. + * + * Description: This helper function creates and names a kernel + * thread. The thread will be stopped: use wake_up_process() to start + * it. See also kthread_run(), kthread_create_on_cpu(). + * + * When woken, the thread will run @threadfn() with @data as its + * argument. @threadfn can either call do_exit() directly if it is a + * standalone thread for which noone will call kthread_stop(), or + * return when 'kthread_should_stop()' is true (which means + * kthread_stop() has been called). The return value should be zero + * or a negative error number; it will be passed to kthread_stop(). + * + * Returns a task_struct or ERR_PTR(-ENOMEM). + */ struct task_struct *kthread_create(int (*threadfn)(void *data), void *data, const char namefmt[], @@ -156,6 +182,15 @@ struct task_struct *kthread_create(int ( } EXPORT_SYMBOL(kthread_create); +/** + * kthread_bind - bind a just-created kthread to a cpu. + * @k: thread created by kthread_create(). + * @cpu: cpu (might not be online, must be possible) for @k to run on. + * + * Description: This function is equivalent to set_cpus_allowed(), + * except that @cpu doesn't need to be online, and the thread must be + * stopped (i.e., just returned from kthread_create(). + */ void kthread_bind(struct task_struct *k, unsigned int cpu) { BUG_ON(k->state != TASK_INTERRUPTIBLE); @@ -166,12 +201,36 @@ void kthread_bind(struct task_struct *k, } EXPORT_SYMBOL(kthread_bind); +/** + * kthread_stop - stop a thread created by kthread_create(). + * @k: thread created by kthread_create(). + * + * Sets kthread_should_stop() for @k to return true, wakes it, and + * waits for it to exit. Your threadfn() must not call do_exit() + * itself if you use this function! This can also be called after + * kthread_create() instead of calling wake_up_process(): the thread + * will exit without calling threadfn(). + * + * Returns the result of threadfn(), or %-EINTR if wake_up_process() + * was never called. + */ int kthread_stop(struct task_struct *k) { return kthread_stop_sem(k, NULL); } EXPORT_SYMBOL(kthread_stop); +/** + * kthread_stop_sem - stop a thread created by kthread_create(). + * @k: thread created by kthread_create(). + * @s: semaphore that @k waits on while idle. + * + * Does essentially the same thing as kthread_stop() above, but wakes + * @k by calling up(@s). + * + * Returns the result of threadfn(), or %-EINTR if wake_up_process() + * was never called. + */ int kthread_stop_sem(struct task_struct *k, struct semaphore *s) { int ret; @@ -210,5 +269,5 @@ static __init int helper_init(void) return 0; } -core_initcall(helper_init); +core_initcall(helper_init); diff --git a/kernel/lockdep.c b/kernel/lockdep.c new file mode 100644 index 0000000..f32ca78 --- /dev/null +++ b/kernel/lockdep.c @@ -0,0 +1,2702 @@ +/* + * kernel/lockdep.c + * + * Runtime locking correctness validator + * + * Started by Ingo Molnar: + * + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + * + * this code maps all the lock dependencies as they occur in a live kernel + * and will warn about the following classes of locking bugs: + * + * - lock inversion scenarios + * - circular lock dependencies + * - hardirq/softirq safe/unsafe locking bugs + * + * Bugs are reported even if the current locking scenario does not cause + * any deadlock at this point. + * + * I.e. if anytime in the past two locks were taken in a different order, + * even if it happened for another task, even if those were different + * locks (but of the same class as this lock), this code will detect it. + * + * Thanks to Arjan van de Ven for coming up with the initial idea of + * mapping lock dependencies runtime. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lockdep_internals.h" + +/* + * hash_lock: protects the lockdep hashes and class/list/hash allocators. + * + * This is one of the rare exceptions where it's justified + * to use a raw spinlock - we really dont want the spinlock + * code to recurse back into the lockdep code. + */ +static raw_spinlock_t hash_lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED; + +static int lockdep_initialized; + +unsigned long nr_list_entries; +static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES]; + +/* + * Allocate a lockdep entry. (assumes hash_lock held, returns + * with NULL on failure) + */ +static struct lock_list *alloc_list_entry(void) +{ + if (nr_list_entries >= MAX_LOCKDEP_ENTRIES) { + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + printk("BUG: MAX_LOCKDEP_ENTRIES too low!\n"); + printk("turning off the locking correctness validator.\n"); + return NULL; + } + return list_entries + nr_list_entries++; +} + +/* + * All data structures here are protected by the global debug_lock. + * + * Mutex key structs only get allocated, once during bootup, and never + * get freed - this significantly simplifies the debugging code. + */ +unsigned long nr_lock_classes; +static struct lock_class lock_classes[MAX_LOCKDEP_KEYS]; + +/* + * We keep a global list of all lock classes. The list only grows, + * never shrinks. The list is only accessed with the lockdep + * spinlock lock held. + */ +LIST_HEAD(all_lock_classes); + +/* + * The lockdep classes are in a hash-table as well, for fast lookup: + */ +#define CLASSHASH_BITS (MAX_LOCKDEP_KEYS_BITS - 1) +#define CLASSHASH_SIZE (1UL << CLASSHASH_BITS) +#define CLASSHASH_MASK (CLASSHASH_SIZE - 1) +#define __classhashfn(key) ((((unsigned long)key >> CLASSHASH_BITS) + (unsigned long)key) & CLASSHASH_MASK) +#define classhashentry(key) (classhash_table + __classhashfn((key))) + +static struct list_head classhash_table[CLASSHASH_SIZE]; + +unsigned long nr_lock_chains; +static struct lock_chain lock_chains[MAX_LOCKDEP_CHAINS]; + +/* + * We put the lock dependency chains into a hash-table as well, to cache + * their existence: + */ +#define CHAINHASH_BITS (MAX_LOCKDEP_CHAINS_BITS-1) +#define CHAINHASH_SIZE (1UL << CHAINHASH_BITS) +#define CHAINHASH_MASK (CHAINHASH_SIZE - 1) +#define __chainhashfn(chain) \ + (((chain >> CHAINHASH_BITS) + chain) & CHAINHASH_MASK) +#define chainhashentry(chain) (chainhash_table + __chainhashfn((chain))) + +static struct list_head chainhash_table[CHAINHASH_SIZE]; + +/* + * The hash key of the lock dependency chains is a hash itself too: + * it's a hash of all locks taken up to that lock, including that lock. + * It's a 64-bit hash, because it's important for the keys to be + * unique. + */ +#define iterate_chain_key(key1, key2) \ + (((key1) << MAX_LOCKDEP_KEYS_BITS/2) ^ \ + ((key1) >> (64-MAX_LOCKDEP_KEYS_BITS/2)) ^ \ + (key2)) + +void lockdep_off(void) +{ + current->lockdep_recursion++; +} + +EXPORT_SYMBOL(lockdep_off); + +void lockdep_on(void) +{ + current->lockdep_recursion--; +} + +EXPORT_SYMBOL(lockdep_on); + +int lockdep_internal(void) +{ + return current->lockdep_recursion != 0; +} + +EXPORT_SYMBOL(lockdep_internal); + +/* + * Debugging switches: + */ + +#define VERBOSE 0 +#ifdef VERBOSE +# define VERY_VERBOSE 0 +#endif + +#if VERBOSE +# define HARDIRQ_VERBOSE 1 +# define SOFTIRQ_VERBOSE 1 +#else +# define HARDIRQ_VERBOSE 0 +# define SOFTIRQ_VERBOSE 0 +#endif + +#if VERBOSE || HARDIRQ_VERBOSE || SOFTIRQ_VERBOSE +/* + * Quick filtering for interesting events: + */ +static int class_filter(struct lock_class *class) +{ + if (class->name_version == 1 && + !strcmp(class->name, "&rl->lock")) + return 1; + if (class->name_version == 1 && + !strcmp(class->name, "&ni->mrec_lock")) + return 1; + if (class->name_version == 1 && + !strcmp(class->name, "mft_ni_runlist_lock")) + return 1; + if (class->name_version == 1 && + !strcmp(class->name, "mft_ni_mrec_lock")) + return 1; + if (class->name_version == 1 && + !strcmp(class->name, "&vol->lcnbmp_lock")) + return 1; + return 0; +} +#endif + +static int verbose(struct lock_class *class) +{ +#if VERBOSE + return class_filter(class); +#endif + return 0; +} + +#ifdef CONFIG_TRACE_IRQFLAGS + +static int hardirq_verbose(struct lock_class *class) +{ +#if HARDIRQ_VERBOSE + return class_filter(class); +#endif + return 0; +} + +static int softirq_verbose(struct lock_class *class) +{ +#if SOFTIRQ_VERBOSE + return class_filter(class); +#endif + return 0; +} + +#endif + +/* + * Stack-trace: tightly packed array of stack backtrace + * addresses. Protected by the hash_lock. + */ +unsigned long nr_stack_trace_entries; +static unsigned long stack_trace[MAX_STACK_TRACE_ENTRIES]; + +static int save_trace(struct stack_trace *trace) +{ + trace->nr_entries = 0; + trace->max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries; + trace->entries = stack_trace + nr_stack_trace_entries; + + save_stack_trace(trace, NULL, 0, 3); + + trace->max_entries = trace->nr_entries; + + nr_stack_trace_entries += trace->nr_entries; + if (DEBUG_LOCKS_WARN_ON(nr_stack_trace_entries > MAX_STACK_TRACE_ENTRIES)) + return 0; + + if (nr_stack_trace_entries == MAX_STACK_TRACE_ENTRIES) { + __raw_spin_unlock(&hash_lock); + if (debug_locks_off()) { + printk("BUG: MAX_STACK_TRACE_ENTRIES too low!\n"); + printk("turning off the locking correctness validator.\n"); + dump_stack(); + } + return 0; + } + + return 1; +} + +unsigned int nr_hardirq_chains; +unsigned int nr_softirq_chains; +unsigned int nr_process_chains; +unsigned int max_lockdep_depth; +unsigned int max_recursion_depth; + +#ifdef CONFIG_DEBUG_LOCKDEP +/* + * We cannot printk in early bootup code. Not even early_printk() + * might work. So we mark any initialization errors and printk + * about it later on, in lockdep_info(). + */ +static int lockdep_init_error; + +/* + * Various lockdep statistics: + */ +atomic_t chain_lookup_hits; +atomic_t chain_lookup_misses; +atomic_t hardirqs_on_events; +atomic_t hardirqs_off_events; +atomic_t redundant_hardirqs_on; +atomic_t redundant_hardirqs_off; +atomic_t softirqs_on_events; +atomic_t softirqs_off_events; +atomic_t redundant_softirqs_on; +atomic_t redundant_softirqs_off; +atomic_t nr_unused_locks; +atomic_t nr_cyclic_checks; +atomic_t nr_cyclic_check_recursions; +atomic_t nr_find_usage_forwards_checks; +atomic_t nr_find_usage_forwards_recursions; +atomic_t nr_find_usage_backwards_checks; +atomic_t nr_find_usage_backwards_recursions; +# define debug_atomic_inc(ptr) atomic_inc(ptr) +# define debug_atomic_dec(ptr) atomic_dec(ptr) +# define debug_atomic_read(ptr) atomic_read(ptr) +#else +# define debug_atomic_inc(ptr) do { } while (0) +# define debug_atomic_dec(ptr) do { } while (0) +# define debug_atomic_read(ptr) 0 +#endif + +/* + * Locking printouts: + */ + +static const char *usage_str[] = +{ + [LOCK_USED] = "initial-use ", + [LOCK_USED_IN_HARDIRQ] = "in-hardirq-W", + [LOCK_USED_IN_SOFTIRQ] = "in-softirq-W", + [LOCK_ENABLED_SOFTIRQS] = "softirq-on-W", + [LOCK_ENABLED_HARDIRQS] = "hardirq-on-W", + [LOCK_USED_IN_HARDIRQ_READ] = "in-hardirq-R", + [LOCK_USED_IN_SOFTIRQ_READ] = "in-softirq-R", + [LOCK_ENABLED_SOFTIRQS_READ] = "softirq-on-R", + [LOCK_ENABLED_HARDIRQS_READ] = "hardirq-on-R", +}; + +const char * __get_key_name(struct lockdep_subclass_key *key, char *str) +{ + unsigned long offs, size; + char *modname; + + return kallsyms_lookup((unsigned long)key, &size, &offs, &modname, str); +} + +void +get_usage_chars(struct lock_class *class, char *c1, char *c2, char *c3, char *c4) +{ + *c1 = '.', *c2 = '.', *c3 = '.', *c4 = '.'; + + if (class->usage_mask & LOCKF_USED_IN_HARDIRQ) + *c1 = '+'; + else + if (class->usage_mask & LOCKF_ENABLED_HARDIRQS) + *c1 = '-'; + + if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ) + *c2 = '+'; + else + if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS) + *c2 = '-'; + + if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ) + *c3 = '-'; + if (class->usage_mask & LOCKF_USED_IN_HARDIRQ_READ) { + *c3 = '+'; + if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ) + *c3 = '?'; + } + + if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS_READ) + *c4 = '-'; + if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ_READ) { + *c4 = '+'; + if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS_READ) + *c4 = '?'; + } +} + +static void print_lock_name(struct lock_class *class) +{ + char str[128], c1, c2, c3, c4; + const char *name; + + get_usage_chars(class, &c1, &c2, &c3, &c4); + + name = class->name; + if (!name) { + name = __get_key_name(class->key, str); + printk(" (%s", name); + } else { + printk(" (%s", name); + if (class->name_version > 1) + printk("#%d", class->name_version); + if (class->subclass) + printk("/%d", class->subclass); + } + printk("){%c%c%c%c}", c1, c2, c3, c4); +} + +static void print_lockdep_cache(struct lockdep_map *lock) +{ + const char *name; + char str[128]; + + name = lock->name; + if (!name) + name = __get_key_name(lock->key->subkeys, str); + + printk("%s", name); +} + +static void print_lock(struct held_lock *hlock) +{ + print_lock_name(hlock->class); + printk(", at: "); + print_ip_sym(hlock->acquire_ip); +} + +static void lockdep_print_held_locks(struct task_struct *curr) +{ + int i, depth = curr->lockdep_depth; + + if (!depth) { + printk("no locks held by %s/%d.\n", curr->comm, curr->pid); + return; + } + printk("%d lock%s held by %s/%d:\n", + depth, depth > 1 ? "s" : "", curr->comm, curr->pid); + + for (i = 0; i < depth; i++) { + printk(" #%d: ", i); + print_lock(curr->held_locks + i); + } +} +/* + * Helper to print a nice hierarchy of lock dependencies: + */ +static void print_spaces(int nr) +{ + int i; + + for (i = 0; i < nr; i++) + printk(" "); +} + +static void print_lock_class_header(struct lock_class *class, int depth) +{ + int bit; + + print_spaces(depth); + printk("->"); + print_lock_name(class); + printk(" ops: %lu", class->ops); + printk(" {\n"); + + for (bit = 0; bit < LOCK_USAGE_STATES; bit++) { + if (class->usage_mask & (1 << bit)) { + int len = depth; + + print_spaces(depth); + len += printk(" %s", usage_str[bit]); + len += printk(" at:\n"); + print_stack_trace(class->usage_traces + bit, len); + } + } + print_spaces(depth); + printk(" }\n"); + + print_spaces(depth); + printk(" ... key at: "); + print_ip_sym((unsigned long)class->key); +} + +/* + * printk all lock dependencies starting at : + */ +static void print_lock_dependencies(struct lock_class *class, int depth) +{ + struct lock_list *entry; + + if (DEBUG_LOCKS_WARN_ON(depth >= 20)) + return; + + print_lock_class_header(class, depth); + + list_for_each_entry(entry, &class->locks_after, entry) { + DEBUG_LOCKS_WARN_ON(!entry->class); + print_lock_dependencies(entry->class, depth + 1); + + print_spaces(depth); + printk(" ... acquired at:\n"); + print_stack_trace(&entry->trace, 2); + printk("\n"); + } +} + +/* + * Add a new dependency to the head of the list: + */ +static int add_lock_to_list(struct lock_class *class, struct lock_class *this, + struct list_head *head, unsigned long ip) +{ + struct lock_list *entry; + /* + * Lock not present yet - get a new dependency struct and + * add it to the list: + */ + entry = alloc_list_entry(); + if (!entry) + return 0; + + entry->class = this; + save_trace(&entry->trace); + + /* + * Since we never remove from the dependency list, the list can + * be walked lockless by other CPUs, it's only allocation + * that must be protected by the spinlock. But this also means + * we must make new entries visible only once writes to the + * entry become visible - hence the RCU op: + */ + list_add_tail_rcu(&entry->entry, head); + + return 1; +} + +/* + * Recursive, forwards-direction lock-dependency checking, used for + * both noncyclic checking and for hardirq-unsafe/softirq-unsafe + * checking. + * + * (to keep the stackframe of the recursive functions small we + * use these global variables, and we also mark various helper + * functions as noinline.) + */ +static struct held_lock *check_source, *check_target; + +/* + * Print a dependency chain entry (this is only done when a deadlock + * has been detected): + */ +static noinline int +print_circular_bug_entry(struct lock_list *target, unsigned int depth) +{ + if (debug_locks_silent) + return 0; + printk("\n-> #%u", depth); + print_lock_name(target->class); + printk(":\n"); + print_stack_trace(&target->trace, 6); + + return 0; +} + +/* + * When a circular dependency is detected, print the + * header first: + */ +static noinline int +print_circular_bug_header(struct lock_list *entry, unsigned int depth) +{ + struct task_struct *curr = current; + + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + if (debug_locks_silent) + return 0; + + printk("\n=======================================================\n"); + printk( "[ INFO: possible circular locking dependency detected ]\n"); + printk( "-------------------------------------------------------\n"); + printk("%s/%d is trying to acquire lock:\n", + curr->comm, curr->pid); + print_lock(check_source); + printk("\nbut task is already holding lock:\n"); + print_lock(check_target); + printk("\nwhich lock already depends on the new lock.\n\n"); + printk("\nthe existing dependency chain (in reverse order) is:\n"); + + print_circular_bug_entry(entry, depth); + + return 0; +} + +static noinline int print_circular_bug_tail(void) +{ + struct task_struct *curr = current; + struct lock_list this; + + if (debug_locks_silent) + return 0; + + this.class = check_source->class; + save_trace(&this.trace); + print_circular_bug_entry(&this, 0); + + printk("\nother info that might help us debug this:\n\n"); + lockdep_print_held_locks(curr); + + printk("\nstack backtrace:\n"); + dump_stack(); + + return 0; +} + +static int noinline print_infinite_recursion_bug(void) +{ + __raw_spin_unlock(&hash_lock); + DEBUG_LOCKS_WARN_ON(1); + + return 0; +} + +/* + * Prove that the dependency graph starting at can not + * lead to . Print an error and return 0 if it does. + */ +static noinline int +check_noncircular(struct lock_class *source, unsigned int depth) +{ + struct lock_list *entry; + + debug_atomic_inc(&nr_cyclic_check_recursions); + if (depth > max_recursion_depth) + max_recursion_depth = depth; + if (depth >= 20) + return print_infinite_recursion_bug(); + /* + * Check this lock's dependency list: + */ + list_for_each_entry(entry, &source->locks_after, entry) { + if (entry->class == check_target->class) + return print_circular_bug_header(entry, depth+1); + debug_atomic_inc(&nr_cyclic_checks); + if (!check_noncircular(entry->class, depth+1)) + return print_circular_bug_entry(entry, depth+1); + } + return 1; +} + +static int very_verbose(struct lock_class *class) +{ +#if VERY_VERBOSE + return class_filter(class); +#endif + return 0; +} +#ifdef CONFIG_TRACE_IRQFLAGS + +/* + * Forwards and backwards subgraph searching, for the purposes of + * proving that two subgraphs can be connected by a new dependency + * without creating any illegal irq-safe -> irq-unsafe lock dependency. + */ +static enum lock_usage_bit find_usage_bit; +static struct lock_class *forwards_match, *backwards_match; + +/* + * Find a node in the forwards-direction dependency sub-graph starting + * at that matches . + * + * Return 2 if such a node exists in the subgraph, and put that node + * into . + * + * Return 1 otherwise and keep unchanged. + * Return 0 on error. + */ +static noinline int +find_usage_forwards(struct lock_class *source, unsigned int depth) +{ + struct lock_list *entry; + int ret; + + if (depth > max_recursion_depth) + max_recursion_depth = depth; + if (depth >= 20) + return print_infinite_recursion_bug(); + + debug_atomic_inc(&nr_find_usage_forwards_checks); + if (source->usage_mask & (1 << find_usage_bit)) { + forwards_match = source; + return 2; + } + + /* + * Check this lock's dependency list: + */ + list_for_each_entry(entry, &source->locks_after, entry) { + debug_atomic_inc(&nr_find_usage_forwards_recursions); + ret = find_usage_forwards(entry->class, depth+1); + if (ret == 2 || ret == 0) + return ret; + } + return 1; +} + +/* + * Find a node in the backwards-direction dependency sub-graph starting + * at that matches . + * + * Return 2 if such a node exists in the subgraph, and put that node + * into . + * + * Return 1 otherwise and keep unchanged. + * Return 0 on error. + */ +static noinline int +find_usage_backwards(struct lock_class *source, unsigned int depth) +{ + struct lock_list *entry; + int ret; + + if (depth > max_recursion_depth) + max_recursion_depth = depth; + if (depth >= 20) + return print_infinite_recursion_bug(); + + debug_atomic_inc(&nr_find_usage_backwards_checks); + if (source->usage_mask & (1 << find_usage_bit)) { + backwards_match = source; + return 2; + } + + /* + * Check this lock's dependency list: + */ + list_for_each_entry(entry, &source->locks_before, entry) { + debug_atomic_inc(&nr_find_usage_backwards_recursions); + ret = find_usage_backwards(entry->class, depth+1); + if (ret == 2 || ret == 0) + return ret; + } + return 1; +} + +static int +print_bad_irq_dependency(struct task_struct *curr, + struct held_lock *prev, + struct held_lock *next, + enum lock_usage_bit bit1, + enum lock_usage_bit bit2, + const char *irqclass) +{ + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + if (debug_locks_silent) + return 0; + + printk("\n======================================================\n"); + printk( "[ INFO: %s-safe -> %s-unsafe lock order detected ]\n", + irqclass, irqclass); + printk( "------------------------------------------------------\n"); + printk("%s/%d [HC%u[%lu]:SC%u[%lu]:HE%u:SE%u] is trying to acquire:\n", + curr->comm, curr->pid, + curr->hardirq_context, hardirq_count() >> HARDIRQ_SHIFT, + curr->softirq_context, softirq_count() >> SOFTIRQ_SHIFT, + curr->hardirqs_enabled, + curr->softirqs_enabled); + print_lock(next); + + printk("\nand this task is already holding:\n"); + print_lock(prev); + printk("which would create a new lock dependency:\n"); + print_lock_name(prev->class); + printk(" ->"); + print_lock_name(next->class); + printk("\n"); + + printk("\nbut this new dependency connects a %s-irq-safe lock:\n", + irqclass); + print_lock_name(backwards_match); + printk("\n... which became %s-irq-safe at:\n", irqclass); + + print_stack_trace(backwards_match->usage_traces + bit1, 1); + + printk("\nto a %s-irq-unsafe lock:\n", irqclass); + print_lock_name(forwards_match); + printk("\n... which became %s-irq-unsafe at:\n", irqclass); + printk("..."); + + print_stack_trace(forwards_match->usage_traces + bit2, 1); + + printk("\nother info that might help us debug this:\n\n"); + lockdep_print_held_locks(curr); + + printk("\nthe %s-irq-safe lock's dependencies:\n", irqclass); + print_lock_dependencies(backwards_match, 0); + + printk("\nthe %s-irq-unsafe lock's dependencies:\n", irqclass); + print_lock_dependencies(forwards_match, 0); + + printk("\nstack backtrace:\n"); + dump_stack(); + + return 0; +} + +static int +check_usage(struct task_struct *curr, struct held_lock *prev, + struct held_lock *next, enum lock_usage_bit bit_backwards, + enum lock_usage_bit bit_forwards, const char *irqclass) +{ + int ret; + + find_usage_bit = bit_backwards; + /* fills in */ + ret = find_usage_backwards(prev->class, 0); + if (!ret || ret == 1) + return ret; + + find_usage_bit = bit_forwards; + ret = find_usage_forwards(next->class, 0); + if (!ret || ret == 1) + return ret; + /* ret == 2 */ + return print_bad_irq_dependency(curr, prev, next, + bit_backwards, bit_forwards, irqclass); +} + +#endif + +static int +print_deadlock_bug(struct task_struct *curr, struct held_lock *prev, + struct held_lock *next) +{ + debug_locks_off(); + __raw_spin_unlock(&hash_lock); + if (debug_locks_silent) + return 0; + + printk("\n=============================================\n"); + printk( "[ INFO: possible recursive locking detected ]\n"); + printk( "---------------------------------------------\n"); + printk("%s/%d is trying to acquire lock:\n", + curr->comm, curr->pid); + print_lock(next); + printk("\nbut task is already holding lock:\n"); + print_lock(prev); + + printk("\nother info that might help us debug this:\n"); + lockdep_print_held_locks(curr); + + printk("\nstack backtrace:\n"); + dump_stack(); + + return 0; +} + +/* + * Check whether we are holding such a class already. + * + * (Note that this has to be done separately, because the graph cannot + * detect such classes of deadlocks.) + * + * Returns: 0 on deadlock detected, 1 on OK, 2 on recursive read + */ +static int +check_deadlock(struct task_struct *curr, struct held_lock *next, + struct lockdep_map *next_instance, int read) +{ + struct held_lock *prev; + int i; + + for (i = 0; i < curr->lockdep_depth; i++) { + prev = curr->held_locks + i; + if (prev->class != next->class) + continue; + /* + * Allow read-after-read recursion of the same + * lock class (i.e. read_lock(lock)+read_lock(lock)): + */ + if ((read == 2) && prev->read) + return 2; + return print_deadlock_bug(curr, prev, next); + } + return 1; +} + +/* + * There was a chain-cache miss, and we are about to add a new dependency + * to a previous lock. We recursively validate the following rules: + * + * - would the adding of the -> dependency create a + * circular dependency in the graph? [== circular deadlock] + * + * - does the new prev->next dependency connect any hardirq-safe lock + * (in the full backwards-subgraph starting at ) with any + * hardirq-unsafe lock (in the full forwards-subgraph starting at + * )? [== illegal lock inversion with hardirq contexts] + * + * - does the new prev->next dependency connect any softirq-safe lock + * (in the full backwards-subgraph starting at ) with any + * softirq-unsafe lock (in the full forwards-subgraph starting at + * )? [== illegal lock inversion with softirq contexts] + * + * any of these scenarios could lead to a deadlock. + * + * Then if all the validations pass, we add the forwards and backwards + * dependency. + */ +static int +check_prev_add(struct task_struct *curr, struct held_lock *prev, + struct held_lock *next) +{ + struct lock_list *entry; + int ret; + + /* + * Prove that the new -> dependency would not + * create a circular dependency in the graph. (We do this by + * forward-recursing into the graph starting at , and + * checking whether we can reach .) + * + * We are using global variables to control the recursion, to + * keep the stackframe size of the recursive functions low: + */ + check_source = next; + check_target = prev; + if (!(check_noncircular(next->class, 0))) + return print_circular_bug_tail(); + +#ifdef CONFIG_TRACE_IRQFLAGS + /* + * Prove that the new dependency does not connect a hardirq-safe + * lock with a hardirq-unsafe lock - to achieve this we search + * the backwards-subgraph starting at , and the + * forwards-subgraph starting at : + */ + if (!check_usage(curr, prev, next, LOCK_USED_IN_HARDIRQ, + LOCK_ENABLED_HARDIRQS, "hard")) + return 0; + + /* + * Prove that the new dependency does not connect a hardirq-safe-read + * lock with a hardirq-unsafe lock - to achieve this we search + * the backwards-subgraph starting at , and the + * forwards-subgraph starting at : + */ + if (!check_usage(curr, prev, next, LOCK_USED_IN_HARDIRQ_READ, + LOCK_ENABLED_HARDIRQS, "hard-read")) + return 0; + + /* + * Prove that the new dependency does not connect a softirq-safe + * lock with a softirq-unsafe lock - to achieve this we search + * the backwards-subgraph starting at , and the + * forwards-subgraph starting at : + */ + if (!check_usage(curr, prev, next, LOCK_USED_IN_SOFTIRQ, + LOCK_ENABLED_SOFTIRQS, "soft")) + return 0; + /* + * Prove that the new dependency does not connect a softirq-safe-read + * lock with a softirq-unsafe lock - to achieve this we search + * the backwards-subgraph starting at , and the + * forwards-subgraph starting at : + */ + if (!check_usage(curr, prev, next, LOCK_USED_IN_SOFTIRQ_READ, + LOCK_ENABLED_SOFTIRQS, "soft")) + return 0; +#endif + /* + * For recursive read-locks we do all the dependency checks, + * but we dont store read-triggered dependencies (only + * write-triggered dependencies). This ensures that only the + * write-side dependencies matter, and that if for example a + * write-lock never takes any other locks, then the reads are + * equivalent to a NOP. + */ + if (next->read == 2 || prev->read == 2) + return 1; + /* + * Is the -> dependency already present? + * + * (this may occur even though this is a new chain: consider + * e.g. the L1 -> L2 -> L3 -> L4 and the L5 -> L1 -> L2 -> L3 + * chains - the second one will be new, but L1 already has + * L2 added to its dependency list, due to the first chain.) + */ + list_for_each_entry(entry, &prev->class->locks_after, entry) { + if (entry->class == next->class) + return 2; + } + + /* + * Ok, all validations passed, add the new lock + * to the previous lock's dependency list: + */ + ret = add_lock_to_list(prev->class, next->class, + &prev->class->locks_after, next->acquire_ip); + if (!ret) + return 0; + /* + * Return value of 2 signals 'dependency already added', + * in that case we dont have to add the backlink either. + */ + if (ret == 2) + return 2; + ret = add_lock_to_list(next->class, prev->class, + &next->class->locks_before, next->acquire_ip); + + /* + * Debugging printouts: + */ + if (verbose(prev->class) || verbose(next->class)) { + __raw_spin_unlock(&hash_lock); + printk("\n new dependency: "); + print_lock_name(prev->class); + printk(" => "); + print_lock_name(next->class); + printk("\n"); + dump_stack(); + __raw_spin_lock(&hash_lock); + } + return 1; +} + +/* + * Add the dependency to all directly-previous locks that are 'relevant'. + * The ones that are relevant are (in increasing distance from curr): + * all consecutive trylock entries and the final non-trylock entry - or + * the end of this context's lock-chain - whichever comes first. + */ +static int +check_prevs_add(struct task_struct *curr, struct held_lock *next) +{ + int depth = curr->lockdep_depth; + struct held_lock *hlock; + + /* + * Debugging checks. + * + * Depth must not be zero for a non-head lock: + */ + if (!depth) + goto out_bug; + /* + * At least two relevant locks must exist for this + * to be a head: + */ + if (curr->held_locks[depth].irq_context != + curr->held_locks[depth-1].irq_context) + goto out_bug; + + for (;;) { + hlock = curr->held_locks + depth-1; + /* + * Only non-recursive-read entries get new dependencies + * added: + */ + if (hlock->read != 2) { + check_prev_add(curr, hlock, next); + /* + * Stop after the first non-trylock entry, + * as non-trylock entries have added their + * own direct dependencies already, so this + * lock is connected to them indirectly: + */ + if (!hlock->trylock) + break; + } + depth--; + /* + * End of lock-stack? + */ + if (!depth) + break; + /* + * Stop the search if we cross into another context: + */ + if (curr->held_locks[depth].irq_context != + curr->held_locks[depth-1].irq_context) + break; + } + return 1; +out_bug: + __raw_spin_unlock(&hash_lock); + DEBUG_LOCKS_WARN_ON(1); + + return 0; +} + + +/* + * Is this the address of a static object: + */ +static int static_obj(void *obj) +{ + unsigned long start = (unsigned long) &_stext, + end = (unsigned long) &_end, + addr = (unsigned long) obj; +#ifdef CONFIG_SMP + int i; +#endif + + /* + * static variable? + */ + if ((addr >= start) && (addr < end)) + return 1; + +#ifdef CONFIG_SMP + /* + * percpu var? + */ + for_each_possible_cpu(i) { + start = (unsigned long) &__per_cpu_start + per_cpu_offset(i); + end = (unsigned long) &__per_cpu_end + per_cpu_offset(i); + + if ((addr >= start) && (addr < end)) + return 1; + } +#endif + + /* + * module var? + */ + return is_module_address(addr); +} + +/* + * To make lock name printouts unique, we calculate a unique + * class->name_version generation counter: + */ +static int count_matching_names(struct lock_class *new_class) +{ + struct lock_class *class; + int count = 0; + + if (!new_class->name) + return 0; + + list_for_each_entry(class, &all_lock_classes, lock_entry) { + if (new_class->key - new_class->subclass == class->key) + return class->name_version; + if (class->name && !strcmp(class->name, new_class->name)) + count = max(count, class->name_version); + } + + return count + 1; +} + +extern void __error_too_big_MAX_LOCKDEP_SUBCLASSES(void); + +/* + * Register a lock's class in the hash-table, if the class is not present + * yet. Otherwise we look it up. We cache the result in the lock object + * itself, so actual lookup of the hash should be once per lock object. + */ +static inline struct lock_class * +register_lock_class(struct lockdep_map *lock, unsigned int subclass) +{ + struct lockdep_subclass_key *key; + struct list_head *hash_head; + struct lock_class *class; + +#ifdef CONFIG_DEBUG_LOCKDEP + /* + * If the architecture calls into lockdep before initializing + * the hashes then we'll warn about it later. (we cannot printk + * right now) + */ + if (unlikely(!lockdep_initialized)) { + lockdep_init(); + lockdep_init_error = 1; + } +#endif + + /* + * Static locks do not have their class-keys yet - for them the key + * is the lock object itself: + */ + if (unlikely(!lock->key)) + lock->key = (void *)lock; + + /* + * NOTE: the class-key must be unique. For dynamic locks, a static + * lock_class_key variable is passed in through the mutex_init() + * (or spin_lock_init()) call - which acts as the key. For static + * locks we use the lock object itself as the key. + */ + if (sizeof(struct lock_class_key) > sizeof(struct lock_class)) + __error_too_big_MAX_LOCKDEP_SUBCLASSES(); + + key = lock->key->subkeys + subclass; + + hash_head = classhashentry(key); + + /* + * We can walk the hash lockfree, because the hash only + * grows, and we are careful when adding entries to the end: + */ + list_for_each_entry(class, hash_head, hash_entry) + if (class->key == key) + goto out_set; + + /* + * Debug-check: all keys must be persistent! + */ + if (!static_obj(lock->key)) { + debug_locks_off(); + printk("INFO: trying to register non-static key.\n"); + printk("the code is fine but needs lockdep annotation.\n"); + printk("turning off the locking correctness validator.\n"); + dump_stack(); + + return NULL; + } + + __raw_spin_lock(&hash_lock); + /* + * We have to do the hash-walk again, to avoid races + * with another CPU: + */ + list_for_each_entry(class, hash_head, hash_entry) + if (class->key == key) + goto out_unlock_set; + /* + * Allocate a new key from the static array, and add it to + * the hash: + */ + if (nr_lock_classes >= MAX_LOCKDEP_KEYS) { + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + printk("BUG: MAX_LOCKDEP_KEYS too low!\n"); + printk("turning off the locking correctness validator.\n"); + return NULL; + } + class = lock_classes + nr_lock_classes++; + debug_atomic_inc(&nr_unused_locks); + class->key = key; + class->name = lock->name; + class->subclass = subclass; + INIT_LIST_HEAD(&class->lock_entry); + INIT_LIST_HEAD(&class->locks_before); + INIT_LIST_HEAD(&class->locks_after); + class->name_version = count_matching_names(class); + /* + * We use RCU's safe list-add method to make + * parallel walking of the hash-list safe: + */ + list_add_tail_rcu(&class->hash_entry, hash_head); + + if (verbose(class)) { + __raw_spin_unlock(&hash_lock); + printk("\nnew class %p: %s", class->key, class->name); + if (class->name_version > 1) + printk("#%d", class->name_version); + printk("\n"); + dump_stack(); + __raw_spin_lock(&hash_lock); + } +out_unlock_set: + __raw_spin_unlock(&hash_lock); + +out_set: + lock->class[subclass] = class; + + DEBUG_LOCKS_WARN_ON(class->subclass != subclass); + + return class; +} + +/* + * Look up a dependency chain. If the key is not present yet then + * add it and return 0 - in this case the new dependency chain is + * validated. If the key is already hashed, return 1. + */ +static inline int lookup_chain_cache(u64 chain_key) +{ + struct list_head *hash_head = chainhashentry(chain_key); + struct lock_chain *chain; + + DEBUG_LOCKS_WARN_ON(!irqs_disabled()); + /* + * We can walk it lock-free, because entries only get added + * to the hash: + */ + list_for_each_entry(chain, hash_head, entry) { + if (chain->chain_key == chain_key) { +cache_hit: + debug_atomic_inc(&chain_lookup_hits); + /* + * In the debugging case, force redundant checking + * by returning 1: + */ +#ifdef CONFIG_DEBUG_LOCKDEP + __raw_spin_lock(&hash_lock); + return 1; +#endif + return 0; + } + } + /* + * Allocate a new chain entry from the static array, and add + * it to the hash: + */ + __raw_spin_lock(&hash_lock); + /* + * We have to walk the chain again locked - to avoid duplicates: + */ + list_for_each_entry(chain, hash_head, entry) { + if (chain->chain_key == chain_key) { + __raw_spin_unlock(&hash_lock); + goto cache_hit; + } + } + if (unlikely(nr_lock_chains >= MAX_LOCKDEP_CHAINS)) { + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + printk("BUG: MAX_LOCKDEP_CHAINS too low!\n"); + printk("turning off the locking correctness validator.\n"); + return 0; + } + chain = lock_chains + nr_lock_chains++; + chain->chain_key = chain_key; + list_add_tail_rcu(&chain->entry, hash_head); + debug_atomic_inc(&chain_lookup_misses); +#ifdef CONFIG_TRACE_IRQFLAGS + if (current->hardirq_context) + nr_hardirq_chains++; + else { + if (current->softirq_context) + nr_softirq_chains++; + else + nr_process_chains++; + } +#else + nr_process_chains++; +#endif + + return 1; +} + +/* + * We are building curr_chain_key incrementally, so double-check + * it from scratch, to make sure that it's done correctly: + */ +static void check_chain_key(struct task_struct *curr) +{ +#ifdef CONFIG_DEBUG_LOCKDEP + struct held_lock *hlock, *prev_hlock = NULL; + unsigned int i, id; + u64 chain_key = 0; + + for (i = 0; i < curr->lockdep_depth; i++) { + hlock = curr->held_locks + i; + if (chain_key != hlock->prev_chain_key) { + debug_locks_off(); + printk("hm#1, depth: %u [%u], %016Lx != %016Lx\n", + curr->lockdep_depth, i, + (unsigned long long)chain_key, + (unsigned long long)hlock->prev_chain_key); + WARN_ON(1); + return; + } + id = hlock->class - lock_classes; + DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS); + if (prev_hlock && (prev_hlock->irq_context != + hlock->irq_context)) + chain_key = 0; + chain_key = iterate_chain_key(chain_key, id); + prev_hlock = hlock; + } + if (chain_key != curr->curr_chain_key) { + debug_locks_off(); + printk("hm#2, depth: %u [%u], %016Lx != %016Lx\n", + curr->lockdep_depth, i, + (unsigned long long)chain_key, + (unsigned long long)curr->curr_chain_key); + WARN_ON(1); + } +#endif +} + +#ifdef CONFIG_TRACE_IRQFLAGS + +/* + * print irq inversion bug: + */ +static int +print_irq_inversion_bug(struct task_struct *curr, struct lock_class *other, + struct held_lock *this, int forwards, + const char *irqclass) +{ + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + if (debug_locks_silent) + return 0; + + printk("\n=========================================================\n"); + printk( "[ INFO: possible irq lock inversion dependency detected ]\n"); + printk( "---------------------------------------------------------\n"); + printk("%s/%d just changed the state of lock:\n", + curr->comm, curr->pid); + print_lock(this); + if (forwards) + printk("but this lock took another, %s-irq-unsafe lock in the past:\n", irqclass); + else + printk("but this lock was taken by another, %s-irq-safe lock in the past:\n", irqclass); + print_lock_name(other); + printk("\n\nand interrupts could create inverse lock ordering between them.\n\n"); + + printk("\nother info that might help us debug this:\n"); + lockdep_print_held_locks(curr); + + printk("\nthe first lock's dependencies:\n"); + print_lock_dependencies(this->class, 0); + + printk("\nthe second lock's dependencies:\n"); + print_lock_dependencies(other, 0); + + printk("\nstack backtrace:\n"); + dump_stack(); + + return 0; +} + +/* + * Prove that in the forwards-direction subgraph starting at + * there is no lock matching : + */ +static int +check_usage_forwards(struct task_struct *curr, struct held_lock *this, + enum lock_usage_bit bit, const char *irqclass) +{ + int ret; + + find_usage_bit = bit; + /* fills in */ + ret = find_usage_forwards(this->class, 0); + if (!ret || ret == 1) + return ret; + + return print_irq_inversion_bug(curr, forwards_match, this, 1, irqclass); +} + +/* + * Prove that in the backwards-direction subgraph starting at + * there is no lock matching : + */ +static int +check_usage_backwards(struct task_struct *curr, struct held_lock *this, + enum lock_usage_bit bit, const char *irqclass) +{ + int ret; + + find_usage_bit = bit; + /* fills in */ + ret = find_usage_backwards(this->class, 0); + if (!ret || ret == 1) + return ret; + + return print_irq_inversion_bug(curr, backwards_match, this, 0, irqclass); +} + +static inline void print_irqtrace_events(struct task_struct *curr) +{ + printk("irq event stamp: %u\n", curr->irq_events); + printk("hardirqs last enabled at (%u): ", curr->hardirq_enable_event); + print_ip_sym(curr->hardirq_enable_ip); + printk("hardirqs last disabled at (%u): ", curr->hardirq_disable_event); + print_ip_sym(curr->hardirq_disable_ip); + printk("softirqs last enabled at (%u): ", curr->softirq_enable_event); + print_ip_sym(curr->softirq_enable_ip); + printk("softirqs last disabled at (%u): ", curr->softirq_disable_event); + print_ip_sym(curr->softirq_disable_ip); +} + +#else +static inline void print_irqtrace_events(struct task_struct *curr) +{ +} +#endif + +static int +print_usage_bug(struct task_struct *curr, struct held_lock *this, + enum lock_usage_bit prev_bit, enum lock_usage_bit new_bit) +{ + __raw_spin_unlock(&hash_lock); + debug_locks_off(); + if (debug_locks_silent) + return 0; + + printk("\n=================================\n"); + printk( "[ INFO: inconsistent lock state ]\n"); + printk( "---------------------------------\n"); + + printk("inconsistent {%s} -> {%s} usage.\n", + usage_str[prev_bit], usage_str[new_bit]); + + printk("%s/%d [HC%u[%lu]:SC%u[%lu]:HE%u:SE%u] takes:\n", + curr->comm, curr->pid, + trace_hardirq_context(curr), hardirq_count() >> HARDIRQ_SHIFT, + trace_softirq_context(curr), softirq_count() >> SOFTIRQ_SHIFT, + trace_hardirqs_enabled(curr), + trace_softirqs_enabled(curr)); + print_lock(this); + + printk("{%s} state was registered at:\n", usage_str[prev_bit]); + print_stack_trace(this->class->usage_traces + prev_bit, 1); + + print_irqtrace_events(curr); + printk("\nother info that might help us debug this:\n"); + lockdep_print_held_locks(curr); + + printk("\nstack backtrace:\n"); + dump_stack(); + + return 0; +} + +/* + * Print out an error if an invalid bit is set: + */ +static inline int +valid_state(struct task_struct *curr, struct held_lock *this, + enum lock_usage_bit new_bit, enum lock_usage_bit bad_bit) +{ + if (unlikely(this->class->usage_mask & (1 << bad_bit))) + return print_usage_bug(curr, this, bad_bit, new_bit); + return 1; +} + +#define STRICT_READ_CHECKS 1 + +/* + * Mark a lock with a usage bit, and validate the state transition: + */ +static int mark_lock(struct task_struct *curr, struct held_lock *this, + enum lock_usage_bit new_bit, unsigned long ip) +{ + unsigned int new_mask = 1 << new_bit, ret = 1; + + /* + * If already set then do not dirty the cacheline, + * nor do any checks: + */ + if (likely(this->class->usage_mask & new_mask)) + return 1; + + __raw_spin_lock(&hash_lock); + /* + * Make sure we didnt race: + */ + if (unlikely(this->class->usage_mask & new_mask)) { + __raw_spin_unlock(&hash_lock); + return 1; + } + + this->class->usage_mask |= new_mask; + +#ifdef CONFIG_TRACE_IRQFLAGS + if (new_bit == LOCK_ENABLED_HARDIRQS || + new_bit == LOCK_ENABLED_HARDIRQS_READ) + ip = curr->hardirq_enable_ip; + else if (new_bit == LOCK_ENABLED_SOFTIRQS || + new_bit == LOCK_ENABLED_SOFTIRQS_READ) + ip = curr->softirq_enable_ip; +#endif + if (!save_trace(this->class->usage_traces + new_bit)) + return 0; + + switch (new_bit) { +#ifdef CONFIG_TRACE_IRQFLAGS + case LOCK_USED_IN_HARDIRQ: + if (!valid_state(curr, this, new_bit, LOCK_ENABLED_HARDIRQS)) + return 0; + if (!valid_state(curr, this, new_bit, + LOCK_ENABLED_HARDIRQS_READ)) + return 0; + /* + * just marked it hardirq-safe, check that this lock + * took no hardirq-unsafe lock in the past: + */ + if (!check_usage_forwards(curr, this, + LOCK_ENABLED_HARDIRQS, "hard")) + return 0; +#if STRICT_READ_CHECKS + /* + * just marked it hardirq-safe, check that this lock + * took no hardirq-unsafe-read lock in the past: + */ + if (!check_usage_forwards(curr, this, + LOCK_ENABLED_HARDIRQS_READ, "hard-read")) + return 0; +#endif + if (hardirq_verbose(this->class)) + ret = 2; + break; + case LOCK_USED_IN_SOFTIRQ: + if (!valid_state(curr, this, new_bit, LOCK_ENABLED_SOFTIRQS)) + return 0; + if (!valid_state(curr, this, new_bit, + LOCK_ENABLED_SOFTIRQS_READ)) + return 0; + /* + * just marked it softirq-safe, check that this lock + * took no softirq-unsafe lock in the past: + */ + if (!check_usage_forwards(curr, this, + LOCK_ENABLED_SOFTIRQS, "soft")) + return 0; +#if STRICT_READ_CHECKS + /* + * just marked it softirq-safe, check that this lock + * took no softirq-unsafe-read lock in the past: + */ + if (!check_usage_forwards(curr, this, + LOCK_ENABLED_SOFTIRQS_READ, "soft-read")) + return 0; +#endif + if (softirq_verbose(this->class)) + ret = 2; + break; + case LOCK_USED_IN_HARDIRQ_READ: + if (!valid_state(curr, this, new_bit, LOCK_ENABLED_HARDIRQS)) + return 0; + /* + * just marked it hardirq-read-safe, check that this lock + * took no hardirq-unsafe lock in the past: + */ + if (!check_usage_forwards(curr, this, + LOCK_ENABLED_HARDIRQS, "hard")) + return 0; + if (hardirq_verbose(this->class)) + ret = 2; + break; + case LOCK_USED_IN_SOFTIRQ_READ: + if (!valid_state(curr, this, new_bit, LOCK_ENABLED_SOFTIRQS)) + return 0; + /* + * just marked it softirq-read-safe, check that this lock + * took no softirq-unsafe lock in the past: + */ + if (!check_usage_forwards(curr, this, + LOCK_ENABLED_SOFTIRQS, "soft")) + return 0; + if (softirq_verbose(this->class)) + ret = 2; + break; + case LOCK_ENABLED_HARDIRQS: + if (!valid_state(curr, this, new_bit, LOCK_USED_IN_HARDIRQ)) + return 0; + if (!valid_state(curr, this, new_bit, + LOCK_USED_IN_HARDIRQ_READ)) + return 0; + /* + * just marked it hardirq-unsafe, check that no hardirq-safe + * lock in the system ever took it in the past: + */ + if (!check_usage_backwards(curr, this, + LOCK_USED_IN_HARDIRQ, "hard")) + return 0; +#if STRICT_READ_CHECKS + /* + * just marked it hardirq-unsafe, check that no + * hardirq-safe-read lock in the system ever took + * it in the past: + */ + if (!check_usage_backwards(curr, this, + LOCK_USED_IN_HARDIRQ_READ, "hard-read")) + return 0; +#endif + if (hardirq_verbose(this->class)) + ret = 2; + break; + case LOCK_ENABLED_SOFTIRQS: + if (!valid_state(curr, this, new_bit, LOCK_USED_IN_SOFTIRQ)) + return 0; + if (!valid_state(curr, this, new_bit, + LOCK_USED_IN_SOFTIRQ_READ)) + return 0; + /* + * just marked it softirq-unsafe, check that no softirq-safe + * lock in the system ever took it in the past: + */ + if (!check_usage_backwards(curr, this, + LOCK_USED_IN_SOFTIRQ, "soft")) + return 0; +#if STRICT_READ_CHECKS + /* + * just marked it softirq-unsafe, check that no + * softirq-safe-read lock in the system ever took + * it in the past: + */ + if (!check_usage_backwards(curr, this, + LOCK_USED_IN_SOFTIRQ_READ, "soft-read")) + return 0; +#endif + if (softirq_verbose(this->class)) + ret = 2; + break; + case LOCK_ENABLED_HARDIRQS_READ: + if (!valid_state(curr, this, new_bit, LOCK_USED_IN_HARDIRQ)) + return 0; +#if STRICT_READ_CHECKS + /* + * just marked it hardirq-read-unsafe, check that no + * hardirq-safe lock in the system ever took it in the past: + */ + if (!check_usage_backwards(curr, this, + LOCK_USED_IN_HARDIRQ, "hard")) + return 0; +#endif + if (hardirq_verbose(this->class)) + ret = 2; + break; + case LOCK_ENABLED_SOFTIRQS_READ: + if (!valid_state(curr, this, new_bit, LOCK_USED_IN_SOFTIRQ)) + return 0; +#if STRICT_READ_CHECKS + /* + * just marked it softirq-read-unsafe, check that no + * softirq-safe lock in the system ever took it in the past: + */ + if (!check_usage_backwards(curr, this, + LOCK_USED_IN_SOFTIRQ, "soft")) + return 0; +#endif + if (softirq_verbose(this->class)) + ret = 2; + break; +#endif + case LOCK_USED: + /* + * Add it to the global list of classes: + */ + list_add_tail_rcu(&this->class->lock_entry, &all_lock_classes); + debug_atomic_dec(&nr_unused_locks); + break; + default: + debug_locks_off(); + WARN_ON(1); + return 0; + } + + __raw_spin_unlock(&hash_lock); + + /* + * We must printk outside of the hash_lock: + */ + if (ret == 2) { + printk("\nmarked lock as {%s}:\n", usage_str[new_bit]); + print_lock(this); + print_irqtrace_events(curr); + dump_stack(); + } + + return ret; +} + +#ifdef CONFIG_TRACE_IRQFLAGS +/* + * Mark all held locks with a usage bit: + */ +static int +mark_held_locks(struct task_struct *curr, int hardirq, unsigned long ip) +{ + enum lock_usage_bit usage_bit; + struct held_lock *hlock; + int i; + + for (i = 0; i < curr->lockdep_depth; i++) { + hlock = curr->held_locks + i; + + if (hardirq) { + if (hlock->read) + usage_bit = LOCK_ENABLED_HARDIRQS_READ; + else + usage_bit = LOCK_ENABLED_HARDIRQS; + } else { + if (hlock->read) + usage_bit = LOCK_ENABLED_SOFTIRQS_READ; + else + usage_bit = LOCK_ENABLED_SOFTIRQS; + } + if (!mark_lock(curr, hlock, usage_bit, ip)) + return 0; + } + + return 1; +} + +/* + * Debugging helper: via this flag we know that we are in + * 'early bootup code', and will warn about any invalid irqs-on event: + */ +static int early_boot_irqs_enabled; + +void early_boot_irqs_off(void) +{ + early_boot_irqs_enabled = 0; +} + +void early_boot_irqs_on(void) +{ + early_boot_irqs_enabled = 1; +} + +/* + * Hardirqs will be enabled: + */ +void trace_hardirqs_on(void) +{ + struct task_struct *curr = current; + unsigned long ip; + + if (unlikely(!debug_locks || current->lockdep_recursion)) + return; + + if (DEBUG_LOCKS_WARN_ON(unlikely(!early_boot_irqs_enabled))) + return; + + if (unlikely(curr->hardirqs_enabled)) { + debug_atomic_inc(&redundant_hardirqs_on); + return; + } + /* we'll do an OFF -> ON transition: */ + curr->hardirqs_enabled = 1; + ip = (unsigned long) __builtin_return_address(0); + + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return; + if (DEBUG_LOCKS_WARN_ON(current->hardirq_context)) + return; + /* + * We are going to turn hardirqs on, so set the + * usage bit for all held locks: + */ + if (!mark_held_locks(curr, 1, ip)) + return; + /* + * If we have softirqs enabled, then set the usage + * bit for all held locks. (disabled hardirqs prevented + * this bit from being set before) + */ + if (curr->softirqs_enabled) + if (!mark_held_locks(curr, 0, ip)) + return; + + curr->hardirq_enable_ip = ip; + curr->hardirq_enable_event = ++curr->irq_events; + debug_atomic_inc(&hardirqs_on_events); +} + +EXPORT_SYMBOL(trace_hardirqs_on); + +/* + * Hardirqs were disabled: + */ +void trace_hardirqs_off(void) +{ + struct task_struct *curr = current; + + if (unlikely(!debug_locks || current->lockdep_recursion)) + return; + + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return; + + if (curr->hardirqs_enabled) { + /* + * We have done an ON -> OFF transition: + */ + curr->hardirqs_enabled = 0; + curr->hardirq_disable_ip = _RET_IP_; + curr->hardirq_disable_event = ++curr->irq_events; + debug_atomic_inc(&hardirqs_off_events); + } else + debug_atomic_inc(&redundant_hardirqs_off); +} + +EXPORT_SYMBOL(trace_hardirqs_off); + +/* + * Softirqs will be enabled: + */ +void trace_softirqs_on(unsigned long ip) +{ + struct task_struct *curr = current; + + if (unlikely(!debug_locks)) + return; + + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return; + + if (curr->softirqs_enabled) { + debug_atomic_inc(&redundant_softirqs_on); + return; + } + + /* + * We'll do an OFF -> ON transition: + */ + curr->softirqs_enabled = 1; + curr->softirq_enable_ip = ip; + curr->softirq_enable_event = ++curr->irq_events; + debug_atomic_inc(&softirqs_on_events); + /* + * We are going to turn softirqs on, so set the + * usage bit for all held locks, if hardirqs are + * enabled too: + */ + if (curr->hardirqs_enabled) + mark_held_locks(curr, 0, ip); +} + +/* + * Softirqs were disabled: + */ +void trace_softirqs_off(unsigned long ip) +{ + struct task_struct *curr = current; + + if (unlikely(!debug_locks)) + return; + + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return; + + if (curr->softirqs_enabled) { + /* + * We have done an ON -> OFF transition: + */ + curr->softirqs_enabled = 0; + curr->softirq_disable_ip = ip; + curr->softirq_disable_event = ++curr->irq_events; + debug_atomic_inc(&softirqs_off_events); + DEBUG_LOCKS_WARN_ON(!softirq_count()); + } else + debug_atomic_inc(&redundant_softirqs_off); +} + +#endif + +/* + * Initialize a lock instance's lock-class mapping info: + */ +void lockdep_init_map(struct lockdep_map *lock, const char *name, + struct lock_class_key *key) +{ + if (unlikely(!debug_locks)) + return; + + if (DEBUG_LOCKS_WARN_ON(!key)) + return; + if (DEBUG_LOCKS_WARN_ON(!name)) + return; + /* + * Sanity check, the lock-class key must be persistent: + */ + if (!static_obj(key)) { + printk("BUG: key %p not in .data!\n", key); + DEBUG_LOCKS_WARN_ON(1); + return; + } + lock->name = name; + lock->key = key; + memset(lock->class, 0, sizeof(lock->class[0])*MAX_LOCKDEP_SUBCLASSES); +} + +EXPORT_SYMBOL_GPL(lockdep_init_map); + +/* + * This gets called for every mutex_lock*()/spin_lock*() operation. + * We maintain the dependency maps and validate the locking attempt: + */ +static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, + int trylock, int read, int check, int hardirqs_off, + unsigned long ip) +{ + struct task_struct *curr = current; + struct held_lock *hlock; + struct lock_class *class; + unsigned int depth, id; + int chain_head = 0; + u64 chain_key; + + if (unlikely(!debug_locks)) + return 0; + + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return 0; + + if (unlikely(subclass >= MAX_LOCKDEP_SUBCLASSES)) { + debug_locks_off(); + printk("BUG: MAX_LOCKDEP_SUBCLASSES too low!\n"); + printk("turning off the locking correctness validator.\n"); + return 0; + } + + class = lock->class[subclass]; + /* not cached yet? */ + if (unlikely(!class)) { + class = register_lock_class(lock, subclass); + if (!class) + return 0; + } + debug_atomic_inc((atomic_t *)&class->ops); + if (very_verbose(class)) { + printk("\nacquire class [%p] %s", class->key, class->name); + if (class->name_version > 1) + printk("#%d", class->name_version); + printk("\n"); + dump_stack(); + } + + /* + * Add the lock to the list of currently held locks. + * (we dont increase the depth just yet, up until the + * dependency checks are done) + */ + depth = curr->lockdep_depth; + if (DEBUG_LOCKS_WARN_ON(depth >= MAX_LOCK_DEPTH)) + return 0; + + hlock = curr->held_locks + depth; + + hlock->class = class; + hlock->acquire_ip = ip; + hlock->instance = lock; + hlock->trylock = trylock; + hlock->read = read; + hlock->check = check; + hlock->hardirqs_off = hardirqs_off; + + if (check != 2) + goto out_calc_hash; +#ifdef CONFIG_TRACE_IRQFLAGS + /* + * If non-trylock use in a hardirq or softirq context, then + * mark the lock as used in these contexts: + */ + if (!trylock) { + if (read) { + if (curr->hardirq_context) + if (!mark_lock(curr, hlock, + LOCK_USED_IN_HARDIRQ_READ, ip)) + return 0; + if (curr->softirq_context) + if (!mark_lock(curr, hlock, + LOCK_USED_IN_SOFTIRQ_READ, ip)) + return 0; + } else { + if (curr->hardirq_context) + if (!mark_lock(curr, hlock, LOCK_USED_IN_HARDIRQ, ip)) + return 0; + if (curr->softirq_context) + if (!mark_lock(curr, hlock, LOCK_USED_IN_SOFTIRQ, ip)) + return 0; + } + } + if (!hardirqs_off) { + if (read) { + if (!mark_lock(curr, hlock, + LOCK_ENABLED_HARDIRQS_READ, ip)) + return 0; + if (curr->softirqs_enabled) + if (!mark_lock(curr, hlock, + LOCK_ENABLED_SOFTIRQS_READ, ip)) + return 0; + } else { + if (!mark_lock(curr, hlock, + LOCK_ENABLED_HARDIRQS, ip)) + return 0; + if (curr->softirqs_enabled) + if (!mark_lock(curr, hlock, + LOCK_ENABLED_SOFTIRQS, ip)) + return 0; + } + } +#endif + /* mark it as used: */ + if (!mark_lock(curr, hlock, LOCK_USED, ip)) + return 0; +out_calc_hash: + /* + * Calculate the chain hash: it's the combined has of all the + * lock keys along the dependency chain. We save the hash value + * at every step so that we can get the current hash easily + * after unlock. The chain hash is then used to cache dependency + * results. + * + * The 'key ID' is what is the most compact key value to drive + * the hash, not class->key. + */ + id = class - lock_classes; + if (DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS)) + return 0; + + chain_key = curr->curr_chain_key; + if (!depth) { + if (DEBUG_LOCKS_WARN_ON(chain_key != 0)) + return 0; + chain_head = 1; + } + + hlock->prev_chain_key = chain_key; + +#ifdef CONFIG_TRACE_IRQFLAGS + /* + * Keep track of points where we cross into an interrupt context: + */ + hlock->irq_context = 2*(curr->hardirq_context ? 1 : 0) + + curr->softirq_context; + if (depth) { + struct held_lock *prev_hlock; + + prev_hlock = curr->held_locks + depth-1; + /* + * If we cross into another context, reset the + * hash key (this also prevents the checking and the + * adding of the dependency to 'prev'): + */ + if (prev_hlock->irq_context != hlock->irq_context) { + chain_key = 0; + chain_head = 1; + } + } +#endif + chain_key = iterate_chain_key(chain_key, id); + curr->curr_chain_key = chain_key; + + /* + * Trylock needs to maintain the stack of held locks, but it + * does not add new dependencies, because trylock can be done + * in any order. + * + * We look up the chain_key and do the O(N^2) check and update of + * the dependencies only if this is a new dependency chain. + * (If lookup_chain_cache() returns with 1 it acquires + * hash_lock for us) + */ + if (!trylock && (check == 2) && lookup_chain_cache(chain_key)) { + /* + * Check whether last held lock: + * + * - is irq-safe, if this lock is irq-unsafe + * - is softirq-safe, if this lock is hardirq-unsafe + * + * And check whether the new lock's dependency graph + * could lead back to the previous lock. + * + * any of these scenarios could lead to a deadlock. If + * All validations + */ + int ret = check_deadlock(curr, hlock, lock, read); + + if (!ret) + return 0; + /* + * Mark recursive read, as we jump over it when + * building dependencies (just like we jump over + * trylock entries): + */ + if (ret == 2) + hlock->read = 2; + /* + * Add dependency only if this lock is not the head + * of the chain, and if it's not a secondary read-lock: + */ + if (!chain_head && ret != 2) + if (!check_prevs_add(curr, hlock)) + return 0; + __raw_spin_unlock(&hash_lock); + } + curr->lockdep_depth++; + check_chain_key(curr); + if (unlikely(curr->lockdep_depth >= MAX_LOCK_DEPTH)) { + debug_locks_off(); + printk("BUG: MAX_LOCK_DEPTH too low!\n"); + printk("turning off the locking correctness validator.\n"); + return 0; + } + if (unlikely(curr->lockdep_depth > max_lockdep_depth)) + max_lockdep_depth = curr->lockdep_depth; + + return 1; +} + +static int +print_unlock_inbalance_bug(struct task_struct *curr, struct lockdep_map *lock, + unsigned long ip) +{ + if (!debug_locks_off()) + return 0; + if (debug_locks_silent) + return 0; + + printk("\n=====================================\n"); + printk( "[ BUG: bad unlock balance detected! ]\n"); + printk( "-------------------------------------\n"); + printk("%s/%d is trying to release lock (", + curr->comm, curr->pid); + print_lockdep_cache(lock); + printk(") at:\n"); + print_ip_sym(ip); + printk("but there are no more locks to release!\n"); + printk("\nother info that might help us debug this:\n"); + lockdep_print_held_locks(curr); + + printk("\nstack backtrace:\n"); + dump_stack(); + + return 0; +} + +/* + * Common debugging checks for both nested and non-nested unlock: + */ +static int check_unlock(struct task_struct *curr, struct lockdep_map *lock, + unsigned long ip) +{ + if (unlikely(!debug_locks)) + return 0; + if (DEBUG_LOCKS_WARN_ON(!irqs_disabled())) + return 0; + + if (curr->lockdep_depth <= 0) + return print_unlock_inbalance_bug(curr, lock, ip); + + return 1; +} + +/* + * Remove the lock to the list of currently held locks in a + * potentially non-nested (out of order) manner. This is a + * relatively rare operation, as all the unlock APIs default + * to nested mode (which uses lock_release()): + */ +static int +lock_release_non_nested(struct task_struct *curr, + struct lockdep_map *lock, unsigned long ip) +{ + struct held_lock *hlock, *prev_hlock; + unsigned int depth; + int i; + + /* + * Check whether the lock exists in the current stack + * of held locks: + */ + depth = curr->lockdep_depth; + if (DEBUG_LOCKS_WARN_ON(!depth)) + return 0; + + prev_hlock = NULL; + for (i = depth-1; i >= 0; i--) { + hlock = curr->held_locks + i; + /* + * We must not cross into another context: + */ + if (prev_hlock && prev_hlock->irq_context != hlock->irq_context) + break; + if (hlock->instance == lock) + goto found_it; + prev_hlock = hlock; + } + return print_unlock_inbalance_bug(curr, lock, ip); + +found_it: + /* + * We have the right lock to unlock, 'hlock' points to it. + * Now we remove it from the stack, and add back the other + * entries (if any), recalculating the hash along the way: + */ + curr->lockdep_depth = i; + curr->curr_chain_key = hlock->prev_chain_key; + + for (i++; i < depth; i++) { + hlock = curr->held_locks + i; + if (!__lock_acquire(hlock->instance, + hlock->class->subclass, hlock->trylock, + hlock->read, hlock->check, hlock->hardirqs_off, + hlock->acquire_ip)) + return 0; + } + + if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth - 1)) + return 0; + return 1; +} + +/* + * Remove the lock to the list of currently held locks - this gets + * called on mutex_unlock()/spin_unlock*() (or on a failed + * mutex_lock_interruptible()). This is done for unlocks that nest + * perfectly. (i.e. the current top of the lock-stack is unlocked) + */ +static int lock_release_nested(struct task_struct *curr, + struct lockdep_map *lock, unsigned long ip) +{ + struct held_lock *hlock; + unsigned int depth; + + /* + * Pop off the top of the lock stack: + */ + depth = curr->lockdep_depth - 1; + hlock = curr->held_locks + depth; + + /* + * Is the unlock non-nested: + */ + if (hlock->instance != lock) + return lock_release_non_nested(curr, lock, ip); + curr->lockdep_depth--; + + if (DEBUG_LOCKS_WARN_ON(!depth && (hlock->prev_chain_key != 0))) + return 0; + + curr->curr_chain_key = hlock->prev_chain_key; + +#ifdef CONFIG_DEBUG_LOCKDEP + hlock->prev_chain_key = 0; + hlock->class = NULL; + hlock->acquire_ip = 0; + hlock->irq_context = 0; +#endif + return 1; +} + +/* + * Remove the lock to the list of currently held locks - this gets + * called on mutex_unlock()/spin_unlock*() (or on a failed + * mutex_lock_interruptible()). This is done for unlocks that nest + * perfectly. (i.e. the current top of the lock-stack is unlocked) + */ +static void +__lock_release(struct lockdep_map *lock, int nested, unsigned long ip) +{ + struct task_struct *curr = current; + + if (!check_unlock(curr, lock, ip)) + return; + + if (nested) { + if (!lock_release_nested(curr, lock, ip)) + return; + } else { + if (!lock_release_non_nested(curr, lock, ip)) + return; + } + + check_chain_key(curr); +} + +/* + * Check whether we follow the irq-flags state precisely: + */ +static void check_flags(unsigned long flags) +{ +#if defined(CONFIG_DEBUG_LOCKDEP) && defined(CONFIG_TRACE_IRQFLAGS) + if (!debug_locks) + return; + + if (irqs_disabled_flags(flags)) + DEBUG_LOCKS_WARN_ON(current->hardirqs_enabled); + else + DEBUG_LOCKS_WARN_ON(!current->hardirqs_enabled); + + /* + * We dont accurately track softirq state in e.g. + * hardirq contexts (such as on 4KSTACKS), so only + * check if not in hardirq contexts: + */ + if (!hardirq_count()) { + if (softirq_count()) + DEBUG_LOCKS_WARN_ON(current->softirqs_enabled); + else + DEBUG_LOCKS_WARN_ON(!current->softirqs_enabled); + } + + if (!debug_locks) + print_irqtrace_events(current); +#endif +} + +/* + * We are not always called with irqs disabled - do that here, + * and also avoid lockdep recursion: + */ +void lock_acquire(struct lockdep_map *lock, unsigned int subclass, + int trylock, int read, int check, unsigned long ip) +{ + unsigned long flags; + + if (unlikely(current->lockdep_recursion)) + return; + + raw_local_irq_save(flags); + check_flags(flags); + + current->lockdep_recursion = 1; + __lock_acquire(lock, subclass, trylock, read, check, + irqs_disabled_flags(flags), ip); + current->lockdep_recursion = 0; + raw_local_irq_restore(flags); +} + +EXPORT_SYMBOL_GPL(lock_acquire); + +void lock_release(struct lockdep_map *lock, int nested, unsigned long ip) +{ + unsigned long flags; + + if (unlikely(current->lockdep_recursion)) + return; + + raw_local_irq_save(flags); + check_flags(flags); + current->lockdep_recursion = 1; + __lock_release(lock, nested, ip); + current->lockdep_recursion = 0; + raw_local_irq_restore(flags); +} + +EXPORT_SYMBOL_GPL(lock_release); + +/* + * Used by the testsuite, sanitize the validator state + * after a simulated failure: + */ + +void lockdep_reset(void) +{ + unsigned long flags; + + raw_local_irq_save(flags); + current->curr_chain_key = 0; + current->lockdep_depth = 0; + current->lockdep_recursion = 0; + memset(current->held_locks, 0, MAX_LOCK_DEPTH*sizeof(struct held_lock)); + nr_hardirq_chains = 0; + nr_softirq_chains = 0; + nr_process_chains = 0; + debug_locks = 1; + raw_local_irq_restore(flags); +} + +static void zap_class(struct lock_class *class) +{ + int i; + + /* + * Remove all dependencies this lock is + * involved in: + */ + for (i = 0; i < nr_list_entries; i++) { + if (list_entries[i].class == class) + list_del_rcu(&list_entries[i].entry); + } + /* + * Unhash the class and remove it from the all_lock_classes list: + */ + list_del_rcu(&class->hash_entry); + list_del_rcu(&class->lock_entry); + +} + +static inline int within(void *addr, void *start, unsigned long size) +{ + return addr >= start && addr < start + size; +} + +void lockdep_free_key_range(void *start, unsigned long size) +{ + struct lock_class *class, *next; + struct list_head *head; + unsigned long flags; + int i; + + raw_local_irq_save(flags); + __raw_spin_lock(&hash_lock); + + /* + * Unhash all classes that were created by this module: + */ + for (i = 0; i < CLASSHASH_SIZE; i++) { + head = classhash_table + i; + if (list_empty(head)) + continue; + list_for_each_entry_safe(class, next, head, hash_entry) + if (within(class->key, start, size)) + zap_class(class); + } + + __raw_spin_unlock(&hash_lock); + raw_local_irq_restore(flags); +} + +void lockdep_reset_lock(struct lockdep_map *lock) +{ + struct lock_class *class, *next, *entry; + struct list_head *head; + unsigned long flags; + int i, j; + + raw_local_irq_save(flags); + __raw_spin_lock(&hash_lock); + + /* + * Remove all classes this lock has: + */ + for (i = 0; i < CLASSHASH_SIZE; i++) { + head = classhash_table + i; + if (list_empty(head)) + continue; + list_for_each_entry_safe(class, next, head, hash_entry) { + for (j = 0; j < MAX_LOCKDEP_SUBCLASSES; j++) { + entry = lock->class[j]; + if (class == entry) { + zap_class(class); + lock->class[j] = NULL; + break; + } + } + } + } + + /* + * Debug check: in the end all mapped classes should + * be gone. + */ + for (j = 0; j < MAX_LOCKDEP_SUBCLASSES; j++) { + entry = lock->class[j]; + if (!entry) + continue; + __raw_spin_unlock(&hash_lock); + DEBUG_LOCKS_WARN_ON(1); + raw_local_irq_restore(flags); + return; + } + + __raw_spin_unlock(&hash_lock); + raw_local_irq_restore(flags); +} + +void __init lockdep_init(void) +{ + int i; + + /* + * Some architectures have their own start_kernel() + * code which calls lockdep_init(), while we also + * call lockdep_init() from the start_kernel() itself, + * and we want to initialize the hashes only once: + */ + if (lockdep_initialized) + return; + + for (i = 0; i < CLASSHASH_SIZE; i++) + INIT_LIST_HEAD(classhash_table + i); + + for (i = 0; i < CHAINHASH_SIZE; i++) + INIT_LIST_HEAD(chainhash_table + i); + + lockdep_initialized = 1; +} + +void __init lockdep_info(void) +{ + printk("Lock dependency validator: Copyright (c) 2006 Red Hat, Inc., Ingo Molnar\n"); + + printk("... MAX_LOCKDEP_SUBCLASSES: %lu\n", MAX_LOCKDEP_SUBCLASSES); + printk("... MAX_LOCK_DEPTH: %lu\n", MAX_LOCK_DEPTH); + printk("... MAX_LOCKDEP_KEYS: %lu\n", MAX_LOCKDEP_KEYS); + printk("... CLASSHASH_SIZE: %lu\n", CLASSHASH_SIZE); + printk("... MAX_LOCKDEP_ENTRIES: %lu\n", MAX_LOCKDEP_ENTRIES); + printk("... MAX_LOCKDEP_CHAINS: %lu\n", MAX_LOCKDEP_CHAINS); + printk("... CHAINHASH_SIZE: %lu\n", CHAINHASH_SIZE); + + printk(" memory used by lock dependency info: %lu kB\n", + (sizeof(struct lock_class) * MAX_LOCKDEP_KEYS + + sizeof(struct list_head) * CLASSHASH_SIZE + + sizeof(struct lock_list) * MAX_LOCKDEP_ENTRIES + + sizeof(struct lock_chain) * MAX_LOCKDEP_CHAINS + + sizeof(struct list_head) * CHAINHASH_SIZE) / 1024); + + printk(" per task-struct memory footprint: %lu bytes\n", + sizeof(struct held_lock) * MAX_LOCK_DEPTH); + +#ifdef CONFIG_DEBUG_LOCKDEP + if (lockdep_init_error) + printk("WARNING: lockdep init error! Arch code didnt call lockdep_init() early enough?\n"); +#endif +} + +static inline int in_range(const void *start, const void *addr, const void *end) +{ + return addr >= start && addr <= end; +} + +static void +print_freed_lock_bug(struct task_struct *curr, const void *mem_from, + const void *mem_to) +{ + if (!debug_locks_off()) + return; + if (debug_locks_silent) + return; + + printk("\n=========================\n"); + printk( "[ BUG: held lock freed! ]\n"); + printk( "-------------------------\n"); + printk("%s/%d is freeing memory %p-%p, with a lock still held there!\n", + curr->comm, curr->pid, mem_from, mem_to-1); + lockdep_print_held_locks(curr); + + printk("\nstack backtrace:\n"); + dump_stack(); +} + +/* + * Called when kernel memory is freed (or unmapped), or if a lock + * is destroyed or reinitialized - this code checks whether there is + * any held lock in the memory range of to : + */ +void debug_check_no_locks_freed(const void *mem_from, unsigned long mem_len) +{ + const void *mem_to = mem_from + mem_len, *lock_from, *lock_to; + struct task_struct *curr = current; + struct held_lock *hlock; + unsigned long flags; + int i; + + if (unlikely(!debug_locks)) + return; + + local_irq_save(flags); + for (i = 0; i < curr->lockdep_depth; i++) { + hlock = curr->held_locks + i; + + lock_from = (void *)hlock->instance; + lock_to = (void *)(hlock->instance + 1); + + if (!in_range(mem_from, lock_from, mem_to) && + !in_range(mem_from, lock_to, mem_to)) + continue; + + print_freed_lock_bug(curr, mem_from, mem_to); + break; + } + local_irq_restore(flags); +} + +static void print_held_locks_bug(struct task_struct *curr) +{ + if (!debug_locks_off()) + return; + if (debug_locks_silent) + return; + + printk("\n=====================================\n"); + printk( "[ BUG: lock held at task exit time! ]\n"); + printk( "-------------------------------------\n"); + printk("%s/%d is exiting with locks still held!\n", + curr->comm, curr->pid); + lockdep_print_held_locks(curr); + + printk("\nstack backtrace:\n"); + dump_stack(); +} + +void debug_check_no_locks_held(struct task_struct *task) +{ + if (unlikely(task->lockdep_depth > 0)) + print_held_locks_bug(task); +} + +void debug_show_all_locks(void) +{ + struct task_struct *g, *p; + int count = 10; + int unlock = 1; + + printk("\nShowing all locks held in the system:\n"); + + /* + * Here we try to get the tasklist_lock as hard as possible, + * if not successful after 2 seconds we ignore it (but keep + * trying). This is to enable a debug printout even if a + * tasklist_lock-holding task deadlocks or crashes. + */ +retry: + if (!read_trylock(&tasklist_lock)) { + if (count == 10) + printk("hm, tasklist_lock locked, retrying... "); + if (count) { + count--; + printk(" #%d", 10-count); + mdelay(200); + goto retry; + } + printk(" ignoring it.\n"); + unlock = 0; + } + if (count != 10) + printk(" locked it.\n"); + + do_each_thread(g, p) { + if (p->lockdep_depth) + lockdep_print_held_locks(p); + if (!unlock) + if (read_trylock(&tasklist_lock)) + unlock = 1; + } while_each_thread(g, p); + + printk("\n"); + printk("=============================================\n\n"); + + if (unlock) + read_unlock(&tasklist_lock); +} + +EXPORT_SYMBOL_GPL(debug_show_all_locks); + +void debug_show_held_locks(struct task_struct *task) +{ + lockdep_print_held_locks(task); +} + +EXPORT_SYMBOL_GPL(debug_show_held_locks); + diff --git a/kernel/lockdep_internals.h b/kernel/lockdep_internals.h new file mode 100644 index 0000000..0d355f2 --- /dev/null +++ b/kernel/lockdep_internals.h @@ -0,0 +1,78 @@ +/* + * kernel/lockdep_internals.h + * + * Runtime locking correctness validator + * + * lockdep subsystem internal functions and variables. + */ + +/* + * MAX_LOCKDEP_ENTRIES is the maximum number of lock dependencies + * we track. + * + * We use the per-lock dependency maps in two ways: we grow it by adding + * every to-be-taken lock to all currently held lock's own dependency + * table (if it's not there yet), and we check it for lock order + * conflicts and deadlocks. + */ +#define MAX_LOCKDEP_ENTRIES 8192UL + +#define MAX_LOCKDEP_KEYS_BITS 11 +#define MAX_LOCKDEP_KEYS (1UL << MAX_LOCKDEP_KEYS_BITS) + +#define MAX_LOCKDEP_CHAINS_BITS 13 +#define MAX_LOCKDEP_CHAINS (1UL << MAX_LOCKDEP_CHAINS_BITS) + +/* + * Stack-trace: tightly packed array of stack backtrace + * addresses. Protected by the hash_lock. + */ +#define MAX_STACK_TRACE_ENTRIES 131072UL + +extern struct list_head all_lock_classes; + +extern void +get_usage_chars(struct lock_class *class, char *c1, char *c2, char *c3, char *c4); + +extern const char * __get_key_name(struct lockdep_subclass_key *key, char *str); + +extern unsigned long nr_lock_classes; +extern unsigned long nr_list_entries; +extern unsigned long nr_lock_chains; +extern unsigned long nr_stack_trace_entries; + +extern unsigned int nr_hardirq_chains; +extern unsigned int nr_softirq_chains; +extern unsigned int nr_process_chains; +extern unsigned int max_lockdep_depth; +extern unsigned int max_recursion_depth; + +#ifdef CONFIG_DEBUG_LOCKDEP +/* + * Various lockdep statistics: + */ +extern atomic_t chain_lookup_hits; +extern atomic_t chain_lookup_misses; +extern atomic_t hardirqs_on_events; +extern atomic_t hardirqs_off_events; +extern atomic_t redundant_hardirqs_on; +extern atomic_t redundant_hardirqs_off; +extern atomic_t softirqs_on_events; +extern atomic_t softirqs_off_events; +extern atomic_t redundant_softirqs_on; +extern atomic_t redundant_softirqs_off; +extern atomic_t nr_unused_locks; +extern atomic_t nr_cyclic_checks; +extern atomic_t nr_cyclic_check_recursions; +extern atomic_t nr_find_usage_forwards_checks; +extern atomic_t nr_find_usage_forwards_recursions; +extern atomic_t nr_find_usage_backwards_checks; +extern atomic_t nr_find_usage_backwards_recursions; +# define debug_atomic_inc(ptr) atomic_inc(ptr) +# define debug_atomic_dec(ptr) atomic_dec(ptr) +# define debug_atomic_read(ptr) atomic_read(ptr) +#else +# define debug_atomic_inc(ptr) do { } while (0) +# define debug_atomic_dec(ptr) do { } while (0) +# define debug_atomic_read(ptr) 0 +#endif diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c new file mode 100644 index 0000000..f6e72ea --- /dev/null +++ b/kernel/lockdep_proc.c @@ -0,0 +1,345 @@ +/* + * kernel/lockdep_proc.c + * + * Runtime locking correctness validator + * + * Started by Ingo Molnar: + * + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + * + * Code for /proc/lockdep and /proc/lockdep_stats: + * + */ +#include +#include +#include +#include +#include +#include + +#include "lockdep_internals.h" + +static void *l_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct lock_class *class = v; + + (*pos)++; + + if (class->lock_entry.next != &all_lock_classes) + class = list_entry(class->lock_entry.next, struct lock_class, + lock_entry); + else + class = NULL; + m->private = class; + + return class; +} + +static void *l_start(struct seq_file *m, loff_t *pos) +{ + struct lock_class *class = m->private; + + if (&class->lock_entry == all_lock_classes.next) + seq_printf(m, "all lock classes:\n"); + + return class; +} + +static void l_stop(struct seq_file *m, void *v) +{ +} + +static unsigned long count_forward_deps(struct lock_class *class) +{ + struct lock_list *entry; + unsigned long ret = 1; + + /* + * Recurse this class's dependency list: + */ + list_for_each_entry(entry, &class->locks_after, entry) + ret += count_forward_deps(entry->class); + + return ret; +} + +static unsigned long count_backward_deps(struct lock_class *class) +{ + struct lock_list *entry; + unsigned long ret = 1; + + /* + * Recurse this class's dependency list: + */ + list_for_each_entry(entry, &class->locks_before, entry) + ret += count_backward_deps(entry->class); + + return ret; +} + +static int l_show(struct seq_file *m, void *v) +{ + unsigned long nr_forward_deps, nr_backward_deps; + struct lock_class *class = m->private; + char str[128], c1, c2, c3, c4; + const char *name; + + seq_printf(m, "%p", class->key); +#ifdef CONFIG_DEBUG_LOCKDEP + seq_printf(m, " OPS:%8ld", class->ops); +#endif + nr_forward_deps = count_forward_deps(class); + seq_printf(m, " FD:%5ld", nr_forward_deps); + + nr_backward_deps = count_backward_deps(class); + seq_printf(m, " BD:%5ld", nr_backward_deps); + + get_usage_chars(class, &c1, &c2, &c3, &c4); + seq_printf(m, " %c%c%c%c", c1, c2, c3, c4); + + name = class->name; + if (!name) { + name = __get_key_name(class->key, str); + seq_printf(m, ": %s", name); + } else{ + seq_printf(m, ": %s", name); + if (class->name_version > 1) + seq_printf(m, "#%d", class->name_version); + if (class->subclass) + seq_printf(m, "/%d", class->subclass); + } + seq_puts(m, "\n"); + + return 0; +} + +static struct seq_operations lockdep_ops = { + .start = l_start, + .next = l_next, + .stop = l_stop, + .show = l_show, +}; + +static int lockdep_open(struct inode *inode, struct file *file) +{ + int res = seq_open(file, &lockdep_ops); + if (!res) { + struct seq_file *m = file->private_data; + + if (!list_empty(&all_lock_classes)) + m->private = list_entry(all_lock_classes.next, + struct lock_class, lock_entry); + else + m->private = NULL; + } + return res; +} + +static struct file_operations proc_lockdep_operations = { + .open = lockdep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void lockdep_stats_debug_show(struct seq_file *m) +{ +#ifdef CONFIG_DEBUG_LOCKDEP + unsigned int hi1 = debug_atomic_read(&hardirqs_on_events), + hi2 = debug_atomic_read(&hardirqs_off_events), + hr1 = debug_atomic_read(&redundant_hardirqs_on), + hr2 = debug_atomic_read(&redundant_hardirqs_off), + si1 = debug_atomic_read(&softirqs_on_events), + si2 = debug_atomic_read(&softirqs_off_events), + sr1 = debug_atomic_read(&redundant_softirqs_on), + sr2 = debug_atomic_read(&redundant_softirqs_off); + + seq_printf(m, " chain lookup misses: %11u\n", + debug_atomic_read(&chain_lookup_misses)); + seq_printf(m, " chain lookup hits: %11u\n", + debug_atomic_read(&chain_lookup_hits)); + seq_printf(m, " cyclic checks: %11u\n", + debug_atomic_read(&nr_cyclic_checks)); + seq_printf(m, " cyclic-check recursions: %11u\n", + debug_atomic_read(&nr_cyclic_check_recursions)); + seq_printf(m, " find-mask forwards checks: %11u\n", + debug_atomic_read(&nr_find_usage_forwards_checks)); + seq_printf(m, " find-mask forwards recursions: %11u\n", + debug_atomic_read(&nr_find_usage_forwards_recursions)); + seq_printf(m, " find-mask backwards checks: %11u\n", + debug_atomic_read(&nr_find_usage_backwards_checks)); + seq_printf(m, " find-mask backwards recursions:%11u\n", + debug_atomic_read(&nr_find_usage_backwards_recursions)); + + seq_printf(m, " hardirq on events: %11u\n", hi1); + seq_printf(m, " hardirq off events: %11u\n", hi2); + seq_printf(m, " redundant hardirq ons: %11u\n", hr1); + seq_printf(m, " redundant hardirq offs: %11u\n", hr2); + seq_printf(m, " softirq on events: %11u\n", si1); + seq_printf(m, " softirq off events: %11u\n", si2); + seq_printf(m, " redundant softirq ons: %11u\n", sr1); + seq_printf(m, " redundant softirq offs: %11u\n", sr2); +#endif +} + +static int lockdep_stats_show(struct seq_file *m, void *v) +{ + struct lock_class *class; + unsigned long nr_unused = 0, nr_uncategorized = 0, + nr_irq_safe = 0, nr_irq_unsafe = 0, + nr_softirq_safe = 0, nr_softirq_unsafe = 0, + nr_hardirq_safe = 0, nr_hardirq_unsafe = 0, + nr_irq_read_safe = 0, nr_irq_read_unsafe = 0, + nr_softirq_read_safe = 0, nr_softirq_read_unsafe = 0, + nr_hardirq_read_safe = 0, nr_hardirq_read_unsafe = 0, + sum_forward_deps = 0, factor = 0; + + list_for_each_entry(class, &all_lock_classes, lock_entry) { + + if (class->usage_mask == 0) + nr_unused++; + if (class->usage_mask == LOCKF_USED) + nr_uncategorized++; + if (class->usage_mask & LOCKF_USED_IN_IRQ) + nr_irq_safe++; + if (class->usage_mask & LOCKF_ENABLED_IRQS) + nr_irq_unsafe++; + if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ) + nr_softirq_safe++; + if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS) + nr_softirq_unsafe++; + if (class->usage_mask & LOCKF_USED_IN_HARDIRQ) + nr_hardirq_safe++; + if (class->usage_mask & LOCKF_ENABLED_HARDIRQS) + nr_hardirq_unsafe++; + if (class->usage_mask & LOCKF_USED_IN_IRQ_READ) + nr_irq_read_safe++; + if (class->usage_mask & LOCKF_ENABLED_IRQS_READ) + nr_irq_read_unsafe++; + if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ_READ) + nr_softirq_read_safe++; + if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS_READ) + nr_softirq_read_unsafe++; + if (class->usage_mask & LOCKF_USED_IN_HARDIRQ_READ) + nr_hardirq_read_safe++; + if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ) + nr_hardirq_read_unsafe++; + + sum_forward_deps += count_forward_deps(class); + } +#ifdef CONFIG_LOCKDEP_DEBUG + DEBUG_LOCKS_WARN_ON(debug_atomic_read(&nr_unused_locks) != nr_unused); +#endif + seq_printf(m, " lock-classes: %11lu [max: %lu]\n", + nr_lock_classes, MAX_LOCKDEP_KEYS); + seq_printf(m, " direct dependencies: %11lu [max: %lu]\n", + nr_list_entries, MAX_LOCKDEP_ENTRIES); + seq_printf(m, " indirect dependencies: %11lu\n", + sum_forward_deps); + + /* + * Total number of dependencies: + * + * All irq-safe locks may nest inside irq-unsafe locks, + * plus all the other known dependencies: + */ + seq_printf(m, " all direct dependencies: %11lu\n", + nr_irq_unsafe * nr_irq_safe + + nr_hardirq_unsafe * nr_hardirq_safe + + nr_list_entries); + + /* + * Estimated factor between direct and indirect + * dependencies: + */ + if (nr_list_entries) + factor = sum_forward_deps / nr_list_entries; + + seq_printf(m, " dependency chains: %11lu [max: %lu]\n", + nr_lock_chains, MAX_LOCKDEP_CHAINS); + +#ifdef CONFIG_TRACE_IRQFLAGS + seq_printf(m, " in-hardirq chains: %11u\n", + nr_hardirq_chains); + seq_printf(m, " in-softirq chains: %11u\n", + nr_softirq_chains); +#endif + seq_printf(m, " in-process chains: %11u\n", + nr_process_chains); + seq_printf(m, " stack-trace entries: %11lu [max: %lu]\n", + nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES); + seq_printf(m, " combined max dependencies: %11u\n", + (nr_hardirq_chains + 1) * + (nr_softirq_chains + 1) * + (nr_process_chains + 1) + ); + seq_printf(m, " hardirq-safe locks: %11lu\n", + nr_hardirq_safe); + seq_printf(m, " hardirq-unsafe locks: %11lu\n", + nr_hardirq_unsafe); + seq_printf(m, " softirq-safe locks: %11lu\n", + nr_softirq_safe); + seq_printf(m, " softirq-unsafe locks: %11lu\n", + nr_softirq_unsafe); + seq_printf(m, " irq-safe locks: %11lu\n", + nr_irq_safe); + seq_printf(m, " irq-unsafe locks: %11lu\n", + nr_irq_unsafe); + + seq_printf(m, " hardirq-read-safe locks: %11lu\n", + nr_hardirq_read_safe); + seq_printf(m, " hardirq-read-unsafe locks: %11lu\n", + nr_hardirq_read_unsafe); + seq_printf(m, " softirq-read-safe locks: %11lu\n", + nr_softirq_read_safe); + seq_printf(m, " softirq-read-unsafe locks: %11lu\n", + nr_softirq_read_unsafe); + seq_printf(m, " irq-read-safe locks: %11lu\n", + nr_irq_read_safe); + seq_printf(m, " irq-read-unsafe locks: %11lu\n", + nr_irq_read_unsafe); + + seq_printf(m, " uncategorized locks: %11lu\n", + nr_uncategorized); + seq_printf(m, " unused locks: %11lu\n", + nr_unused); + seq_printf(m, " max locking depth: %11u\n", + max_lockdep_depth); + seq_printf(m, " max recursion depth: %11u\n", + max_recursion_depth); + lockdep_stats_debug_show(m); + seq_printf(m, " debug_locks: %11u\n", + debug_locks); + + return 0; +} + +static int lockdep_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, lockdep_stats_show, NULL); +} + +static struct file_operations proc_lockdep_stats_operations = { + .open = lockdep_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int __init lockdep_proc_init(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_entry("lockdep", S_IRUSR, NULL); + if (entry) + entry->proc_fops = &proc_lockdep_operations; + + entry = create_proc_entry("lockdep_stats", S_IRUSR, NULL); + if (entry) + entry->proc_fops = &proc_lockdep_stats_operations; + + return 0; +} + +__initcall(lockdep_proc_init); + diff --git a/kernel/module.c b/kernel/module.c index bbe0486..35e1b1f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -1,4 +1,4 @@ -/* Rewritten by Rusty Russell, on the backs of many others... +/* Copyright (C) 2002 Richard Henderson Copyright (C) 2001 Rusty Russell, 2002 Rusty Russell IBM. @@ -16,7 +16,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include @@ -40,9 +39,11 @@ #include #include #include #include +#include #include #include #include +#include #if 0 #define DEBUGP printk @@ -120,9 +121,17 @@ extern const struct kernel_symbol __star extern const struct kernel_symbol __stop___ksymtab_gpl[]; extern const struct kernel_symbol __start___ksymtab_gpl_future[]; extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; +extern const struct kernel_symbol __start___ksymtab_unused[]; +extern const struct kernel_symbol __stop___ksymtab_unused[]; +extern const struct kernel_symbol __start___ksymtab_unused_gpl[]; +extern const struct kernel_symbol __stop___ksymtab_unused_gpl[]; +extern const struct kernel_symbol __start___ksymtab_gpl_future[]; +extern const struct kernel_symbol __stop___ksymtab_gpl_future[]; extern const unsigned long __start___kcrctab[]; extern const unsigned long __start___kcrctab_gpl[]; extern const unsigned long __start___kcrctab_gpl_future[]; +extern const unsigned long __start___kcrctab_unused[]; +extern const unsigned long __start___kcrctab_unused_gpl[]; #ifndef CONFIG_MODVERSIONS #define symversion(base, idx) NULL @@ -142,6 +151,17 @@ static const struct kernel_symbol *looku return NULL; } +static void printk_unused_warning(const char *name) +{ + printk(KERN_WARNING "Symbol %s is marked as UNUSED, " + "however this module is using it.\n", name); + printk(KERN_WARNING "This symbol will go away in the future.\n"); + printk(KERN_WARNING "Please evalute if this is the right api to use, " + "and if it really is, submit a report the linux kernel " + "mailinglist together with submitting your code for " + "inclusion.\n"); +} + /* Find a symbol, return value, crc and module which owns it */ static unsigned long __find_symbol(const char *name, struct module **owner, @@ -184,6 +204,25 @@ static unsigned long __find_symbol(const return ks->value; } + ks = lookup_symbol(name, __start___ksymtab_unused, + __stop___ksymtab_unused); + if (ks) { + printk_unused_warning(name); + *crc = symversion(__start___kcrctab_unused, + (ks - __start___ksymtab_unused)); + return ks->value; + } + + if (gplok) + ks = lookup_symbol(name, __start___ksymtab_unused_gpl, + __stop___ksymtab_unused_gpl); + if (ks) { + printk_unused_warning(name); + *crc = symversion(__start___kcrctab_unused_gpl, + (ks - __start___ksymtab_unused_gpl)); + return ks->value; + } + /* Now try modules. */ list_for_each_entry(mod, &modules, list) { *owner = mod; @@ -202,6 +241,23 @@ static unsigned long __find_symbol(const return ks->value; } } + ks = lookup_symbol(name, mod->unused_syms, mod->unused_syms + mod->num_unused_syms); + if (ks) { + printk_unused_warning(name); + *crc = symversion(mod->unused_crcs, (ks - mod->unused_syms)); + return ks->value; + } + + if (gplok) { + ks = lookup_symbol(name, mod->unused_gpl_syms, + mod->unused_gpl_syms + mod->num_unused_gpl_syms); + if (ks) { + printk_unused_warning(name); + *crc = symversion(mod->unused_gpl_crcs, + (ks - mod->unused_gpl_syms)); + return ks->value; + } + } ks = lookup_symbol(name, mod->gpl_future_syms, (mod->gpl_future_syms + mod->num_gpl_future_syms)); @@ -1051,6 +1107,8 @@ static void free_module(struct module *m remove_sect_attrs(mod); mod_kobject_remove(mod); + unwind_remove_table(mod->unwind_info, 0); + /* Arch-specific cleanup. */ module_arch_cleanup(mod); @@ -1063,6 +1121,9 @@ static void free_module(struct module *m if (mod->percpu) percpu_modfree(mod->percpu); + /* Free lock-classes: */ + lockdep_free_key_range(mod->module_core, mod->core_size); + /* Finally, free the core (containing the module structure) */ module_free(mod, mod->module_core); } @@ -1248,16 +1309,6 @@ static void layout_sections(struct modul } } -static inline int license_is_gpl_compatible(const char *license) -{ - return (strcmp(license, "GPL") == 0 - || strcmp(license, "GPL v2") == 0 - || strcmp(license, "GPL and additional rights") == 0 - || strcmp(license, "Dual BSD/GPL") == 0 - || strcmp(license, "Dual MIT/GPL") == 0 - || strcmp(license, "Dual MPL/GPL") == 0); -} - static void set_license(struct module *mod, const char *license) { if (!license) @@ -1326,7 +1377,7 @@ int is_exported(const char *name, const if (!mod && lookup_symbol(name, __start___ksymtab, __stop___ksymtab)) return 1; else - if (lookup_symbol(name, mod->syms, mod->syms + mod->num_syms)) + if (mod && lookup_symbol(name, mod->syms, mod->syms + mod->num_syms)) return 1; else return 0; @@ -1409,10 +1460,27 @@ static struct module *load_module(void _ Elf_Ehdr *hdr; Elf_Shdr *sechdrs; char *secstrings, *args, *modmagic, *strtab = NULL; - unsigned int i, symindex = 0, strindex = 0, setupindex, exindex, - exportindex, modindex, obsparmindex, infoindex, gplindex, - crcindex, gplcrcindex, versindex, pcpuindex, gplfutureindex, - gplfuturecrcindex; + unsigned int i; + unsigned int symindex = 0; + unsigned int strindex = 0; + unsigned int setupindex; + unsigned int exindex; + unsigned int exportindex; + unsigned int modindex; + unsigned int obsparmindex; + unsigned int infoindex; + unsigned int gplindex; + unsigned int crcindex; + unsigned int gplcrcindex; + unsigned int versindex; + unsigned int pcpuindex; + unsigned int gplfutureindex; + unsigned int gplfuturecrcindex; + unsigned int unwindex = 0; + unsigned int unusedindex; + unsigned int unusedcrcindex; + unsigned int unusedgplindex; + unsigned int unusedgplcrcindex; struct module *mod; long err = 0; void *percpu = NULL, *ptr = NULL; /* Stops spurious gcc warning */ @@ -1493,15 +1561,22 @@ #endif exportindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab"); gplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl"); gplfutureindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_gpl_future"); + unusedindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused"); + unusedgplindex = find_sec(hdr, sechdrs, secstrings, "__ksymtab_unused_gpl"); crcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab"); gplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl"); gplfuturecrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_gpl_future"); + unusedcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused"); + unusedgplcrcindex = find_sec(hdr, sechdrs, secstrings, "__kcrctab_unused_gpl"); setupindex = find_sec(hdr, sechdrs, secstrings, "__param"); exindex = find_sec(hdr, sechdrs, secstrings, "__ex_table"); obsparmindex = find_sec(hdr, sechdrs, secstrings, "__obsparm"); versindex = find_sec(hdr, sechdrs, secstrings, "__versions"); infoindex = find_sec(hdr, sechdrs, secstrings, ".modinfo"); pcpuindex = find_pcpusec(hdr, sechdrs, secstrings); +#ifdef ARCH_UNWIND_SECTION_NAME + unwindex = find_sec(hdr, sechdrs, secstrings, ARCH_UNWIND_SECTION_NAME); +#endif /* Don't keep modinfo section */ sechdrs[infoindex].sh_flags &= ~(unsigned long)SHF_ALLOC; @@ -1510,6 +1585,8 @@ #ifdef CONFIG_KALLSYMS sechdrs[symindex].sh_flags |= SHF_ALLOC; sechdrs[strindex].sh_flags |= SHF_ALLOC; #endif + if (unwindex) + sechdrs[unwindex].sh_flags |= SHF_ALLOC; /* Check module struct version now, before we try to use module. */ if (!check_modstruct_version(sechdrs, versindex, mod)) { @@ -1639,14 +1716,27 @@ #endif mod->gpl_crcs = (void *)sechdrs[gplcrcindex].sh_addr; mod->num_gpl_future_syms = sechdrs[gplfutureindex].sh_size / sizeof(*mod->gpl_future_syms); + mod->num_unused_syms = sechdrs[unusedindex].sh_size / + sizeof(*mod->unused_syms); + mod->num_unused_gpl_syms = sechdrs[unusedgplindex].sh_size / + sizeof(*mod->unused_gpl_syms); mod->gpl_future_syms = (void *)sechdrs[gplfutureindex].sh_addr; if (gplfuturecrcindex) mod->gpl_future_crcs = (void *)sechdrs[gplfuturecrcindex].sh_addr; + mod->unused_syms = (void *)sechdrs[unusedindex].sh_addr; + if (unusedcrcindex) + mod->unused_crcs = (void *)sechdrs[unusedcrcindex].sh_addr; + mod->unused_gpl_syms = (void *)sechdrs[unusedgplindex].sh_addr; + if (unusedgplcrcindex) + mod->unused_crcs = (void *)sechdrs[unusedgplcrcindex].sh_addr; + #ifdef CONFIG_MODVERSIONS if ((mod->num_syms && !crcindex) || (mod->num_gpl_syms && !gplcrcindex) || - (mod->num_gpl_future_syms && !gplfuturecrcindex)) { + (mod->num_gpl_future_syms && !gplfuturecrcindex) || + (mod->num_unused_syms && !unusedcrcindex) || + (mod->num_unused_gpl_syms && !unusedgplcrcindex)) { printk(KERN_WARNING "%s: No versions for exported symbols." " Tainting kernel.\n", mod->name); add_taint(TAINT_FORCED_MODULE); @@ -1738,6 +1828,11 @@ #endif goto arch_cleanup; add_sect_attrs(mod, hdr->e_shnum, secstrings, sechdrs); + /* Size of section 0 is 0, so this works well if no unwind info. */ + mod->unwind_info = unwind_add_table(mod, + (void *)sechdrs[unwindex].sh_addr, + sechdrs[unwindex].sh_size); + /* Get rid of temporary copy */ vfree(hdr); @@ -1836,6 +1931,7 @@ sys_init_module(void __user *umod, mod->state = MODULE_STATE_LIVE; /* Drop initial reference. */ module_put(mod); + unwind_remove_table(mod->unwind_info, 1); module_free(mod, mod->module_init); mod->module_init = NULL; mod->init_size = 0; @@ -2066,6 +2162,29 @@ const struct exception_table_entry *sear return e; } +/* + * Is this a valid module address? + */ +int is_module_address(unsigned long addr) +{ + unsigned long flags; + struct module *mod; + + spin_lock_irqsave(&modlist_lock, flags); + + list_for_each_entry(mod, &modules, list) { + if (within(addr, mod->module_core, mod->core_size)) { + spin_unlock_irqrestore(&modlist_lock, flags); + return 1; + } + } + + spin_unlock_irqrestore(&modlist_lock, flags); + + return 0; +} + + /* Is this a valid kernel address? We don't grab the lock: we are oopsing. */ struct module *__module_text_address(unsigned long addr) { diff --git a/kernel/mutex-debug.c b/kernel/mutex-debug.c index f4913c3..e3203c6 100644 --- a/kernel/mutex-debug.c +++ b/kernel/mutex-debug.c @@ -16,395 +16,48 @@ #include #include #include #include +#include #include #include #include +#include #include "mutex-debug.h" /* - * We need a global lock when we walk through the multi-process - * lock tree. Only used in the deadlock-debugging case. - */ -DEFINE_SPINLOCK(debug_mutex_lock); - -/* - * All locks held by all tasks, in a single global list: - */ -LIST_HEAD(debug_mutex_held_locks); - -/* - * In the debug case we carry the caller's instruction pointer into - * other functions, but we dont want the function argument overhead - * in the nondebug case - hence these macros: - */ -#define __IP_DECL__ , unsigned long ip -#define __IP__ , ip -#define __RET_IP__ , (unsigned long)__builtin_return_address(0) - -/* - * "mutex debugging enabled" flag. We turn it off when we detect - * the first problem because we dont want to recurse back - * into the tracing code when doing error printk or - * executing a BUG(): - */ -int debug_mutex_on = 1; - -static void printk_task(struct task_struct *p) -{ - if (p) - printk("%16s:%5d [%p, %3d]", p->comm, p->pid, p, p->prio); - else - printk(""); -} - -static void printk_ti(struct thread_info *ti) -{ - if (ti) - printk_task(ti->task); - else - printk(""); -} - -static void printk_task_short(struct task_struct *p) -{ - if (p) - printk("%s/%d [%p, %3d]", p->comm, p->pid, p, p->prio); - else - printk(""); -} - -static void printk_lock(struct mutex *lock, int print_owner) -{ - printk(" [%p] {%s}\n", lock, lock->name); - - if (print_owner && lock->owner) { - printk(".. held by: "); - printk_ti(lock->owner); - printk("\n"); - } - if (lock->owner) { - printk("... acquired at: "); - print_symbol("%s\n", lock->acquire_ip); - } -} - -/* - * printk locks held by a task: - */ -static void show_task_locks(struct task_struct *p) -{ - switch (p->state) { - case TASK_RUNNING: printk("R"); break; - case TASK_INTERRUPTIBLE: printk("S"); break; - case TASK_UNINTERRUPTIBLE: printk("D"); break; - case TASK_STOPPED: printk("T"); break; - case EXIT_ZOMBIE: printk("Z"); break; - case EXIT_DEAD: printk("X"); break; - default: printk("?"); break; - } - printk_task(p); - if (p->blocked_on) { - struct mutex *lock = p->blocked_on->lock; - - printk(" blocked on mutex:"); - printk_lock(lock, 1); - } else - printk(" (not blocked on mutex)\n"); -} - -/* - * printk all locks held in the system (if filter == NULL), - * or all locks belonging to a single task (if filter != NULL): - */ -void show_held_locks(struct task_struct *filter) -{ - struct list_head *curr, *cursor = NULL; - struct mutex *lock; - struct thread_info *t; - unsigned long flags; - int count = 0; - - if (filter) { - printk("------------------------------\n"); - printk("| showing all locks held by: | ("); - printk_task_short(filter); - printk("):\n"); - printk("------------------------------\n"); - } else { - printk("---------------------------\n"); - printk("| showing all locks held: |\n"); - printk("---------------------------\n"); - } - - /* - * Play safe and acquire the global trace lock. We - * cannot printk with that lock held so we iterate - * very carefully: - */ -next: - debug_spin_lock_save(&debug_mutex_lock, flags); - list_for_each(curr, &debug_mutex_held_locks) { - if (cursor && curr != cursor) - continue; - lock = list_entry(curr, struct mutex, held_list); - t = lock->owner; - if (filter && (t != filter->thread_info)) - continue; - count++; - cursor = curr->next; - debug_spin_lock_restore(&debug_mutex_lock, flags); - - printk("\n#%03d: ", count); - printk_lock(lock, filter ? 0 : 1); - goto next; - } - debug_spin_lock_restore(&debug_mutex_lock, flags); - printk("\n"); -} - -void mutex_debug_show_all_locks(void) -{ - struct task_struct *g, *p; - int count = 10; - int unlock = 1; - - printk("\nShowing all blocking locks in the system:\n"); - - /* - * Here we try to get the tasklist_lock as hard as possible, - * if not successful after 2 seconds we ignore it (but keep - * trying). This is to enable a debug printout even if a - * tasklist_lock-holding task deadlocks or crashes. - */ -retry: - if (!read_trylock(&tasklist_lock)) { - if (count == 10) - printk("hm, tasklist_lock locked, retrying... "); - if (count) { - count--; - printk(" #%d", 10-count); - mdelay(200); - goto retry; - } - printk(" ignoring it.\n"); - unlock = 0; - } - if (count != 10) - printk(" locked it.\n"); - - do_each_thread(g, p) { - show_task_locks(p); - if (!unlock) - if (read_trylock(&tasklist_lock)) - unlock = 1; - } while_each_thread(g, p); - - printk("\n"); - show_held_locks(NULL); - printk("=============================================\n\n"); - - if (unlock) - read_unlock(&tasklist_lock); -} - -static void report_deadlock(struct task_struct *task, struct mutex *lock, - struct mutex *lockblk, unsigned long ip) -{ - printk("\n%s/%d is trying to acquire this lock:\n", - current->comm, current->pid); - printk_lock(lock, 1); - printk("... trying at: "); - print_symbol("%s\n", ip); - show_held_locks(current); - - if (lockblk) { - printk("but %s/%d is deadlocking current task %s/%d!\n\n", - task->comm, task->pid, current->comm, current->pid); - printk("\n%s/%d is blocked on this lock:\n", - task->comm, task->pid); - printk_lock(lockblk, 1); - - show_held_locks(task); - - printk("\n%s/%d's [blocked] stackdump:\n\n", - task->comm, task->pid); - show_stack(task, NULL); - } - - printk("\n%s/%d's [current] stackdump:\n\n", - current->comm, current->pid); - dump_stack(); - mutex_debug_show_all_locks(); - printk("[ turning off deadlock detection. Please report this. ]\n\n"); - local_irq_disable(); -} - -/* - * Recursively check for mutex deadlocks: - */ -static int check_deadlock(struct mutex *lock, int depth, - struct thread_info *ti, unsigned long ip) -{ - struct mutex *lockblk; - struct task_struct *task; - - if (!debug_mutex_on) - return 0; - - ti = lock->owner; - if (!ti) - return 0; - - task = ti->task; - lockblk = NULL; - if (task->blocked_on) - lockblk = task->blocked_on->lock; - - /* Self-deadlock: */ - if (current == task) { - DEBUG_OFF(); - if (depth) - return 1; - printk("\n==========================================\n"); - printk( "[ BUG: lock recursion deadlock detected! |\n"); - printk( "------------------------------------------\n"); - report_deadlock(task, lock, NULL, ip); - return 0; - } - - /* Ugh, something corrupted the lock data structure? */ - if (depth > 20) { - DEBUG_OFF(); - printk("\n===========================================\n"); - printk( "[ BUG: infinite lock dependency detected!? |\n"); - printk( "-------------------------------------------\n"); - report_deadlock(task, lock, lockblk, ip); - return 0; - } - - /* Recursively check for dependencies: */ - if (lockblk && check_deadlock(lockblk, depth+1, ti, ip)) { - printk("\n============================================\n"); - printk( "[ BUG: circular locking deadlock detected! ]\n"); - printk( "--------------------------------------------\n"); - report_deadlock(task, lock, lockblk, ip); - return 0; - } - return 0; -} - -/* - * Called when a task exits, this function checks whether the - * task is holding any locks, and reports the first one if so: - */ -void mutex_debug_check_no_locks_held(struct task_struct *task) -{ - struct list_head *curr, *next; - struct thread_info *t; - unsigned long flags; - struct mutex *lock; - - if (!debug_mutex_on) - return; - - debug_spin_lock_save(&debug_mutex_lock, flags); - list_for_each_safe(curr, next, &debug_mutex_held_locks) { - lock = list_entry(curr, struct mutex, held_list); - t = lock->owner; - if (t != task->thread_info) - continue; - list_del_init(curr); - DEBUG_OFF(); - debug_spin_lock_restore(&debug_mutex_lock, flags); - - printk("BUG: %s/%d, lock held at task exit time!\n", - task->comm, task->pid); - printk_lock(lock, 1); - if (lock->owner != task->thread_info) - printk("exiting task is not even the owner??\n"); - return; - } - debug_spin_lock_restore(&debug_mutex_lock, flags); -} - -/* - * Called when kernel memory is freed (or unmapped), or if a mutex - * is destroyed or reinitialized - this code checks whether there is - * any held lock in the memory range of to : - */ -void mutex_debug_check_no_locks_freed(const void *from, unsigned long len) -{ - struct list_head *curr, *next; - const void *to = from + len; - unsigned long flags; - struct mutex *lock; - void *lock_addr; - - if (!debug_mutex_on) - return; - - debug_spin_lock_save(&debug_mutex_lock, flags); - list_for_each_safe(curr, next, &debug_mutex_held_locks) { - lock = list_entry(curr, struct mutex, held_list); - lock_addr = lock; - if (lock_addr < from || lock_addr >= to) - continue; - list_del_init(curr); - DEBUG_OFF(); - debug_spin_lock_restore(&debug_mutex_lock, flags); - - printk("BUG: %s/%d, active lock [%p(%p-%p)] freed!\n", - current->comm, current->pid, lock, from, to); - dump_stack(); - printk_lock(lock, 1); - if (lock->owner != current_thread_info()) - printk("freeing task is not even the owner??\n"); - return; - } - debug_spin_lock_restore(&debug_mutex_lock, flags); -} - -/* * Must be called with lock->wait_lock held. */ -void debug_mutex_set_owner(struct mutex *lock, - struct thread_info *new_owner __IP_DECL__) +void debug_mutex_set_owner(struct mutex *lock, struct thread_info *new_owner) { lock->owner = new_owner; - DEBUG_WARN_ON(!list_empty(&lock->held_list)); - if (debug_mutex_on) { - list_add_tail(&lock->held_list, &debug_mutex_held_locks); - lock->acquire_ip = ip; - } } -void debug_mutex_init_waiter(struct mutex_waiter *waiter) +void debug_mutex_lock_common(struct mutex *lock, struct mutex_waiter *waiter) { - memset(waiter, 0x11, sizeof(*waiter)); + memset(waiter, MUTEX_DEBUG_INIT, sizeof(*waiter)); waiter->magic = waiter; INIT_LIST_HEAD(&waiter->list); } void debug_mutex_wake_waiter(struct mutex *lock, struct mutex_waiter *waiter) { - SMP_DEBUG_WARN_ON(!spin_is_locked(&lock->wait_lock)); - DEBUG_WARN_ON(list_empty(&lock->wait_list)); - DEBUG_WARN_ON(waiter->magic != waiter); - DEBUG_WARN_ON(list_empty(&waiter->list)); + SMP_DEBUG_LOCKS_WARN_ON(!spin_is_locked(&lock->wait_lock)); + DEBUG_LOCKS_WARN_ON(list_empty(&lock->wait_list)); + DEBUG_LOCKS_WARN_ON(waiter->magic != waiter); + DEBUG_LOCKS_WARN_ON(list_empty(&waiter->list)); } void debug_mutex_free_waiter(struct mutex_waiter *waiter) { - DEBUG_WARN_ON(!list_empty(&waiter->list)); - memset(waiter, 0x22, sizeof(*waiter)); + DEBUG_LOCKS_WARN_ON(!list_empty(&waiter->list)); + memset(waiter, MUTEX_DEBUG_FREE, sizeof(*waiter)); } void debug_mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter, - struct thread_info *ti __IP_DECL__) + struct thread_info *ti) { - SMP_DEBUG_WARN_ON(!spin_is_locked(&lock->wait_lock)); - check_deadlock(lock, 0, ti, ip); + SMP_DEBUG_LOCKS_WARN_ON(!spin_is_locked(&lock->wait_lock)); + /* Mark the current thread as blocked on the lock: */ ti->task->blocked_on = waiter; waiter->lock = lock; @@ -413,9 +66,9 @@ void debug_mutex_add_waiter(struct mutex void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter, struct thread_info *ti) { - DEBUG_WARN_ON(list_empty(&waiter->list)); - DEBUG_WARN_ON(waiter->task != ti->task); - DEBUG_WARN_ON(ti->task->blocked_on != waiter); + DEBUG_LOCKS_WARN_ON(list_empty(&waiter->list)); + DEBUG_LOCKS_WARN_ON(waiter->task != ti->task); + DEBUG_LOCKS_WARN_ON(ti->task->blocked_on != waiter); ti->task->blocked_on = NULL; list_del_init(&waiter->list); @@ -424,24 +77,23 @@ void mutex_remove_waiter(struct mutex *l void debug_mutex_unlock(struct mutex *lock) { - DEBUG_WARN_ON(lock->magic != lock); - DEBUG_WARN_ON(!lock->wait_list.prev && !lock->wait_list.next); - DEBUG_WARN_ON(lock->owner != current_thread_info()); - if (debug_mutex_on) { - DEBUG_WARN_ON(list_empty(&lock->held_list)); - list_del_init(&lock->held_list); - } + DEBUG_LOCKS_WARN_ON(lock->owner != current_thread_info()); + DEBUG_LOCKS_WARN_ON(lock->magic != lock); + DEBUG_LOCKS_WARN_ON(!lock->wait_list.prev && !lock->wait_list.next); + DEBUG_LOCKS_WARN_ON(lock->owner != current_thread_info()); } -void debug_mutex_init(struct mutex *lock, const char *name) +void debug_mutex_init(struct mutex *lock, const char *name, + struct lock_class_key *key) { +#ifdef CONFIG_DEBUG_LOCK_ALLOC /* * Make sure we are not reinitializing a held lock: */ - mutex_debug_check_no_locks_freed((void *)lock, sizeof(*lock)); + debug_check_no_locks_freed((void *)lock, sizeof(*lock)); + lockdep_init_map(&lock->dep_map, name, key); +#endif lock->owner = NULL; - INIT_LIST_HEAD(&lock->held_list); - lock->name = name; lock->magic = lock; } @@ -455,7 +107,7 @@ void debug_mutex_init(struct mutex *lock */ void fastcall mutex_destroy(struct mutex *lock) { - DEBUG_WARN_ON(mutex_is_locked(lock)); + DEBUG_LOCKS_WARN_ON(mutex_is_locked(lock)); lock->magic = NULL; } diff --git a/kernel/mutex-debug.h b/kernel/mutex-debug.h index fd38405..babfbdf 100644 --- a/kernel/mutex-debug.h +++ b/kernel/mutex-debug.h @@ -10,125 +10,44 @@ * More details are in kernel/mutex-debug.c. */ -extern spinlock_t debug_mutex_lock; -extern struct list_head debug_mutex_held_locks; -extern int debug_mutex_on; - -/* - * In the debug case we carry the caller's instruction pointer into - * other functions, but we dont want the function argument overhead - * in the nondebug case - hence these macros: - */ -#define __IP_DECL__ , unsigned long ip -#define __IP__ , ip -#define __RET_IP__ , (unsigned long)__builtin_return_address(0) - /* * This must be called with lock->wait_lock held. */ -extern void debug_mutex_set_owner(struct mutex *lock, - struct thread_info *new_owner __IP_DECL__); +extern void +debug_mutex_set_owner(struct mutex *lock, struct thread_info *new_owner); static inline void debug_mutex_clear_owner(struct mutex *lock) { lock->owner = NULL; } -extern void debug_mutex_init_waiter(struct mutex_waiter *waiter); +extern void debug_mutex_lock_common(struct mutex *lock, + struct mutex_waiter *waiter); extern void debug_mutex_wake_waiter(struct mutex *lock, struct mutex_waiter *waiter); extern void debug_mutex_free_waiter(struct mutex_waiter *waiter); extern void debug_mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter, - struct thread_info *ti __IP_DECL__); + struct thread_info *ti); extern void mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter, struct thread_info *ti); extern void debug_mutex_unlock(struct mutex *lock); -extern void debug_mutex_init(struct mutex *lock, const char *name); - -#define debug_spin_lock(lock) \ - do { \ - local_irq_disable(); \ - if (debug_mutex_on) \ - spin_lock(lock); \ - } while (0) +extern void debug_mutex_init(struct mutex *lock, const char *name, + struct lock_class_key *key); -#define debug_spin_unlock(lock) \ - do { \ - if (debug_mutex_on) \ - spin_unlock(lock); \ - local_irq_enable(); \ - preempt_check_resched(); \ - } while (0) - -#define debug_spin_lock_save(lock, flags) \ +#define spin_lock_mutex(lock, flags) \ do { \ + struct mutex *l = container_of(lock, struct mutex, wait_lock); \ + \ + DEBUG_LOCKS_WARN_ON(in_interrupt()); \ local_irq_save(flags); \ - if (debug_mutex_on) \ - spin_lock(lock); \ + __raw_spin_lock(&(lock)->raw_lock); \ + DEBUG_LOCKS_WARN_ON(l->magic != l); \ } while (0) -#define debug_spin_lock_restore(lock, flags) \ +#define spin_unlock_mutex(lock, flags) \ do { \ - if (debug_mutex_on) \ - spin_unlock(lock); \ + __raw_spin_unlock(&(lock)->raw_lock); \ local_irq_restore(flags); \ preempt_check_resched(); \ } while (0) - -#define spin_lock_mutex(lock) \ - do { \ - struct mutex *l = container_of(lock, struct mutex, wait_lock); \ - \ - DEBUG_WARN_ON(in_interrupt()); \ - debug_spin_lock(&debug_mutex_lock); \ - spin_lock(lock); \ - DEBUG_WARN_ON(l->magic != l); \ - } while (0) - -#define spin_unlock_mutex(lock) \ - do { \ - spin_unlock(lock); \ - debug_spin_unlock(&debug_mutex_lock); \ - } while (0) - -#define DEBUG_OFF() \ -do { \ - if (debug_mutex_on) { \ - debug_mutex_on = 0; \ - console_verbose(); \ - if (spin_is_locked(&debug_mutex_lock)) \ - spin_unlock(&debug_mutex_lock); \ - } \ -} while (0) - -#define DEBUG_BUG() \ -do { \ - if (debug_mutex_on) { \ - DEBUG_OFF(); \ - BUG(); \ - } \ -} while (0) - -#define DEBUG_WARN_ON(c) \ -do { \ - if (unlikely(c && debug_mutex_on)) { \ - DEBUG_OFF(); \ - WARN_ON(1); \ - } \ -} while (0) - -# define DEBUG_BUG_ON(c) \ -do { \ - if (unlikely(c)) \ - DEBUG_BUG(); \ -} while (0) - -#ifdef CONFIG_SMP -# define SMP_DEBUG_WARN_ON(c) DEBUG_WARN_ON(c) -# define SMP_DEBUG_BUG_ON(c) DEBUG_BUG_ON(c) -#else -# define SMP_DEBUG_WARN_ON(c) do { } while (0) -# define SMP_DEBUG_BUG_ON(c) do { } while (0) -#endif - diff --git a/kernel/mutex.c b/kernel/mutex.c index 5449b21..8c71cf7 100644 --- a/kernel/mutex.c +++ b/kernel/mutex.c @@ -17,6 +17,7 @@ #include #include #include #include +#include /* * In the DEBUG case we are using the "NULL fastpath" for mutexes, @@ -38,13 +39,14 @@ #endif * * It is not allowed to initialize an already locked mutex. */ -void fastcall __mutex_init(struct mutex *lock, const char *name) +void +__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) { atomic_set(&lock->count, 1); spin_lock_init(&lock->wait_lock); INIT_LIST_HEAD(&lock->wait_list); - debug_mutex_init(lock, name); + debug_mutex_init(lock, name, key); } EXPORT_SYMBOL(__mutex_init); @@ -56,7 +58,7 @@ EXPORT_SYMBOL(__mutex_init); * branch is predicted by the CPU as default-untaken. */ static void fastcall noinline __sched -__mutex_lock_slowpath(atomic_t *lock_count __IP_DECL__); +__mutex_lock_slowpath(atomic_t *lock_count); /*** * mutex_lock - acquire the mutex @@ -79,7 +81,7 @@ __mutex_lock_slowpath(atomic_t *lock_cou * * This function is similar to (but not equivalent to) down(). */ -void fastcall __sched mutex_lock(struct mutex *lock) +void inline fastcall __sched mutex_lock(struct mutex *lock) { might_sleep(); /* @@ -92,7 +94,7 @@ void fastcall __sched mutex_lock(struct EXPORT_SYMBOL(mutex_lock); static void fastcall noinline __sched -__mutex_unlock_slowpath(atomic_t *lock_count __IP_DECL__); +__mutex_unlock_slowpath(atomic_t *lock_count); /*** * mutex_unlock - release the mutex @@ -120,17 +122,18 @@ EXPORT_SYMBOL(mutex_unlock); * Lock a mutex (possibly interruptible), slowpath: */ static inline int __sched -__mutex_lock_common(struct mutex *lock, long state __IP_DECL__) +__mutex_lock_common(struct mutex *lock, long state, unsigned int subclass) { struct task_struct *task = current; struct mutex_waiter waiter; unsigned int old_val; + unsigned long flags; - debug_mutex_init_waiter(&waiter); + spin_lock_mutex(&lock->wait_lock, flags); - spin_lock_mutex(&lock->wait_lock); - - debug_mutex_add_waiter(lock, &waiter, task->thread_info, ip); + debug_mutex_lock_common(lock, &waiter); + mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_); + debug_mutex_add_waiter(lock, &waiter, task->thread_info); /* add waiting tasks to the end of the waitqueue (FIFO): */ list_add_tail(&waiter.list, &lock->wait_list); @@ -157,7 +160,8 @@ __mutex_lock_common(struct mutex *lock, if (unlikely(state == TASK_INTERRUPTIBLE && signal_pending(task))) { mutex_remove_waiter(lock, &waiter, task->thread_info); - spin_unlock_mutex(&lock->wait_lock); + mutex_release(&lock->dep_map, 1, _RET_IP_); + spin_unlock_mutex(&lock->wait_lock, flags); debug_mutex_free_waiter(&waiter); return -EINTR; @@ -165,48 +169,57 @@ __mutex_lock_common(struct mutex *lock, __set_task_state(task, state); /* didnt get the lock, go to sleep: */ - spin_unlock_mutex(&lock->wait_lock); + spin_unlock_mutex(&lock->wait_lock, flags); schedule(); - spin_lock_mutex(&lock->wait_lock); + spin_lock_mutex(&lock->wait_lock, flags); } /* got the lock - rejoice! */ mutex_remove_waiter(lock, &waiter, task->thread_info); - debug_mutex_set_owner(lock, task->thread_info __IP__); + debug_mutex_set_owner(lock, task->thread_info); /* set it to 0 if there are no waiters left: */ if (likely(list_empty(&lock->wait_list))) atomic_set(&lock->count, 0); - spin_unlock_mutex(&lock->wait_lock); + spin_unlock_mutex(&lock->wait_lock, flags); debug_mutex_free_waiter(&waiter); - DEBUG_WARN_ON(list_empty(&lock->held_list)); - DEBUG_WARN_ON(lock->owner != task->thread_info); - return 0; } static void fastcall noinline __sched -__mutex_lock_slowpath(atomic_t *lock_count __IP_DECL__) +__mutex_lock_slowpath(atomic_t *lock_count) { struct mutex *lock = container_of(lock_count, struct mutex, count); - __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE __IP__); + __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, 0); +} + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +void __sched +mutex_lock_nested(struct mutex *lock, unsigned int subclass) +{ + might_sleep(); + __mutex_lock_common(lock, TASK_UNINTERRUPTIBLE, subclass); } +EXPORT_SYMBOL_GPL(mutex_lock_nested); +#endif + /* * Release the lock, slowpath: */ -static fastcall noinline void -__mutex_unlock_slowpath(atomic_t *lock_count __IP_DECL__) +static fastcall inline void +__mutex_unlock_common_slowpath(atomic_t *lock_count, int nested) { struct mutex *lock = container_of(lock_count, struct mutex, count); + unsigned long flags; - DEBUG_WARN_ON(lock->owner != current_thread_info()); - - spin_lock_mutex(&lock->wait_lock); + spin_lock_mutex(&lock->wait_lock, flags); + mutex_release(&lock->dep_map, nested, _RET_IP_); + debug_mutex_unlock(lock); /* * some architectures leave the lock unlocked in the fastpath failure @@ -216,8 +229,6 @@ __mutex_unlock_slowpath(atomic_t *lock_c if (__mutex_slowpath_needs_to_unlock()) atomic_set(&lock->count, 1); - debug_mutex_unlock(lock); - if (!list_empty(&lock->wait_list)) { /* get the first entry from the wait-list: */ struct mutex_waiter *waiter = @@ -231,7 +242,16 @@ __mutex_unlock_slowpath(atomic_t *lock_c debug_mutex_clear_owner(lock); - spin_unlock_mutex(&lock->wait_lock); + spin_unlock_mutex(&lock->wait_lock, flags); +} + +/* + * Release the lock, slowpath: + */ +static fastcall noinline void +__mutex_unlock_slowpath(atomic_t *lock_count) +{ + __mutex_unlock_common_slowpath(lock_count, 1); } /* @@ -239,7 +259,7 @@ __mutex_unlock_slowpath(atomic_t *lock_c * mutex_lock_interruptible() and mutex_trylock(). */ static int fastcall noinline __sched -__mutex_lock_interruptible_slowpath(atomic_t *lock_count __IP_DECL__); +__mutex_lock_interruptible_slowpath(atomic_t *lock_count); /*** * mutex_lock_interruptible - acquire the mutex, interruptable @@ -262,11 +282,11 @@ int fastcall __sched mutex_lock_interrup EXPORT_SYMBOL(mutex_lock_interruptible); static int fastcall noinline __sched -__mutex_lock_interruptible_slowpath(atomic_t *lock_count __IP_DECL__) +__mutex_lock_interruptible_slowpath(atomic_t *lock_count) { struct mutex *lock = container_of(lock_count, struct mutex, count); - return __mutex_lock_common(lock, TASK_INTERRUPTIBLE __IP__); + return __mutex_lock_common(lock, TASK_INTERRUPTIBLE, 0); } /* @@ -276,18 +296,21 @@ __mutex_lock_interruptible_slowpath(atom static inline int __mutex_trylock_slowpath(atomic_t *lock_count) { struct mutex *lock = container_of(lock_count, struct mutex, count); + unsigned long flags; int prev; - spin_lock_mutex(&lock->wait_lock); + spin_lock_mutex(&lock->wait_lock, flags); prev = atomic_xchg(&lock->count, -1); - if (likely(prev == 1)) - debug_mutex_set_owner(lock, current_thread_info() __RET_IP__); + if (likely(prev == 1)) { + debug_mutex_set_owner(lock, current_thread_info()); + mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_); + } /* Set it back to 0 if there are no waiters: */ if (likely(list_empty(&lock->wait_list))) atomic_set(&lock->count, 0); - spin_unlock_mutex(&lock->wait_lock); + spin_unlock_mutex(&lock->wait_lock, flags); return prev == 1; } @@ -306,7 +329,7 @@ static inline int __mutex_trylock_slowpa * This function must not be used in interrupt context. The * mutex must be released by the same task that acquired it. */ -int fastcall mutex_trylock(struct mutex *lock) +int fastcall __sched mutex_trylock(struct mutex *lock) { return __mutex_fastpath_trylock(&lock->count, __mutex_trylock_slowpath); diff --git a/kernel/mutex.h b/kernel/mutex.h index 00fe84e..a075daf 100644 --- a/kernel/mutex.h +++ b/kernel/mutex.h @@ -9,27 +9,22 @@ * !CONFIG_DEBUG_MUTEXES case. Most of them are NOPs: */ -#define spin_lock_mutex(lock) spin_lock(lock) -#define spin_unlock_mutex(lock) spin_unlock(lock) +#define spin_lock_mutex(lock, flags) \ + do { spin_lock(lock); (void)(flags); } while (0) +#define spin_unlock_mutex(lock, flags) \ + do { spin_unlock(lock); (void)(flags); } while (0) #define mutex_remove_waiter(lock, waiter, ti) \ __list_del((waiter)->list.prev, (waiter)->list.next) -#define DEBUG_WARN_ON(c) do { } while (0) #define debug_mutex_set_owner(lock, new_owner) do { } while (0) #define debug_mutex_clear_owner(lock) do { } while (0) -#define debug_mutex_init_waiter(waiter) do { } while (0) #define debug_mutex_wake_waiter(lock, waiter) do { } while (0) #define debug_mutex_free_waiter(waiter) do { } while (0) -#define debug_mutex_add_waiter(lock, waiter, ti, ip) do { } while (0) +#define debug_mutex_add_waiter(lock, waiter, ti) do { } while (0) #define debug_mutex_unlock(lock) do { } while (0) -#define debug_mutex_init(lock, name) do { } while (0) - -/* - * Return-address parameters/declarations. They are very useful for - * debugging, but add overhead in the !DEBUG case - so we go the - * trouble of using this not too elegant but zero-cost solution: - */ -#define __IP_DECL__ -#define __IP__ -#define __RET_IP__ +#define debug_mutex_init(lock, name, key) do { } while (0) +static inline void +debug_mutex_lock_common(struct mutex *lock, struct mutex_waiter *waiter) +{ +} diff --git a/kernel/panic.c b/kernel/panic.c index cc2a4c9..ab13f0f 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -8,7 +8,6 @@ * This function is used through-out the kernel (including mm and fs) * to indicate a major problem. */ -#include #include #include #include diff --git a/kernel/params.c b/kernel/params.c index af43ecd..91aea7a 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include diff --git a/kernel/pid.c b/kernel/pid.c index eeb836b..93e212f 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -218,7 +218,7 @@ struct pid * fastcall find_pid(int nr) return NULL; } -int fastcall attach_pid(task_t *task, enum pid_type type, int nr) +int fastcall attach_pid(struct task_struct *task, enum pid_type type, int nr) { struct pid_link *link; struct pid *pid; @@ -233,7 +233,7 @@ int fastcall attach_pid(task_t *task, en return 0; } -void fastcall detach_pid(task_t *task, enum pid_type type) +void fastcall detach_pid(struct task_struct *task, enum pid_type type) { struct pid_link *link; struct pid *pid; @@ -267,7 +267,7 @@ struct task_struct * fastcall pid_task(s /* * Must be called under rcu_read_lock() or with tasklist_lock read-held. */ -task_t *find_task_by_pid_type(int type, int nr) +struct task_struct *find_task_by_pid_type(int type, int nr) { return pid_task(find_pid(nr), type); } diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index ce0dfb8..ae44a70 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -36,6 +36,24 @@ config PM_DEBUG code. This is helpful when debugging and reporting various PM bugs, like suspend support. +config PM_TRACE + bool "Suspend/resume event tracing" + depends on PM && PM_DEBUG && X86_32 && EXPERIMENTAL + default n + ---help--- + This enables some cheesy code to save the last PM event point in the + RTC across reboots, so that you can debug a machine that just hangs + during suspend (or more commonly, during resume). + + To use this debugging feature you should attempt to suspend the machine, + then reboot it, then run + + dmesg -s 1000000 | grep 'hash matches' + + CAUTION: this option will cause your machine's real-time clock to be + set to an invalid time after a resume. + + config SOFTWARE_SUSPEND bool "Software Suspend" depends on PM && SWAP && (X86 && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP) @@ -82,18 +100,6 @@ config PM_STD_PARTITION suspended image to. It will simply pick the first available swap device. -config SWSUSP_ENCRYPT - bool "Encrypt suspend image" - depends on SOFTWARE_SUSPEND && CRYPTO=y && (CRYPTO_AES=y || CRYPTO_AES_586=y || CRYPTO_AES_X86_64=y) - default "" - ---help--- - To prevent data gathering from swap after resume you can encrypt - the suspend image with a temporary key that is deleted on - resume. - - Note that the temporary key is stored unencrypted on disk while the - system is suspended. - config SUSPEND_SMP bool depends on HOTPLUG_CPU && X86 && PM diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 81d4d98..e13e740 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c @@ -231,7 +231,7 @@ static int software_resume(void) late_initcall(software_resume); -static char * pm_disk_modes[] = { +static const char * const pm_disk_modes[] = { [PM_DISK_FIRMWARE] = "firmware", [PM_DISK_PLATFORM] = "platform", [PM_DISK_SHUTDOWN] = "shutdown", diff --git a/kernel/power/main.c b/kernel/power/main.c index a6d9ef4..6d295c7 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c @@ -15,7 +15,7 @@ #include #include #include #include - +#include #include "power.h" @@ -86,6 +86,7 @@ static int suspend_prepare(suspend_state goto Thaw; } + suspend_console(); if ((error = device_suspend(PMSG_SUSPEND))) { printk(KERN_ERR "Some devices failed to suspend\n"); goto Finish; @@ -133,6 +134,7 @@ int suspend_enter(suspend_state_t state) static void suspend_finish(suspend_state_t state) { device_resume(); + resume_console(); thaw_processes(); enable_nonboot_cpus(); if (pm_ops && pm_ops->finish) @@ -143,7 +145,7 @@ static void suspend_finish(suspend_state -static char *pm_states[PM_SUSPEND_MAX] = { +static const char * const pm_states[PM_SUSPEND_MAX] = { [PM_SUSPEND_STANDBY] = "standby", [PM_SUSPEND_MEM] = "mem", #ifdef CONFIG_SOFTWARE_SUSPEND @@ -260,7 +262,7 @@ static ssize_t state_show(struct subsyst static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n) { suspend_state_t state = PM_SUSPEND_STANDBY; - char ** s; + const char * const *s; char *p; int error; int len; diff --git a/kernel/power/power.h b/kernel/power/power.h index f06f12f..57a7929 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -55,7 +55,7 @@ struct snapshot_handle { unsigned int page; unsigned int page_offset; unsigned int prev; - struct pbe *pbe; + struct pbe *pbe, *last_pbe; void *buffer; unsigned int buf_offset; }; diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 3eeedbb..24c96f3 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -150,6 +150,10 @@ int restore_highmem(void) } return 0; } +#else +static inline unsigned int count_highmem_pages(void) {return 0;} +static inline int save_highmem(void) {return 0;} +static inline int restore_highmem(void) {return 0;} #endif static int pfn_is_nosave(unsigned long pfn) @@ -293,62 +297,29 @@ static inline void create_pbe_list(struc } } -/** - * On resume it is necessary to trace and eventually free the unsafe - * pages that have been allocated, because they are needed for I/O - * (on x86-64 we likely will "eat" these pages once again while - * creating the temporary page translation tables) - */ - -struct eaten_page { - struct eaten_page *next; - char padding[PAGE_SIZE - sizeof(void *)]; -}; - -static struct eaten_page *eaten_pages = NULL; - -static void release_eaten_pages(void) -{ - struct eaten_page *p, *q; - - p = eaten_pages; - while (p) { - q = p->next; - /* We don't want swsusp_free() to free this page again */ - ClearPageNosave(virt_to_page(p)); - free_page((unsigned long)p); - p = q; - } - eaten_pages = NULL; -} +static unsigned int unsafe_pages; /** * @safe_needed - on resume, for storing the PBE list and the image, * we can only use memory pages that do not conflict with the pages - * which had been used before suspend. + * used before suspend. * * The unsafe pages are marked with the PG_nosave_free flag - * - * Allocated but unusable (ie eaten) memory pages should be marked - * so that swsusp_free() can release them + * and we count them using unsafe_pages */ static inline void *alloc_image_page(gfp_t gfp_mask, int safe_needed) { void *res; + res = (void *)get_zeroed_page(gfp_mask); if (safe_needed) - do { + while (res && PageNosaveFree(virt_to_page(res))) { + /* The page is unsafe, mark it for swsusp_free() */ + SetPageNosave(virt_to_page(res)); + unsafe_pages++; res = (void *)get_zeroed_page(gfp_mask); - if (res && PageNosaveFree(virt_to_page(res))) { - /* This is for swsusp_free() */ - SetPageNosave(virt_to_page(res)); - ((struct eaten_page *)res)->next = eaten_pages; - eaten_pages = res; - } - } while (res && PageNosaveFree(virt_to_page(res))); - else - res = (void *)get_zeroed_page(gfp_mask); + } if (res) { SetPageNosave(virt_to_page(res)); SetPageNosaveFree(virt_to_page(res)); @@ -374,7 +345,8 @@ unsigned long get_safe_page(gfp_t gfp_ma * On each page we set up a list of struct_pbe elements. */ -struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, int safe_needed) +static struct pbe *alloc_pagedir(unsigned int nr_pages, gfp_t gfp_mask, + int safe_needed) { unsigned int num; struct pbe *pblist, *pbe; @@ -642,6 +614,8 @@ static int mark_unsafe_pages(struct pbe return -EFAULT; } + unsafe_pages = 0; + return 0; } @@ -719,42 +693,99 @@ static inline struct pbe *unpack_orig_ad } /** - * create_image - use metadata contained in the PBE list + * prepare_image - use metadata contained in the PBE list * pointed to by pagedir_nosave to mark the pages that will * be overwritten in the process of restoring the system - * memory state from the image and allocate memory for - * the image avoiding these pages + * memory state from the image ("unsafe" pages) and allocate + * memory for the image + * + * The idea is to allocate the PBE list first and then + * allocate as many pages as it's needed for the image data, + * but not to assign these pages to the PBEs initially. + * Instead, we just mark them as allocated and create a list + * of "safe" which will be used later */ -static int create_image(struct snapshot_handle *handle) +struct safe_page { + struct safe_page *next; + char padding[PAGE_SIZE - sizeof(void *)]; +}; + +static struct safe_page *safe_pages; + +static int prepare_image(struct snapshot_handle *handle) { int error = 0; - struct pbe *p, *pblist; + unsigned int nr_pages = nr_copy_pages; + struct pbe *p, *pblist = NULL; p = pagedir_nosave; error = mark_unsafe_pages(p); if (!error) { - pblist = alloc_pagedir(nr_copy_pages, GFP_ATOMIC, 1); + pblist = alloc_pagedir(nr_pages, GFP_ATOMIC, 1); if (pblist) copy_page_backup_list(pblist, p); free_pagedir(p, 0); if (!pblist) error = -ENOMEM; } - if (!error) - error = alloc_data_pages(pblist, GFP_ATOMIC, 1); + safe_pages = NULL; + if (!error && nr_pages > unsafe_pages) { + nr_pages -= unsafe_pages; + while (nr_pages--) { + struct safe_page *ptr; + + ptr = (struct safe_page *)get_zeroed_page(GFP_ATOMIC); + if (!ptr) { + error = -ENOMEM; + break; + } + if (!PageNosaveFree(virt_to_page(ptr))) { + /* The page is "safe", add it to the list */ + ptr->next = safe_pages; + safe_pages = ptr; + } + /* Mark the page as allocated */ + SetPageNosave(virt_to_page(ptr)); + SetPageNosaveFree(virt_to_page(ptr)); + } + } if (!error) { - release_eaten_pages(); pagedir_nosave = pblist; } else { - pagedir_nosave = NULL; handle->pbe = NULL; - nr_copy_pages = 0; - nr_meta_pages = 0; + swsusp_free(); } return error; } +static void *get_buffer(struct snapshot_handle *handle) +{ + struct pbe *pbe = handle->pbe, *last = handle->last_pbe; + struct page *page = virt_to_page(pbe->orig_address); + + if (PageNosave(page) && PageNosaveFree(page)) { + /* + * We have allocated the "original" page frame and we can + * use it directly to store the read page + */ + pbe->address = 0; + if (last && last->next) + last->next = NULL; + return (void *)pbe->orig_address; + } + /* + * The "original" page frame has not been allocated and we have to + * use a "safe" page frame to store the read page + */ + pbe->address = (unsigned long)safe_pages; + safe_pages = safe_pages->next; + if (last) + last->next = pbe; + handle->last_pbe = pbe; + return (void *)pbe->address; +} + /** * snapshot_write_next - used for writing the system memory snapshot. * @@ -799,15 +830,16 @@ int snapshot_write_next(struct snapshot_ } else if (handle->prev <= nr_meta_pages) { handle->pbe = unpack_orig_addresses(buffer, handle->pbe); if (!handle->pbe) { - error = create_image(handle); + error = prepare_image(handle); if (error) return error; handle->pbe = pagedir_nosave; - handle->buffer = (void *)handle->pbe->address; + handle->last_pbe = NULL; + handle->buffer = get_buffer(handle); } } else { handle->pbe = handle->pbe->next; - handle->buffer = (void *)handle->pbe->address; + handle->buffer = get_buffer(handle); } handle->prev = handle->page; } diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c index c4016cb..17f669c 100644 --- a/kernel/power/swsusp.c +++ b/kernel/power/swsusp.c @@ -67,9 +67,9 @@ unsigned int count_highmem_pages(void); int save_highmem(void); int restore_highmem(void); #else -static int save_highmem(void) { return 0; } -static int restore_highmem(void) { return 0; } -static unsigned int count_highmem_pages(void) { return 0; } +static inline int save_highmem(void) { return 0; } +static inline int restore_highmem(void) { return 0; } +static inline unsigned int count_highmem_pages(void) { return 0; } #endif /** @@ -175,6 +175,12 @@ void free_all_swap_pages(int swap, struc */ #define SHRINK_BITE 10000 +static inline unsigned long __shrink_memory(long tmp) +{ + if (tmp > SHRINK_BITE) + tmp = SHRINK_BITE; + return shrink_all_memory(tmp); +} int swsusp_shrink_memory(void) { @@ -192,15 +198,17 @@ int swsusp_shrink_memory(void) PAGES_FOR_IO; tmp = size; for_each_zone (zone) - if (!is_highmem(zone)) + if (!is_highmem(zone) && populated_zone(zone)) { tmp -= zone->free_pages; + tmp += zone->lowmem_reserve[ZONE_NORMAL]; + } if (tmp > 0) { - tmp = shrink_all_memory(SHRINK_BITE); + tmp = __shrink_memory(tmp); if (!tmp) return -ENOMEM; pages += tmp; } else if (size > image_size / PAGE_SIZE) { - tmp = shrink_all_memory(SHRINK_BITE); + tmp = __shrink_memory(size - (image_size / PAGE_SIZE)); pages += tmp; } printk("\b%c", p[i++%4]); diff --git a/kernel/printk.c b/kernel/printk.c index c056f33..bdba5d8 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -24,8 +24,8 @@ #include #include #include #include +#include #include /* For in_interrupt() */ -#include #include #include #include @@ -67,6 +67,7 @@ EXPORT_SYMBOL(oops_in_progress); * driver system. */ static DECLARE_MUTEX(console_sem); +static DECLARE_MUTEX(secondary_console_sem); struct console *console_drivers; /* * This is used for debugging the mess that is the VT code by @@ -76,7 +77,7 @@ struct console *console_drivers; * path in the console code where we end up in places I want * locked without the console sempahore held */ -static int console_locked; +static int console_locked, console_suspended; /* * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars @@ -326,7 +327,9 @@ static void __call_console_drivers(unsig struct console *con; for (con = console_drivers; con; con = con->next) { - if ((con->flags & CON_ENABLED) && con->write) + if ((con->flags & CON_ENABLED) && con->write && + (cpu_online(smp_processor_id()) || + (con->flags & CON_ANYTIME))) con->write(con, &LOG_BUF(start), end - start); } } @@ -436,6 +439,7 @@ static int printk_time = 1; #else static int printk_time = 0; #endif +module_param(printk_time, int, S_IRUGO | S_IWUSR); static int __init printk_time_setup(char *str) { @@ -452,6 +456,18 @@ __attribute__((weak)) unsigned long long return sched_clock(); } +/* Check if we have any console registered that can be called early in boot. */ +static int have_callable_console(void) +{ + struct console *con; + + for (con = console_drivers; con; con = con->next) + if (con->flags & CON_ANYTIME) + return 1; + + return 0; +} + /** * printk - print a kernel message * @fmt: format string @@ -502,7 +518,9 @@ asmlinkage int vprintk(const char *fmt, zap_locks(); /* This stops the holder of console_sem just where we want him */ - spin_lock_irqsave(&logbuf_lock, flags); + local_irq_save(flags); + lockdep_off(); + spin_lock(&logbuf_lock); printk_cpu = smp_processor_id(); /* Emit the output into the temporary buffer */ @@ -565,27 +583,31 @@ asmlinkage int vprintk(const char *fmt, log_level_unknown = 1; } - if (!cpu_online(smp_processor_id())) { + if (!down_trylock(&console_sem)) { /* - * Some console drivers may assume that per-cpu resources have - * been allocated. So don't allow them to be called by this - * CPU until it is officially up. We shouldn't be calling into - * random console drivers on a CPU which doesn't exist yet.. + * We own the drivers. We can drop the spinlock and + * let release_console_sem() print the text, maybe ... */ - printk_cpu = UINT_MAX; - spin_unlock_irqrestore(&logbuf_lock, flags); - goto out; - } - if (!down_trylock(&console_sem)) { console_locked = 1; + printk_cpu = UINT_MAX; + spin_unlock(&logbuf_lock); + /* - * We own the drivers. We can drop the spinlock and let - * release_console_sem() print the text + * Console drivers may assume that per-cpu resources have + * been allocated. So unless they're explicitly marked as + * being able to cope (CON_ANYTIME) don't call them until + * this CPU is officially up. */ - printk_cpu = UINT_MAX; - spin_unlock_irqrestore(&logbuf_lock, flags); - console_may_schedule = 0; - release_console_sem(); + if (cpu_online(smp_processor_id()) || have_callable_console()) { + console_may_schedule = 0; + release_console_sem(); + } else { + /* Release by hand to avoid flushing the buffer. */ + console_locked = 0; + up(&console_sem); + } + lockdep_on(); + local_irq_restore(flags); } else { /* * Someone else owns the drivers. We drop the spinlock, which @@ -593,9 +615,11 @@ asmlinkage int vprintk(const char *fmt, * console drivers with the output which we just produced. */ printk_cpu = UINT_MAX; - spin_unlock_irqrestore(&logbuf_lock, flags); + spin_unlock(&logbuf_lock); + lockdep_on(); + local_irq_restore(flags); } -out: + preempt_enable(); return printed_len; } @@ -698,6 +722,23 @@ int __init add_preferred_console(char *n } /** + * suspend_console - suspend the console subsystem + * + * This disables printk() while we go into suspend states + */ +void suspend_console(void) +{ + acquire_console_sem(); + console_suspended = 1; +} + +void resume_console(void) +{ + console_suspended = 0; + release_console_sem(); +} + +/** * acquire_console_sem - lock the console system for exclusive use. * * Acquires a semaphore which guarantees that the caller has @@ -708,6 +749,10 @@ int __init add_preferred_console(char *n void acquire_console_sem(void) { BUG_ON(in_interrupt()); + if (console_suspended) { + down(&secondary_console_sem); + return; + } down(&console_sem); console_locked = 1; console_may_schedule = 1; @@ -750,6 +795,10 @@ void release_console_sem(void) unsigned long _con_start, _log_end; unsigned long wake_klogd = 0; + if (console_suspended) { + up(&secondary_console_sem); + return; + } for ( ; ; ) { spin_lock_irqsave(&logbuf_lock, flags); wake_klogd |= log_start - log_end; @@ -766,8 +815,15 @@ void release_console_sem(void) console_may_schedule = 0; up(&console_sem); spin_unlock_irqrestore(&logbuf_lock, flags); - if (wake_klogd && !oops_in_progress && waitqueue_active(&log_wait)) - wake_up_interruptible(&log_wait); + if (wake_klogd && !oops_in_progress && waitqueue_active(&log_wait)) { + /* + * If we printk from within the lock dependency code, + * from within the scheduler code, then do not lock + * up due to self-recursion: + */ + if (!lockdep_internal()) + wake_up_interruptible(&log_wait); + } } EXPORT_SYMBOL(release_console_sem); diff --git a/kernel/profile.c b/kernel/profile.c index 68afe12..d5bd75e 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -13,7 +13,6 @@ * to resolve timer interrupt livelocks, William Irwin, Oracle, 2004 */ -#include #include #include #include @@ -299,7 +298,7 @@ out: } #ifdef CONFIG_HOTPLUG_CPU -static int profile_cpu_callback(struct notifier_block *info, +static int __devinit profile_cpu_callback(struct notifier_block *info, unsigned long action, void *__cpu) { int node, cpu = (unsigned long)__cpu; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 921c22a..9a111f7 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -28,7 +28,7 @@ #include * * Must be called with the tasklist lock write-held. */ -void __ptrace_link(task_t *child, task_t *new_parent) +void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) { BUG_ON(!list_empty(&child->ptrace_list)); if (child->parent == new_parent) @@ -46,7 +46,7 @@ void __ptrace_link(task_t *child, task_t * TASK_TRACED, resume it now. * Requires that irqs be disabled. */ -void ptrace_untrace(task_t *child) +void ptrace_untrace(struct task_struct *child) { spin_lock(&child->sighand->siglock); if (child->state == TASK_TRACED) { @@ -65,7 +65,7 @@ void ptrace_untrace(task_t *child) * * Must be called with the tasklist lock write-held. */ -void __ptrace_unlink(task_t *child) +void __ptrace_unlink(struct task_struct *child) { BUG_ON(!child->ptrace); @@ -120,8 +120,18 @@ int ptrace_check_attach(struct task_stru static int may_attach(struct task_struct *task) { - if (!task->mm) - return -EPERM; + /* May we inspect the given task? + * This check is used both for attaching with ptrace + * and for allowing access to sensitive information in /proc. + * + * ptrace_attach denies several cases that /proc allows + * because setting up the necessary parent/child relationship + * or halting the specified task is impossible. + */ + int dumpable = 0; + /* Don't let security modules deny introspection */ + if (task == current) + return 0; if (((current->uid != task->euid) || (current->uid != task->suid) || (current->uid != task->uid) || @@ -130,7 +140,9 @@ static int may_attach(struct task_struct (current->gid != task->gid)) && !capable(CAP_SYS_PTRACE)) return -EPERM; smp_rmb(); - if (!task->mm->dumpable && !capable(CAP_SYS_PTRACE)) + if (task->mm) + dumpable = task->mm->dumpable; + if (!dumpable && !capable(CAP_SYS_PTRACE)) return -EPERM; return security_ptrace(current, task); @@ -176,6 +188,8 @@ repeat: goto repeat; } + if (!task->mm) + goto bad; /* the same process cannot be attached many times */ if (task->ptrace & PT_PTRACED) goto bad; @@ -200,7 +214,7 @@ out: return retval; } -void __ptrace_detach(struct task_struct *child, unsigned int data) +static inline void __ptrace_detach(struct task_struct *child, unsigned int data) { child->exit_code = data; /* .. re-parent .. */ @@ -219,6 +233,7 @@ int ptrace_detach(struct task_struct *ch ptrace_disable(child); write_lock_irq(&tasklist_lock); + /* protect against de_thread()->release_task() */ if (child->ptrace) __ptrace_detach(child, data); write_unlock_irq(&tasklist_lock); diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index 2058f88..759805c 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c @@ -53,13 +53,13 @@ #include static struct rcu_ctrlblk rcu_ctrlblk = { .cur = -300, .completed = -300, - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(&rcu_ctrlblk.lock), .cpumask = CPU_MASK_NONE, }; static struct rcu_ctrlblk rcu_bh_ctrlblk = { .cur = -300, .completed = -300, - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(&rcu_bh_ctrlblk.lock), .cpumask = CPU_MASK_NONE, }; @@ -182,6 +182,15 @@ long rcu_batches_completed(void) return rcu_ctrlblk.completed; } +/* + * Return the number of RCU batches processed thus far. Useful + * for debug and statistics. + */ +long rcu_batches_completed_bh(void) +{ + return rcu_bh_ctrlblk.completed; +} + static void rcu_barrier_callback(struct rcu_head *notused) { if (atomic_dec_and_test(&rcu_barrier_cpu_count)) @@ -539,7 +548,7 @@ static void __devinit rcu_online_cpu(int tasklet_init(&per_cpu(rcu_tasklet, cpu), rcu_process_callbacks, 0UL); } -static int rcu_cpu_notify(struct notifier_block *self, +static int __devinit rcu_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { long cpu = (long)hcpu; @@ -556,7 +565,7 @@ static int rcu_cpu_notify(struct notifie return NOTIFY_OK; } -static struct notifier_block rcu_nb = { +static struct notifier_block __devinitdata rcu_nb = { .notifier_call = rcu_cpu_notify, }; @@ -612,14 +621,6 @@ void synchronize_rcu(void) wait_for_completion(&rcu.completion); } -/* - * Deprecated, use synchronize_rcu() or synchronize_sched() instead. - */ -void synchronize_kernel(void) -{ - synchronize_rcu(); -} - module_param(blimit, int, 0); module_param(qhimark, int, 0); module_param(qlowmark, int, 0); @@ -627,7 +628,7 @@ #ifdef CONFIG_SMP module_param(rsinterval, int, 0); #endif EXPORT_SYMBOL_GPL(rcu_batches_completed); -EXPORT_SYMBOL_GPL_FUTURE(call_rcu); /* WARNING: GPL-only in April 2006. */ -EXPORT_SYMBOL_GPL_FUTURE(call_rcu_bh); /* WARNING: GPL-only in April 2006. */ +EXPORT_SYMBOL_GPL(rcu_batches_completed_bh); +EXPORT_SYMBOL_GPL(call_rcu); +EXPORT_SYMBOL_GPL(call_rcu_bh); EXPORT_SYMBOL_GPL(synchronize_rcu); -EXPORT_SYMBOL_GPL_FUTURE(synchronize_kernel); /* WARNING: GPL-only in April 2006. */ diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 8154e75..4d1c3d2 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c @@ -1,5 +1,5 @@ /* - * Read-Copy Update /proc-based torture test facility + * Read-Copy Update module-based torture test facility * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -53,6 +53,7 @@ static int stat_interval; /* Interval be static int verbose; /* Print more debug info. */ static int test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */ static int shuffle_interval = 5; /* Interval between shuffles (in sec)*/ +static char *torture_type = "rcu"; /* What to torture. */ module_param(nreaders, int, 0); MODULE_PARM_DESC(nreaders, "Number of RCU reader threads"); @@ -64,13 +65,16 @@ module_param(test_no_idle_hz, bool, 0); MODULE_PARM_DESC(test_no_idle_hz, "Test support for tickless idle CPUs"); module_param(shuffle_interval, int, 0); MODULE_PARM_DESC(shuffle_interval, "Number of seconds between shuffles"); -#define TORTURE_FLAG "rcutorture: " +module_param(torture_type, charp, 0); +MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh)"); + +#define TORTURE_FLAG "-torture:" #define PRINTK_STRING(s) \ - do { printk(KERN_ALERT TORTURE_FLAG s "\n"); } while (0) + do { printk(KERN_ALERT "%s" TORTURE_FLAG s "\n", torture_type); } while (0) #define VERBOSE_PRINTK_STRING(s) \ - do { if (verbose) printk(KERN_ALERT TORTURE_FLAG s "\n"); } while (0) + do { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG s "\n", torture_type); } while (0) #define VERBOSE_PRINTK_ERRSTRING(s) \ - do { if (verbose) printk(KERN_ALERT TORTURE_FLAG "!!! " s "\n"); } while (0) + do { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0) static char printk_buf[4096]; @@ -139,28 +143,6 @@ rcu_torture_free(struct rcu_torture *p) spin_unlock_bh(&rcu_torture_lock); } -static void -rcu_torture_cb(struct rcu_head *p) -{ - int i; - struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu); - - if (fullstop) { - /* Test is ending, just drop callbacks on the floor. */ - /* The next initialization will pick up the pieces. */ - return; - } - i = rp->rtort_pipe_count; - if (i > RCU_TORTURE_PIPE_LEN) - i = RCU_TORTURE_PIPE_LEN; - atomic_inc(&rcu_torture_wcount[i]); - if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { - rp->rtort_mbtest = 0; - rcu_torture_free(rp); - } else - call_rcu(p, rcu_torture_cb); -} - struct rcu_random_state { unsigned long rrs_state; unsigned long rrs_count; @@ -191,6 +173,119 @@ rcu_random(struct rcu_random_state *rrsp } /* + * Operations vector for selecting different types of tests. + */ + +struct rcu_torture_ops { + void (*init)(void); + void (*cleanup)(void); + int (*readlock)(void); + void (*readunlock)(int idx); + int (*completed)(void); + void (*deferredfree)(struct rcu_torture *p); + int (*stats)(char *page); + char *name; +}; +static struct rcu_torture_ops *cur_ops = NULL; + +/* + * Definitions for rcu torture testing. + */ + +static int rcu_torture_read_lock(void) +{ + rcu_read_lock(); + return 0; +} + +static void rcu_torture_read_unlock(int idx) +{ + rcu_read_unlock(); +} + +static int rcu_torture_completed(void) +{ + return rcu_batches_completed(); +} + +static void +rcu_torture_cb(struct rcu_head *p) +{ + int i; + struct rcu_torture *rp = container_of(p, struct rcu_torture, rtort_rcu); + + if (fullstop) { + /* Test is ending, just drop callbacks on the floor. */ + /* The next initialization will pick up the pieces. */ + return; + } + i = rp->rtort_pipe_count; + if (i > RCU_TORTURE_PIPE_LEN) + i = RCU_TORTURE_PIPE_LEN; + atomic_inc(&rcu_torture_wcount[i]); + if (++rp->rtort_pipe_count >= RCU_TORTURE_PIPE_LEN) { + rp->rtort_mbtest = 0; + rcu_torture_free(rp); + } else + cur_ops->deferredfree(rp); +} + +static void rcu_torture_deferred_free(struct rcu_torture *p) +{ + call_rcu(&p->rtort_rcu, rcu_torture_cb); +} + +static struct rcu_torture_ops rcu_ops = { + .init = NULL, + .cleanup = NULL, + .readlock = rcu_torture_read_lock, + .readunlock = rcu_torture_read_unlock, + .completed = rcu_torture_completed, + .deferredfree = rcu_torture_deferred_free, + .stats = NULL, + .name = "rcu" +}; + +/* + * Definitions for rcu_bh torture testing. + */ + +static int rcu_bh_torture_read_lock(void) +{ + rcu_read_lock_bh(); + return 0; +} + +static void rcu_bh_torture_read_unlock(int idx) +{ + rcu_read_unlock_bh(); +} + +static int rcu_bh_torture_completed(void) +{ + return rcu_batches_completed_bh(); +} + +static void rcu_bh_torture_deferred_free(struct rcu_torture *p) +{ + call_rcu_bh(&p->rtort_rcu, rcu_torture_cb); +} + +static struct rcu_torture_ops rcu_bh_ops = { + .init = NULL, + .cleanup = NULL, + .readlock = rcu_bh_torture_read_lock, + .readunlock = rcu_bh_torture_read_unlock, + .completed = rcu_bh_torture_completed, + .deferredfree = rcu_bh_torture_deferred_free, + .stats = NULL, + .name = "rcu_bh" +}; + +static struct rcu_torture_ops *torture_ops[] = + { &rcu_ops, &rcu_bh_ops, NULL }; + +/* * RCU torture writer kthread. Repeatedly substitutes a new structure * for that pointed to by rcu_torture_current, freeing the old structure * after a series of grace periods (the "pipeline"). @@ -209,8 +304,6 @@ rcu_torture_writer(void *arg) do { schedule_timeout_uninterruptible(1); - if (rcu_batches_completed() == oldbatch) - continue; if ((rp = rcu_torture_alloc()) == NULL) continue; rp->rtort_pipe_count = 0; @@ -225,10 +318,10 @@ rcu_torture_writer(void *arg) i = RCU_TORTURE_PIPE_LEN; atomic_inc(&rcu_torture_wcount[i]); old_rp->rtort_pipe_count++; - call_rcu(&old_rp->rtort_rcu, rcu_torture_cb); + cur_ops->deferredfree(old_rp); } rcu_torture_current_version++; - oldbatch = rcu_batches_completed(); + oldbatch = cur_ops->completed(); } while (!kthread_should_stop() && !fullstop); VERBOSE_PRINTK_STRING("rcu_torture_writer task stopping"); while (!kthread_should_stop()) @@ -246,6 +339,7 @@ static int rcu_torture_reader(void *arg) { int completed; + int idx; DEFINE_RCU_RANDOM(rand); struct rcu_torture *p; int pipe_count; @@ -254,12 +348,12 @@ rcu_torture_reader(void *arg) set_user_nice(current, 19); do { - rcu_read_lock(); - completed = rcu_batches_completed(); + idx = cur_ops->readlock(); + completed = cur_ops->completed(); p = rcu_dereference(rcu_torture_current); if (p == NULL) { /* Wait for rcu_torture_writer to get underway */ - rcu_read_unlock(); + cur_ops->readunlock(idx); schedule_timeout_interruptible(HZ); continue; } @@ -273,14 +367,14 @@ rcu_torture_reader(void *arg) pipe_count = RCU_TORTURE_PIPE_LEN; } ++__get_cpu_var(rcu_torture_count)[pipe_count]; - completed = rcu_batches_completed() - completed; + completed = cur_ops->completed() - completed; if (completed > RCU_TORTURE_PIPE_LEN) { /* Should not happen, but... */ completed = RCU_TORTURE_PIPE_LEN; } ++__get_cpu_var(rcu_torture_batch)[completed]; preempt_enable(); - rcu_read_unlock(); + cur_ops->readunlock(idx); schedule(); } while (!kthread_should_stop() && !fullstop); VERBOSE_PRINTK_STRING("rcu_torture_reader task stopping"); @@ -311,7 +405,7 @@ rcu_torture_printk(char *page) if (pipesummary[i] != 0) break; } - cnt += sprintf(&page[cnt], "rcutorture: "); + cnt += sprintf(&page[cnt], "%s%s ", torture_type, TORTURE_FLAG); cnt += sprintf(&page[cnt], "rtc: %p ver: %ld tfle: %d rta: %d rtaf: %d rtf: %d " "rtmbe: %d", @@ -324,7 +418,7 @@ rcu_torture_printk(char *page) atomic_read(&n_rcu_torture_mberror)); if (atomic_read(&n_rcu_torture_mberror) != 0) cnt += sprintf(&page[cnt], " !!!"); - cnt += sprintf(&page[cnt], "\nrcutorture: "); + cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); if (i > 1) { cnt += sprintf(&page[cnt], "!!! "); atomic_inc(&n_rcu_torture_error); @@ -332,17 +426,19 @@ rcu_torture_printk(char *page) cnt += sprintf(&page[cnt], "Reader Pipe: "); for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) cnt += sprintf(&page[cnt], " %ld", pipesummary[i]); - cnt += sprintf(&page[cnt], "\nrcutorture: "); + cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); cnt += sprintf(&page[cnt], "Reader Batch: "); - for (i = 0; i < RCU_TORTURE_PIPE_LEN; i++) + for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) cnt += sprintf(&page[cnt], " %ld", batchsummary[i]); - cnt += sprintf(&page[cnt], "\nrcutorture: "); + cnt += sprintf(&page[cnt], "\n%s%s ", torture_type, TORTURE_FLAG); cnt += sprintf(&page[cnt], "Free-Block Circulation: "); for (i = 0; i < RCU_TORTURE_PIPE_LEN + 1; i++) { cnt += sprintf(&page[cnt], " %d", atomic_read(&rcu_torture_wcount[i])); } cnt += sprintf(&page[cnt], "\n"); + if (cur_ops->stats != NULL) + cnt += cur_ops->stats(&page[cnt]); return cnt; } @@ -444,11 +540,11 @@ rcu_torture_shuffle(void *arg) static inline void rcu_torture_print_module_parms(char *tag) { - printk(KERN_ALERT TORTURE_FLAG "--- %s: nreaders=%d " + printk(KERN_ALERT "%s" TORTURE_FLAG "--- %s: nreaders=%d " "stat_interval=%d verbose=%d test_no_idle_hz=%d " "shuffle_interval = %d\n", - tag, nrealreaders, stat_interval, verbose, test_no_idle_hz, - shuffle_interval); + torture_type, tag, nrealreaders, stat_interval, verbose, + test_no_idle_hz, shuffle_interval); } static void @@ -493,6 +589,9 @@ rcu_torture_cleanup(void) rcu_barrier(); rcu_torture_stats_print(); /* -After- the stats thread is stopped! */ + + if (cur_ops->cleanup != NULL) + cur_ops->cleanup(); if (atomic_read(&n_rcu_torture_error)) rcu_torture_print_module_parms("End of test: FAILURE"); else @@ -508,6 +607,20 @@ rcu_torture_init(void) /* Process args and tell the world that the torturer is on the job. */ + for (i = 0; cur_ops = torture_ops[i], cur_ops != NULL; i++) { + cur_ops = torture_ops[i]; + if (strcmp(torture_type, cur_ops->name) == 0) { + break; + } + } + if (cur_ops == NULL) { + printk(KERN_ALERT "rcutorture: invalid torture type: \"%s\"\n", + torture_type); + return (-EINVAL); + } + if (cur_ops->init != NULL) + cur_ops->init(); /* no "goto unwind" prior to this point!!! */ + if (nreaders >= 0) nrealreaders = nreaders; else diff --git a/kernel/resource.c b/kernel/resource.c index e3080fc..129cf04 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -7,7 +7,6 @@ * Arbitrary resource management. */ -#include #include #include #include @@ -23,20 +22,18 @@ #include struct resource ioport_resource = { .name = "PCI IO", - .start = 0x0000, + .start = 0, .end = IO_SPACE_LIMIT, .flags = IORESOURCE_IO, }; - EXPORT_SYMBOL(ioport_resource); struct resource iomem_resource = { .name = "PCI mem", - .start = 0UL, - .end = ~0UL, + .start = 0, + .end = -1, .flags = IORESOURCE_MEM, }; - EXPORT_SYMBOL(iomem_resource); static DEFINE_RWLOCK(resource_lock); @@ -83,10 +80,10 @@ static int r_show(struct seq_file *m, vo for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent) if (p->parent == root) break; - seq_printf(m, "%*s%0*lx-%0*lx : %s\n", + seq_printf(m, "%*s%0*llx-%0*llx : %s\n", depth * 2, "", - width, r->start, - width, r->end, + width, (unsigned long long) r->start, + width, (unsigned long long) r->end, r->name ? r->name : ""); return 0; } @@ -151,8 +148,8 @@ #endif /* CONFIG_PROC_FS */ /* Return the conflict entry if you can't request it */ static struct resource * __request_resource(struct resource *root, struct resource *new) { - unsigned long start = new->start; - unsigned long end = new->end; + resource_size_t start = new->start; + resource_size_t end = new->end; struct resource *tmp, **p; if (end < start) @@ -232,15 +229,52 @@ int release_resource(struct resource *ol EXPORT_SYMBOL(release_resource); +#ifdef CONFIG_MEMORY_HOTPLUG +/* + * Finds the lowest memory reosurce exists within [res->start.res->end) + * the caller must specify res->start, res->end, res->flags. + * If found, returns 0, res is overwritten, if not found, returns -1. + */ +int find_next_system_ram(struct resource *res) +{ + resource_size_t start, end; + struct resource *p; + + BUG_ON(!res); + + start = res->start; + end = res->end; + + read_lock(&resource_lock); + for (p = iomem_resource.child; p ; p = p->sibling) { + /* system ram is just marked as IORESOURCE_MEM */ + if (p->flags != res->flags) + continue; + if (p->start > end) { + p = NULL; + break; + } + if (p->start >= start) + break; + } + read_unlock(&resource_lock); + if (!p) + return -1; + /* copy data */ + res->start = p->start; + res->end = p->end; + return 0; +} +#endif + /* * Find empty slot in the resource tree given range and alignment. */ static int find_resource(struct resource *root, struct resource *new, - unsigned long size, - unsigned long min, unsigned long max, - unsigned long align, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, void (*alignf)(void *, struct resource *, - unsigned long, unsigned long), + resource_size_t, resource_size_t), void *alignf_data) { struct resource *this = root->child; @@ -282,11 +316,10 @@ static int find_resource(struct resource * Allocate empty slot in the resource tree given range and alignment. */ int allocate_resource(struct resource *root, struct resource *new, - unsigned long size, - unsigned long min, unsigned long max, - unsigned long align, + resource_size_t size, resource_size_t min, + resource_size_t max, resource_size_t align, void (*alignf)(void *, struct resource *, - unsigned long, unsigned long), + resource_size_t, resource_size_t), void *alignf_data) { int err; @@ -378,10 +411,10 @@ EXPORT_SYMBOL(insert_resource); * arguments. Returns -EBUSY if it can't fit. Existing children of * the resource are assumed to be immutable. */ -int adjust_resource(struct resource *res, unsigned long start, unsigned long size) +int adjust_resource(struct resource *res, resource_size_t start, resource_size_t size) { struct resource *tmp, *parent = res->parent; - unsigned long end = start + size - 1; + resource_size_t end = start + size - 1; int result = -EBUSY; write_lock(&resource_lock); @@ -428,7 +461,9 @@ EXPORT_SYMBOL(adjust_resource); * * Release-region releases a matching busy region. */ -struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name) +struct resource * __request_region(struct resource *parent, + resource_size_t start, resource_size_t n, + const char *name) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); @@ -464,7 +499,8 @@ struct resource * __request_region(struc EXPORT_SYMBOL(__request_region); -int __check_region(struct resource *parent, unsigned long start, unsigned long n) +int __check_region(struct resource *parent, resource_size_t start, + resource_size_t n) { struct resource * res; @@ -479,10 +515,11 @@ int __check_region(struct resource *pare EXPORT_SYMBOL(__check_region); -void __release_region(struct resource *parent, unsigned long start, unsigned long n) +void __release_region(struct resource *parent, resource_size_t start, + resource_size_t n) { struct resource **p; - unsigned long end; + resource_size_t end; p = &parent->child; end = start + n - 1; @@ -511,7 +548,9 @@ void __release_region(struct resource *p write_unlock(&resource_lock); - printk(KERN_WARNING "Trying to free nonexistent resource <%08lx-%08lx>\n", start, end); + printk(KERN_WARNING "Trying to free nonexistent resource " + "<%016llx-%016llx>\n", (unsigned long long)start, + (unsigned long long)end); } EXPORT_SYMBOL(__release_region); diff --git a/kernel/rtmutex-debug.c b/kernel/rtmutex-debug.c new file mode 100644 index 0000000..0c1faa9 --- /dev/null +++ b/kernel/rtmutex-debug.c @@ -0,0 +1,242 @@ +/* + * RT-Mutexes: blocking mutual exclusion locks with PI support + * + * started by Ingo Molnar and Thomas Gleixner: + * + * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2006 Timesys Corp., Thomas Gleixner + * + * This code is based on the rt.c implementation in the preempt-rt tree. + * Portions of said code are + * + * Copyright (C) 2004 LynuxWorks, Inc., Igor Manyilov, Bill Huey + * Copyright (C) 2006 Esben Nielsen + * Copyright (C) 2006 Kihon Technologies Inc., + * Steven Rostedt + * + * See rt.c in preempt-rt for proper credits and further information + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtmutex_common.h" + +#ifdef CONFIG_DEBUG_RT_MUTEXES +# include "rtmutex-debug.h" +#else +# include "rtmutex.h" +#endif + +# define TRACE_WARN_ON(x) WARN_ON(x) +# define TRACE_BUG_ON(x) BUG_ON(x) + +# define TRACE_OFF() \ +do { \ + if (rt_trace_on) { \ + rt_trace_on = 0; \ + console_verbose(); \ + if (spin_is_locked(¤t->pi_lock)) \ + spin_unlock(¤t->pi_lock); \ + } \ +} while (0) + +# define TRACE_OFF_NOLOCK() \ +do { \ + if (rt_trace_on) { \ + rt_trace_on = 0; \ + console_verbose(); \ + } \ +} while (0) + +# define TRACE_BUG_LOCKED() \ +do { \ + TRACE_OFF(); \ + BUG(); \ +} while (0) + +# define TRACE_WARN_ON_LOCKED(c) \ +do { \ + if (unlikely(c)) { \ + TRACE_OFF(); \ + WARN_ON(1); \ + } \ +} while (0) + +# define TRACE_BUG_ON_LOCKED(c) \ +do { \ + if (unlikely(c)) \ + TRACE_BUG_LOCKED(); \ +} while (0) + +#ifdef CONFIG_SMP +# define SMP_TRACE_BUG_ON_LOCKED(c) TRACE_BUG_ON_LOCKED(c) +#else +# define SMP_TRACE_BUG_ON_LOCKED(c) do { } while (0) +#endif + +/* + * deadlock detection flag. We turn it off when we detect + * the first problem because we dont want to recurse back + * into the tracing code when doing error printk or + * executing a BUG(): + */ +int rt_trace_on = 1; + +void deadlock_trace_off(void) +{ + rt_trace_on = 0; +} + +static void printk_task(struct task_struct *p) +{ + if (p) + printk("%16s:%5d [%p, %3d]", p->comm, p->pid, p, p->prio); + else + printk(""); +} + +static void printk_lock(struct rt_mutex *lock, int print_owner) +{ + if (lock->name) + printk(" [%p] {%s}\n", + lock, lock->name); + else + printk(" [%p] {%s:%d}\n", + lock, lock->file, lock->line); + + if (print_owner && rt_mutex_owner(lock)) { + printk(".. ->owner: %p\n", lock->owner); + printk(".. held by: "); + printk_task(rt_mutex_owner(lock)); + printk("\n"); + } +} + +void rt_mutex_debug_task_free(struct task_struct *task) +{ + WARN_ON(!plist_head_empty(&task->pi_waiters)); + WARN_ON(task->pi_blocked_on); +} + +/* + * We fill out the fields in the waiter to store the information about + * the deadlock. We print when we return. act_waiter can be NULL in + * case of a remove waiter operation. + */ +void debug_rt_mutex_deadlock(int detect, struct rt_mutex_waiter *act_waiter, + struct rt_mutex *lock) +{ + struct task_struct *task; + + if (!rt_trace_on || detect || !act_waiter) + return; + + task = rt_mutex_owner(act_waiter->lock); + if (task && task != current) { + act_waiter->deadlock_task_pid = task->pid; + act_waiter->deadlock_lock = lock; + } +} + +void debug_rt_mutex_print_deadlock(struct rt_mutex_waiter *waiter) +{ + struct task_struct *task; + + if (!waiter->deadlock_lock || !rt_trace_on) + return; + + task = find_task_by_pid(waiter->deadlock_task_pid); + if (!task) + return; + + TRACE_OFF_NOLOCK(); + + printk("\n============================================\n"); + printk( "[ BUG: circular locking deadlock detected! ]\n"); + printk( "--------------------------------------------\n"); + printk("%s/%d is deadlocking current task %s/%d\n\n", + task->comm, task->pid, current->comm, current->pid); + + printk("\n1) %s/%d is trying to acquire this lock:\n", + current->comm, current->pid); + printk_lock(waiter->lock, 1); + + printk("\n2) %s/%d is blocked on this lock:\n", task->comm, task->pid); + printk_lock(waiter->deadlock_lock, 1); + + debug_show_held_locks(current); + debug_show_held_locks(task); + + printk("\n%s/%d's [blocked] stackdump:\n\n", task->comm, task->pid); + show_stack(task, NULL); + printk("\n%s/%d's [current] stackdump:\n\n", + current->comm, current->pid); + dump_stack(); + debug_show_all_locks(); + + printk("[ turning off deadlock detection." + "Please report this trace. ]\n\n"); + local_irq_disable(); +} + +void debug_rt_mutex_lock(struct rt_mutex *lock) +{ +} + +void debug_rt_mutex_unlock(struct rt_mutex *lock) +{ + TRACE_WARN_ON_LOCKED(rt_mutex_owner(lock) != current); +} + +void +debug_rt_mutex_proxy_lock(struct rt_mutex *lock, struct task_struct *powner) +{ +} + +void debug_rt_mutex_proxy_unlock(struct rt_mutex *lock) +{ + TRACE_WARN_ON_LOCKED(!rt_mutex_owner(lock)); +} + +void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter) +{ + memset(waiter, 0x11, sizeof(*waiter)); + plist_node_init(&waiter->list_entry, MAX_PRIO); + plist_node_init(&waiter->pi_list_entry, MAX_PRIO); +} + +void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter) +{ + TRACE_WARN_ON(!plist_node_empty(&waiter->list_entry)); + TRACE_WARN_ON(!plist_node_empty(&waiter->pi_list_entry)); + TRACE_WARN_ON(waiter->task); + memset(waiter, 0x22, sizeof(*waiter)); +} + +void debug_rt_mutex_init(struct rt_mutex *lock, const char *name) +{ + /* + * Make sure we are not reinitializing a held lock: + */ + debug_check_no_locks_freed((void *)lock, sizeof(*lock)); + lock->name = name; +} + +void +rt_mutex_deadlock_account_lock(struct rt_mutex *lock, struct task_struct *task) +{ +} + +void rt_mutex_deadlock_account_unlock(struct task_struct *task) +{ +} + diff --git a/kernel/rtmutex-debug.h b/kernel/rtmutex-debug.h new file mode 100644 index 0000000..14193d5 --- /dev/null +++ b/kernel/rtmutex-debug.h @@ -0,0 +1,33 @@ +/* + * RT-Mutexes: blocking mutual exclusion locks with PI support + * + * started by Ingo Molnar and Thomas Gleixner: + * + * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2006, Timesys Corp., Thomas Gleixner + * + * This file contains macros used solely by rtmutex.c. Debug version. + */ + +extern void +rt_mutex_deadlock_account_lock(struct rt_mutex *lock, struct task_struct *task); +extern void rt_mutex_deadlock_account_unlock(struct task_struct *task); +extern void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter); +extern void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter); +extern void debug_rt_mutex_init(struct rt_mutex *lock, const char *name); +extern void debug_rt_mutex_lock(struct rt_mutex *lock); +extern void debug_rt_mutex_unlock(struct rt_mutex *lock); +extern void debug_rt_mutex_proxy_lock(struct rt_mutex *lock, + struct task_struct *powner); +extern void debug_rt_mutex_proxy_unlock(struct rt_mutex *lock); +extern void debug_rt_mutex_deadlock(int detect, struct rt_mutex_waiter *waiter, + struct rt_mutex *lock); +extern void debug_rt_mutex_print_deadlock(struct rt_mutex_waiter *waiter); +# define debug_rt_mutex_reset_waiter(w) \ + do { (w)->deadlock_lock = NULL; } while (0) + +static inline int debug_rt_mutex_detect_deadlock(struct rt_mutex_waiter *waiter, + int detect) +{ + return (waiter != NULL); +} diff --git a/kernel/rtmutex-tester.c b/kernel/rtmutex-tester.c new file mode 100644 index 0000000..494dac8 --- /dev/null +++ b/kernel/rtmutex-tester.c @@ -0,0 +1,440 @@ +/* + * RT-Mutex-tester: scriptable tester for rt mutexes + * + * started by Thomas Gleixner: + * + * Copyright (C) 2006, Timesys Corp., Thomas Gleixner + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rtmutex.h" + +#define MAX_RT_TEST_THREADS 8 +#define MAX_RT_TEST_MUTEXES 8 + +static spinlock_t rttest_lock; +static atomic_t rttest_event; + +struct test_thread_data { + int opcode; + int opdata; + int mutexes[MAX_RT_TEST_MUTEXES]; + int bkl; + int event; + struct sys_device sysdev; +}; + +static struct test_thread_data thread_data[MAX_RT_TEST_THREADS]; +static struct task_struct *threads[MAX_RT_TEST_THREADS]; +static struct rt_mutex mutexes[MAX_RT_TEST_MUTEXES]; + +enum test_opcodes { + RTTEST_NOP = 0, + RTTEST_SCHEDOT, /* 1 Sched other, data = nice */ + RTTEST_SCHEDRT, /* 2 Sched fifo, data = prio */ + RTTEST_LOCK, /* 3 Lock uninterruptible, data = lockindex */ + RTTEST_LOCKNOWAIT, /* 4 Lock uninterruptible no wait in wakeup, data = lockindex */ + RTTEST_LOCKINT, /* 5 Lock interruptible, data = lockindex */ + RTTEST_LOCKINTNOWAIT, /* 6 Lock interruptible no wait in wakeup, data = lockindex */ + RTTEST_LOCKCONT, /* 7 Continue locking after the wakeup delay */ + RTTEST_UNLOCK, /* 8 Unlock, data = lockindex */ + RTTEST_LOCKBKL, /* 9 Lock BKL */ + RTTEST_UNLOCKBKL, /* 10 Unlock BKL */ + RTTEST_SIGNAL, /* 11 Signal other test thread, data = thread id */ + RTTEST_RESETEVENT = 98, /* 98 Reset event counter */ + RTTEST_RESET = 99, /* 99 Reset all pending operations */ +}; + +static int handle_op(struct test_thread_data *td, int lockwakeup) +{ + int i, id, ret = -EINVAL; + + switch(td->opcode) { + + case RTTEST_NOP: + return 0; + + case RTTEST_LOCKCONT: + td->mutexes[td->opdata] = 1; + td->event = atomic_add_return(1, &rttest_event); + return 0; + + case RTTEST_RESET: + for (i = 0; i < MAX_RT_TEST_MUTEXES; i++) { + if (td->mutexes[i] == 4) { + rt_mutex_unlock(&mutexes[i]); + td->mutexes[i] = 0; + } + } + + if (!lockwakeup && td->bkl == 4) { + unlock_kernel(); + td->bkl = 0; + } + return 0; + + case RTTEST_RESETEVENT: + atomic_set(&rttest_event, 0); + return 0; + + default: + if (lockwakeup) + return ret; + } + + switch(td->opcode) { + + case RTTEST_LOCK: + case RTTEST_LOCKNOWAIT: + id = td->opdata; + if (id < 0 || id >= MAX_RT_TEST_MUTEXES) + return ret; + + td->mutexes[id] = 1; + td->event = atomic_add_return(1, &rttest_event); + rt_mutex_lock(&mutexes[id]); + td->event = atomic_add_return(1, &rttest_event); + td->mutexes[id] = 4; + return 0; + + case RTTEST_LOCKINT: + case RTTEST_LOCKINTNOWAIT: + id = td->opdata; + if (id < 0 || id >= MAX_RT_TEST_MUTEXES) + return ret; + + td->mutexes[id] = 1; + td->event = atomic_add_return(1, &rttest_event); + ret = rt_mutex_lock_interruptible(&mutexes[id], 0); + td->event = atomic_add_return(1, &rttest_event); + td->mutexes[id] = ret ? 0 : 4; + return ret ? -EINTR : 0; + + case RTTEST_UNLOCK: + id = td->opdata; + if (id < 0 || id >= MAX_RT_TEST_MUTEXES || td->mutexes[id] != 4) + return ret; + + td->event = atomic_add_return(1, &rttest_event); + rt_mutex_unlock(&mutexes[id]); + td->event = atomic_add_return(1, &rttest_event); + td->mutexes[id] = 0; + return 0; + + case RTTEST_LOCKBKL: + if (td->bkl) + return 0; + td->bkl = 1; + lock_kernel(); + td->bkl = 4; + return 0; + + case RTTEST_UNLOCKBKL: + if (td->bkl != 4) + break; + unlock_kernel(); + td->bkl = 0; + return 0; + + default: + break; + } + return ret; +} + +/* + * Schedule replacement for rtsem_down(). Only called for threads with + * PF_MUTEX_TESTER set. + * + * This allows us to have finegrained control over the event flow. + * + */ +void schedule_rt_mutex_test(struct rt_mutex *mutex) +{ + int tid, op, dat; + struct test_thread_data *td; + + /* We have to lookup the task */ + for (tid = 0; tid < MAX_RT_TEST_THREADS; tid++) { + if (threads[tid] == current) + break; + } + + BUG_ON(tid == MAX_RT_TEST_THREADS); + + td = &thread_data[tid]; + + op = td->opcode; + dat = td->opdata; + + switch (op) { + case RTTEST_LOCK: + case RTTEST_LOCKINT: + case RTTEST_LOCKNOWAIT: + case RTTEST_LOCKINTNOWAIT: + if (mutex != &mutexes[dat]) + break; + + if (td->mutexes[dat] != 1) + break; + + td->mutexes[dat] = 2; + td->event = atomic_add_return(1, &rttest_event); + break; + + case RTTEST_LOCKBKL: + default: + break; + } + + schedule(); + + + switch (op) { + case RTTEST_LOCK: + case RTTEST_LOCKINT: + if (mutex != &mutexes[dat]) + return; + + if (td->mutexes[dat] != 2) + return; + + td->mutexes[dat] = 3; + td->event = atomic_add_return(1, &rttest_event); + break; + + case RTTEST_LOCKNOWAIT: + case RTTEST_LOCKINTNOWAIT: + if (mutex != &mutexes[dat]) + return; + + if (td->mutexes[dat] != 2) + return; + + td->mutexes[dat] = 1; + td->event = atomic_add_return(1, &rttest_event); + return; + + case RTTEST_LOCKBKL: + return; + default: + return; + } + + td->opcode = 0; + + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + + if (td->opcode > 0) { + int ret; + + set_current_state(TASK_RUNNING); + ret = handle_op(td, 1); + set_current_state(TASK_INTERRUPTIBLE); + if (td->opcode == RTTEST_LOCKCONT) + break; + td->opcode = ret; + } + + /* Wait for the next command to be executed */ + schedule(); + } + + /* Restore previous command and data */ + td->opcode = op; + td->opdata = dat; +} + +static int test_func(void *data) +{ + struct test_thread_data *td = data; + int ret; + + current->flags |= PF_MUTEX_TESTER; + allow_signal(SIGHUP); + + for(;;) { + + set_current_state(TASK_INTERRUPTIBLE); + + if (td->opcode > 0) { + set_current_state(TASK_RUNNING); + ret = handle_op(td, 0); + set_current_state(TASK_INTERRUPTIBLE); + td->opcode = ret; + } + + /* Wait for the next command to be executed */ + schedule(); + + if (signal_pending(current)) + flush_signals(current); + + if(kthread_should_stop()) + break; + } + return 0; +} + +/** + * sysfs_test_command - interface for test commands + * @dev: thread reference + * @buf: command for actual step + * @count: length of buffer + * + * command syntax: + * + * opcode:data + */ +static ssize_t sysfs_test_command(struct sys_device *dev, const char *buf, + size_t count) +{ + struct sched_param schedpar; + struct test_thread_data *td; + char cmdbuf[32]; + int op, dat, tid, ret; + + td = container_of(dev, struct test_thread_data, sysdev); + tid = td->sysdev.id; + + /* strings from sysfs write are not 0 terminated! */ + if (count >= sizeof(cmdbuf)) + return -EINVAL; + + /* strip of \n: */ + if (buf[count-1] == '\n') + count--; + if (count < 1) + return -EINVAL; + + memcpy(cmdbuf, buf, count); + cmdbuf[count] = 0; + + if (sscanf(cmdbuf, "%d:%d", &op, &dat) != 2) + return -EINVAL; + + switch (op) { + case RTTEST_SCHEDOT: + schedpar.sched_priority = 0; + ret = sched_setscheduler(threads[tid], SCHED_NORMAL, &schedpar); + if (ret) + return ret; + set_user_nice(current, 0); + break; + + case RTTEST_SCHEDRT: + schedpar.sched_priority = dat; + ret = sched_setscheduler(threads[tid], SCHED_FIFO, &schedpar); + if (ret) + return ret; + break; + + case RTTEST_SIGNAL: + send_sig(SIGHUP, threads[tid], 0); + break; + + default: + if (td->opcode > 0) + return -EBUSY; + td->opdata = dat; + td->opcode = op; + wake_up_process(threads[tid]); + } + + return count; +} + +/** + * sysfs_test_status - sysfs interface for rt tester + * @dev: thread to query + * @buf: char buffer to be filled with thread status info + */ +static ssize_t sysfs_test_status(struct sys_device *dev, char *buf) +{ + struct test_thread_data *td; + struct task_struct *tsk; + char *curr = buf; + int i; + + td = container_of(dev, struct test_thread_data, sysdev); + tsk = threads[td->sysdev.id]; + + spin_lock(&rttest_lock); + + curr += sprintf(curr, + "O: %4d, E:%8d, S: 0x%08lx, P: %4d, N: %4d, B: %p, K: %d, M:", + td->opcode, td->event, tsk->state, + (MAX_RT_PRIO - 1) - tsk->prio, + (MAX_RT_PRIO - 1) - tsk->normal_prio, + tsk->pi_blocked_on, td->bkl); + + for (i = MAX_RT_TEST_MUTEXES - 1; i >=0 ; i--) + curr += sprintf(curr, "%d", td->mutexes[i]); + + spin_unlock(&rttest_lock); + + curr += sprintf(curr, ", T: %p, R: %p\n", tsk, + mutexes[td->sysdev.id].owner); + + return curr - buf; +} + +static SYSDEV_ATTR(status, 0600, sysfs_test_status, NULL); +static SYSDEV_ATTR(command, 0600, NULL, sysfs_test_command); + +static struct sysdev_class rttest_sysclass = { + set_kset_name("rttest"), +}; + +static int init_test_thread(int id) +{ + thread_data[id].sysdev.cls = &rttest_sysclass; + thread_data[id].sysdev.id = id; + + threads[id] = kthread_run(test_func, &thread_data[id], "rt-test-%d", id); + if (IS_ERR(threads[id])) + return PTR_ERR(threads[id]); + + return sysdev_register(&thread_data[id].sysdev); +} + +static int init_rttest(void) +{ + int ret, i; + + spin_lock_init(&rttest_lock); + + for (i = 0; i < MAX_RT_TEST_MUTEXES; i++) + rt_mutex_init(&mutexes[i]); + + ret = sysdev_class_register(&rttest_sysclass); + if (ret) + return ret; + + for (i = 0; i < MAX_RT_TEST_THREADS; i++) { + ret = init_test_thread(i); + if (ret) + break; + ret = sysdev_create_file(&thread_data[i].sysdev, &attr_status); + if (ret) + break; + ret = sysdev_create_file(&thread_data[i].sysdev, &attr_command); + if (ret) + break; + } + + printk("Initializing RT-Tester: %s\n", ret ? "Failed" : "OK" ); + + return ret; +} + +device_initcall(init_rttest); diff --git a/kernel/rtmutex.c b/kernel/rtmutex.c new file mode 100644 index 0000000..d2ef13b --- /dev/null +++ b/kernel/rtmutex.c @@ -0,0 +1,989 @@ +/* + * RT-Mutexes: simple blocking mutual exclusion locks with PI support + * + * started by Ingo Molnar and Thomas Gleixner. + * + * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2005-2006 Timesys Corp., Thomas Gleixner + * Copyright (C) 2005 Kihon Technologies Inc., Steven Rostedt + * Copyright (C) 2006 Esben Nielsen + */ +#include +#include +#include +#include + +#include "rtmutex_common.h" + +#ifdef CONFIG_DEBUG_RT_MUTEXES +# include "rtmutex-debug.h" +#else +# include "rtmutex.h" +#endif + +/* + * lock->owner state tracking: + * + * lock->owner holds the task_struct pointer of the owner. Bit 0 and 1 + * are used to keep track of the "owner is pending" and "lock has + * waiters" state. + * + * owner bit1 bit0 + * NULL 0 0 lock is free (fast acquire possible) + * NULL 0 1 invalid state + * NULL 1 0 Transitional State* + * NULL 1 1 invalid state + * taskpointer 0 0 lock is held (fast release possible) + * taskpointer 0 1 task is pending owner + * taskpointer 1 0 lock is held and has waiters + * taskpointer 1 1 task is pending owner and lock has more waiters + * + * Pending ownership is assigned to the top (highest priority) + * waiter of the lock, when the lock is released. The thread is woken + * up and can now take the lock. Until the lock is taken (bit 0 + * cleared) a competing higher priority thread can steal the lock + * which puts the woken up thread back on the waiters list. + * + * The fast atomic compare exchange based acquire and release is only + * possible when bit 0 and 1 of lock->owner are 0. + * + * (*) There's a small time where the owner can be NULL and the + * "lock has waiters" bit is set. This can happen when grabbing the lock. + * To prevent a cmpxchg of the owner releasing the lock, we need to set this + * bit before looking at the lock, hence the reason this is a transitional + * state. + */ + +static void +rt_mutex_set_owner(struct rt_mutex *lock, struct task_struct *owner, + unsigned long mask) +{ + unsigned long val = (unsigned long)owner | mask; + + if (rt_mutex_has_waiters(lock)) + val |= RT_MUTEX_HAS_WAITERS; + + lock->owner = (struct task_struct *)val; +} + +static inline void clear_rt_mutex_waiters(struct rt_mutex *lock) +{ + lock->owner = (struct task_struct *) + ((unsigned long)lock->owner & ~RT_MUTEX_HAS_WAITERS); +} + +static void fixup_rt_mutex_waiters(struct rt_mutex *lock) +{ + if (!rt_mutex_has_waiters(lock)) + clear_rt_mutex_waiters(lock); +} + +/* + * We can speed up the acquire/release, if the architecture + * supports cmpxchg and if there's no debugging state to be set up + */ +#if defined(__HAVE_ARCH_CMPXCHG) && !defined(CONFIG_DEBUG_RT_MUTEXES) +# define rt_mutex_cmpxchg(l,c,n) (cmpxchg(&l->owner, c, n) == c) +static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) +{ + unsigned long owner, *p = (unsigned long *) &lock->owner; + + do { + owner = *p; + } while (cmpxchg(p, owner, owner | RT_MUTEX_HAS_WAITERS) != owner); +} +#else +# define rt_mutex_cmpxchg(l,c,n) (0) +static inline void mark_rt_mutex_waiters(struct rt_mutex *lock) +{ + lock->owner = (struct task_struct *) + ((unsigned long)lock->owner | RT_MUTEX_HAS_WAITERS); +} +#endif + +/* + * Calculate task priority from the waiter list priority + * + * Return task->normal_prio when the waiter list is empty or when + * the waiter is not allowed to do priority boosting + */ +int rt_mutex_getprio(struct task_struct *task) +{ + if (likely(!task_has_pi_waiters(task))) + return task->normal_prio; + + return min(task_top_pi_waiter(task)->pi_list_entry.prio, + task->normal_prio); +} + +/* + * Adjust the priority of a task, after its pi_waiters got modified. + * + * This can be both boosting and unboosting. task->pi_lock must be held. + */ +static void __rt_mutex_adjust_prio(struct task_struct *task) +{ + int prio = rt_mutex_getprio(task); + + if (task->prio != prio) + rt_mutex_setprio(task, prio); +} + +/* + * Adjust task priority (undo boosting). Called from the exit path of + * rt_mutex_slowunlock() and rt_mutex_slowlock(). + * + * (Note: We do this outside of the protection of lock->wait_lock to + * allow the lock to be taken while or before we readjust the priority + * of task. We do not use the spin_xx_mutex() variants here as we are + * outside of the debug path.) + */ +static void rt_mutex_adjust_prio(struct task_struct *task) +{ + unsigned long flags; + + spin_lock_irqsave(&task->pi_lock, flags); + __rt_mutex_adjust_prio(task); + spin_unlock_irqrestore(&task->pi_lock, flags); +} + +/* + * Max number of times we'll walk the boosting chain: + */ +int max_lock_depth = 1024; + +/* + * Adjust the priority chain. Also used for deadlock detection. + * Decreases task's usage by one - may thus free the task. + * Returns 0 or -EDEADLK. + */ +static int rt_mutex_adjust_prio_chain(struct task_struct *task, + int deadlock_detect, + struct rt_mutex *orig_lock, + struct rt_mutex_waiter *orig_waiter, + struct task_struct *top_task) +{ + struct rt_mutex *lock; + struct rt_mutex_waiter *waiter, *top_waiter = orig_waiter; + int detect_deadlock, ret = 0, depth = 0; + unsigned long flags; + + detect_deadlock = debug_rt_mutex_detect_deadlock(orig_waiter, + deadlock_detect); + + /* + * The (de)boosting is a step by step approach with a lot of + * pitfalls. We want this to be preemptible and we want hold a + * maximum of two locks per step. So we have to check + * carefully whether things change under us. + */ + again: + if (++depth > max_lock_depth) { + static int prev_max; + + /* + * Print this only once. If the admin changes the limit, + * print a new message when reaching the limit again. + */ + if (prev_max != max_lock_depth) { + prev_max = max_lock_depth; + printk(KERN_WARNING "Maximum lock depth %d reached " + "task: %s (%d)\n", max_lock_depth, + top_task->comm, top_task->pid); + } + put_task_struct(task); + + return deadlock_detect ? -EDEADLK : 0; + } + retry: + /* + * Task can not go away as we did a get_task() before ! + */ + spin_lock_irqsave(&task->pi_lock, flags); + + waiter = task->pi_blocked_on; + /* + * Check whether the end of the boosting chain has been + * reached or the state of the chain has changed while we + * dropped the locks. + */ + if (!waiter || !waiter->task) + goto out_unlock_pi; + + if (top_waiter && (!task_has_pi_waiters(task) || + top_waiter != task_top_pi_waiter(task))) + goto out_unlock_pi; + + /* + * When deadlock detection is off then we check, if further + * priority adjustment is necessary. + */ + if (!detect_deadlock && waiter->list_entry.prio == task->prio) + goto out_unlock_pi; + + lock = waiter->lock; + if (!spin_trylock(&lock->wait_lock)) { + spin_unlock_irqrestore(&task->pi_lock, flags); + cpu_relax(); + goto retry; + } + + /* Deadlock detection */ + if (lock == orig_lock || rt_mutex_owner(lock) == top_task) { + debug_rt_mutex_deadlock(deadlock_detect, orig_waiter, lock); + spin_unlock(&lock->wait_lock); + ret = deadlock_detect ? -EDEADLK : 0; + goto out_unlock_pi; + } + + top_waiter = rt_mutex_top_waiter(lock); + + /* Requeue the waiter */ + plist_del(&waiter->list_entry, &lock->wait_list); + waiter->list_entry.prio = task->prio; + plist_add(&waiter->list_entry, &lock->wait_list); + + /* Release the task */ + spin_unlock_irqrestore(&task->pi_lock, flags); + put_task_struct(task); + + /* Grab the next task */ + task = rt_mutex_owner(lock); + spin_lock_irqsave(&task->pi_lock, flags); + + if (waiter == rt_mutex_top_waiter(lock)) { + /* Boost the owner */ + plist_del(&top_waiter->pi_list_entry, &task->pi_waiters); + waiter->pi_list_entry.prio = waiter->list_entry.prio; + plist_add(&waiter->pi_list_entry, &task->pi_waiters); + __rt_mutex_adjust_prio(task); + + } else if (top_waiter == waiter) { + /* Deboost the owner */ + plist_del(&waiter->pi_list_entry, &task->pi_waiters); + waiter = rt_mutex_top_waiter(lock); + waiter->pi_list_entry.prio = waiter->list_entry.prio; + plist_add(&waiter->pi_list_entry, &task->pi_waiters); + __rt_mutex_adjust_prio(task); + } + + get_task_struct(task); + spin_unlock_irqrestore(&task->pi_lock, flags); + + top_waiter = rt_mutex_top_waiter(lock); + spin_unlock(&lock->wait_lock); + + if (!detect_deadlock && waiter != top_waiter) + goto out_put_task; + + goto again; + + out_unlock_pi: + spin_unlock_irqrestore(&task->pi_lock, flags); + out_put_task: + put_task_struct(task); + + return ret; +} + +/* + * Optimization: check if we can steal the lock from the + * assigned pending owner [which might not have taken the + * lock yet]: + */ +static inline int try_to_steal_lock(struct rt_mutex *lock) +{ + struct task_struct *pendowner = rt_mutex_owner(lock); + struct rt_mutex_waiter *next; + unsigned long flags; + + if (!rt_mutex_owner_pending(lock)) + return 0; + + if (pendowner == current) + return 1; + + spin_lock_irqsave(&pendowner->pi_lock, flags); + if (current->prio >= pendowner->prio) { + spin_unlock_irqrestore(&pendowner->pi_lock, flags); + return 0; + } + + /* + * Check if a waiter is enqueued on the pending owners + * pi_waiters list. Remove it and readjust pending owners + * priority. + */ + if (likely(!rt_mutex_has_waiters(lock))) { + spin_unlock_irqrestore(&pendowner->pi_lock, flags); + return 1; + } + + /* No chain handling, pending owner is not blocked on anything: */ + next = rt_mutex_top_waiter(lock); + plist_del(&next->pi_list_entry, &pendowner->pi_waiters); + __rt_mutex_adjust_prio(pendowner); + spin_unlock_irqrestore(&pendowner->pi_lock, flags); + + /* + * We are going to steal the lock and a waiter was + * enqueued on the pending owners pi_waiters queue. So + * we have to enqueue this waiter into + * current->pi_waiters list. This covers the case, + * where current is boosted because it holds another + * lock and gets unboosted because the booster is + * interrupted, so we would delay a waiter with higher + * priority as current->normal_prio. + * + * Note: in the rare case of a SCHED_OTHER task changing + * its priority and thus stealing the lock, next->task + * might be current: + */ + if (likely(next->task != current)) { + spin_lock_irqsave(¤t->pi_lock, flags); + plist_add(&next->pi_list_entry, ¤t->pi_waiters); + __rt_mutex_adjust_prio(current); + spin_unlock_irqrestore(¤t->pi_lock, flags); + } + return 1; +} + +/* + * Try to take an rt-mutex + * + * This fails + * - when the lock has a real owner + * - when a different pending owner exists and has higher priority than current + * + * Must be called with lock->wait_lock held. + */ +static int try_to_take_rt_mutex(struct rt_mutex *lock) +{ + /* + * We have to be careful here if the atomic speedups are + * enabled, such that, when + * - no other waiter is on the lock + * - the lock has been released since we did the cmpxchg + * the lock can be released or taken while we are doing the + * checks and marking the lock with RT_MUTEX_HAS_WAITERS. + * + * The atomic acquire/release aware variant of + * mark_rt_mutex_waiters uses a cmpxchg loop. After setting + * the WAITERS bit, the atomic release / acquire can not + * happen anymore and lock->wait_lock protects us from the + * non-atomic case. + * + * Note, that this might set lock->owner = + * RT_MUTEX_HAS_WAITERS in the case the lock is not contended + * any more. This is fixed up when we take the ownership. + * This is the transitional state explained at the top of this file. + */ + mark_rt_mutex_waiters(lock); + + if (rt_mutex_owner(lock) && !try_to_steal_lock(lock)) + return 0; + + /* We got the lock. */ + debug_rt_mutex_lock(lock); + + rt_mutex_set_owner(lock, current, 0); + + rt_mutex_deadlock_account_lock(lock, current); + + return 1; +} + +/* + * Task blocks on lock. + * + * Prepare waiter and propagate pi chain + * + * This must be called with lock->wait_lock held. + */ +static int task_blocks_on_rt_mutex(struct rt_mutex *lock, + struct rt_mutex_waiter *waiter, + int detect_deadlock) +{ + struct task_struct *owner = rt_mutex_owner(lock); + struct rt_mutex_waiter *top_waiter = waiter; + unsigned long flags; + int boost = 0, res; + + spin_lock_irqsave(¤t->pi_lock, flags); + __rt_mutex_adjust_prio(current); + waiter->task = current; + waiter->lock = lock; + plist_node_init(&waiter->list_entry, current->prio); + plist_node_init(&waiter->pi_list_entry, current->prio); + + /* Get the top priority waiter on the lock */ + if (rt_mutex_has_waiters(lock)) + top_waiter = rt_mutex_top_waiter(lock); + plist_add(&waiter->list_entry, &lock->wait_list); + + current->pi_blocked_on = waiter; + + spin_unlock_irqrestore(¤t->pi_lock, flags); + + if (waiter == rt_mutex_top_waiter(lock)) { + spin_lock_irqsave(&owner->pi_lock, flags); + plist_del(&top_waiter->pi_list_entry, &owner->pi_waiters); + plist_add(&waiter->pi_list_entry, &owner->pi_waiters); + + __rt_mutex_adjust_prio(owner); + if (owner->pi_blocked_on) { + boost = 1; + /* gets dropped in rt_mutex_adjust_prio_chain()! */ + get_task_struct(owner); + } + spin_unlock_irqrestore(&owner->pi_lock, flags); + } + else if (debug_rt_mutex_detect_deadlock(waiter, detect_deadlock)) { + spin_lock_irqsave(&owner->pi_lock, flags); + if (owner->pi_blocked_on) { + boost = 1; + /* gets dropped in rt_mutex_adjust_prio_chain()! */ + get_task_struct(owner); + } + spin_unlock_irqrestore(&owner->pi_lock, flags); + } + if (!boost) + return 0; + + spin_unlock(&lock->wait_lock); + + res = rt_mutex_adjust_prio_chain(owner, detect_deadlock, lock, waiter, + current); + + spin_lock(&lock->wait_lock); + + return res; +} + +/* + * Wake up the next waiter on the lock. + * + * Remove the top waiter from the current tasks waiter list and from + * the lock waiter list. Set it as pending owner. Then wake it up. + * + * Called with lock->wait_lock held. + */ +static void wakeup_next_waiter(struct rt_mutex *lock) +{ + struct rt_mutex_waiter *waiter; + struct task_struct *pendowner; + unsigned long flags; + + spin_lock_irqsave(¤t->pi_lock, flags); + + waiter = rt_mutex_top_waiter(lock); + plist_del(&waiter->list_entry, &lock->wait_list); + + /* + * Remove it from current->pi_waiters. We do not adjust a + * possible priority boost right now. We execute wakeup in the + * boosted mode and go back to normal after releasing + * lock->wait_lock. + */ + plist_del(&waiter->pi_list_entry, ¤t->pi_waiters); + pendowner = waiter->task; + waiter->task = NULL; + + rt_mutex_set_owner(lock, pendowner, RT_MUTEX_OWNER_PENDING); + + spin_unlock_irqrestore(¤t->pi_lock, flags); + + /* + * Clear the pi_blocked_on variable and enqueue a possible + * waiter into the pi_waiters list of the pending owner. This + * prevents that in case the pending owner gets unboosted a + * waiter with higher priority than pending-owner->normal_prio + * is blocked on the unboosted (pending) owner. + */ + spin_lock_irqsave(&pendowner->pi_lock, flags); + + WARN_ON(!pendowner->pi_blocked_on); + WARN_ON(pendowner->pi_blocked_on != waiter); + WARN_ON(pendowner->pi_blocked_on->lock != lock); + + pendowner->pi_blocked_on = NULL; + + if (rt_mutex_has_waiters(lock)) { + struct rt_mutex_waiter *next; + + next = rt_mutex_top_waiter(lock); + plist_add(&next->pi_list_entry, &pendowner->pi_waiters); + } + spin_unlock_irqrestore(&pendowner->pi_lock, flags); + + wake_up_process(pendowner); +} + +/* + * Remove a waiter from a lock + * + * Must be called with lock->wait_lock held + */ +static void remove_waiter(struct rt_mutex *lock, + struct rt_mutex_waiter *waiter) +{ + int first = (waiter == rt_mutex_top_waiter(lock)); + struct task_struct *owner = rt_mutex_owner(lock); + unsigned long flags; + int boost = 0; + + spin_lock_irqsave(¤t->pi_lock, flags); + plist_del(&waiter->list_entry, &lock->wait_list); + waiter->task = NULL; + current->pi_blocked_on = NULL; + spin_unlock_irqrestore(¤t->pi_lock, flags); + + if (first && owner != current) { + + spin_lock_irqsave(&owner->pi_lock, flags); + + plist_del(&waiter->pi_list_entry, &owner->pi_waiters); + + if (rt_mutex_has_waiters(lock)) { + struct rt_mutex_waiter *next; + + next = rt_mutex_top_waiter(lock); + plist_add(&next->pi_list_entry, &owner->pi_waiters); + } + __rt_mutex_adjust_prio(owner); + + if (owner->pi_blocked_on) { + boost = 1; + /* gets dropped in rt_mutex_adjust_prio_chain()! */ + get_task_struct(owner); + } + spin_unlock_irqrestore(&owner->pi_lock, flags); + } + + WARN_ON(!plist_node_empty(&waiter->pi_list_entry)); + + if (!boost) + return; + + spin_unlock(&lock->wait_lock); + + rt_mutex_adjust_prio_chain(owner, 0, lock, NULL, current); + + spin_lock(&lock->wait_lock); +} + +/* + * Recheck the pi chain, in case we got a priority setting + * + * Called from sched_setscheduler + */ +void rt_mutex_adjust_pi(struct task_struct *task) +{ + struct rt_mutex_waiter *waiter; + unsigned long flags; + + spin_lock_irqsave(&task->pi_lock, flags); + + waiter = task->pi_blocked_on; + if (!waiter || waiter->list_entry.prio == task->prio) { + spin_unlock_irqrestore(&task->pi_lock, flags); + return; + } + + /* gets dropped in rt_mutex_adjust_prio_chain()! */ + get_task_struct(task); + spin_unlock_irqrestore(&task->pi_lock, flags); + + rt_mutex_adjust_prio_chain(task, 0, NULL, NULL, task); +} + +/* + * Slow path lock function: + */ +static int __sched +rt_mutex_slowlock(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, + int detect_deadlock) +{ + struct rt_mutex_waiter waiter; + int ret = 0; + + debug_rt_mutex_init_waiter(&waiter); + waiter.task = NULL; + + spin_lock(&lock->wait_lock); + + /* Try to acquire the lock again: */ + if (try_to_take_rt_mutex(lock)) { + spin_unlock(&lock->wait_lock); + return 0; + } + + set_current_state(state); + + /* Setup the timer, when timeout != NULL */ + if (unlikely(timeout)) + hrtimer_start(&timeout->timer, timeout->timer.expires, + HRTIMER_ABS); + + for (;;) { + /* Try to acquire the lock: */ + if (try_to_take_rt_mutex(lock)) + break; + + /* + * TASK_INTERRUPTIBLE checks for signals and + * timeout. Ignored otherwise. + */ + if (unlikely(state == TASK_INTERRUPTIBLE)) { + /* Signal pending? */ + if (signal_pending(current)) + ret = -EINTR; + if (timeout && !timeout->task) + ret = -ETIMEDOUT; + if (ret) + break; + } + + /* + * waiter.task is NULL the first time we come here and + * when we have been woken up by the previous owner + * but the lock got stolen by a higher prio task. + */ + if (!waiter.task) { + ret = task_blocks_on_rt_mutex(lock, &waiter, + detect_deadlock); + /* + * If we got woken up by the owner then start loop + * all over without going into schedule to try + * to get the lock now: + */ + if (unlikely(!waiter.task)) + continue; + + if (unlikely(ret)) + break; + } + + spin_unlock(&lock->wait_lock); + + debug_rt_mutex_print_deadlock(&waiter); + + if (waiter.task) + schedule_rt_mutex(lock); + + spin_lock(&lock->wait_lock); + set_current_state(state); + } + + set_current_state(TASK_RUNNING); + + if (unlikely(waiter.task)) + remove_waiter(lock, &waiter); + + /* + * try_to_take_rt_mutex() sets the waiter bit + * unconditionally. We might have to fix that up. + */ + fixup_rt_mutex_waiters(lock); + + spin_unlock(&lock->wait_lock); + + /* Remove pending timer: */ + if (unlikely(timeout)) + hrtimer_cancel(&timeout->timer); + + /* + * Readjust priority, when we did not get the lock. We might + * have been the pending owner and boosted. Since we did not + * take the lock, the PI boost has to go. + */ + if (unlikely(ret)) + rt_mutex_adjust_prio(current); + + debug_rt_mutex_free_waiter(&waiter); + + return ret; +} + +/* + * Slow path try-lock function: + */ +static inline int +rt_mutex_slowtrylock(struct rt_mutex *lock) +{ + int ret = 0; + + spin_lock(&lock->wait_lock); + + if (likely(rt_mutex_owner(lock) != current)) { + + ret = try_to_take_rt_mutex(lock); + /* + * try_to_take_rt_mutex() sets the lock waiters + * bit unconditionally. Clean this up. + */ + fixup_rt_mutex_waiters(lock); + } + + spin_unlock(&lock->wait_lock); + + return ret; +} + +/* + * Slow path to release a rt-mutex: + */ +static void __sched +rt_mutex_slowunlock(struct rt_mutex *lock) +{ + spin_lock(&lock->wait_lock); + + debug_rt_mutex_unlock(lock); + + rt_mutex_deadlock_account_unlock(current); + + if (!rt_mutex_has_waiters(lock)) { + lock->owner = NULL; + spin_unlock(&lock->wait_lock); + return; + } + + wakeup_next_waiter(lock); + + spin_unlock(&lock->wait_lock); + + /* Undo pi boosting if necessary: */ + rt_mutex_adjust_prio(current); +} + +/* + * debug aware fast / slowpath lock,trylock,unlock + * + * The atomic acquire/release ops are compiled away, when either the + * architecture does not support cmpxchg or when debugging is enabled. + */ +static inline int +rt_mutex_fastlock(struct rt_mutex *lock, int state, + int detect_deadlock, + int (*slowfn)(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, + int detect_deadlock)) +{ + if (!detect_deadlock && likely(rt_mutex_cmpxchg(lock, NULL, current))) { + rt_mutex_deadlock_account_lock(lock, current); + return 0; + } else + return slowfn(lock, state, NULL, detect_deadlock); +} + +static inline int +rt_mutex_timed_fastlock(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, int detect_deadlock, + int (*slowfn)(struct rt_mutex *lock, int state, + struct hrtimer_sleeper *timeout, + int detect_deadlock)) +{ + if (!detect_deadlock && likely(rt_mutex_cmpxchg(lock, NULL, current))) { + rt_mutex_deadlock_account_lock(lock, current); + return 0; + } else + return slowfn(lock, state, timeout, detect_deadlock); +} + +static inline int +rt_mutex_fasttrylock(struct rt_mutex *lock, + int (*slowfn)(struct rt_mutex *lock)) +{ + if (likely(rt_mutex_cmpxchg(lock, NULL, current))) { + rt_mutex_deadlock_account_lock(lock, current); + return 1; + } + return slowfn(lock); +} + +static inline void +rt_mutex_fastunlock(struct rt_mutex *lock, + void (*slowfn)(struct rt_mutex *lock)) +{ + if (likely(rt_mutex_cmpxchg(lock, current, NULL))) + rt_mutex_deadlock_account_unlock(current); + else + slowfn(lock); +} + +/** + * rt_mutex_lock - lock a rt_mutex + * + * @lock: the rt_mutex to be locked + */ +void __sched rt_mutex_lock(struct rt_mutex *lock) +{ + might_sleep(); + + rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, 0, rt_mutex_slowlock); +} +EXPORT_SYMBOL_GPL(rt_mutex_lock); + +/** + * rt_mutex_lock_interruptible - lock a rt_mutex interruptible + * + * @lock: the rt_mutex to be locked + * @detect_deadlock: deadlock detection on/off + * + * Returns: + * 0 on success + * -EINTR when interrupted by a signal + * -EDEADLK when the lock would deadlock (when deadlock detection is on) + */ +int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock, + int detect_deadlock) +{ + might_sleep(); + + return rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, + detect_deadlock, rt_mutex_slowlock); +} +EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible); + +/** + * rt_mutex_lock_interruptible_ktime - lock a rt_mutex interruptible + * the timeout structure is provided + * by the caller + * + * @lock: the rt_mutex to be locked + * @timeout: timeout structure or NULL (no timeout) + * @detect_deadlock: deadlock detection on/off + * + * Returns: + * 0 on success + * -EINTR when interrupted by a signal + * -ETIMEOUT when the timeout expired + * -EDEADLK when the lock would deadlock (when deadlock detection is on) + */ +int +rt_mutex_timed_lock(struct rt_mutex *lock, struct hrtimer_sleeper *timeout, + int detect_deadlock) +{ + might_sleep(); + + return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout, + detect_deadlock, rt_mutex_slowlock); +} +EXPORT_SYMBOL_GPL(rt_mutex_timed_lock); + +/** + * rt_mutex_trylock - try to lock a rt_mutex + * + * @lock: the rt_mutex to be locked + * + * Returns 1 on success and 0 on contention + */ +int __sched rt_mutex_trylock(struct rt_mutex *lock) +{ + return rt_mutex_fasttrylock(lock, rt_mutex_slowtrylock); +} +EXPORT_SYMBOL_GPL(rt_mutex_trylock); + +/** + * rt_mutex_unlock - unlock a rt_mutex + * + * @lock: the rt_mutex to be unlocked + */ +void __sched rt_mutex_unlock(struct rt_mutex *lock) +{ + rt_mutex_fastunlock(lock, rt_mutex_slowunlock); +} +EXPORT_SYMBOL_GPL(rt_mutex_unlock); + +/*** + * rt_mutex_destroy - mark a mutex unusable + * @lock: the mutex to be destroyed + * + * This function marks the mutex uninitialized, and any subsequent + * use of the mutex is forbidden. The mutex must not be locked when + * this function is called. + */ +void rt_mutex_destroy(struct rt_mutex *lock) +{ + WARN_ON(rt_mutex_is_locked(lock)); +#ifdef CONFIG_DEBUG_RT_MUTEXES + lock->magic = NULL; +#endif +} + +EXPORT_SYMBOL_GPL(rt_mutex_destroy); + +/** + * __rt_mutex_init - initialize the rt lock + * + * @lock: the rt lock to be initialized + * + * Initialize the rt lock to unlocked state. + * + * Initializing of a locked rt lock is not allowed + */ +void __rt_mutex_init(struct rt_mutex *lock, const char *name) +{ + lock->owner = NULL; + spin_lock_init(&lock->wait_lock); + plist_head_init(&lock->wait_list, &lock->wait_lock); + + debug_rt_mutex_init(lock, name); +} +EXPORT_SYMBOL_GPL(__rt_mutex_init); + +/** + * rt_mutex_init_proxy_locked - initialize and lock a rt_mutex on behalf of a + * proxy owner + * + * @lock: the rt_mutex to be locked + * @proxy_owner:the task to set as owner + * + * No locking. Caller has to do serializing itself + * Special API call for PI-futex support + */ +void rt_mutex_init_proxy_locked(struct rt_mutex *lock, + struct task_struct *proxy_owner) +{ + __rt_mutex_init(lock, NULL); + debug_rt_mutex_proxy_lock(lock, proxy_owner); + rt_mutex_set_owner(lock, proxy_owner, 0); + rt_mutex_deadlock_account_lock(lock, proxy_owner); +} + +/** + * rt_mutex_proxy_unlock - release a lock on behalf of owner + * + * @lock: the rt_mutex to be locked + * + * No locking. Caller has to do serializing itself + * Special API call for PI-futex support + */ +void rt_mutex_proxy_unlock(struct rt_mutex *lock, + struct task_struct *proxy_owner) +{ + debug_rt_mutex_proxy_unlock(lock); + rt_mutex_set_owner(lock, NULL, 0); + rt_mutex_deadlock_account_unlock(proxy_owner); +} + +/** + * rt_mutex_next_owner - return the next owner of the lock + * + * @lock: the rt lock query + * + * Returns the next owner of the lock or NULL + * + * Caller has to serialize against other accessors to the lock + * itself. + * + * Special API call for PI-futex support + */ +struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock) +{ + if (!rt_mutex_has_waiters(lock)) + return NULL; + + return rt_mutex_top_waiter(lock)->task; +} diff --git a/kernel/rtmutex.h b/kernel/rtmutex.h new file mode 100644 index 0000000..a1a1dd0 --- /dev/null +++ b/kernel/rtmutex.h @@ -0,0 +1,26 @@ +/* + * RT-Mutexes: blocking mutual exclusion locks with PI support + * + * started by Ingo Molnar and Thomas Gleixner: + * + * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2006, Timesys Corp., Thomas Gleixner + * + * This file contains macros used solely by rtmutex.c. + * Non-debug version. + */ + +#define rt_mutex_deadlock_check(l) (0) +#define rt_mutex_deadlock_account_lock(m, t) do { } while (0) +#define rt_mutex_deadlock_account_unlock(l) do { } while (0) +#define debug_rt_mutex_init_waiter(w) do { } while (0) +#define debug_rt_mutex_free_waiter(w) do { } while (0) +#define debug_rt_mutex_lock(l) do { } while (0) +#define debug_rt_mutex_proxy_lock(l,p) do { } while (0) +#define debug_rt_mutex_proxy_unlock(l) do { } while (0) +#define debug_rt_mutex_unlock(l) do { } while (0) +#define debug_rt_mutex_init(m, n) do { } while (0) +#define debug_rt_mutex_deadlock(d, a ,l) do { } while (0) +#define debug_rt_mutex_print_deadlock(w) do { } while (0) +#define debug_rt_mutex_detect_deadlock(w,d) (d) +#define debug_rt_mutex_reset_waiter(w) do { } while (0) diff --git a/kernel/rtmutex_common.h b/kernel/rtmutex_common.h new file mode 100644 index 0000000..9c75856 --- /dev/null +++ b/kernel/rtmutex_common.h @@ -0,0 +1,123 @@ +/* + * RT Mutexes: blocking mutual exclusion locks with PI support + * + * started by Ingo Molnar and Thomas Gleixner: + * + * Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2006, Timesys Corp., Thomas Gleixner + * + * This file contains the private data structure and API definitions. + */ + +#ifndef __KERNEL_RTMUTEX_COMMON_H +#define __KERNEL_RTMUTEX_COMMON_H + +#include + +/* + * The rtmutex in kernel tester is independent of rtmutex debugging. We + * call schedule_rt_mutex_test() instead of schedule() for the tasks which + * belong to the tester. That way we can delay the wakeup path of those + * threads to provoke lock stealing and testing of complex boosting scenarios. + */ +#ifdef CONFIG_RT_MUTEX_TESTER + +extern void schedule_rt_mutex_test(struct rt_mutex *lock); + +#define schedule_rt_mutex(_lock) \ + do { \ + if (!(current->flags & PF_MUTEX_TESTER)) \ + schedule(); \ + else \ + schedule_rt_mutex_test(_lock); \ + } while (0) + +#else +# define schedule_rt_mutex(_lock) schedule() +#endif + +/* + * This is the control structure for tasks blocked on a rt_mutex, + * which is allocated on the kernel stack on of the blocked task. + * + * @list_entry: pi node to enqueue into the mutex waiters list + * @pi_list_entry: pi node to enqueue into the mutex owner waiters list + * @task: task reference to the blocked task + */ +struct rt_mutex_waiter { + struct plist_node list_entry; + struct plist_node pi_list_entry; + struct task_struct *task; + struct rt_mutex *lock; +#ifdef CONFIG_DEBUG_RT_MUTEXES + unsigned long ip; + pid_t deadlock_task_pid; + struct rt_mutex *deadlock_lock; +#endif +}; + +/* + * Various helpers to access the waiters-plist: + */ +static inline int rt_mutex_has_waiters(struct rt_mutex *lock) +{ + return !plist_head_empty(&lock->wait_list); +} + +static inline struct rt_mutex_waiter * +rt_mutex_top_waiter(struct rt_mutex *lock) +{ + struct rt_mutex_waiter *w; + + w = plist_first_entry(&lock->wait_list, struct rt_mutex_waiter, + list_entry); + BUG_ON(w->lock != lock); + + return w; +} + +static inline int task_has_pi_waiters(struct task_struct *p) +{ + return !plist_head_empty(&p->pi_waiters); +} + +static inline struct rt_mutex_waiter * +task_top_pi_waiter(struct task_struct *p) +{ + return plist_first_entry(&p->pi_waiters, struct rt_mutex_waiter, + pi_list_entry); +} + +/* + * lock->owner state tracking: + */ +#define RT_MUTEX_OWNER_PENDING 1UL +#define RT_MUTEX_HAS_WAITERS 2UL +#define RT_MUTEX_OWNER_MASKALL 3UL + +static inline struct task_struct *rt_mutex_owner(struct rt_mutex *lock) +{ + return (struct task_struct *) + ((unsigned long)lock->owner & ~RT_MUTEX_OWNER_MASKALL); +} + +static inline struct task_struct *rt_mutex_real_owner(struct rt_mutex *lock) +{ + return (struct task_struct *) + ((unsigned long)lock->owner & ~RT_MUTEX_HAS_WAITERS); +} + +static inline unsigned long rt_mutex_owner_pending(struct rt_mutex *lock) +{ + return (unsigned long)lock->owner & RT_MUTEX_OWNER_PENDING; +} + +/* + * PI-futex support (proxy locking functions, etc.): + */ +extern struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock); +extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock, + struct task_struct *proxy_owner); +extern void rt_mutex_proxy_unlock(struct rt_mutex *lock, + struct task_struct *proxy_owner); +#endif diff --git a/kernel/rwsem.c b/kernel/rwsem.c new file mode 100644 index 0000000..291ded5 --- /dev/null +++ b/kernel/rwsem.c @@ -0,0 +1,147 @@ +/* kernel/rwsem.c: R/W semaphores, public implementation + * + * Written by David Howells (dhowells@redhat.com). + * Derived from asm-i386/semaphore.h + */ + +#include +#include +#include +#include + +#include +#include + +/* + * lock for reading + */ +void down_read(struct rw_semaphore *sem) +{ + might_sleep(); + rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_); + + __down_read(sem); +} + +EXPORT_SYMBOL(down_read); + +/* + * trylock for reading -- returns 1 if successful, 0 if contention + */ +int down_read_trylock(struct rw_semaphore *sem) +{ + int ret = __down_read_trylock(sem); + + if (ret == 1) + rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_); + return ret; +} + +EXPORT_SYMBOL(down_read_trylock); + +/* + * lock for writing + */ +void down_write(struct rw_semaphore *sem) +{ + might_sleep(); + rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); + + __down_write(sem); +} + +EXPORT_SYMBOL(down_write); + +/* + * trylock for writing -- returns 1 if successful, 0 if contention + */ +int down_write_trylock(struct rw_semaphore *sem) +{ + int ret = __down_write_trylock(sem); + + if (ret == 1) + rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); + return ret; +} + +EXPORT_SYMBOL(down_write_trylock); + +/* + * release a read lock + */ +void up_read(struct rw_semaphore *sem) +{ + rwsem_release(&sem->dep_map, 1, _RET_IP_); + + __up_read(sem); +} + +EXPORT_SYMBOL(up_read); + +/* + * release a write lock + */ +void up_write(struct rw_semaphore *sem) +{ + rwsem_release(&sem->dep_map, 1, _RET_IP_); + + __up_write(sem); +} + +EXPORT_SYMBOL(up_write); + +/* + * downgrade write lock to read lock + */ +void downgrade_write(struct rw_semaphore *sem) +{ + /* + * lockdep: a downgraded write will live on as a write + * dependency. + */ + __downgrade_write(sem); +} + +EXPORT_SYMBOL(downgrade_write); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +void down_read_nested(struct rw_semaphore *sem, int subclass) +{ + might_sleep(); + rwsem_acquire_read(&sem->dep_map, subclass, 0, _RET_IP_); + + __down_read(sem); +} + +EXPORT_SYMBOL(down_read_nested); + +void down_read_non_owner(struct rw_semaphore *sem) +{ + might_sleep(); + + __down_read(sem); +} + +EXPORT_SYMBOL(down_read_non_owner); + +void down_write_nested(struct rw_semaphore *sem, int subclass) +{ + might_sleep(); + rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_); + + __down_write_nested(sem, subclass); +} + +EXPORT_SYMBOL(down_write_nested); + +void up_read_non_owner(struct rw_semaphore *sem) +{ + __up_read(sem); +} + +EXPORT_SYMBOL(up_read_non_owner); + +#endif + + diff --git a/kernel/sched.c b/kernel/sched.c index c13f1bd..4ee400f 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -30,6 +30,7 @@ #include #include #include #include +#include #include #include #include @@ -168,29 +169,28 @@ #define TASK_PREEMPTS_CURR(p, rq) \ */ #define SCALE_PRIO(x, prio) \ - max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO/2), MIN_TIMESLICE) + max(x * (MAX_PRIO - prio) / (MAX_USER_PRIO / 2), MIN_TIMESLICE) -static unsigned int task_timeslice(task_t *p) +static unsigned int static_prio_timeslice(int static_prio) { - if (p->static_prio < NICE_TO_PRIO(0)) - return SCALE_PRIO(DEF_TIMESLICE*4, p->static_prio); + if (static_prio < NICE_TO_PRIO(0)) + return SCALE_PRIO(DEF_TIMESLICE * 4, static_prio); else - return SCALE_PRIO(DEF_TIMESLICE, p->static_prio); + return SCALE_PRIO(DEF_TIMESLICE, static_prio); +} + +static inline unsigned int task_timeslice(struct task_struct *p) +{ + return static_prio_timeslice(p->static_prio); } -#define task_hot(p, now, sd) ((long long) ((now) - (p)->last_ran) \ - < (long long) (sd)->cache_hot_time) /* * These are the runqueue data structures: */ -#define BITMAP_SIZE ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long)) - -typedef struct runqueue runqueue_t; - struct prio_array { unsigned int nr_active; - unsigned long bitmap[BITMAP_SIZE]; + DECLARE_BITMAP(bitmap, MAX_PRIO+1); /* include 1 bit for delimiter */ struct list_head queue[MAX_PRIO]; }; @@ -201,7 +201,7 @@ struct prio_array { * (such as the load balancing or the thread migration code), lock * acquire operations must be ordered by ascending &runqueue. */ -struct runqueue { +struct rq { spinlock_t lock; /* @@ -209,6 +209,7 @@ struct runqueue { * remote CPUs use both these fields when doing load calculation. */ unsigned long nr_running; + unsigned long raw_weighted_load; #ifdef CONFIG_SMP unsigned long cpu_load[3]; #endif @@ -224,9 +225,9 @@ #endif unsigned long expired_timestamp; unsigned long long timestamp_last_tick; - task_t *curr, *idle; + struct task_struct *curr, *idle; struct mm_struct *prev_mm; - prio_array_t *active, *expired, arrays[2]; + struct prio_array *active, *expired, arrays[2]; int best_expired_prio; atomic_t nr_iowait; @@ -237,9 +238,8 @@ #ifdef CONFIG_SMP int active_balance; int push_cpu; - task_t *migration_thread; + struct task_struct *migration_thread; struct list_head migration_queue; - int cpu; #endif #ifdef CONFIG_SCHEDSTATS @@ -261,9 +261,10 @@ #ifdef CONFIG_SCHEDSTATS unsigned long ttwu_cnt; unsigned long ttwu_local; #endif + struct lock_class_key rq_lock_key; }; -static DEFINE_PER_CPU(struct runqueue, runqueues); +static DEFINE_PER_CPU(struct rq, runqueues); /* * The domain tree (rq->sd) is protected by RCU's quiescent state transition. @@ -272,8 +273,8 @@ static DEFINE_PER_CPU(struct runqueue, r * The domain tree of any CPU may only be accessed from within * preempt-disabled sections. */ -#define for_each_domain(cpu, domain) \ -for (domain = rcu_dereference(cpu_rq(cpu)->sd); domain; domain = domain->parent) +#define for_each_domain(cpu, __sd) \ + for (__sd = rcu_dereference(cpu_rq(cpu)->sd); __sd; __sd = __sd->parent) #define cpu_rq(cpu) (&per_cpu(runqueues, (cpu))) #define this_rq() (&__get_cpu_var(runqueues)) @@ -288,26 +289,33 @@ # define finish_arch_switch(prev) do { } #endif #ifndef __ARCH_WANT_UNLOCKED_CTXSW -static inline int task_running(runqueue_t *rq, task_t *p) +static inline int task_running(struct rq *rq, struct task_struct *p) { return rq->curr == p; } -static inline void prepare_lock_switch(runqueue_t *rq, task_t *next) +static inline void prepare_lock_switch(struct rq *rq, struct task_struct *next) { } -static inline void finish_lock_switch(runqueue_t *rq, task_t *prev) +static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) { #ifdef CONFIG_DEBUG_SPINLOCK /* this is a valid case when another task releases the spinlock */ rq->lock.owner = current; #endif + /* + * If we are tracking spinlock dependencies then we have to + * fix up the runqueue lock - which gets 'carried over' from + * prev into current: + */ + spin_acquire(&rq->lock.dep_map, 0, 0, _THIS_IP_); + spin_unlock_irq(&rq->lock); } #else /* __ARCH_WANT_UNLOCKED_CTXSW */ -static inline int task_running(runqueue_t *rq, task_t *p) +static inline int task_running(struct rq *rq, struct task_struct *p) { #ifdef CONFIG_SMP return p->oncpu; @@ -316,7 +324,7 @@ #else #endif } -static inline void prepare_lock_switch(runqueue_t *rq, task_t *next) +static inline void prepare_lock_switch(struct rq *rq, struct task_struct *next) { #ifdef CONFIG_SMP /* @@ -333,7 +341,7 @@ #else #endif } -static inline void finish_lock_switch(runqueue_t *rq, task_t *prev) +static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev) { #ifdef CONFIG_SMP /* @@ -351,14 +359,33 @@ #endif #endif /* __ARCH_WANT_UNLOCKED_CTXSW */ /* + * __task_rq_lock - lock the runqueue a given task resides on. + * Must be called interrupts disabled. + */ +static inline struct rq *__task_rq_lock(struct task_struct *p) + __acquires(rq->lock) +{ + struct rq *rq; + +repeat_lock_task: + rq = task_rq(p); + spin_lock(&rq->lock); + if (unlikely(rq != task_rq(p))) { + spin_unlock(&rq->lock); + goto repeat_lock_task; + } + return rq; +} + +/* * task_rq_lock - lock the runqueue a given task resides on and disable * interrupts. Note the ordering: we can safely lookup the task_rq without * explicitly disabling preemption. */ -static inline runqueue_t *task_rq_lock(task_t *p, unsigned long *flags) +static struct rq *task_rq_lock(struct task_struct *p, unsigned long *flags) __acquires(rq->lock) { - struct runqueue *rq; + struct rq *rq; repeat_lock_task: local_irq_save(*flags); @@ -371,7 +398,13 @@ repeat_lock_task: return rq; } -static inline void task_rq_unlock(runqueue_t *rq, unsigned long *flags) +static inline void __task_rq_unlock(struct rq *rq) + __releases(rq->lock) +{ + spin_unlock(&rq->lock); +} + +static inline void task_rq_unlock(struct rq *rq, unsigned long *flags) __releases(rq->lock) { spin_unlock_irqrestore(&rq->lock, *flags); @@ -391,7 +424,7 @@ static int show_schedstat(struct seq_fil seq_printf(seq, "version %d\n", SCHEDSTAT_VERSION); seq_printf(seq, "timestamp %lu\n", jiffies); for_each_online_cpu(cpu) { - runqueue_t *rq = cpu_rq(cpu); + struct rq *rq = cpu_rq(cpu); #ifdef CONFIG_SMP struct sched_domain *sd; int dcnt = 0; @@ -478,10 +511,10 @@ #endif /* * rq_lock - lock a given runqueue and disable interrupts. */ -static inline runqueue_t *this_rq_lock(void) +static inline struct rq *this_rq_lock(void) __acquires(rq->lock) { - runqueue_t *rq; + struct rq *rq; local_irq_disable(); rq = this_rq(); @@ -506,7 +539,7 @@ #ifdef CONFIG_SCHEDSTATS * long it was from the *first* time it was queued to the time that it * finally hit a cpu. */ -static inline void sched_info_dequeued(task_t *t) +static inline void sched_info_dequeued(struct task_struct *t) { t->sched_info.last_queued = 0; } @@ -516,10 +549,10 @@ static inline void sched_info_dequeued(t * long it was waiting to run. We also note when it began so that we * can keep stats on how long its timeslice is. */ -static void sched_info_arrive(task_t *t) +static void sched_info_arrive(struct task_struct *t) { unsigned long now = jiffies, diff = 0; - struct runqueue *rq = task_rq(t); + struct rq *rq = task_rq(t); if (t->sched_info.last_queued) diff = now - t->sched_info.last_queued; @@ -550,7 +583,7 @@ static void sched_info_arrive(task_t *t) * the timestamp if it is already not set. It's assumed that * sched_info_dequeued() will clear that stamp when appropriate. */ -static inline void sched_info_queued(task_t *t) +static inline void sched_info_queued(struct task_struct *t) { if (!t->sched_info.last_queued) t->sched_info.last_queued = jiffies; @@ -560,9 +593,9 @@ static inline void sched_info_queued(tas * Called when a process ceases being the active-running process, either * voluntarily or involuntarily. Now we can calculate how long we ran. */ -static inline void sched_info_depart(task_t *t) +static inline void sched_info_depart(struct task_struct *t) { - struct runqueue *rq = task_rq(t); + struct rq *rq = task_rq(t); unsigned long diff = jiffies - t->sched_info.last_arrival; t->sched_info.cpu_time += diff; @@ -576,9 +609,10 @@ static inline void sched_info_depart(tas * their time slice. (This may also be called when switching to or from * the idle task.) We are only called when prev != next. */ -static inline void sched_info_switch(task_t *prev, task_t *next) +static inline void +sched_info_switch(struct task_struct *prev, struct task_struct *next) { - struct runqueue *rq = task_rq(prev); + struct rq *rq = task_rq(prev); /* * prev now departs the cpu. It's not interesting to record @@ -599,7 +633,7 @@ #endif /* CONFIG_SCHEDSTATS */ /* * Adding/removing a task to/from a priority array: */ -static void dequeue_task(struct task_struct *p, prio_array_t *array) +static void dequeue_task(struct task_struct *p, struct prio_array *array) { array->nr_active--; list_del(&p->run_list); @@ -607,7 +641,7 @@ static void dequeue_task(struct task_str __clear_bit(p->prio, array->bitmap); } -static void enqueue_task(struct task_struct *p, prio_array_t *array) +static void enqueue_task(struct task_struct *p, struct prio_array *array) { sched_info_queued(p); list_add_tail(&p->run_list, array->queue + p->prio); @@ -620,12 +654,13 @@ static void enqueue_task(struct task_str * Put task to the end of the run list without the overhead of dequeue * followed by enqueue. */ -static void requeue_task(struct task_struct *p, prio_array_t *array) +static void requeue_task(struct task_struct *p, struct prio_array *array) { list_move_tail(&p->run_list, array->queue + p->prio); } -static inline void enqueue_task_head(struct task_struct *p, prio_array_t *array) +static inline void +enqueue_task_head(struct task_struct *p, struct prio_array *array) { list_add(&p->run_list, array->queue + p->prio); __set_bit(p->prio, array->bitmap); @@ -634,7 +669,7 @@ static inline void enqueue_task_head(str } /* - * effective_prio - return the priority that is based on the static + * __normal_prio - return the priority that is based on the static * priority but is modified by bonuses/penalties. * * We scale the actual sleep average [0 .... MAX_SLEEP_AVG] @@ -647,13 +682,11 @@ static inline void enqueue_task_head(str * * Both properties are important to certain workloads. */ -static int effective_prio(task_t *p) + +static inline int __normal_prio(struct task_struct *p) { int bonus, prio; - if (rt_task(p)) - return p->prio; - bonus = CURRENT_BONUS(p) - MAX_BONUS / 2; prio = p->static_prio - bonus; @@ -665,57 +698,165 @@ static int effective_prio(task_t *p) } /* + * To aid in avoiding the subversion of "niceness" due to uneven distribution + * of tasks with abnormal "nice" values across CPUs the contribution that + * each task makes to its run queue's load is weighted according to its + * scheduling class and "nice" value. For SCHED_NORMAL tasks this is just a + * scaled version of the new time slice allocation that they receive on time + * slice expiry etc. + */ + +/* + * Assume: static_prio_timeslice(NICE_TO_PRIO(0)) == DEF_TIMESLICE + * If static_prio_timeslice() is ever changed to break this assumption then + * this code will need modification + */ +#define TIME_SLICE_NICE_ZERO DEF_TIMESLICE +#define LOAD_WEIGHT(lp) \ + (((lp) * SCHED_LOAD_SCALE) / TIME_SLICE_NICE_ZERO) +#define PRIO_TO_LOAD_WEIGHT(prio) \ + LOAD_WEIGHT(static_prio_timeslice(prio)) +#define RTPRIO_TO_LOAD_WEIGHT(rp) \ + (PRIO_TO_LOAD_WEIGHT(MAX_RT_PRIO) + LOAD_WEIGHT(rp)) + +static void set_load_weight(struct task_struct *p) +{ + if (has_rt_policy(p)) { +#ifdef CONFIG_SMP + if (p == task_rq(p)->migration_thread) + /* + * The migration thread does the actual balancing. + * Giving its load any weight will skew balancing + * adversely. + */ + p->load_weight = 0; + else +#endif + p->load_weight = RTPRIO_TO_LOAD_WEIGHT(p->rt_priority); + } else + p->load_weight = PRIO_TO_LOAD_WEIGHT(p->static_prio); +} + +static inline void +inc_raw_weighted_load(struct rq *rq, const struct task_struct *p) +{ + rq->raw_weighted_load += p->load_weight; +} + +static inline void +dec_raw_weighted_load(struct rq *rq, const struct task_struct *p) +{ + rq->raw_weighted_load -= p->load_weight; +} + +static inline void inc_nr_running(struct task_struct *p, struct rq *rq) +{ + rq->nr_running++; + inc_raw_weighted_load(rq, p); +} + +static inline void dec_nr_running(struct task_struct *p, struct rq *rq) +{ + rq->nr_running--; + dec_raw_weighted_load(rq, p); +} + +/* + * Calculate the expected normal priority: i.e. priority + * without taking RT-inheritance into account. Might be + * boosted by interactivity modifiers. Changes upon fork, + * setprio syscalls, and whenever the interactivity + * estimator recalculates. + */ +static inline int normal_prio(struct task_struct *p) +{ + int prio; + + if (has_rt_policy(p)) + prio = MAX_RT_PRIO-1 - p->rt_priority; + else + prio = __normal_prio(p); + return prio; +} + +/* + * Calculate the current priority, i.e. the priority + * taken into account by the scheduler. This value might + * be boosted by RT tasks, or might be boosted by + * interactivity modifiers. Will be RT if the task got + * RT-boosted. If not then it returns p->normal_prio. + */ +static int effective_prio(struct task_struct *p) +{ + p->normal_prio = normal_prio(p); + /* + * If we are RT tasks or we were boosted to RT priority, + * keep the priority unchanged. Otherwise, update priority + * to the normal priority: + */ + if (!rt_prio(p->prio)) + return p->normal_prio; + return p->prio; +} + +/* * __activate_task - move a task to the runqueue. */ -static void __activate_task(task_t *p, runqueue_t *rq) +static void __activate_task(struct task_struct *p, struct rq *rq) { - prio_array_t *target = rq->active; + struct prio_array *target = rq->active; if (batch_task(p)) target = rq->expired; enqueue_task(p, target); - rq->nr_running++; + inc_nr_running(p, rq); } /* * __activate_idle_task - move idle task to the _front_ of runqueue. */ -static inline void __activate_idle_task(task_t *p, runqueue_t *rq) +static inline void __activate_idle_task(struct task_struct *p, struct rq *rq) { enqueue_task_head(p, rq->active); - rq->nr_running++; + inc_nr_running(p, rq); } -static int recalc_task_prio(task_t *p, unsigned long long now) +/* + * Recalculate p->normal_prio and p->prio after having slept, + * updating the sleep-average too: + */ +static int recalc_task_prio(struct task_struct *p, unsigned long long now) { /* Caller must always ensure 'now >= p->timestamp' */ - unsigned long long __sleep_time = now - p->timestamp; - unsigned long sleep_time; + unsigned long sleep_time = now - p->timestamp; if (batch_task(p)) sleep_time = 0; - else { - if (__sleep_time > NS_MAX_SLEEP_AVG) - sleep_time = NS_MAX_SLEEP_AVG; - else - sleep_time = (unsigned long)__sleep_time; - } if (likely(sleep_time > 0)) { /* - * User tasks that sleep a long time are categorised as - * idle. They will only have their sleep_avg increased to a - * level that makes them just interactive priority to stay - * active yet prevent them suddenly becoming cpu hogs and - * starving other processes. + * This ceiling is set to the lowest priority that would allow + * a task to be reinserted into the active array on timeslice + * completion. */ - if (p->mm && sleep_time > INTERACTIVE_SLEEP(p)) { - unsigned long ceiling; + unsigned long ceiling = INTERACTIVE_SLEEP(p); - ceiling = JIFFIES_TO_NS(MAX_SLEEP_AVG - - DEF_TIMESLICE); - if (p->sleep_avg < ceiling) - p->sleep_avg = ceiling; + if (p->mm && sleep_time > ceiling && p->sleep_avg < ceiling) { + /* + * Prevents user tasks from achieving best priority + * with one single large enough sleep. + */ + p->sleep_avg = ceiling; + /* + * Using INTERACTIVE_SLEEP() as a ceiling places a + * nice(0) task 1ms sleep away from promotion, and + * gives it 700ms to round-robin with no chance of + * being demoted. This is more than generous, so + * mark this sleep as non-interactive to prevent the + * on-runqueue bonus logic from intervening should + * this task not receive cpu immediately. + */ + p->sleep_type = SLEEP_NONINTERACTIVE; } else { /* * Tasks waking from uninterruptible sleep are @@ -723,12 +864,12 @@ static int recalc_task_prio(task_t *p, u * are likely to be waiting on I/O */ if (p->sleep_type == SLEEP_NONINTERACTIVE && p->mm) { - if (p->sleep_avg >= INTERACTIVE_SLEEP(p)) + if (p->sleep_avg >= ceiling) sleep_time = 0; else if (p->sleep_avg + sleep_time >= - INTERACTIVE_SLEEP(p)) { - p->sleep_avg = INTERACTIVE_SLEEP(p); - sleep_time = 0; + ceiling) { + p->sleep_avg = ceiling; + sleep_time = 0; } } @@ -742,9 +883,9 @@ static int recalc_task_prio(task_t *p, u */ p->sleep_avg += sleep_time; - if (p->sleep_avg > NS_MAX_SLEEP_AVG) - p->sleep_avg = NS_MAX_SLEEP_AVG; } + if (p->sleep_avg > NS_MAX_SLEEP_AVG) + p->sleep_avg = NS_MAX_SLEEP_AVG; } return effective_prio(p); @@ -756,7 +897,7 @@ static int recalc_task_prio(task_t *p, u * Update all the scheduling statistics stuff. (sleep average * calculation, priority modifiers, etc.) */ -static void activate_task(task_t *p, runqueue_t *rq, int local) +static void activate_task(struct task_struct *p, struct rq *rq, int local) { unsigned long long now; @@ -764,7 +905,7 @@ static void activate_task(task_t *p, run #ifdef CONFIG_SMP if (!local) { /* Compensate for drifting sched_clock */ - runqueue_t *this_rq = this_rq(); + struct rq *this_rq = this_rq(); now = (now - this_rq->timestamp_last_tick) + rq->timestamp_last_tick; } @@ -803,9 +944,9 @@ #endif /* * deactivate_task - remove a task from the runqueue. */ -static void deactivate_task(struct task_struct *p, runqueue_t *rq) +static void deactivate_task(struct task_struct *p, struct rq *rq) { - rq->nr_running--; + dec_nr_running(p, rq); dequeue_task(p, p->array); p->array = NULL; } @@ -818,7 +959,12 @@ static void deactivate_task(struct task_ * the target CPU. */ #ifdef CONFIG_SMP -static void resched_task(task_t *p) + +#ifndef tsk_is_polling +#define tsk_is_polling(t) test_tsk_thread_flag(t, TIF_POLLING_NRFLAG) +#endif + +static void resched_task(struct task_struct *p) { int cpu; @@ -833,13 +979,13 @@ static void resched_task(task_t *p) if (cpu == smp_processor_id()) return; - /* NEED_RESCHED must be visible before we test POLLING_NRFLAG */ + /* NEED_RESCHED must be visible before we test polling */ smp_mb(); - if (!test_tsk_thread_flag(p, TIF_POLLING_NRFLAG)) + if (!tsk_is_polling(p)) smp_send_reschedule(cpu); } #else -static inline void resched_task(task_t *p) +static inline void resched_task(struct task_struct *p) { assert_spin_locked(&task_rq(p)->lock); set_tsk_need_resched(p); @@ -850,28 +996,35 @@ #endif * task_curr - is this task currently executing on a CPU? * @p: the task in question. */ -inline int task_curr(const task_t *p) +inline int task_curr(const struct task_struct *p) { return cpu_curr(task_cpu(p)) == p; } +/* Used instead of source_load when we know the type == 0 */ +unsigned long weighted_cpuload(const int cpu) +{ + return cpu_rq(cpu)->raw_weighted_load; +} + #ifdef CONFIG_SMP -typedef struct { +struct migration_req { struct list_head list; - task_t *task; + struct task_struct *task; int dest_cpu; struct completion done; -} migration_req_t; +}; /* * The task's runqueue lock must be held. * Returns true if you have to wait for migration thread. */ -static int migrate_task(task_t *p, int dest_cpu, migration_req_t *req) +static int +migrate_task(struct task_struct *p, int dest_cpu, struct migration_req *req) { - runqueue_t *rq = task_rq(p); + struct rq *rq = task_rq(p); /* * If the task is not on a runqueue (and not running), then @@ -886,6 +1039,7 @@ static int migrate_task(task_t *p, int d req->task = p; req->dest_cpu = dest_cpu; list_add(&req->list, &rq->migration_queue); + return 1; } @@ -898,10 +1052,10 @@ static int migrate_task(task_t *p, int d * smp_call_function() if an IPI is sent by the same process we are * waiting to become inactive. */ -void wait_task_inactive(task_t *p) +void wait_task_inactive(struct task_struct *p) { unsigned long flags; - runqueue_t *rq; + struct rq *rq; int preempted; repeat: @@ -932,7 +1086,7 @@ repeat: * to another CPU then no harm is done and the purpose has been * achieved as well. */ -void kick_process(task_t *p) +void kick_process(struct task_struct *p) { int cpu; @@ -944,32 +1098,45 @@ void kick_process(task_t *p) } /* - * Return a low guess at the load of a migration-source cpu. + * Return a low guess at the load of a migration-source cpu weighted + * according to the scheduling class and "nice" value. * * We want to under-estimate the load of migration sources, to * balance conservatively. */ static inline unsigned long source_load(int cpu, int type) { - runqueue_t *rq = cpu_rq(cpu); - unsigned long load_now = rq->nr_running * SCHED_LOAD_SCALE; + struct rq *rq = cpu_rq(cpu); + if (type == 0) - return load_now; + return rq->raw_weighted_load; - return min(rq->cpu_load[type-1], load_now); + return min(rq->cpu_load[type-1], rq->raw_weighted_load); } /* - * Return a high guess at the load of a migration-target cpu + * Return a high guess at the load of a migration-target cpu weighted + * according to the scheduling class and "nice" value. */ static inline unsigned long target_load(int cpu, int type) { - runqueue_t *rq = cpu_rq(cpu); - unsigned long load_now = rq->nr_running * SCHED_LOAD_SCALE; + struct rq *rq = cpu_rq(cpu); + if (type == 0) - return load_now; + return rq->raw_weighted_load; + + return max(rq->cpu_load[type-1], rq->raw_weighted_load); +} + +/* + * Return the average load per task on the cpu's run queue + */ +static inline unsigned long cpu_avg_load_per_task(int cpu) +{ + struct rq *rq = cpu_rq(cpu); + unsigned long n = rq->nr_running; - return max(rq->cpu_load[type-1], load_now); + return n ? rq->raw_weighted_load / n : SCHED_LOAD_SCALE; } /* @@ -1042,7 +1209,7 @@ find_idlest_cpu(struct sched_group *grou cpus_and(tmp, group->cpumask, p->cpus_allowed); for_each_cpu_mask(i, tmp) { - load = source_load(i, 0); + load = weighted_cpuload(i); if (load < min_load || (load == min_load && i == this_cpu)) { min_load = load; @@ -1069,9 +1236,15 @@ static int sched_balance_self(int cpu, i struct task_struct *t = current; struct sched_domain *tmp, *sd = NULL; - for_each_domain(cpu, tmp) + for_each_domain(cpu, tmp) { + /* + * If power savings logic is enabled for a domain, stop there. + */ + if (tmp->flags & SD_POWERSAVINGS_BALANCE) + break; if (tmp->flags & flag) sd = tmp; + } while (sd) { cpumask_t span; @@ -1116,7 +1289,7 @@ #endif /* CONFIG_SMP */ * Returns the CPU we should wake onto. */ #if defined(ARCH_HAS_SCHED_WAKE_IDLE) -static int wake_idle(int cpu, task_t *p) +static int wake_idle(int cpu, struct task_struct *p) { cpumask_t tmp; struct sched_domain *sd; @@ -1139,7 +1312,7 @@ static int wake_idle(int cpu, task_t *p) return cpu; } #else -static inline int wake_idle(int cpu, task_t *p) +static inline int wake_idle(int cpu, struct task_struct *p) { return cpu; } @@ -1159,15 +1332,15 @@ #endif * * returns failure only if the task is already active. */ -static int try_to_wake_up(task_t *p, unsigned int state, int sync) +static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync) { int cpu, this_cpu, success = 0; unsigned long flags; long old_state; - runqueue_t *rq; + struct rq *rq; #ifdef CONFIG_SMP - unsigned long load, this_load; struct sched_domain *sd, *this_sd = NULL; + unsigned long load, this_load; int new_cpu; #endif @@ -1221,17 +1394,19 @@ #ifdef CONFIG_SMP if (this_sd->flags & SD_WAKE_AFFINE) { unsigned long tl = this_load; + unsigned long tl_per_task = cpu_avg_load_per_task(this_cpu); + /* * If sync wakeup then subtract the (maximum possible) * effect of the currently running task from the load * of the current CPU: */ if (sync) - tl -= SCHED_LOAD_SCALE; + tl -= current->load_weight; if ((tl <= load && - tl + target_load(cpu, idx) <= SCHED_LOAD_SCALE) || - 100*(tl + SCHED_LOAD_SCALE) <= imbalance*load) { + tl + target_load(cpu, idx) <= tl_per_task) || + 100*(tl + p->load_weight) <= imbalance*load) { /* * This domain has SD_WAKE_AFFINE and * p is cache cold in this domain, and @@ -1315,15 +1490,14 @@ out: return success; } -int fastcall wake_up_process(task_t *p) +int fastcall wake_up_process(struct task_struct *p) { return try_to_wake_up(p, TASK_STOPPED | TASK_TRACED | TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 0); } - EXPORT_SYMBOL(wake_up_process); -int fastcall wake_up_state(task_t *p, unsigned int state) +int fastcall wake_up_state(struct task_struct *p, unsigned int state) { return try_to_wake_up(p, state, 0); } @@ -1332,7 +1506,7 @@ int fastcall wake_up_state(task_t *p, un * Perform scheduler related setup for a newly forked process p. * p is forked by current. */ -void fastcall sched_fork(task_t *p, int clone_flags) +void fastcall sched_fork(struct task_struct *p, int clone_flags) { int cpu = get_cpu(); @@ -1348,6 +1522,12 @@ #endif * event cannot wake it up and insert it on the runqueue either. */ p->state = TASK_RUNNING; + + /* + * Make sure we do not leak PI boosting priority to the child: + */ + p->prio = current->normal_prio; + INIT_LIST_HEAD(&p->run_list); p->array = NULL; #ifdef CONFIG_SCHEDSTATS @@ -1394,11 +1574,11 @@ #endif * that must be done for every newly created context, then puts the task * on the runqueue and wakes it. */ -void fastcall wake_up_new_task(task_t *p, unsigned long clone_flags) +void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags) { + struct rq *rq, *this_rq; unsigned long flags; int this_cpu, cpu; - runqueue_t *rq, *this_rq; rq = task_rq_lock(p, &flags); BUG_ON(p->state != TASK_RUNNING); @@ -1427,10 +1607,11 @@ void fastcall wake_up_new_task(task_t *p __activate_task(p, rq); else { p->prio = current->prio; + p->normal_prio = current->normal_prio; list_add_tail(&p->run_list, ¤t->run_list); p->array = current->array; p->array->nr_active++; - rq->nr_running++; + inc_nr_running(p, rq); } set_need_resched(); } else @@ -1477,10 +1658,10 @@ void fastcall wake_up_new_task(task_t *p * artificially, because any timeslice recovered here * was given away by the parent in the first place.) */ -void fastcall sched_exit(task_t *p) +void fastcall sched_exit(struct task_struct *p) { unsigned long flags; - runqueue_t *rq; + struct rq *rq; /* * If the child was a (relative-) CPU hog then decrease @@ -1511,7 +1692,7 @@ void fastcall sched_exit(task_t *p) * prepare_task_switch sets up locking and calls architecture specific * hooks. */ -static inline void prepare_task_switch(runqueue_t *rq, task_t *next) +static inline void prepare_task_switch(struct rq *rq, struct task_struct *next) { prepare_lock_switch(rq, next); prepare_arch_switch(next); @@ -1532,7 +1713,7 @@ static inline void prepare_task_switch(r * with the lock held can cause deadlocks; see schedule() for * details.) */ -static inline void finish_task_switch(runqueue_t *rq, task_t *prev) +static inline void finish_task_switch(struct rq *rq, struct task_struct *prev) __releases(rq->lock) { struct mm_struct *mm = rq->prev_mm; @@ -1570,10 +1751,11 @@ static inline void finish_task_switch(ru * schedule_tail - first thing a freshly forked thread must call. * @prev: the thread we just switched away from. */ -asmlinkage void schedule_tail(task_t *prev) +asmlinkage void schedule_tail(struct task_struct *prev) __releases(rq->lock) { - runqueue_t *rq = this_rq(); + struct rq *rq = this_rq(); + finish_task_switch(rq, prev); #ifdef __ARCH_WANT_UNLOCKED_CTXSW /* In this case, finish_task_switch does not reenable preemption */ @@ -1587,8 +1769,9 @@ #endif * context_switch - switch to the new MM and the new * thread's register state. */ -static inline -task_t * context_switch(runqueue_t *rq, task_t *prev, task_t *next) +static inline struct task_struct * +context_switch(struct rq *rq, struct task_struct *prev, + struct task_struct *next) { struct mm_struct *mm = next->mm; struct mm_struct *oldmm = prev->active_mm; @@ -1605,6 +1788,7 @@ task_t * context_switch(runqueue_t *rq, WARN_ON(rq->prev_mm); rq->prev_mm = oldmm; } + spin_release(&rq->lock.dep_map, 1, _THIS_IP_); /* Here we just switch the register state and the stack. */ switch_to(prev, next, prev); @@ -1648,7 +1832,8 @@ unsigned long nr_uninterruptible(void) unsigned long long nr_context_switches(void) { - unsigned long long i, sum = 0; + int i; + unsigned long long sum = 0; for_each_possible_cpu(i) sum += cpu_rq(i)->nr_switches; @@ -1684,15 +1869,21 @@ unsigned long nr_active(void) #ifdef CONFIG_SMP /* + * Is this task likely cache-hot: + */ +static inline int +task_hot(struct task_struct *p, unsigned long long now, struct sched_domain *sd) +{ + return (long long)(now - p->last_ran) < (long long)sd->cache_hot_time; +} + +/* * double_rq_lock - safely lock two runqueues * - * We must take them in cpu order to match code in - * dependent_sleeper and wake_dependent_sleeper. - * * Note this does not disable interrupts like task_rq_lock, * you need to do so manually before calling. */ -static void double_rq_lock(runqueue_t *rq1, runqueue_t *rq2) +static void double_rq_lock(struct rq *rq1, struct rq *rq2) __acquires(rq1->lock) __acquires(rq2->lock) { @@ -1700,7 +1891,7 @@ static void double_rq_lock(runqueue_t *r spin_lock(&rq1->lock); __acquire(rq2->lock); /* Fake it out ;) */ } else { - if (rq1->cpu < rq2->cpu) { + if (rq1 < rq2) { spin_lock(&rq1->lock); spin_lock(&rq2->lock); } else { @@ -1716,7 +1907,7 @@ static void double_rq_lock(runqueue_t *r * Note this does not restore interrupts like task_rq_unlock, * you need to do so manually after calling. */ -static void double_rq_unlock(runqueue_t *rq1, runqueue_t *rq2) +static void double_rq_unlock(struct rq *rq1, struct rq *rq2) __releases(rq1->lock) __releases(rq2->lock) { @@ -1730,13 +1921,13 @@ static void double_rq_unlock(runqueue_t /* * double_lock_balance - lock the busiest runqueue, this_rq is locked already. */ -static void double_lock_balance(runqueue_t *this_rq, runqueue_t *busiest) +static void double_lock_balance(struct rq *this_rq, struct rq *busiest) __releases(this_rq->lock) __acquires(busiest->lock) __acquires(this_rq->lock) { if (unlikely(!spin_trylock(&busiest->lock))) { - if (busiest->cpu < this_rq->cpu) { + if (busiest < this_rq) { spin_unlock(&this_rq->lock); spin_lock(&busiest->lock); spin_lock(&this_rq->lock); @@ -1751,11 +1942,11 @@ static void double_lock_balance(runqueue * allow dest_cpu, which will force the cpu onto dest_cpu. Then * the cpu_allowed mask is restored. */ -static void sched_migrate_task(task_t *p, int dest_cpu) +static void sched_migrate_task(struct task_struct *p, int dest_cpu) { - migration_req_t req; - runqueue_t *rq; + struct migration_req req; unsigned long flags; + struct rq *rq; rq = task_rq_lock(p, &flags); if (!cpu_isset(dest_cpu, p->cpus_allowed) @@ -1766,11 +1957,13 @@ static void sched_migrate_task(task_t *p if (migrate_task(p, dest_cpu, &req)) { /* Need to wait for migration thread (might exit: take ref). */ struct task_struct *mt = rq->migration_thread; + get_task_struct(mt); task_rq_unlock(rq, &flags); wake_up_process(mt); put_task_struct(mt); wait_for_completion(&req.done); + return; } out: @@ -1794,14 +1987,14 @@ void sched_exec(void) * pull_task - move a task from a remote runqueue to the local runqueue. * Both runqueues must be locked. */ -static -void pull_task(runqueue_t *src_rq, prio_array_t *src_array, task_t *p, - runqueue_t *this_rq, prio_array_t *this_array, int this_cpu) +static void pull_task(struct rq *src_rq, struct prio_array *src_array, + struct task_struct *p, struct rq *this_rq, + struct prio_array *this_array, int this_cpu) { dequeue_task(p, src_array); - src_rq->nr_running--; + dec_nr_running(p, src_rq); set_task_cpu(p, this_cpu); - this_rq->nr_running++; + inc_nr_running(p, this_rq); enqueue_task(p, this_array); p->timestamp = (p->timestamp - src_rq->timestamp_last_tick) + this_rq->timestamp_last_tick; @@ -1817,7 +2010,7 @@ void pull_task(runqueue_t *src_rq, prio_ * can_migrate_task - may task p from runqueue rq be migrated to this_cpu? */ static -int can_migrate_task(task_t *p, runqueue_t *rq, int this_cpu, +int can_migrate_task(struct task_struct *p, struct rq *rq, int this_cpu, struct sched_domain *sd, enum idle_type idle, int *all_pinned) { @@ -1848,26 +2041,42 @@ int can_migrate_task(task_t *p, runqueue return 1; } +#define rq_best_prio(rq) min((rq)->curr->prio, (rq)->best_expired_prio) + /* - * move_tasks tries to move up to max_nr_move tasks from busiest to this_rq, - * as part of a balancing operation within "domain". Returns the number of - * tasks moved. + * move_tasks tries to move up to max_nr_move tasks and max_load_move weighted + * load from busiest to this_rq, as part of a balancing operation within + * "domain". Returns the number of tasks moved. * * Called with both runqueues locked. */ -static int move_tasks(runqueue_t *this_rq, int this_cpu, runqueue_t *busiest, - unsigned long max_nr_move, struct sched_domain *sd, - enum idle_type idle, int *all_pinned) +static int move_tasks(struct rq *this_rq, int this_cpu, struct rq *busiest, + unsigned long max_nr_move, unsigned long max_load_move, + struct sched_domain *sd, enum idle_type idle, + int *all_pinned) { - prio_array_t *array, *dst_array; + int idx, pulled = 0, pinned = 0, this_best_prio, best_prio, + best_prio_seen, skip_for_load; + struct prio_array *array, *dst_array; struct list_head *head, *curr; - int idx, pulled = 0, pinned = 0; - task_t *tmp; + struct task_struct *tmp; + long rem_load_move; - if (max_nr_move == 0) + if (max_nr_move == 0 || max_load_move == 0) goto out; + rem_load_move = max_load_move; pinned = 1; + this_best_prio = rq_best_prio(this_rq); + best_prio = rq_best_prio(busiest); + /* + * Enable handling of the case where there is more than one task + * with the best priority. If the current running task is one + * of those with prio==best_prio we know it won't be moved + * and therefore it's safe to override the skip (based on load) of + * any task we find with that prio. + */ + best_prio_seen = best_prio == busiest->curr->prio; /* * We first consider expired tasks. Those will likely not be @@ -1903,11 +2112,22 @@ skip_bitmap: head = array->queue + idx; curr = head->prev; skip_queue: - tmp = list_entry(curr, task_t, run_list); + tmp = list_entry(curr, struct task_struct, run_list); curr = curr->prev; - if (!can_migrate_task(tmp, busiest, this_cpu, sd, idle, &pinned)) { + /* + * To help distribute high priority tasks accross CPUs we don't + * skip a task if it will be the highest priority task (i.e. smallest + * prio value) on its new queue regardless of its load weight + */ + skip_for_load = tmp->load_weight > rem_load_move; + if (skip_for_load && idx < this_best_prio) + skip_for_load = !best_prio_seen && idx == best_prio; + if (skip_for_load || + !can_migrate_task(tmp, busiest, this_cpu, sd, idle, &pinned)) { + + best_prio_seen |= idx == best_prio; if (curr != head) goto skip_queue; idx++; @@ -1921,9 +2141,15 @@ #endif pull_task(busiest, array, tmp, this_rq, dst_array, this_cpu); pulled++; + rem_load_move -= tmp->load_weight; - /* We only want to steal up to the prescribed number of tasks. */ - if (pulled < max_nr_move) { + /* + * We only want to steal up to the prescribed number of tasks + * and the prescribed amount of weighted load. + */ + if (pulled < max_nr_move && rem_load_move > 0) { + if (idx < this_best_prio) + this_best_prio = idx; if (curr != head) goto skip_queue; idx++; @@ -1944,8 +2170,8 @@ out: /* * find_busiest_group finds and returns the busiest CPU group within the - * domain. It calculates and returns the number of tasks which should be - * moved to restore balance via the imbalance parameter. + * domain. It calculates and returns the amount of weighted load which + * should be moved to restore balance via the imbalance parameter. */ static struct sched_group * find_busiest_group(struct sched_domain *sd, int this_cpu, @@ -1954,9 +2180,19 @@ find_busiest_group(struct sched_domain * struct sched_group *busiest = NULL, *this = NULL, *group = sd->groups; unsigned long max_load, avg_load, total_load, this_load, total_pwr; unsigned long max_pull; + unsigned long busiest_load_per_task, busiest_nr_running; + unsigned long this_load_per_task, this_nr_running; int load_idx; +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) + int power_savings_balance = 1; + unsigned long leader_nr_running = 0, min_load_per_task = 0; + unsigned long min_nr_running = ULONG_MAX; + struct sched_group *group_min = NULL, *group_leader = NULL; +#endif max_load = this_load = total_load = total_pwr = 0; + busiest_load_per_task = busiest_nr_running = 0; + this_load_per_task = this_nr_running = 0; if (idle == NOT_IDLE) load_idx = sd->busy_idx; else if (idle == NEWLY_IDLE) @@ -1965,16 +2201,19 @@ find_busiest_group(struct sched_domain * load_idx = sd->idle_idx; do { - unsigned long load; + unsigned long load, group_capacity; int local_group; int i; + unsigned long sum_nr_running, sum_weighted_load; local_group = cpu_isset(this_cpu, group->cpumask); /* Tally up the load of all CPUs in the group */ - avg_load = 0; + sum_weighted_load = sum_nr_running = avg_load = 0; for_each_cpu_mask(i, group->cpumask) { + struct rq *rq = cpu_rq(i); + if (*sd_idle && !idle_cpu(i)) *sd_idle = 0; @@ -1985,6 +2224,8 @@ find_busiest_group(struct sched_domain * load = source_load(i, load_idx); avg_load += load; + sum_nr_running += rq->nr_running; + sum_weighted_load += rq->raw_weighted_load; } total_load += avg_load; @@ -1993,17 +2234,80 @@ find_busiest_group(struct sched_domain * /* Adjust by relative CPU power of the group */ avg_load = (avg_load * SCHED_LOAD_SCALE) / group->cpu_power; + group_capacity = group->cpu_power / SCHED_LOAD_SCALE; + if (local_group) { this_load = avg_load; this = group; - } else if (avg_load > max_load) { + this_nr_running = sum_nr_running; + this_load_per_task = sum_weighted_load; + } else if (avg_load > max_load && + sum_nr_running > group_capacity) { max_load = avg_load; busiest = group; + busiest_nr_running = sum_nr_running; + busiest_load_per_task = sum_weighted_load; + } + +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) + /* + * Busy processors will not participate in power savings + * balance. + */ + if (idle == NOT_IDLE || !(sd->flags & SD_POWERSAVINGS_BALANCE)) + goto group_next; + + /* + * If the local group is idle or completely loaded + * no need to do power savings balance at this domain + */ + if (local_group && (this_nr_running >= group_capacity || + !this_nr_running)) + power_savings_balance = 0; + + /* + * If a group is already running at full capacity or idle, + * don't include that group in power savings calculations + */ + if (!power_savings_balance || sum_nr_running >= group_capacity + || !sum_nr_running) + goto group_next; + + /* + * Calculate the group which has the least non-idle load. + * This is the group from where we need to pick up the load + * for saving power + */ + if ((sum_nr_running < min_nr_running) || + (sum_nr_running == min_nr_running && + first_cpu(group->cpumask) < + first_cpu(group_min->cpumask))) { + group_min = group; + min_nr_running = sum_nr_running; + min_load_per_task = sum_weighted_load / + sum_nr_running; + } + + /* + * Calculate the group which is almost near its + * capacity but still has some space to pick up some load + * from other group and save more power + */ + if (sum_nr_running <= group_capacity - 1) { + if (sum_nr_running > leader_nr_running || + (sum_nr_running == leader_nr_running && + first_cpu(group->cpumask) > + first_cpu(group_leader->cpumask))) { + group_leader = group; + leader_nr_running = sum_nr_running; + } } +group_next: +#endif group = group->next; } while (group != sd->groups); - if (!busiest || this_load >= max_load || max_load <= SCHED_LOAD_SCALE) + if (!busiest || this_load >= max_load || busiest_nr_running == 0) goto out_balanced; avg_load = (SCHED_LOAD_SCALE * total_load) / total_pwr; @@ -2012,6 +2316,7 @@ find_busiest_group(struct sched_domain * 100*max_load <= sd->imbalance_pct*this_load) goto out_balanced; + busiest_load_per_task /= busiest_nr_running; /* * We're trying to get all the cpus to the average_load, so we don't * want to push ourselves above the average load, nor do we wish to @@ -2023,21 +2328,49 @@ find_busiest_group(struct sched_domain * * by pulling tasks to us. Be careful of negative numbers as they'll * appear as very large values with unsigned longs. */ + if (max_load <= busiest_load_per_task) + goto out_balanced; + + /* + * In the presence of smp nice balancing, certain scenarios can have + * max load less than avg load(as we skip the groups at or below + * its cpu_power, while calculating max_load..) + */ + if (max_load < avg_load) { + *imbalance = 0; + goto small_imbalance; + } /* Don't want to pull so many tasks that a group would go idle */ - max_pull = min(max_load - avg_load, max_load - SCHED_LOAD_SCALE); + max_pull = min(max_load - avg_load, max_load - busiest_load_per_task); /* How much load to actually move to equalise the imbalance */ *imbalance = min(max_pull * busiest->cpu_power, (avg_load - this_load) * this->cpu_power) / SCHED_LOAD_SCALE; - if (*imbalance < SCHED_LOAD_SCALE) { - unsigned long pwr_now = 0, pwr_move = 0; - unsigned long tmp; + /* + * if *imbalance is less than the average load per runnable task + * there is no gaurantee that any tasks will be moved so we'll have + * a think about bumping its value to force at least one task to be + * moved + */ + if (*imbalance < busiest_load_per_task) { + unsigned long tmp, pwr_now, pwr_move; + unsigned int imbn; + +small_imbalance: + pwr_move = pwr_now = 0; + imbn = 2; + if (this_nr_running) { + this_load_per_task /= this_nr_running; + if (busiest_load_per_task > this_load_per_task) + imbn = 1; + } else + this_load_per_task = SCHED_LOAD_SCALE; - if (max_load - this_load >= SCHED_LOAD_SCALE*2) { - *imbalance = 1; + if (max_load - this_load >= busiest_load_per_task * imbn) { + *imbalance = busiest_load_per_task; return busiest; } @@ -2047,39 +2380,47 @@ find_busiest_group(struct sched_domain * * moving them. */ - pwr_now += busiest->cpu_power*min(SCHED_LOAD_SCALE, max_load); - pwr_now += this->cpu_power*min(SCHED_LOAD_SCALE, this_load); + pwr_now += busiest->cpu_power * + min(busiest_load_per_task, max_load); + pwr_now += this->cpu_power * + min(this_load_per_task, this_load); pwr_now /= SCHED_LOAD_SCALE; /* Amount of load we'd subtract */ - tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/busiest->cpu_power; + tmp = busiest_load_per_task*SCHED_LOAD_SCALE/busiest->cpu_power; if (max_load > tmp) - pwr_move += busiest->cpu_power*min(SCHED_LOAD_SCALE, - max_load - tmp); + pwr_move += busiest->cpu_power * + min(busiest_load_per_task, max_load - tmp); /* Amount of load we'd add */ if (max_load*busiest->cpu_power < - SCHED_LOAD_SCALE*SCHED_LOAD_SCALE) + busiest_load_per_task*SCHED_LOAD_SCALE) tmp = max_load*busiest->cpu_power/this->cpu_power; else - tmp = SCHED_LOAD_SCALE*SCHED_LOAD_SCALE/this->cpu_power; - pwr_move += this->cpu_power*min(SCHED_LOAD_SCALE, this_load + tmp); + tmp = busiest_load_per_task*SCHED_LOAD_SCALE/this->cpu_power; + pwr_move += this->cpu_power*min(this_load_per_task, this_load + tmp); pwr_move /= SCHED_LOAD_SCALE; /* Move if we gain throughput */ if (pwr_move <= pwr_now) goto out_balanced; - *imbalance = 1; - return busiest; + *imbalance = busiest_load_per_task; } - /* Get rid of the scaling factor, rounding down as we divide */ - *imbalance = *imbalance / SCHED_LOAD_SCALE; return busiest; out_balanced: +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) + if (idle == NOT_IDLE || !(sd->flags & SD_POWERSAVINGS_BALANCE)) + goto ret; + if (this == group_leader && group_leader != group_min) { + *imbalance = min_load_per_task; + return group_min; + } +ret: +#endif *imbalance = 0; return NULL; } @@ -2087,19 +2428,23 @@ out_balanced: /* * find_busiest_queue - find the busiest runqueue among the cpus in group. */ -static runqueue_t *find_busiest_queue(struct sched_group *group, - enum idle_type idle) +static struct rq * +find_busiest_queue(struct sched_group *group, enum idle_type idle, + unsigned long imbalance) { - unsigned long load, max_load = 0; - runqueue_t *busiest = NULL; + struct rq *busiest = NULL, *rq; + unsigned long max_load = 0; int i; for_each_cpu_mask(i, group->cpumask) { - load = source_load(i, 0); + rq = cpu_rq(i); + + if (rq->nr_running == 1 && rq->raw_weighted_load > imbalance) + continue; - if (load > max_load) { - max_load = load; - busiest = cpu_rq(i); + if (rq->raw_weighted_load > max_load) { + max_load = rq->raw_weighted_load; + busiest = rq; } } @@ -2112,23 +2457,27 @@ static runqueue_t *find_busiest_queue(st */ #define MAX_PINNED_INTERVAL 512 +static inline unsigned long minus_1_or_zero(unsigned long n) +{ + return n > 0 ? n - 1 : 0; +} + /* * Check this_cpu to ensure it is balanced within domain. Attempt to move * tasks if there is an imbalance. * * Called with this_rq unlocked. */ -static int load_balance(int this_cpu, runqueue_t *this_rq, +static int load_balance(int this_cpu, struct rq *this_rq, struct sched_domain *sd, enum idle_type idle) { + int nr_moved, all_pinned = 0, active_balance = 0, sd_idle = 0; struct sched_group *group; - runqueue_t *busiest; unsigned long imbalance; - int nr_moved, all_pinned = 0; - int active_balance = 0; - int sd_idle = 0; + struct rq *busiest; - if (idle != NOT_IDLE && sd->flags & SD_SHARE_CPUPOWER) + if (idle != NOT_IDLE && sd->flags & SD_SHARE_CPUPOWER && + !sched_smt_power_savings) sd_idle = 1; schedstat_inc(sd, lb_cnt[idle]); @@ -2139,7 +2488,7 @@ static int load_balance(int this_cpu, ru goto out_balanced; } - busiest = find_busiest_queue(group, idle); + busiest = find_busiest_queue(group, idle, imbalance); if (!busiest) { schedstat_inc(sd, lb_nobusyq[idle]); goto out_balanced; @@ -2159,7 +2508,8 @@ static int load_balance(int this_cpu, ru */ double_rq_lock(this_rq, busiest); nr_moved = move_tasks(this_rq, this_cpu, busiest, - imbalance, sd, idle, &all_pinned); + minus_1_or_zero(busiest->nr_running), + imbalance, sd, idle, &all_pinned); double_rq_unlock(this_rq, busiest); /* All tasks on this runqueue were pinned by CPU affinity */ @@ -2216,7 +2566,8 @@ static int load_balance(int this_cpu, ru sd->balance_interval *= 2; } - if (!nr_moved && !sd_idle && sd->flags & SD_SHARE_CPUPOWER) + if (!nr_moved && !sd_idle && sd->flags & SD_SHARE_CPUPOWER && + !sched_smt_power_savings) return -1; return nr_moved; @@ -2231,7 +2582,8 @@ out_one_pinned: (sd->balance_interval < sd->max_interval)) sd->balance_interval *= 2; - if (!sd_idle && sd->flags & SD_SHARE_CPUPOWER) + if (!sd_idle && sd->flags & SD_SHARE_CPUPOWER && + !sched_smt_power_savings) return -1; return 0; } @@ -2243,16 +2595,16 @@ out_one_pinned: * Called from schedule when this_rq is about to become idle (NEWLY_IDLE). * this_rq is locked. */ -static int load_balance_newidle(int this_cpu, runqueue_t *this_rq, - struct sched_domain *sd) +static int +load_balance_newidle(int this_cpu, struct rq *this_rq, struct sched_domain *sd) { struct sched_group *group; - runqueue_t *busiest = NULL; + struct rq *busiest = NULL; unsigned long imbalance; int nr_moved = 0; int sd_idle = 0; - if (sd->flags & SD_SHARE_CPUPOWER) + if (sd->flags & SD_SHARE_CPUPOWER && !sched_smt_power_savings) sd_idle = 1; schedstat_inc(sd, lb_cnt[NEWLY_IDLE]); @@ -2262,7 +2614,7 @@ static int load_balance_newidle(int this goto out_balanced; } - busiest = find_busiest_queue(group, NEWLY_IDLE); + busiest = find_busiest_queue(group, NEWLY_IDLE, imbalance); if (!busiest) { schedstat_inc(sd, lb_nobusyq[NEWLY_IDLE]); goto out_balanced; @@ -2277,6 +2629,7 @@ static int load_balance_newidle(int this /* Attempt to move tasks */ double_lock_balance(this_rq, busiest); nr_moved = move_tasks(this_rq, this_cpu, busiest, + minus_1_or_zero(busiest->nr_running), imbalance, sd, NEWLY_IDLE, NULL); spin_unlock(&busiest->lock); } @@ -2292,9 +2645,11 @@ static int load_balance_newidle(int this out_balanced: schedstat_inc(sd, lb_balanced[NEWLY_IDLE]); - if (!sd_idle && sd->flags & SD_SHARE_CPUPOWER) + if (!sd_idle && sd->flags & SD_SHARE_CPUPOWER && + !sched_smt_power_savings) return -1; sd->nr_balance_failed = 0; + return 0; } @@ -2302,16 +2657,15 @@ out_balanced: * idle_balance is called by schedule() if this_cpu is about to become * idle. Attempts to pull tasks from other CPUs. */ -static void idle_balance(int this_cpu, runqueue_t *this_rq) +static void idle_balance(int this_cpu, struct rq *this_rq) { struct sched_domain *sd; for_each_domain(this_cpu, sd) { if (sd->flags & SD_BALANCE_NEWIDLE) { - if (load_balance_newidle(this_cpu, this_rq, sd)) { - /* We've pulled tasks over so stop searching */ + /* If we've pulled tasks over stop searching: */ + if (load_balance_newidle(this_cpu, this_rq, sd)) break; - } } } } @@ -2324,14 +2678,14 @@ static void idle_balance(int this_cpu, r * * Called with busiest_rq locked. */ -static void active_load_balance(runqueue_t *busiest_rq, int busiest_cpu) +static void active_load_balance(struct rq *busiest_rq, int busiest_cpu) { - struct sched_domain *sd; - runqueue_t *target_rq; int target_cpu = busiest_rq->push_cpu; + struct sched_domain *sd; + struct rq *target_rq; + /* Is there any task to move? */ if (busiest_rq->nr_running <= 1) - /* no task to move */ return; target_rq = cpu_rq(target_cpu); @@ -2347,21 +2701,22 @@ static void active_load_balance(runqueue double_lock_balance(busiest_rq, target_rq); /* Search for an sd spanning us and the target CPU. */ - for_each_domain(target_cpu, sd) + for_each_domain(target_cpu, sd) { if ((sd->flags & SD_LOAD_BALANCE) && - cpu_isset(busiest_cpu, sd->span)) + cpu_isset(busiest_cpu, sd->span)) break; + } - if (unlikely(sd == NULL)) - goto out; - - schedstat_inc(sd, alb_cnt); + if (likely(sd)) { + schedstat_inc(sd, alb_cnt); - if (move_tasks(target_rq, target_cpu, busiest_rq, 1, sd, SCHED_IDLE, NULL)) - schedstat_inc(sd, alb_pushed); - else - schedstat_inc(sd, alb_failed); -out: + if (move_tasks(target_rq, target_cpu, busiest_rq, 1, + RTPRIO_TO_LOAD_WEIGHT(100), sd, SCHED_IDLE, + NULL)) + schedstat_inc(sd, alb_pushed); + else + schedstat_inc(sd, alb_failed); + } spin_unlock(&target_rq->lock); } @@ -2374,23 +2729,27 @@ out: * Balancing parameters are set up in arch_init_sched_domains. */ -/* Don't have all balancing operations going off at once */ -#define CPU_OFFSET(cpu) (HZ * cpu / NR_CPUS) +/* Don't have all balancing operations going off at once: */ +static inline unsigned long cpu_offset(int cpu) +{ + return jiffies + cpu * HZ / NR_CPUS; +} -static void rebalance_tick(int this_cpu, runqueue_t *this_rq, - enum idle_type idle) +static void +rebalance_tick(int this_cpu, struct rq *this_rq, enum idle_type idle) { - unsigned long old_load, this_load; - unsigned long j = jiffies + CPU_OFFSET(this_cpu); + unsigned long this_load, interval, j = cpu_offset(this_cpu); struct sched_domain *sd; - int i; + int i, scale; + + this_load = this_rq->raw_weighted_load; + + /* Update our load: */ + for (i = 0, scale = 1; i < 3; i++, scale <<= 1) { + unsigned long old_load, new_load; - this_load = this_rq->nr_running * SCHED_LOAD_SCALE; - /* Update our load */ - for (i = 0; i < 3; i++) { - unsigned long new_load = this_load; - int scale = 1 << i; old_load = this_rq->cpu_load[i]; + new_load = this_load; /* * Round up the averaging division if load is increasing. This * prevents us from getting stuck on 9 if the load is 10, for @@ -2402,8 +2761,6 @@ static void rebalance_tick(int this_cpu, } for_each_domain(this_cpu, sd) { - unsigned long interval; - if (!(sd->flags & SD_LOAD_BALANCE)) continue; @@ -2433,17 +2790,18 @@ #else /* * on UP we do not need to balance between CPUs: */ -static inline void rebalance_tick(int cpu, runqueue_t *rq, enum idle_type idle) +static inline void rebalance_tick(int cpu, struct rq *rq, enum idle_type idle) { } -static inline void idle_balance(int cpu, runqueue_t *rq) +static inline void idle_balance(int cpu, struct rq *rq) { } #endif -static inline int wake_priority_sleeper(runqueue_t *rq) +static inline int wake_priority_sleeper(struct rq *rq) { int ret = 0; + #ifdef CONFIG_SCHED_SMT spin_lock(&rq->lock); /* @@ -2467,25 +2825,26 @@ EXPORT_PER_CPU_SYMBOL(kstat); * This is called on clock ticks and on context switches. * Bank in p->sched_time the ns elapsed since the last tick or switch. */ -static inline void update_cpu_clock(task_t *p, runqueue_t *rq, - unsigned long long now) +static inline void +update_cpu_clock(struct task_struct *p, struct rq *rq, unsigned long long now) { - unsigned long long last = max(p->timestamp, rq->timestamp_last_tick); - p->sched_time += now - last; + p->sched_time += now - max(p->timestamp, rq->timestamp_last_tick); } /* * Return current->sched_time plus any more ns on the sched_clock * that have not yet been banked. */ -unsigned long long current_sched_time(const task_t *tsk) +unsigned long long current_sched_time(const struct task_struct *p) { unsigned long long ns; unsigned long flags; + local_irq_save(flags); - ns = max(tsk->timestamp, task_rq(tsk)->timestamp_last_tick); - ns = tsk->sched_time + (sched_clock() - ns); + ns = max(p->timestamp, task_rq(p)->timestamp_last_tick); + ns = p->sched_time + sched_clock() - ns; local_irq_restore(flags); + return ns; } @@ -2499,11 +2858,16 @@ unsigned long long current_sched_time(co * increasing number of running tasks. We also ignore the interactivity * if a better static_prio task has expired: */ -#define EXPIRED_STARVING(rq) \ - ((STARVATION_LIMIT && ((rq)->expired_timestamp && \ - (jiffies - (rq)->expired_timestamp >= \ - STARVATION_LIMIT * ((rq)->nr_running) + 1))) || \ - ((rq)->curr->static_prio > (rq)->best_expired_prio)) +static inline int expired_starving(struct rq *rq) +{ + if (rq->curr->static_prio > rq->best_expired_prio) + return 1; + if (!STARVATION_LIMIT || !rq->expired_timestamp) + return 0; + if (jiffies - rq->expired_timestamp > STARVATION_LIMIT * rq->nr_running) + return 1; + return 0; +} /* * Account user cpu time to a process. @@ -2536,7 +2900,7 @@ void account_system_time(struct task_str cputime_t cputime) { struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; - runqueue_t *rq = this_rq(); + struct rq *rq = this_rq(); cputime64_t tmp; p->stime = cputime_add(p->stime, cputime); @@ -2566,7 +2930,7 @@ void account_steal_time(struct task_stru { struct cpu_usage_stat *cpustat = &kstat_this_cpu.cpustat; cputime64_t tmp = cputime_to_cputime64(steal); - runqueue_t *rq = this_rq(); + struct rq *rq = this_rq(); if (p == rq->idle) { p->stime = cputime_add(p->stime, steal); @@ -2587,10 +2951,10 @@ void account_steal_time(struct task_stru */ void scheduler_tick(void) { - int cpu = smp_processor_id(); - runqueue_t *rq = this_rq(); - task_t *p = current; unsigned long long now = sched_clock(); + struct task_struct *p = current; + int cpu = smp_processor_id(); + struct rq *rq = cpu_rq(cpu); update_cpu_clock(p, rq, now); @@ -2640,7 +3004,7 @@ void scheduler_tick(void) if (!rq->expired_timestamp) rq->expired_timestamp = jiffies; - if (!TASK_INTERACTIVE(p) || EXPIRED_STARVING(rq)) { + if (!TASK_INTERACTIVE(p) || expired_starving(rq)) { enqueue_task(p, rq->expired); if (p->static_prio < rq->best_expired_prio) rq->best_expired_prio = p->static_prio; @@ -2679,55 +3043,42 @@ out: } #ifdef CONFIG_SCHED_SMT -static inline void wakeup_busy_runqueue(runqueue_t *rq) +static inline void wakeup_busy_runqueue(struct rq *rq) { /* If an SMT runqueue is sleeping due to priority reasons wake it up */ if (rq->curr == rq->idle && rq->nr_running) resched_task(rq->idle); } -static void wake_sleeping_dependent(int this_cpu, runqueue_t *this_rq) +/* + * Called with interrupt disabled and this_rq's runqueue locked. + */ +static void wake_sleeping_dependent(int this_cpu) { struct sched_domain *tmp, *sd = NULL; - cpumask_t sibling_map; int i; - for_each_domain(this_cpu, tmp) - if (tmp->flags & SD_SHARE_CPUPOWER) + for_each_domain(this_cpu, tmp) { + if (tmp->flags & SD_SHARE_CPUPOWER) { sd = tmp; + break; + } + } if (!sd) return; - /* - * Unlock the current runqueue because we have to lock in - * CPU order to avoid deadlocks. Caller knows that we might - * unlock. We keep IRQs disabled. - */ - spin_unlock(&this_rq->lock); + for_each_cpu_mask(i, sd->span) { + struct rq *smt_rq = cpu_rq(i); - sibling_map = sd->span; - - for_each_cpu_mask(i, sibling_map) - spin_lock(&cpu_rq(i)->lock); - /* - * We clear this CPU from the mask. This both simplifies the - * inner loop and keps this_rq locked when we exit: - */ - cpu_clear(this_cpu, sibling_map); - - for_each_cpu_mask(i, sibling_map) { - runqueue_t *smt_rq = cpu_rq(i); + if (i == this_cpu) + continue; + if (unlikely(!spin_trylock(&smt_rq->lock))) + continue; wakeup_busy_runqueue(smt_rq); + spin_unlock(&smt_rq->lock); } - - for_each_cpu_mask(i, sibling_map) - spin_unlock(&cpu_rq(i)->lock); - /* - * We exit with this_cpu's rq still held and IRQs - * still disabled: - */ } /* @@ -2735,57 +3086,53 @@ static void wake_sleeping_dependent(int * utilize, if another task runs on a sibling. This models the * slowdown effect of other tasks running on siblings: */ -static inline unsigned long smt_slice(task_t *p, struct sched_domain *sd) +static inline unsigned long +smt_slice(struct task_struct *p, struct sched_domain *sd) { return p->time_slice * (100 - sd->per_cpu_gain) / 100; } -static int dependent_sleeper(int this_cpu, runqueue_t *this_rq) +/* + * To minimise lock contention and not have to drop this_rq's runlock we only + * trylock the sibling runqueues and bypass those runqueues if we fail to + * acquire their lock. As we only trylock the normal locking order does not + * need to be obeyed. + */ +static int +dependent_sleeper(int this_cpu, struct rq *this_rq, struct task_struct *p) { struct sched_domain *tmp, *sd = NULL; - cpumask_t sibling_map; - prio_array_t *array; int ret = 0, i; - task_t *p; - for_each_domain(this_cpu, tmp) - if (tmp->flags & SD_SHARE_CPUPOWER) + /* kernel/rt threads do not participate in dependent sleeping */ + if (!p->mm || rt_task(p)) + return 0; + + for_each_domain(this_cpu, tmp) { + if (tmp->flags & SD_SHARE_CPUPOWER) { sd = tmp; + break; + } + } if (!sd) return 0; - /* - * The same locking rules and details apply as for - * wake_sleeping_dependent(): - */ - spin_unlock(&this_rq->lock); - sibling_map = sd->span; - for_each_cpu_mask(i, sibling_map) - spin_lock(&cpu_rq(i)->lock); - cpu_clear(this_cpu, sibling_map); + for_each_cpu_mask(i, sd->span) { + struct task_struct *smt_curr; + struct rq *smt_rq; - /* - * Establish next task to be run - it might have gone away because - * we released the runqueue lock above: - */ - if (!this_rq->nr_running) - goto out_unlock; - array = this_rq->active; - if (!array->nr_active) - array = this_rq->expired; - BUG_ON(!array->nr_active); + if (i == this_cpu) + continue; - p = list_entry(array->queue[sched_find_first_bit(array->bitmap)].next, - task_t, run_list); + smt_rq = cpu_rq(i); + if (unlikely(!spin_trylock(&smt_rq->lock))) + continue; - for_each_cpu_mask(i, sibling_map) { - runqueue_t *smt_rq = cpu_rq(i); - task_t *smt_curr = smt_rq->curr; + smt_curr = smt_rq->curr; - /* Kernel threads do not participate in dependent sleeping */ - if (!p->mm || !smt_curr->mm || rt_task(p)) - goto check_smt_task; + if (!smt_curr->mm) + goto unlock; /* * If a user task with lower static priority than the @@ -2803,49 +3150,23 @@ static int dependent_sleeper(int this_cp if ((jiffies % DEF_TIMESLICE) > (sd->per_cpu_gain * DEF_TIMESLICE / 100)) ret = 1; - } else + } else { if (smt_curr->static_prio < p->static_prio && !TASK_PREEMPTS_CURR(p, smt_rq) && smt_slice(smt_curr, sd) > task_timeslice(p)) ret = 1; - -check_smt_task: - if ((!smt_curr->mm && smt_curr != smt_rq->idle) || - rt_task(smt_curr)) - continue; - if (!p->mm) { - wakeup_busy_runqueue(smt_rq); - continue; - } - - /* - * Reschedule a lower priority task on the SMT sibling for - * it to be put to sleep, or wake it up if it has been put to - * sleep for priority reasons to see if it should run now. - */ - if (rt_task(p)) { - if ((jiffies % DEF_TIMESLICE) > - (sd->per_cpu_gain * DEF_TIMESLICE / 100)) - resched_task(smt_curr); - } else { - if (TASK_PREEMPTS_CURR(p, smt_rq) && - smt_slice(p, sd) > task_timeslice(smt_curr)) - resched_task(smt_curr); - else - wakeup_busy_runqueue(smt_rq); } +unlock: + spin_unlock(&smt_rq->lock); } -out_unlock: - for_each_cpu_mask(i, sibling_map) - spin_unlock(&cpu_rq(i)->lock); return ret; } #else -static inline void wake_sleeping_dependent(int this_cpu, runqueue_t *this_rq) +static inline void wake_sleeping_dependent(int this_cpu) { } - -static inline int dependent_sleeper(int this_cpu, runqueue_t *this_rq) +static inline int +dependent_sleeper(int this_cpu, struct rq *this_rq, struct task_struct *p) { return 0; } @@ -2858,12 +3179,13 @@ void fastcall add_preempt_count(int val) /* * Underflow? */ - BUG_ON((preempt_count() < 0)); + if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0))) + return; preempt_count() += val; /* * Spinlock count overflowing soon? */ - BUG_ON((preempt_count() & PREEMPT_MASK) >= PREEMPT_MASK-10); + DEBUG_LOCKS_WARN_ON((preempt_count() & PREEMPT_MASK) >= PREEMPT_MASK-10); } EXPORT_SYMBOL(add_preempt_count); @@ -2872,11 +3194,15 @@ void fastcall sub_preempt_count(int val) /* * Underflow? */ - BUG_ON(val > preempt_count()); + if (DEBUG_LOCKS_WARN_ON(val > preempt_count())) + return; /* * Is the spinlock portion underflowing? */ - BUG_ON((val < PREEMPT_MASK) && !(preempt_count() & PREEMPT_MASK)); + if (DEBUG_LOCKS_WARN_ON((val < PREEMPT_MASK) && + !(preempt_count() & PREEMPT_MASK))) + return; + preempt_count() -= val; } EXPORT_SYMBOL(sub_preempt_count); @@ -2894,14 +3220,14 @@ static inline int interactive_sleep(enum */ asmlinkage void __sched schedule(void) { - long *switch_count; - task_t *prev, *next; - runqueue_t *rq; - prio_array_t *array; + struct task_struct *prev, *next; + struct prio_array *array; struct list_head *queue; unsigned long long now; unsigned long run_time; int cpu, idx, new_prio; + long *switch_count; + struct rq *rq; /* * Test if we are atomic. Since do_exit() needs to call into @@ -2967,32 +3293,13 @@ need_resched_nonpreemptible: cpu = smp_processor_id(); if (unlikely(!rq->nr_running)) { -go_idle: idle_balance(cpu, rq); if (!rq->nr_running) { next = rq->idle; rq->expired_timestamp = 0; - wake_sleeping_dependent(cpu, rq); - /* - * wake_sleeping_dependent() might have released - * the runqueue, so break out if we got new - * tasks meanwhile: - */ - if (!rq->nr_running) - goto switch_tasks; - } - } else { - if (dependent_sleeper(cpu, rq)) { - next = rq->idle; + wake_sleeping_dependent(cpu); goto switch_tasks; } - /* - * dependent_sleeper() releases and reacquires the runqueue - * lock, hence go into the idle loop if the rq went - * empty meanwhile: - */ - if (unlikely(!rq->nr_running)) - goto go_idle; } array = rq->active; @@ -3010,7 +3317,7 @@ go_idle: idx = sched_find_first_bit(array->bitmap); queue = array->queue + idx; - next = list_entry(queue->next, task_t, run_list); + next = list_entry(queue->next, struct task_struct, run_list); if (!rt_task(next) && interactive_sleep(next->sleep_type)) { unsigned long long delta = now - next->timestamp; @@ -3030,6 +3337,8 @@ go_idle: } } next->sleep_type = SLEEP_NORMAL; + if (dependent_sleeper(cpu, rq, next)) + next = rq->idle; switch_tasks: if (next == rq->idle) schedstat_inc(rq, sched_goidle); @@ -3071,7 +3380,6 @@ switch_tasks: if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) goto need_resched; } - EXPORT_SYMBOL(schedule); #ifdef CONFIG_PREEMPT @@ -3116,7 +3424,6 @@ #endif if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) goto need_resched; } - EXPORT_SYMBOL(preempt_schedule); /* @@ -3165,10 +3472,8 @@ #endif /* CONFIG_PREEMPT */ int default_wake_function(wait_queue_t *curr, unsigned mode, int sync, void *key) { - task_t *p = curr->private; - return try_to_wake_up(p, mode, sync); + return try_to_wake_up(curr->private, mode, sync); } - EXPORT_SYMBOL(default_wake_function); /* @@ -3186,13 +3491,11 @@ static void __wake_up_common(wait_queue_ struct list_head *tmp, *next; list_for_each_safe(tmp, next, &q->task_list) { - wait_queue_t *curr; - unsigned flags; - curr = list_entry(tmp, wait_queue_t, task_list); - flags = curr->flags; + wait_queue_t *curr = list_entry(tmp, wait_queue_t, task_list); + unsigned flags = curr->flags; + if (curr->func(curr, mode, sync, key) && - (flags & WQ_FLAG_EXCLUSIVE) && - !--nr_exclusive) + (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; } } @@ -3213,7 +3516,6 @@ void fastcall __wake_up(wait_queue_head_ __wake_up_common(q, mode, nr_exclusive, 0, key); spin_unlock_irqrestore(&q->lock, flags); } - EXPORT_SYMBOL(__wake_up); /* @@ -3282,6 +3584,7 @@ EXPORT_SYMBOL(complete_all); void fastcall __sched wait_for_completion(struct completion *x) { might_sleep(); + spin_lock_irq(&x->wait.lock); if (!x->done) { DECLARE_WAITQUEUE(wait, current); @@ -3426,7 +3729,6 @@ void fastcall __sched interruptible_slee schedule(); SLEEP_ON_TAIL } - EXPORT_SYMBOL(interruptible_sleep_on); long fastcall __sched @@ -3442,7 +3744,6 @@ interruptible_sleep_on_timeout(wait_queu return timeout; } - EXPORT_SYMBOL(interruptible_sleep_on_timeout); void fastcall __sched sleep_on(wait_queue_head_t *q) @@ -3455,7 +3756,6 @@ void fastcall __sched sleep_on(wait_queu schedule(); SLEEP_ON_TAIL } - EXPORT_SYMBOL(sleep_on); long fastcall __sched sleep_on_timeout(wait_queue_head_t *q, long timeout) @@ -3473,12 +3773,65 @@ long fastcall __sched sleep_on_timeout(w EXPORT_SYMBOL(sleep_on_timeout); -void set_user_nice(task_t *p, long nice) +#ifdef CONFIG_RT_MUTEXES + +/* + * rt_mutex_setprio - set the current priority of a task + * @p: task + * @prio: prio value (kernel-internal form) + * + * This function changes the 'effective' priority of a task. It does + * not touch ->normal_prio like __setscheduler(). + * + * Used by the rt_mutex code to implement priority inheritance logic. + */ +void rt_mutex_setprio(struct task_struct *p, int prio) { + struct prio_array *array; unsigned long flags; - prio_array_t *array; - runqueue_t *rq; - int old_prio, new_prio, delta; + struct rq *rq; + int oldprio; + + BUG_ON(prio < 0 || prio > MAX_PRIO); + + rq = task_rq_lock(p, &flags); + + oldprio = p->prio; + array = p->array; + if (array) + dequeue_task(p, array); + p->prio = prio; + + if (array) { + /* + * If changing to an RT priority then queue it + * in the active array! + */ + if (rt_task(p)) + array = rq->active; + enqueue_task(p, array); + /* + * Reschedule if we are currently running on this runqueue and + * our priority decreased, or if we are not currently running on + * this runqueue and our priority is higher than the current's + */ + if (task_running(rq, p)) { + if (p->prio > oldprio) + resched_task(rq->curr); + } else if (TASK_PREEMPTS_CURR(p, rq)) + resched_task(rq->curr); + } + task_rq_unlock(rq, &flags); +} + +#endif + +void set_user_nice(struct task_struct *p, long nice) +{ + struct prio_array *array; + int old_prio, delta; + unsigned long flags; + struct rq *rq; if (TASK_NICE(p) == nice || nice < -20 || nice > 19) return; @@ -3493,22 +3846,25 @@ void set_user_nice(task_t *p, long nice) * it wont have any effect on scheduling until the task is * not SCHED_NORMAL/SCHED_BATCH: */ - if (rt_task(p)) { + if (has_rt_policy(p)) { p->static_prio = NICE_TO_PRIO(nice); goto out_unlock; } array = p->array; - if (array) + if (array) { dequeue_task(p, array); + dec_raw_weighted_load(rq, p); + } - old_prio = p->prio; - new_prio = NICE_TO_PRIO(nice); - delta = new_prio - old_prio; p->static_prio = NICE_TO_PRIO(nice); - p->prio += delta; + set_load_weight(p); + old_prio = p->prio; + p->prio = effective_prio(p); + delta = p->prio - old_prio; if (array) { enqueue_task(p, array); + inc_raw_weighted_load(rq, p); /* * If the task increased its priority or is running and * lowered its priority, then reschedule its CPU: @@ -3519,7 +3875,6 @@ void set_user_nice(task_t *p, long nice) out_unlock: task_rq_unlock(rq, &flags); } - EXPORT_SYMBOL(set_user_nice); /* @@ -3527,10 +3882,11 @@ EXPORT_SYMBOL(set_user_nice); * @p: task * @nice: nice value */ -int can_nice(const task_t *p, const int nice) +int can_nice(const struct task_struct *p, const int nice) { /* convert nice value [19,-20] to rlimit style value [1,40] */ int nice_rlim = 20 - nice; + return (nice_rlim <= p->signal->rlim[RLIMIT_NICE].rlim_cur || capable(CAP_SYS_NICE)); } @@ -3546,8 +3902,7 @@ #ifdef __ARCH_WANT_SYS_NICE */ asmlinkage long sys_nice(int increment) { - int retval; - long nice; + long nice, retval; /* * Setpriority might change our priority at the same moment. @@ -3586,7 +3941,7 @@ #endif * RT tasks are offset by -200. Normal tasks are centered * around 0, value goes from -16 to +15. */ -int task_prio(const task_t *p) +int task_prio(const struct task_struct *p) { return p->prio - MAX_RT_PRIO; } @@ -3595,7 +3950,7 @@ int task_prio(const task_t *p) * task_nice - return the nice value of a given task. * @p: the task in question. */ -int task_nice(const task_t *p) +int task_nice(const struct task_struct *p) { return TASK_NICE(p); } @@ -3614,7 +3969,7 @@ int idle_cpu(int cpu) * idle_task - return the idle task for a given cpu. * @cpu: the processor in question. */ -task_t *idle_task(int cpu) +struct task_struct *idle_task(int cpu) { return cpu_rq(cpu)->idle; } @@ -3623,7 +3978,7 @@ task_t *idle_task(int cpu) * find_process_by_pid - find a process with a matching PID value. * @pid: the pid in question. */ -static inline task_t *find_process_by_pid(pid_t pid) +static inline struct task_struct *find_process_by_pid(pid_t pid) { return pid ? find_task_by_pid(pid) : current; } @@ -3632,18 +3987,18 @@ static inline task_t *find_process_by_pi static void __setscheduler(struct task_struct *p, int policy, int prio) { BUG_ON(p->array); + p->policy = policy; p->rt_priority = prio; - if (policy != SCHED_NORMAL && policy != SCHED_BATCH) { - p->prio = MAX_RT_PRIO-1 - p->rt_priority; - } else { - p->prio = p->static_prio; - /* - * SCHED_BATCH tasks are treated as perpetual CPU hogs: - */ - if (policy == SCHED_BATCH) - p->sleep_avg = 0; - } + p->normal_prio = normal_prio(p); + /* we are holding p->pi_lock already */ + p->prio = rt_mutex_getprio(p); + /* + * SCHED_BATCH tasks are treated as perpetual CPU hogs: + */ + if (policy == SCHED_BATCH) + p->sleep_avg = 0; + set_load_weight(p); } /** @@ -3656,12 +4011,13 @@ static void __setscheduler(struct task_s int sched_setscheduler(struct task_struct *p, int policy, struct sched_param *param) { - int retval; - int oldprio, oldpolicy = -1; - prio_array_t *array; + int retval, oldprio, oldpolicy = -1; + struct prio_array *array; unsigned long flags; - runqueue_t *rq; + struct rq *rq; + /* may grab non-irq protected spin_locks */ + BUG_ON(in_interrupt()); recheck: /* double check policy once rq lock held */ if (policy < 0) @@ -3710,14 +4066,20 @@ recheck: if (retval) return retval; /* + * make sure no PI-waiters arrive (or leave) while we are + * changing the priority of the task: + */ + spin_lock_irqsave(&p->pi_lock, flags); + /* * To be able to change p->policy safely, the apropriate * runqueue lock must be held. */ - rq = task_rq_lock(p, &flags); + rq = __task_rq_lock(p); /* recheck policy now with rq lock held */ if (unlikely(oldpolicy != -1 && oldpolicy != p->policy)) { policy = oldpolicy = -1; - task_rq_unlock(rq, &flags); + __task_rq_unlock(rq); + spin_unlock_irqrestore(&p->pi_lock, flags); goto recheck; } array = p->array; @@ -3738,7 +4100,11 @@ recheck: } else if (TASK_PREEMPTS_CURR(p, rq)) resched_task(rq->curr); } - task_rq_unlock(rq, &flags); + __task_rq_unlock(rq); + spin_unlock_irqrestore(&p->pi_lock, flags); + + rt_mutex_adjust_pi(p); + return 0; } EXPORT_SYMBOL_GPL(sched_setscheduler); @@ -3746,9 +4112,9 @@ EXPORT_SYMBOL_GPL(sched_setscheduler); static int do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param) { - int retval; struct sched_param lparam; struct task_struct *p; + int retval; if (!param || pid < 0) return -EINVAL; @@ -3760,8 +4126,11 @@ do_sched_setscheduler(pid_t pid, int pol read_unlock_irq(&tasklist_lock); return -ESRCH; } - retval = sched_setscheduler(p, policy, &lparam); + get_task_struct(p); read_unlock_irq(&tasklist_lock); + retval = sched_setscheduler(p, policy, &lparam); + put_task_struct(p); + return retval; } @@ -3797,8 +4166,8 @@ asmlinkage long sys_sched_setparam(pid_t */ asmlinkage long sys_sched_getscheduler(pid_t pid) { + struct task_struct *p; int retval = -EINVAL; - task_t *p; if (pid < 0) goto out_nounlock; @@ -3825,8 +4194,8 @@ out_nounlock: asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param __user *param) { struct sched_param lp; + struct task_struct *p; int retval = -EINVAL; - task_t *p; if (!param || pid < 0) goto out_nounlock; @@ -3859,9 +4228,9 @@ out_unlock: long sched_setaffinity(pid_t pid, cpumask_t new_mask) { - task_t *p; - int retval; cpumask_t cpus_allowed; + struct task_struct *p; + int retval; lock_cpu_hotplug(); read_lock(&tasklist_lock); @@ -3886,6 +4255,10 @@ long sched_setaffinity(pid_t pid, cpumas !capable(CAP_SYS_NICE)) goto out_unlock; + retval = security_task_setscheduler(p, 0, NULL); + if (retval) + goto out_unlock; + cpus_allowed = cpuset_cpus_allowed(p); cpus_and(new_mask, new_mask, cpus_allowed); retval = set_cpus_allowed(p, new_mask); @@ -3943,8 +4316,8 @@ #endif long sched_getaffinity(pid_t pid, cpumask_t *mask) { + struct task_struct *p; int retval; - task_t *p; lock_cpu_hotplug(); read_lock(&tasklist_lock); @@ -3954,7 +4327,10 @@ long sched_getaffinity(pid_t pid, cpumas if (!p) goto out_unlock; - retval = 0; + retval = security_task_getscheduler(p); + if (retval) + goto out_unlock; + cpus_and(*mask, p->cpus_allowed, cpu_online_map); out_unlock: @@ -4000,9 +4376,8 @@ asmlinkage long sys_sched_getaffinity(pi */ asmlinkage long sys_sched_yield(void) { - runqueue_t *rq = this_rq_lock(); - prio_array_t *array = current->array; - prio_array_t *target = rq->expired; + struct rq *rq = this_rq_lock(); + struct prio_array *array = current->array, *target = rq->expired; schedstat_inc(rq, yld_cnt); /* @@ -4036,6 +4411,7 @@ asmlinkage long sys_sched_yield(void) * no need to preempt or enable interrupts: */ __release(rq->lock); + spin_release(&rq->lock.dep_map, 1, _THIS_IP_); _raw_spin_unlock(&rq->lock); preempt_enable_no_resched(); @@ -4044,17 +4420,25 @@ asmlinkage long sys_sched_yield(void) return 0; } -static inline void __cond_resched(void) +static inline int __resched_legal(void) { + if (unlikely(preempt_count())) + return 0; + if (unlikely(system_state != SYSTEM_RUNNING)) + return 0; + return 1; +} + +static void __cond_resched(void) +{ +#ifdef CONFIG_DEBUG_SPINLOCK_SLEEP + __might_sleep(__FILE__, __LINE__); +#endif /* * The BKS might be reacquired before we have dropped * PREEMPT_ACTIVE, which could trigger a second * cond_resched() call. */ - if (unlikely(preempt_count())) - return; - if (unlikely(system_state != SYSTEM_RUNNING)) - return; do { add_preempt_count(PREEMPT_ACTIVE); schedule(); @@ -4064,13 +4448,12 @@ static inline void __cond_resched(void) int __sched cond_resched(void) { - if (need_resched()) { + if (need_resched() && __resched_legal()) { __cond_resched(); return 1; } return 0; } - EXPORT_SYMBOL(cond_resched); /* @@ -4091,7 +4474,8 @@ int cond_resched_lock(spinlock_t *lock) ret = 1; spin_lock(lock); } - if (need_resched()) { + if (need_resched() && __resched_legal()) { + spin_release(&lock->dep_map, 1, _THIS_IP_); _raw_spin_unlock(lock); preempt_enable_no_resched(); __cond_resched(); @@ -4100,25 +4484,24 @@ int cond_resched_lock(spinlock_t *lock) } return ret; } - EXPORT_SYMBOL(cond_resched_lock); int __sched cond_resched_softirq(void) { BUG_ON(!in_softirq()); - if (need_resched()) { - __local_bh_enable(); + if (need_resched() && __resched_legal()) { + raw_local_irq_disable(); + _local_bh_enable(); + raw_local_irq_enable(); __cond_resched(); local_bh_disable(); return 1; } return 0; } - EXPORT_SYMBOL(cond_resched_softirq); - /** * yield - yield the current processor to other threads. * @@ -4130,7 +4513,6 @@ void __sched yield(void) set_current_state(TASK_RUNNING); sys_sched_yield(); } - EXPORT_SYMBOL(yield); /* @@ -4142,18 +4524,17 @@ EXPORT_SYMBOL(yield); */ void __sched io_schedule(void) { - struct runqueue *rq = &per_cpu(runqueues, raw_smp_processor_id()); + struct rq *rq = &__raw_get_cpu_var(runqueues); atomic_inc(&rq->nr_iowait); schedule(); atomic_dec(&rq->nr_iowait); } - EXPORT_SYMBOL(io_schedule); long __sched io_schedule_timeout(long timeout) { - struct runqueue *rq = &per_cpu(runqueues, raw_smp_processor_id()); + struct rq *rq = &__raw_get_cpu_var(runqueues); long ret; atomic_inc(&rq->nr_iowait); @@ -4220,9 +4601,9 @@ asmlinkage long sys_sched_get_priority_m asmlinkage long sys_sched_rr_get_interval(pid_t pid, struct timespec __user *interval) { + struct task_struct *p; int retval = -EINVAL; struct timespec t; - task_t *p; if (pid < 0) goto out_nounlock; @@ -4237,7 +4618,7 @@ long sys_sched_rr_get_interval(pid_t pid if (retval) goto out_unlock; - jiffies_to_timespec(p->policy & SCHED_FIFO ? + jiffies_to_timespec(p->policy == SCHED_FIFO ? 0 : task_timeslice(p), &t); read_unlock(&tasklist_lock); retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0; @@ -4250,28 +4631,32 @@ out_unlock: static inline struct task_struct *eldest_child(struct task_struct *p) { - if (list_empty(&p->children)) return NULL; + if (list_empty(&p->children)) + return NULL; return list_entry(p->children.next,struct task_struct,sibling); } static inline struct task_struct *older_sibling(struct task_struct *p) { - if (p->sibling.prev==&p->parent->children) return NULL; + if (p->sibling.prev==&p->parent->children) + return NULL; return list_entry(p->sibling.prev,struct task_struct,sibling); } static inline struct task_struct *younger_sibling(struct task_struct *p) { - if (p->sibling.next==&p->parent->children) return NULL; + if (p->sibling.next==&p->parent->children) + return NULL; return list_entry(p->sibling.next,struct task_struct,sibling); } -static void show_task(task_t *p) +static const char *stat_nam[] = { "R", "S", "D", "T", "t", "Z", "X" }; + +static void show_task(struct task_struct *p) { - task_t *relative; - unsigned state; + struct task_struct *relative; unsigned long free = 0; - static const char *stat_nam[] = { "R", "S", "D", "T", "t", "Z", "X" }; + unsigned state; printk("%-13.13s ", p->comm); state = p->state ? __ffs(p->state) + 1 : 0; @@ -4322,7 +4707,7 @@ #endif void show_state(void) { - task_t *g, *p; + struct task_struct *g, *p; #if (BITS_PER_LONG == 32) printk("\n" @@ -4344,7 +4729,7 @@ #endif } while_each_thread(g, p); read_unlock(&tasklist_lock); - mutex_debug_show_all_locks(); + debug_show_all_locks(); } /** @@ -4355,15 +4740,15 @@ #endif * NOTE: this function does not set the idle thread's NEED_RESCHED * flag, to make booting more robust. */ -void __devinit init_idle(task_t *idle, int cpu) +void __devinit init_idle(struct task_struct *idle, int cpu) { - runqueue_t *rq = cpu_rq(cpu); + struct rq *rq = cpu_rq(cpu); unsigned long flags; idle->timestamp = sched_clock(); idle->sleep_avg = 0; idle->array = NULL; - idle->prio = MAX_PRIO; + idle->prio = idle->normal_prio = MAX_PRIO; idle->state = TASK_RUNNING; idle->cpus_allowed = cpumask_of_cpu(cpu); set_task_cpu(idle, cpu); @@ -4396,7 +4781,7 @@ #ifdef CONFIG_SMP /* * This is how migration works: * - * 1) we queue a migration_req_t structure in the source CPU's + * 1) we queue a struct migration_req structure in the source CPU's * runqueue and wake up that CPU's migration thread. * 2) we down() the locked semaphore => thread blocks. * 3) migration thread wakes up (implicitly it forces the migrated @@ -4418,12 +4803,12 @@ #ifdef CONFIG_SMP * task must not exit() & deallocate itself prematurely. The * call is not atomic; no spinlocks may be held. */ -int set_cpus_allowed(task_t *p, cpumask_t new_mask) +int set_cpus_allowed(struct task_struct *p, cpumask_t new_mask) { + struct migration_req req; unsigned long flags; + struct rq *rq; int ret = 0; - migration_req_t req; - runqueue_t *rq; rq = task_rq_lock(p, &flags); if (!cpus_intersects(new_mask, cpu_online_map)) { @@ -4446,9 +4831,9 @@ int set_cpus_allowed(task_t *p, cpumask_ } out: task_rq_unlock(rq, &flags); + return ret; } - EXPORT_SYMBOL_GPL(set_cpus_allowed); /* @@ -4459,13 +4844,16 @@ EXPORT_SYMBOL_GPL(set_cpus_allowed); * * So we race with normal scheduler movements, but that's OK, as long * as the task is no longer on this CPU. + * + * Returns non-zero if task was successfully migrated. */ -static void __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) +static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) { - runqueue_t *rq_dest, *rq_src; + struct rq *rq_dest, *rq_src; + int ret = 0; if (unlikely(cpu_is_offline(dest_cpu))) - return; + return ret; rq_src = cpu_rq(src_cpu); rq_dest = cpu_rq(dest_cpu); @@ -4493,9 +4881,10 @@ static void __migrate_task(struct task_s if (TASK_PREEMPTS_CURR(p, rq_dest)) resched_task(rq_dest->curr); } - + ret = 1; out: double_rq_unlock(rq_src, rq_dest); + return ret; } /* @@ -4505,16 +4894,16 @@ out: */ static int migration_thread(void *data) { - runqueue_t *rq; int cpu = (long)data; + struct rq *rq; rq = cpu_rq(cpu); BUG_ON(rq->migration_thread != current); set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { + struct migration_req *req; struct list_head *head; - migration_req_t *req; try_to_freeze(); @@ -4538,7 +4927,7 @@ static int migration_thread(void *data) set_current_state(TASK_INTERRUPTIBLE); continue; } - req = list_entry(head->next, migration_req_t, list); + req = list_entry(head->next, struct migration_req, list); list_del_init(head->next); spin_unlock(&rq->lock); @@ -4563,36 +4952,42 @@ wait_to_die: #ifdef CONFIG_HOTPLUG_CPU /* Figure out where task on dead CPU should go, use force if neccessary. */ -static void move_task_off_dead_cpu(int dead_cpu, struct task_struct *tsk) +static void move_task_off_dead_cpu(int dead_cpu, struct task_struct *p) { - int dest_cpu; + unsigned long flags; cpumask_t mask; + struct rq *rq; + int dest_cpu; +restart: /* On same node? */ mask = node_to_cpumask(cpu_to_node(dead_cpu)); - cpus_and(mask, mask, tsk->cpus_allowed); + cpus_and(mask, mask, p->cpus_allowed); dest_cpu = any_online_cpu(mask); /* On any allowed CPU? */ if (dest_cpu == NR_CPUS) - dest_cpu = any_online_cpu(tsk->cpus_allowed); + dest_cpu = any_online_cpu(p->cpus_allowed); /* No more Mr. Nice Guy. */ if (dest_cpu == NR_CPUS) { - cpus_setall(tsk->cpus_allowed); - dest_cpu = any_online_cpu(tsk->cpus_allowed); + rq = task_rq_lock(p, &flags); + cpus_setall(p->cpus_allowed); + dest_cpu = any_online_cpu(p->cpus_allowed); + task_rq_unlock(rq, &flags); /* * Don't tell them about moving exiting tasks or * kernel threads (both mm NULL), since they never * leave kernel. */ - if (tsk->mm && printk_ratelimit()) + if (p->mm && printk_ratelimit()) printk(KERN_INFO "process %d (%s) no " "longer affine to cpu%d\n", - tsk->pid, tsk->comm, dead_cpu); + p->pid, p->comm, dead_cpu); } - __migrate_task(tsk, dead_cpu, dest_cpu); + if (!__migrate_task(p, dead_cpu, dest_cpu)) + goto restart; } /* @@ -4602,9 +4997,9 @@ static void move_task_off_dead_cpu(int d * their home CPUs. So we just add the counter to another CPU's counter, * to keep the global sum constant after CPU-down: */ -static void migrate_nr_uninterruptible(runqueue_t *rq_src) +static void migrate_nr_uninterruptible(struct rq *rq_src) { - runqueue_t *rq_dest = cpu_rq(any_online_cpu(CPU_MASK_ALL)); + struct rq *rq_dest = cpu_rq(any_online_cpu(CPU_MASK_ALL)); unsigned long flags; local_irq_save(flags); @@ -4618,48 +5013,51 @@ static void migrate_nr_uninterruptible(r /* Run through task list and migrate tasks from the dead cpu. */ static void migrate_live_tasks(int src_cpu) { - struct task_struct *tsk, *t; + struct task_struct *p, *t; write_lock_irq(&tasklist_lock); - do_each_thread(t, tsk) { - if (tsk == current) + do_each_thread(t, p) { + if (p == current) continue; - if (task_cpu(tsk) == src_cpu) - move_task_off_dead_cpu(src_cpu, tsk); - } while_each_thread(t, tsk); + if (task_cpu(p) == src_cpu) + move_task_off_dead_cpu(src_cpu, p); + } while_each_thread(t, p); write_unlock_irq(&tasklist_lock); } /* Schedules idle task to be the next runnable task on current CPU. * It does so by boosting its priority to highest possible and adding it to - * the _front_ of runqueue. Used by CPU offline code. + * the _front_ of the runqueue. Used by CPU offline code. */ void sched_idle_next(void) { - int cpu = smp_processor_id(); - runqueue_t *rq = this_rq(); + int this_cpu = smp_processor_id(); + struct rq *rq = cpu_rq(this_cpu); struct task_struct *p = rq->idle; unsigned long flags; /* cpu has to be offline */ - BUG_ON(cpu_online(cpu)); + BUG_ON(cpu_online(this_cpu)); - /* Strictly not necessary since rest of the CPUs are stopped by now - * and interrupts disabled on current cpu. + /* + * Strictly not necessary since rest of the CPUs are stopped by now + * and interrupts disabled on the current cpu. */ spin_lock_irqsave(&rq->lock, flags); __setscheduler(p, SCHED_FIFO, MAX_RT_PRIO-1); - /* Add idle task to _front_ of it's priority queue */ + + /* Add idle task to the _front_ of its priority queue: */ __activate_idle_task(p, rq); spin_unlock_irqrestore(&rq->lock, flags); } -/* Ensures that the idle task is using init_mm right before its cpu goes +/* + * Ensures that the idle task is using init_mm right before its cpu goes * offline. */ void idle_task_exit(void) @@ -4673,17 +5071,17 @@ void idle_task_exit(void) mmdrop(mm); } -static void migrate_dead(unsigned int dead_cpu, task_t *tsk) +static void migrate_dead(unsigned int dead_cpu, struct task_struct *p) { - struct runqueue *rq = cpu_rq(dead_cpu); + struct rq *rq = cpu_rq(dead_cpu); /* Must be exiting, otherwise would be on tasklist. */ - BUG_ON(tsk->exit_state != EXIT_ZOMBIE && tsk->exit_state != EXIT_DEAD); + BUG_ON(p->exit_state != EXIT_ZOMBIE && p->exit_state != EXIT_DEAD); /* Cannot have done final schedule yet: would have vanished. */ - BUG_ON(tsk->flags & PF_DEAD); + BUG_ON(p->flags & PF_DEAD); - get_task_struct(tsk); + get_task_struct(p); /* * Drop lock around migration; if someone else moves it, @@ -4691,25 +5089,25 @@ static void migrate_dead(unsigned int de * fine. */ spin_unlock_irq(&rq->lock); - move_task_off_dead_cpu(dead_cpu, tsk); + move_task_off_dead_cpu(dead_cpu, p); spin_lock_irq(&rq->lock); - put_task_struct(tsk); + put_task_struct(p); } /* release_task() removes task from tasklist, so we won't find dead tasks. */ static void migrate_dead_tasks(unsigned int dead_cpu) { - unsigned arr, i; - struct runqueue *rq = cpu_rq(dead_cpu); + struct rq *rq = cpu_rq(dead_cpu); + unsigned int arr, i; for (arr = 0; arr < 2; arr++) { for (i = 0; i < MAX_PRIO; i++) { struct list_head *list = &rq->arrays[arr].queue[i]; + while (!list_empty(list)) - migrate_dead(dead_cpu, - list_entry(list->next, task_t, - run_list)); + migrate_dead(dead_cpu, list_entry(list->next, + struct task_struct, run_list)); } } } @@ -4719,13 +5117,13 @@ #endif /* CONFIG_HOTPLUG_CPU */ * migration_call - callback that gets triggered when a CPU is added. * Here we can start up the necessary migration thread for the new CPU. */ -static int migration_call(struct notifier_block *nfb, unsigned long action, - void *hcpu) +static int __cpuinit +migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) { - int cpu = (long)hcpu; struct task_struct *p; - struct runqueue *rq; + int cpu = (long)hcpu; unsigned long flags; + struct rq *rq; switch (action) { case CPU_UP_PREPARE: @@ -4740,18 +5138,23 @@ static int migration_call(struct notifie task_rq_unlock(rq, &flags); cpu_rq(cpu)->migration_thread = p; break; + case CPU_ONLINE: /* Strictly unneccessary, as first user will wake it. */ wake_up_process(cpu_rq(cpu)->migration_thread); break; + #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: + if (!cpu_rq(cpu)->migration_thread) + break; /* Unbind it from offline cpu so it can run. Fall thru. */ kthread_bind(cpu_rq(cpu)->migration_thread, any_online_cpu(cpu_online_map)); kthread_stop(cpu_rq(cpu)->migration_thread); cpu_rq(cpu)->migration_thread = NULL; break; + case CPU_DEAD: migrate_live_tasks(cpu); rq = cpu_rq(cpu); @@ -4772,9 +5175,10 @@ #ifdef CONFIG_HOTPLUG_CPU * the requestors. */ spin_lock_irq(&rq->lock); while (!list_empty(&rq->migration_queue)) { - migration_req_t *req; + struct migration_req *req; + req = list_entry(rq->migration_queue.next, - migration_req_t, list); + struct migration_req, list); list_del_init(&req->list); complete(&req->done); } @@ -4788,7 +5192,7 @@ #endif /* Register at highest priority so that task migration (migrate_all_tasks) * happens before everything else. */ -static struct notifier_block migration_notifier = { +static struct notifier_block __cpuinitdata migration_notifier = { .notifier_call = migration_call, .priority = 10 }; @@ -4796,10 +5200,12 @@ static struct notifier_block migration_n int __init migration_init(void) { void *cpu = (void *)(long)smp_processor_id(); - /* Start one for boot CPU. */ + + /* Start one for the boot CPU: */ migration_call(&migration_notifier, CPU_UP_PREPARE, cpu); migration_call(&migration_notifier, CPU_ONLINE, cpu); register_cpu_notifier(&migration_notifier); + return 0; } #endif @@ -4895,7 +5301,7 @@ static void sched_domain_debug(struct sc } while (sd); } #else -#define sched_domain_debug(sd, cpu) {} +# define sched_domain_debug(sd, cpu) do { } while (0) #endif static int sd_degenerate(struct sched_domain *sd) @@ -4921,8 +5327,8 @@ static int sd_degenerate(struct sched_do return 1; } -static int sd_parent_degenerate(struct sched_domain *sd, - struct sched_domain *parent) +static int +sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent) { unsigned long cflags = sd->flags, pflags = parent->flags; @@ -4955,7 +5361,7 @@ static int sd_parent_degenerate(struct s */ static void cpu_attach_domain(struct sched_domain *sd, int cpu) { - runqueue_t *rq = cpu_rq(cpu); + struct rq *rq = cpu_rq(cpu); struct sched_domain *tmp; /* Remove the sched domains which do not contribute to scheduling. */ @@ -5217,8 +5623,8 @@ static void touch_cache(void *__cache, u /* * Measure the cache-cost of one task migration. Returns in units of nsec. */ -static unsigned long long measure_one(void *cache, unsigned long size, - int source, int target) +static unsigned long long +measure_one(void *cache, unsigned long size, int source, int target) { cpumask_t mask, saved_mask; unsigned long long t0, t1, t2, t3, cost; @@ -5568,9 +5974,9 @@ static int find_next_best_node(int node, */ static cpumask_t sched_domain_node_span(int node) { - int i; - cpumask_t span, nodemask; DECLARE_BITMAP(used_nodes, MAX_NUMNODES); + cpumask_t span, nodemask; + int i; cpus_clear(span); bitmap_zero(used_nodes, MAX_NUMNODES); @@ -5581,6 +5987,7 @@ static cpumask_t sched_domain_node_span( for (i = 1; i < SD_NODES_PER_DOMAIN; i++) { int next_node = find_next_best_node(node, used_nodes); + nodemask = node_to_cpumask(next_node); cpus_or(span, span, nodemask); } @@ -5589,22 +5996,27 @@ static cpumask_t sched_domain_node_span( } #endif +int sched_smt_power_savings = 0, sched_mc_power_savings = 0; + /* - * At the moment, CONFIG_SCHED_SMT is never defined, but leave it in so we - * can switch it on easily if needed. + * SMT sched-domains: */ #ifdef CONFIG_SCHED_SMT static DEFINE_PER_CPU(struct sched_domain, cpu_domains); static struct sched_group sched_group_cpus[NR_CPUS]; + static int cpu_to_cpu_group(int cpu) { return cpu; } #endif +/* + * multi-core sched-domains: + */ #ifdef CONFIG_SCHED_MC static DEFINE_PER_CPU(struct sched_domain, core_domains); -static struct sched_group sched_group_core[NR_CPUS]; +static struct sched_group *sched_group_core_bycpu[NR_CPUS]; #endif #if defined(CONFIG_SCHED_MC) && defined(CONFIG_SCHED_SMT) @@ -5620,10 +6032,11 @@ static int cpu_to_core_group(int cpu) #endif static DEFINE_PER_CPU(struct sched_domain, phys_domains); -static struct sched_group sched_group_phys[NR_CPUS]; +static struct sched_group *sched_group_phys_bycpu[NR_CPUS]; + static int cpu_to_phys_group(int cpu) { -#if defined(CONFIG_SCHED_MC) +#ifdef CONFIG_SCHED_MC cpumask_t mask = cpu_coregroup_map(cpu); return first_cpu(mask); #elif defined(CONFIG_SCHED_SMT) @@ -5677,13 +6090,74 @@ next_sg: } #endif +/* Free memory allocated for various sched_group structures */ +static void free_sched_groups(const cpumask_t *cpu_map) +{ + int cpu; +#ifdef CONFIG_NUMA + int i; + + for_each_cpu_mask(cpu, *cpu_map) { + struct sched_group *sched_group_allnodes + = sched_group_allnodes_bycpu[cpu]; + struct sched_group **sched_group_nodes + = sched_group_nodes_bycpu[cpu]; + + if (sched_group_allnodes) { + kfree(sched_group_allnodes); + sched_group_allnodes_bycpu[cpu] = NULL; + } + + if (!sched_group_nodes) + continue; + + for (i = 0; i < MAX_NUMNODES; i++) { + cpumask_t nodemask = node_to_cpumask(i); + struct sched_group *oldsg, *sg = sched_group_nodes[i]; + + cpus_and(nodemask, nodemask, *cpu_map); + if (cpus_empty(nodemask)) + continue; + + if (sg == NULL) + continue; + sg = sg->next; +next_sg: + oldsg = sg; + sg = sg->next; + kfree(oldsg); + if (oldsg != sched_group_nodes[i]) + goto next_sg; + } + kfree(sched_group_nodes); + sched_group_nodes_bycpu[cpu] = NULL; + } +#endif + for_each_cpu_mask(cpu, *cpu_map) { + if (sched_group_phys_bycpu[cpu]) { + kfree(sched_group_phys_bycpu[cpu]); + sched_group_phys_bycpu[cpu] = NULL; + } +#ifdef CONFIG_SCHED_MC + if (sched_group_core_bycpu[cpu]) { + kfree(sched_group_core_bycpu[cpu]); + sched_group_core_bycpu[cpu] = NULL; + } +#endif + } +} + /* * Build sched domains for a given set of cpus and attach the sched domains * to the individual cpus */ -void build_sched_domains(const cpumask_t *cpu_map) +static int build_sched_domains(const cpumask_t *cpu_map) { int i; + struct sched_group *sched_group_phys = NULL; +#ifdef CONFIG_SCHED_MC + struct sched_group *sched_group_core = NULL; +#endif #ifdef CONFIG_NUMA struct sched_group **sched_group_nodes = NULL; struct sched_group *sched_group_allnodes = NULL; @@ -5691,11 +6165,11 @@ #ifdef CONFIG_NUMA /* * Allocate the per-node list of sched groups */ - sched_group_nodes = kmalloc(sizeof(struct sched_group*)*MAX_NUMNODES, - GFP_ATOMIC); + sched_group_nodes = kzalloc(sizeof(struct sched_group*)*MAX_NUMNODES, + GFP_KERNEL); if (!sched_group_nodes) { printk(KERN_WARNING "Can not alloc sched group node list\n"); - return; + return -ENOMEM; } sched_group_nodes_bycpu[first_cpu(*cpu_map)] = sched_group_nodes; #endif @@ -5721,7 +6195,7 @@ #ifdef CONFIG_NUMA if (!sched_group_allnodes) { printk(KERN_WARNING "Can not alloc allnodes sched group\n"); - break; + goto error; } sched_group_allnodes_bycpu[i] = sched_group_allnodes; @@ -5742,6 +6216,18 @@ #ifdef CONFIG_NUMA cpus_and(sd->span, sd->span, *cpu_map); #endif + if (!sched_group_phys) { + sched_group_phys + = kmalloc(sizeof(struct sched_group) * NR_CPUS, + GFP_KERNEL); + if (!sched_group_phys) { + printk (KERN_WARNING "Can not alloc phys sched" + "group\n"); + goto error; + } + sched_group_phys_bycpu[i] = sched_group_phys; + } + p = sd; sd = &per_cpu(phys_domains, i); group = cpu_to_phys_group(i); @@ -5751,6 +6237,18 @@ #endif sd->groups = &sched_group_phys[group]; #ifdef CONFIG_SCHED_MC + if (!sched_group_core) { + sched_group_core + = kmalloc(sizeof(struct sched_group) * NR_CPUS, + GFP_KERNEL); + if (!sched_group_core) { + printk (KERN_WARNING "Can not alloc core sched" + "group\n"); + goto error; + } + sched_group_core_bycpu[i] = sched_group_core; + } + p = sd; sd = &per_cpu(core_domains, i); group = cpu_to_core_group(i); @@ -5834,24 +6332,21 @@ #ifdef CONFIG_NUMA domainspan = sched_domain_node_span(i); cpus_and(domainspan, domainspan, *cpu_map); - sg = kmalloc(sizeof(struct sched_group), GFP_KERNEL); + sg = kmalloc_node(sizeof(struct sched_group), GFP_KERNEL, i); + if (!sg) { + printk(KERN_WARNING "Can not alloc domain group for " + "node %d\n", i); + goto error; + } sched_group_nodes[i] = sg; for_each_cpu_mask(j, nodemask) { struct sched_domain *sd; sd = &per_cpu(node_domains, j); sd->groups = sg; - if (sd->groups == NULL) { - /* Turn off balancing if we have no groups */ - sd->flags = 0; - } - } - if (!sg) { - printk(KERN_WARNING - "Can not alloc domain group for node %d\n", i); - continue; } sg->cpu_power = 0; sg->cpumask = nodemask; + sg->next = sg; cpus_or(covered, covered, nodemask); prev = sg; @@ -5870,54 +6365,90 @@ #ifdef CONFIG_NUMA if (cpus_empty(tmp)) continue; - sg = kmalloc(sizeof(struct sched_group), GFP_KERNEL); + sg = kmalloc_node(sizeof(struct sched_group), + GFP_KERNEL, i); if (!sg) { printk(KERN_WARNING "Can not alloc domain group for node %d\n", j); - break; + goto error; } sg->cpu_power = 0; sg->cpumask = tmp; + sg->next = prev->next; cpus_or(covered, covered, tmp); prev->next = sg; prev = sg; } - prev->next = sched_group_nodes[i]; } #endif /* Calculate CPU power for physical packages and nodes */ +#ifdef CONFIG_SCHED_SMT for_each_cpu_mask(i, *cpu_map) { - int power; struct sched_domain *sd; -#ifdef CONFIG_SCHED_SMT sd = &per_cpu(cpu_domains, i); - power = SCHED_LOAD_SCALE; - sd->groups->cpu_power = power; + sd->groups->cpu_power = SCHED_LOAD_SCALE; + } #endif #ifdef CONFIG_SCHED_MC + for_each_cpu_mask(i, *cpu_map) { + int power; + struct sched_domain *sd; sd = &per_cpu(core_domains, i); - power = SCHED_LOAD_SCALE + (cpus_weight(sd->groups->cpumask)-1) + if (sched_smt_power_savings) + power = SCHED_LOAD_SCALE * cpus_weight(sd->groups->cpumask); + else + power = SCHED_LOAD_SCALE + (cpus_weight(sd->groups->cpumask)-1) * SCHED_LOAD_SCALE / 10; sd->groups->cpu_power = power; + } +#endif + for_each_cpu_mask(i, *cpu_map) { + struct sched_domain *sd; +#ifdef CONFIG_SCHED_MC sd = &per_cpu(phys_domains, i); + if (i != first_cpu(sd->groups->cpumask)) + continue; - /* - * This has to be < 2 * SCHED_LOAD_SCALE - * Lets keep it SCHED_LOAD_SCALE, so that - * while calculating NUMA group's cpu_power - * we can simply do - * numa_group->cpu_power += phys_group->cpu_power; - * - * See "only add power once for each physical pkg" - * comment below - */ - sd->groups->cpu_power = SCHED_LOAD_SCALE; + sd->groups->cpu_power = 0; + if (sched_mc_power_savings || sched_smt_power_savings) { + int j; + + for_each_cpu_mask(j, sd->groups->cpumask) { + struct sched_domain *sd1; + sd1 = &per_cpu(core_domains, j); + /* + * for each core we will add once + * to the group in physical domain + */ + if (j != first_cpu(sd1->groups->cpumask)) + continue; + + if (sched_smt_power_savings) + sd->groups->cpu_power += sd1->groups->cpu_power; + else + sd->groups->cpu_power += SCHED_LOAD_SCALE; + } + } else + /* + * This has to be < 2 * SCHED_LOAD_SCALE + * Lets keep it SCHED_LOAD_SCALE, so that + * while calculating NUMA group's cpu_power + * we can simply do + * numa_group->cpu_power += phys_group->cpu_power; + * + * See "only add power once for each physical pkg" + * comment below + */ + sd->groups->cpu_power = SCHED_LOAD_SCALE; #else + int power; sd = &per_cpu(phys_domains, i); - power = SCHED_LOAD_SCALE + SCHED_LOAD_SCALE * - (cpus_weight(sd->groups->cpumask)-1) / 10; + if (sched_smt_power_savings) + power = SCHED_LOAD_SCALE * cpus_weight(sd->groups->cpumask); + else + power = SCHED_LOAD_SCALE; sd->groups->cpu_power = power; #endif } @@ -5945,13 +6476,20 @@ #endif * Tune cache-hot values: */ calibrate_migration_costs(cpu_map); + + return 0; + +error: + free_sched_groups(cpu_map); + return -ENOMEM; } /* * Set up scheduler domains and groups. Callers must hold the hotplug lock. */ -static void arch_init_sched_domains(const cpumask_t *cpu_map) +static int arch_init_sched_domains(const cpumask_t *cpu_map) { cpumask_t cpu_default_map; + int err; /* * Setup mask for cpus without special case scheduling requirements. @@ -5960,51 +6498,14 @@ static void arch_init_sched_domains(cons */ cpus_andnot(cpu_default_map, *cpu_map, cpu_isolated_map); - build_sched_domains(&cpu_default_map); + err = build_sched_domains(&cpu_default_map); + + return err; } static void arch_destroy_sched_domains(const cpumask_t *cpu_map) { -#ifdef CONFIG_NUMA - int i; - int cpu; - - for_each_cpu_mask(cpu, *cpu_map) { - struct sched_group *sched_group_allnodes - = sched_group_allnodes_bycpu[cpu]; - struct sched_group **sched_group_nodes - = sched_group_nodes_bycpu[cpu]; - - if (sched_group_allnodes) { - kfree(sched_group_allnodes); - sched_group_allnodes_bycpu[cpu] = NULL; - } - - if (!sched_group_nodes) - continue; - - for (i = 0; i < MAX_NUMNODES; i++) { - cpumask_t nodemask = node_to_cpumask(i); - struct sched_group *oldsg, *sg = sched_group_nodes[i]; - - cpus_and(nodemask, nodemask, *cpu_map); - if (cpus_empty(nodemask)) - continue; - - if (sg == NULL) - continue; - sg = sg->next; -next_sg: - oldsg = sg; - sg = sg->next; - kfree(oldsg); - if (oldsg != sched_group_nodes[i]) - goto next_sg; - } - kfree(sched_group_nodes); - sched_group_nodes_bycpu[cpu] = NULL; - } -#endif + free_sched_groups(cpu_map); } /* @@ -6029,9 +6530,10 @@ static void detach_destroy_domains(const * correct sched domains * Call with hotplug lock held */ -void partition_sched_domains(cpumask_t *partition1, cpumask_t *partition2) +int partition_sched_domains(cpumask_t *partition1, cpumask_t *partition2) { cpumask_t change_map; + int err = 0; cpus_and(*partition1, *partition1, cpu_online_map); cpus_and(*partition2, *partition2, cpu_online_map); @@ -6040,11 +6542,90 @@ void partition_sched_domains(cpumask_t * /* Detach sched domains from all of the affected cpus */ detach_destroy_domains(&change_map); if (!cpus_empty(*partition1)) - build_sched_domains(partition1); - if (!cpus_empty(*partition2)) - build_sched_domains(partition2); + err = build_sched_domains(partition1); + if (!err && !cpus_empty(*partition2)) + err = build_sched_domains(partition2); + + return err; +} + +#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) +int arch_reinit_sched_domains(void) +{ + int err; + + lock_cpu_hotplug(); + detach_destroy_domains(&cpu_online_map); + err = arch_init_sched_domains(&cpu_online_map); + unlock_cpu_hotplug(); + + return err; } +static ssize_t sched_power_savings_store(const char *buf, size_t count, int smt) +{ + int ret; + + if (buf[0] != '0' && buf[0] != '1') + return -EINVAL; + + if (smt) + sched_smt_power_savings = (buf[0] == '1'); + else + sched_mc_power_savings = (buf[0] == '1'); + + ret = arch_reinit_sched_domains(); + + return ret ? ret : count; +} + +int sched_create_sysfs_power_savings_entries(struct sysdev_class *cls) +{ + int err = 0; + +#ifdef CONFIG_SCHED_SMT + if (smt_capable()) + err = sysfs_create_file(&cls->kset.kobj, + &attr_sched_smt_power_savings.attr); +#endif +#ifdef CONFIG_SCHED_MC + if (!err && mc_capable()) + err = sysfs_create_file(&cls->kset.kobj, + &attr_sched_mc_power_savings.attr); +#endif + return err; +} +#endif + +#ifdef CONFIG_SCHED_MC +static ssize_t sched_mc_power_savings_show(struct sys_device *dev, char *page) +{ + return sprintf(page, "%u\n", sched_mc_power_savings); +} +static ssize_t sched_mc_power_savings_store(struct sys_device *dev, + const char *buf, size_t count) +{ + return sched_power_savings_store(buf, count, 0); +} +SYSDEV_ATTR(sched_mc_power_savings, 0644, sched_mc_power_savings_show, + sched_mc_power_savings_store); +#endif + +#ifdef CONFIG_SCHED_SMT +static ssize_t sched_smt_power_savings_show(struct sys_device *dev, char *page) +{ + return sprintf(page, "%u\n", sched_smt_power_savings); +} +static ssize_t sched_smt_power_savings_store(struct sys_device *dev, + const char *buf, size_t count) +{ + return sched_power_savings_store(buf, count, 1); +} +SYSDEV_ATTR(sched_smt_power_savings, 0644, sched_smt_power_savings_show, + sched_smt_power_savings_store); +#endif + + #ifdef CONFIG_HOTPLUG_CPU /* * Force a reinitialization of the sched domains hierarchy. The domains @@ -6098,6 +6679,7 @@ int in_sched_functions(unsigned long add { /* Linker adds these: start and end of __sched functions */ extern char __sched_text_start[], __sched_text_end[]; + return in_lock_functions(addr) || (addr >= (unsigned long)__sched_text_start && addr < (unsigned long)__sched_text_end); @@ -6105,14 +6687,15 @@ int in_sched_functions(unsigned long add void __init sched_init(void) { - runqueue_t *rq; int i, j, k; for_each_possible_cpu(i) { - prio_array_t *array; + struct prio_array *array; + struct rq *rq; rq = cpu_rq(i); spin_lock_init(&rq->lock); + lockdep_set_class(&rq->lock, &rq->rq_lock_key); rq->nr_running = 0; rq->active = rq->arrays; rq->expired = rq->arrays + 1; @@ -6126,7 +6709,6 @@ #ifdef CONFIG_SMP rq->push_cpu = 0; rq->migration_thread = NULL; INIT_LIST_HEAD(&rq->migration_queue); - rq->cpu = i; #endif atomic_set(&rq->nr_iowait, 0); @@ -6141,6 +6723,7 @@ #endif } } + set_load_weight(&init_task); /* * The boot idle thread does lazy MMU switching as well: */ @@ -6159,7 +6742,7 @@ #endif #ifdef CONFIG_DEBUG_SPINLOCK_SLEEP void __might_sleep(char *file, int line) { -#if defined(in_atomic) +#ifdef in_atomic static unsigned long prev_jiffy; /* ratelimiting */ if ((in_atomic() || irqs_disabled()) && @@ -6181,17 +6764,18 @@ #endif #ifdef CONFIG_MAGIC_SYSRQ void normalize_rt_tasks(void) { + struct prio_array *array; struct task_struct *p; - prio_array_t *array; unsigned long flags; - runqueue_t *rq; + struct rq *rq; read_lock_irq(&tasklist_lock); - for_each_process (p) { + for_each_process(p) { if (!rt_task(p)) continue; - rq = task_rq_lock(p, &flags); + spin_lock_irqsave(&p->pi_lock, flags); + rq = __task_rq_lock(p); array = p->array; if (array) @@ -6202,7 +6786,8 @@ void normalize_rt_tasks(void) resched_task(rq->curr); } - task_rq_unlock(rq, &flags); + __task_rq_unlock(rq); + spin_unlock_irqrestore(&p->pi_lock, flags); } read_unlock_irq(&tasklist_lock); } @@ -6226,7 +6811,7 @@ #ifdef CONFIG_IA64 * * ONLY VALID WHEN THE WHOLE SYSTEM IS STOPPED! */ -task_t *curr_task(int cpu) +struct task_struct *curr_task(int cpu) { return cpu_curr(cpu); } @@ -6246,7 +6831,7 @@ task_t *curr_task(int cpu) * * ONLY VALID WHEN THE WHOLE SYSTEM IS STOPPED! */ -void set_curr_task(int cpu, task_t *p) +void set_curr_task(int cpu, struct task_struct *p) { cpu_curr(cpu) = p; } diff --git a/kernel/signal.c b/kernel/signal.c index e5f8aea..7fe874d 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -10,7 +10,6 @@ * to allow signals to be sent reliably. */ -#include #include #include #include @@ -23,12 +22,12 @@ #include #include #include #include -#include #include #include #include #include #include +#include "audit.h" /* audit_signal_info() */ /* * SLAB caches for signal bits. @@ -584,7 +583,7 @@ static int check_kill_permission(int sig && !capable(CAP_KILL)) return error; - error = security_task_kill(t, info, sig); + error = security_task_kill(t, info, sig, 0); if (!error) audit_signal_info(sig, t); /* Let audit system see the signal */ return error; @@ -1107,7 +1106,7 @@ kill_proc_info(int sig, struct siginfo * /* like kill_proc_info(), but doesn't use uid/euid of "current" */ int kill_proc_info_as_uid(int sig, struct siginfo *info, pid_t pid, - uid_t uid, uid_t euid) + uid_t uid, uid_t euid, u32 secid) { int ret = -EINVAL; struct task_struct *p; @@ -1127,6 +1126,9 @@ int kill_proc_info_as_uid(int sig, struc ret = -EPERM; goto out_unlock; } + ret = security_task_kill(p, info, sig, secid); + if (ret) + goto out_unlock; if (sig && p->sighand) { unsigned long flags; spin_lock_irqsave(&p->sighand->siglock, flags); @@ -1531,6 +1533,35 @@ static void do_notify_parent_cldstop(str spin_unlock_irqrestore(&sighand->siglock, flags); } +static inline int may_ptrace_stop(void) +{ + if (!likely(current->ptrace & PT_PTRACED)) + return 0; + + if (unlikely(current->parent == current->real_parent && + (current->ptrace & PT_ATTACHED))) + return 0; + + if (unlikely(current->signal == current->parent->signal) && + unlikely(current->signal->flags & SIGNAL_GROUP_EXIT)) + return 0; + + /* + * Are we in the middle of do_coredump? + * If so and our tracer is also part of the coredump stopping + * is a deadlock situation, and pointless because our tracer + * is dead so don't allow us to stop. + * If SIGKILL was already sent before the caller unlocked + * ->siglock we must see ->core_waiters != 0. Otherwise it + * is safe to enter schedule(). + */ + if (unlikely(current->mm->core_waiters) && + unlikely(current->mm == current->parent->mm)) + return 0; + + return 1; +} + /* * This must be called with current->sighand->siglock held. * @@ -1559,11 +1590,7 @@ static void ptrace_stop(int exit_code, i spin_unlock_irq(¤t->sighand->siglock); try_to_freeze(); read_lock(&tasklist_lock); - if (likely(current->ptrace & PT_PTRACED) && - likely(current->parent != current->real_parent || - !(current->ptrace & PT_ATTACHED)) && - (likely(current->parent->signal != current->signal) || - !unlikely(current->signal->flags & SIGNAL_GROUP_EXIT))) { + if (may_ptrace_stop()) { do_notify_parent_cldstop(current, CLD_TRAPPED); read_unlock(&tasklist_lock); schedule(); diff --git a/kernel/softirq.c b/kernel/softirq.c index 336f92d..215541e 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -62,6 +62,119 @@ static inline void wakeup_softirqd(void) } /* + * This one is for softirq.c-internal use, + * where hardirqs are disabled legitimately: + */ +static void __local_bh_disable(unsigned long ip) +{ + unsigned long flags; + + WARN_ON_ONCE(in_irq()); + + raw_local_irq_save(flags); + add_preempt_count(SOFTIRQ_OFFSET); + /* + * Were softirqs turned off above: + */ + if (softirq_count() == SOFTIRQ_OFFSET) + trace_softirqs_off(ip); + raw_local_irq_restore(flags); +} + +void local_bh_disable(void) +{ + __local_bh_disable((unsigned long)__builtin_return_address(0)); +} + +EXPORT_SYMBOL(local_bh_disable); + +void __local_bh_enable(void) +{ + WARN_ON_ONCE(in_irq()); + + /* + * softirqs should never be enabled by __local_bh_enable(), + * it always nests inside local_bh_enable() sections: + */ + WARN_ON_ONCE(softirq_count() == SOFTIRQ_OFFSET); + + sub_preempt_count(SOFTIRQ_OFFSET); +} +EXPORT_SYMBOL_GPL(__local_bh_enable); + +/* + * Special-case - softirqs can safely be enabled in + * cond_resched_softirq(), or by __do_softirq(), + * without processing still-pending softirqs: + */ +void _local_bh_enable(void) +{ + WARN_ON_ONCE(in_irq()); + WARN_ON_ONCE(!irqs_disabled()); + + if (softirq_count() == SOFTIRQ_OFFSET) + trace_softirqs_on((unsigned long)__builtin_return_address(0)); + sub_preempt_count(SOFTIRQ_OFFSET); +} + +EXPORT_SYMBOL(_local_bh_enable); + +void local_bh_enable(void) +{ + unsigned long flags; + + WARN_ON_ONCE(in_irq()); + WARN_ON_ONCE(irqs_disabled()); + + local_irq_save(flags); + /* + * Are softirqs going to be turned on now: + */ + if (softirq_count() == SOFTIRQ_OFFSET) + trace_softirqs_on((unsigned long)__builtin_return_address(0)); + /* + * Keep preemption disabled until we are done with + * softirq processing: + */ + sub_preempt_count(SOFTIRQ_OFFSET - 1); + + if (unlikely(!in_interrupt() && local_softirq_pending())) + do_softirq(); + + dec_preempt_count(); + local_irq_restore(flags); + preempt_check_resched(); +} +EXPORT_SYMBOL(local_bh_enable); + +void local_bh_enable_ip(unsigned long ip) +{ + unsigned long flags; + + WARN_ON_ONCE(in_irq()); + + local_irq_save(flags); + /* + * Are softirqs going to be turned on now: + */ + if (softirq_count() == SOFTIRQ_OFFSET) + trace_softirqs_on(ip); + /* + * Keep preemption disabled until we are done with + * softirq processing: + */ + sub_preempt_count(SOFTIRQ_OFFSET - 1); + + if (unlikely(!in_interrupt() && local_softirq_pending())) + do_softirq(); + + dec_preempt_count(); + local_irq_restore(flags); + preempt_check_resched(); +} +EXPORT_SYMBOL(local_bh_enable_ip); + +/* * We restart softirq processing MAX_SOFTIRQ_RESTART times, * and we fall back to softirqd after that. * @@ -80,8 +193,11 @@ asmlinkage void __do_softirq(void) int cpu; pending = local_softirq_pending(); + account_system_vtime(current); + + __local_bh_disable((unsigned long)__builtin_return_address(0)); + trace_softirq_enter(); - local_bh_disable(); cpu = smp_processor_id(); restart: /* Reset the pending bitmask before enabling irqs */ @@ -109,7 +225,10 @@ restart: if (pending) wakeup_softirqd(); - __local_bh_enable(); + trace_softirq_exit(); + + account_system_vtime(current); + _local_bh_enable(); } #ifndef __ARCH_HAS_DO_SOFTIRQ @@ -136,23 +255,6 @@ EXPORT_SYMBOL(do_softirq); #endif -void local_bh_enable(void) -{ - WARN_ON(irqs_disabled()); - /* - * Keep preemption disabled until we are done with - * softirq processing: - */ - sub_preempt_count(SOFTIRQ_OFFSET - 1); - - if (unlikely(!in_interrupt() && local_softirq_pending())) - do_softirq(); - - dec_preempt_count(); - preempt_check_resched(); -} -EXPORT_SYMBOL(local_bh_enable); - #ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED # define invoke_softirq() __do_softirq() #else @@ -165,6 +267,7 @@ #endif void irq_exit(void) { account_system_vtime(current); + trace_hardirq_exit(); sub_preempt_count(IRQ_EXIT_OFFSET); if (!in_interrupt() && local_softirq_pending()) invoke_softirq(); @@ -446,7 +549,7 @@ static void takeover_tasklets(unsigned i } #endif /* CONFIG_HOTPLUG_CPU */ -static int cpu_callback(struct notifier_block *nfb, +static int __devinit cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -470,6 +573,8 @@ static int cpu_callback(struct notifier_ break; #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: + if (!per_cpu(ksoftirqd, hotcpu)) + break; /* Unbind so it can run. Fall thru. */ kthread_bind(per_cpu(ksoftirqd, hotcpu), any_online_cpu(cpu_online_map)); @@ -484,7 +589,7 @@ #endif /* CONFIG_HOTPLUG_CPU */ return NOTIFY_OK; } -static struct notifier_block cpu_nfb = { +static struct notifier_block __devinitdata cpu_nfb = { .notifier_call = cpu_callback }; diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 14c7faf..6b76caa 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -36,7 +36,7 @@ static struct notifier_block panic_block void touch_softlockup_watchdog(void) { - per_cpu(touch_timestamp, raw_smp_processor_id()) = jiffies; + __raw_get_cpu_var(touch_timestamp) = jiffies; } EXPORT_SYMBOL(touch_softlockup_watchdog); @@ -104,7 +104,7 @@ static int watchdog(void * __bind_cpu) /* * Create/destroy watchdog threads as CPUs come and go: */ -static int +static int __devinit cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { int hotcpu = (unsigned long)hcpu; @@ -127,6 +127,8 @@ cpu_callback(struct notifier_block *nfb, break; #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: + if (!per_cpu(watchdog_task, hotcpu)) + break; /* Unbind so it can run. Fall thru. */ kthread_bind(per_cpu(watchdog_task, hotcpu), any_online_cpu(cpu_online_map)); @@ -140,7 +142,7 @@ #endif /* CONFIG_HOTPLUG_CPU */ return NOTIFY_OK; } -static struct notifier_block cpu_nfb = { +static struct notifier_block __devinitdata cpu_nfb = { .notifier_call = cpu_callback }; diff --git a/kernel/spinlock.c b/kernel/spinlock.c index d1b8107..bfd6ad9 100644 --- a/kernel/spinlock.c +++ b/kernel/spinlock.c @@ -9,11 +9,11 @@ * SMP and the DEBUG_SPINLOCK cases. (UP-nondebug inlines them) */ -#include #include #include #include #include +#include #include /* @@ -30,8 +30,10 @@ EXPORT_SYMBOL(generic__raw_read_trylock) int __lockfunc _spin_trylock(spinlock_t *lock) { preempt_disable(); - if (_raw_spin_trylock(lock)) + if (_raw_spin_trylock(lock)) { + spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); return 1; + } preempt_enable(); return 0; @@ -41,8 +43,10 @@ EXPORT_SYMBOL(_spin_trylock); int __lockfunc _read_trylock(rwlock_t *lock) { preempt_disable(); - if (_raw_read_trylock(lock)) + if (_raw_read_trylock(lock)) { + rwlock_acquire_read(&lock->dep_map, 0, 1, _RET_IP_); return 1; + } preempt_enable(); return 0; @@ -52,19 +56,28 @@ EXPORT_SYMBOL(_read_trylock); int __lockfunc _write_trylock(rwlock_t *lock) { preempt_disable(); - if (_raw_write_trylock(lock)) + if (_raw_write_trylock(lock)) { + rwlock_acquire(&lock->dep_map, 0, 1, _RET_IP_); return 1; + } preempt_enable(); return 0; } EXPORT_SYMBOL(_write_trylock); -#if !defined(CONFIG_PREEMPT) || !defined(CONFIG_SMP) +/* + * If lockdep is enabled then we use the non-preemption spin-ops + * even on CONFIG_PREEMPT, because lockdep assumes that interrupts are + * not re-enabled during lock-acquire (which the preempt-spin-ops do): + */ +#if !defined(CONFIG_PREEMPT) || !defined(CONFIG_SMP) || \ + defined(CONFIG_PROVE_LOCKING) void __lockfunc _read_lock(rwlock_t *lock) { preempt_disable(); + rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_); _raw_read_lock(lock); } EXPORT_SYMBOL(_read_lock); @@ -75,7 +88,17 @@ unsigned long __lockfunc _spin_lock_irqs local_irq_save(flags); preempt_disable(); + spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); + /* + * On lockdep we dont want the hand-coded irq-enable of + * _raw_spin_lock_flags() code, because lockdep assumes + * that interrupts are not re-enabled during lock-acquire: + */ +#ifdef CONFIG_PROVE_LOCKING + _raw_spin_lock(lock); +#else _raw_spin_lock_flags(lock, &flags); +#endif return flags; } EXPORT_SYMBOL(_spin_lock_irqsave); @@ -84,6 +107,7 @@ void __lockfunc _spin_lock_irq(spinlock_ { local_irq_disable(); preempt_disable(); + spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_spin_lock(lock); } EXPORT_SYMBOL(_spin_lock_irq); @@ -92,6 +116,7 @@ void __lockfunc _spin_lock_bh(spinlock_t { local_bh_disable(); preempt_disable(); + spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_spin_lock(lock); } EXPORT_SYMBOL(_spin_lock_bh); @@ -102,6 +127,7 @@ unsigned long __lockfunc _read_lock_irqs local_irq_save(flags); preempt_disable(); + rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_); _raw_read_lock(lock); return flags; } @@ -111,6 +137,7 @@ void __lockfunc _read_lock_irq(rwlock_t { local_irq_disable(); preempt_disable(); + rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_); _raw_read_lock(lock); } EXPORT_SYMBOL(_read_lock_irq); @@ -119,6 +146,7 @@ void __lockfunc _read_lock_bh(rwlock_t * { local_bh_disable(); preempt_disable(); + rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_); _raw_read_lock(lock); } EXPORT_SYMBOL(_read_lock_bh); @@ -129,6 +157,7 @@ unsigned long __lockfunc _write_lock_irq local_irq_save(flags); preempt_disable(); + rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_write_lock(lock); return flags; } @@ -138,6 +167,7 @@ void __lockfunc _write_lock_irq(rwlock_t { local_irq_disable(); preempt_disable(); + rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_write_lock(lock); } EXPORT_SYMBOL(_write_lock_irq); @@ -146,6 +176,7 @@ void __lockfunc _write_lock_bh(rwlock_t { local_bh_disable(); preempt_disable(); + rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_write_lock(lock); } EXPORT_SYMBOL(_write_lock_bh); @@ -153,6 +184,7 @@ EXPORT_SYMBOL(_write_lock_bh); void __lockfunc _spin_lock(spinlock_t *lock) { preempt_disable(); + spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_spin_lock(lock); } @@ -161,6 +193,7 @@ EXPORT_SYMBOL(_spin_lock); void __lockfunc _write_lock(rwlock_t *lock) { preempt_disable(); + rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_); _raw_write_lock(lock); } @@ -256,8 +289,22 @@ BUILD_LOCK_OPS(write, rwlock); #endif /* CONFIG_PREEMPT */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + +void __lockfunc _spin_lock_nested(spinlock_t *lock, int subclass) +{ + preempt_disable(); + spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_); + _raw_spin_lock(lock); +} + +EXPORT_SYMBOL(_spin_lock_nested); + +#endif + void __lockfunc _spin_unlock(spinlock_t *lock) { + spin_release(&lock->dep_map, 1, _RET_IP_); _raw_spin_unlock(lock); preempt_enable(); } @@ -265,6 +312,7 @@ EXPORT_SYMBOL(_spin_unlock); void __lockfunc _write_unlock(rwlock_t *lock) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_write_unlock(lock); preempt_enable(); } @@ -272,6 +320,7 @@ EXPORT_SYMBOL(_write_unlock); void __lockfunc _read_unlock(rwlock_t *lock) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_read_unlock(lock); preempt_enable(); } @@ -279,6 +328,7 @@ EXPORT_SYMBOL(_read_unlock); void __lockfunc _spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags) { + spin_release(&lock->dep_map, 1, _RET_IP_); _raw_spin_unlock(lock); local_irq_restore(flags); preempt_enable(); @@ -287,6 +337,7 @@ EXPORT_SYMBOL(_spin_unlock_irqrestore); void __lockfunc _spin_unlock_irq(spinlock_t *lock) { + spin_release(&lock->dep_map, 1, _RET_IP_); _raw_spin_unlock(lock); local_irq_enable(); preempt_enable(); @@ -295,14 +346,16 @@ EXPORT_SYMBOL(_spin_unlock_irq); void __lockfunc _spin_unlock_bh(spinlock_t *lock) { + spin_release(&lock->dep_map, 1, _RET_IP_); _raw_spin_unlock(lock); preempt_enable_no_resched(); - local_bh_enable(); + local_bh_enable_ip((unsigned long)__builtin_return_address(0)); } EXPORT_SYMBOL(_spin_unlock_bh); void __lockfunc _read_unlock_irqrestore(rwlock_t *lock, unsigned long flags) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_read_unlock(lock); local_irq_restore(flags); preempt_enable(); @@ -311,6 +364,7 @@ EXPORT_SYMBOL(_read_unlock_irqrestore); void __lockfunc _read_unlock_irq(rwlock_t *lock) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_read_unlock(lock); local_irq_enable(); preempt_enable(); @@ -319,14 +373,16 @@ EXPORT_SYMBOL(_read_unlock_irq); void __lockfunc _read_unlock_bh(rwlock_t *lock) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_read_unlock(lock); preempt_enable_no_resched(); - local_bh_enable(); + local_bh_enable_ip((unsigned long)__builtin_return_address(0)); } EXPORT_SYMBOL(_read_unlock_bh); void __lockfunc _write_unlock_irqrestore(rwlock_t *lock, unsigned long flags) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_write_unlock(lock); local_irq_restore(flags); preempt_enable(); @@ -335,6 +391,7 @@ EXPORT_SYMBOL(_write_unlock_irqrestore); void __lockfunc _write_unlock_irq(rwlock_t *lock) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_write_unlock(lock); local_irq_enable(); preempt_enable(); @@ -343,9 +400,10 @@ EXPORT_SYMBOL(_write_unlock_irq); void __lockfunc _write_unlock_bh(rwlock_t *lock) { + rwlock_release(&lock->dep_map, 1, _RET_IP_); _raw_write_unlock(lock); preempt_enable_no_resched(); - local_bh_enable(); + local_bh_enable_ip((unsigned long)__builtin_return_address(0)); } EXPORT_SYMBOL(_write_unlock_bh); @@ -353,11 +411,13 @@ int __lockfunc _spin_trylock_bh(spinlock { local_bh_disable(); preempt_disable(); - if (_raw_spin_trylock(lock)) + if (_raw_spin_trylock(lock)) { + spin_acquire(&lock->dep_map, 0, 1, _RET_IP_); return 1; + } preempt_enable_no_resched(); - local_bh_enable(); + local_bh_enable_ip((unsigned long)__builtin_return_address(0)); return 0; } EXPORT_SYMBOL(_spin_trylock_bh); diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c new file mode 100644 index 0000000..b71816e --- /dev/null +++ b/kernel/stacktrace.c @@ -0,0 +1,24 @@ +/* + * kernel/stacktrace.c + * + * Stack trace management functions + * + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + */ +#include +#include +#include + +void print_stack_trace(struct stack_trace *trace, int spaces) +{ + int i, j; + + for (i = 0; i < trace->nr_entries; i++) { + unsigned long ip = trace->entries[i]; + + for (j = 0; j < spaces + 1; j++) + printk(" "); + print_ip_sym(ip); + } +} + diff --git a/kernel/sys.c b/kernel/sys.c index 0b6ec0e..dbb3b9c 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -4,7 +4,6 @@ * Copyright (C) 1991, 1992 Linus Torvalds */ -#include #include #include #include @@ -13,7 +12,6 @@ #include #include #include #include -#include #include #include #include @@ -57,6 +55,12 @@ #endif #ifndef GET_FPEXC_CTL # define GET_FPEXC_CTL(a,b) (-EINVAL) #endif +#ifndef GET_ENDIAN +# define GET_ENDIAN(a,b) (-EINVAL) +#endif +#ifndef SET_ENDIAN +# define SET_ENDIAN(a,b) (-EINVAL) +#endif /* * this is where the system-wide overflow UID and GID are defined, for @@ -132,14 +136,15 @@ static int __kprobes notifier_call_chain unsigned long val, void *v) { int ret = NOTIFY_DONE; - struct notifier_block *nb; + struct notifier_block *nb, *next_nb; nb = rcu_dereference(*nl); while (nb) { + next_nb = rcu_dereference(nb->next); ret = nb->notifier_call(nb, val, v); if ((ret & NOTIFY_STOP_MASK) == NOTIFY_STOP_MASK) break; - nb = rcu_dereference(nb->next); + nb = next_nb; } return ret; } @@ -583,7 +588,7 @@ void emergency_restart(void) } EXPORT_SYMBOL_GPL(emergency_restart); -void kernel_restart_prepare(char *cmd) +static void kernel_restart_prepare(char *cmd) { blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd); system_state = SYSTEM_RESTART; @@ -617,7 +622,7 @@ EXPORT_SYMBOL_GPL(kernel_restart); * Move into place and start executing a preloaded standalone * executable. If nothing was preloaded return an error. */ -void kernel_kexec(void) +static void kernel_kexec(void) { #ifdef CONFIG_KEXEC struct kimage *image; @@ -631,7 +636,6 @@ #ifdef CONFIG_KEXEC machine_kexec(image); #endif } -EXPORT_SYMBOL_GPL(kernel_kexec); void kernel_shutdown_prepare(enum system_states state) { @@ -1860,23 +1864,20 @@ out: * fields when reaping, so a sample either gets all the additions of a * given child after it's reaped, or none so this sample is before reaping. * - * tasklist_lock locking optimisation: - * If we are current and single threaded, we do not need to take the tasklist - * lock or the siglock. No one else can take our signal_struct away, - * no one else can reap the children to update signal->c* counters, and - * no one else can race with the signal-> fields. - * If we do not take the tasklist_lock, the signal-> fields could be read - * out of order while another thread was just exiting. So we place a - * read memory barrier when we avoid the lock. On the writer side, - * write memory barrier is implied in __exit_signal as __exit_signal releases - * the siglock spinlock after updating the signal-> fields. - * - * We don't really need the siglock when we access the non c* fields - * of the signal_struct (for RUSAGE_SELF) even in multithreaded - * case, since we take the tasklist lock for read and the non c* signal-> - * fields are updated only in __exit_signal, which is called with - * tasklist_lock taken for write, hence these two threads cannot execute - * concurrently. + * Locking: + * We need to take the siglock for CHILDEREN, SELF and BOTH + * for the cases current multithreaded, non-current single threaded + * non-current multithreaded. Thread traversal is now safe with + * the siglock held. + * Strictly speaking, we donot need to take the siglock if we are current and + * single threaded, as no one else can take our signal_struct away, no one + * else can reap the children to update signal->c* counters, and no one else + * can race with the signal-> fields. If we do not take any lock, the + * signal-> fields could be read out of order while another thread was just + * exiting. So we should place a read memory barrier when we avoid the lock. + * On the writer side, write memory barrier is implied in __exit_signal + * as __exit_signal releases the siglock spinlock after updating the signal-> + * fields. But we don't do this yet to keep things simple. * */ @@ -1885,35 +1886,25 @@ static void k_getrusage(struct task_stru struct task_struct *t; unsigned long flags; cputime_t utime, stime; - int need_lock = 0; memset((char *) r, 0, sizeof *r); utime = stime = cputime_zero; - if (p != current || !thread_group_empty(p)) - need_lock = 1; - - if (need_lock) { - read_lock(&tasklist_lock); - if (unlikely(!p->signal)) { - read_unlock(&tasklist_lock); - return; - } - } else - /* See locking comments above */ - smp_rmb(); + rcu_read_lock(); + if (!lock_task_sighand(p, &flags)) { + rcu_read_unlock(); + return; + } switch (who) { case RUSAGE_BOTH: case RUSAGE_CHILDREN: - spin_lock_irqsave(&p->sighand->siglock, flags); utime = p->signal->cutime; stime = p->signal->cstime; r->ru_nvcsw = p->signal->cnvcsw; r->ru_nivcsw = p->signal->cnivcsw; r->ru_minflt = p->signal->cmin_flt; r->ru_majflt = p->signal->cmaj_flt; - spin_unlock_irqrestore(&p->sighand->siglock, flags); if (who == RUSAGE_CHILDREN) break; @@ -1941,8 +1932,9 @@ static void k_getrusage(struct task_stru BUG(); } - if (need_lock) - read_unlock(&tasklist_lock); + unlock_task_sighand(p, &flags); + rcu_read_unlock(); + cputime_to_timeval(utime, &r->ru_utime); cputime_to_timeval(stime, &r->ru_stime); } @@ -2057,6 +2049,13 @@ asmlinkage long sys_prctl(int option, un return -EFAULT; return 0; } + case PR_GET_ENDIAN: + error = GET_ENDIAN(current, arg2); + break; + case PR_SET_ENDIAN: + error = SET_ENDIAN(current, arg2); + break; + default: error = -EINVAL; break; diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 5433195..6991bec 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -87,6 +87,7 @@ cond_syscall(sys_inotify_init); cond_syscall(sys_inotify_add_watch); cond_syscall(sys_inotify_rm_watch); cond_syscall(sys_migrate_pages); +cond_syscall(sys_move_pages); cond_syscall(sys_chown16); cond_syscall(sys_fchown16); cond_syscall(sys_getegid16); @@ -132,3 +133,4 @@ cond_syscall(sys_mincore); cond_syscall(sys_madvise); cond_syscall(sys_mremap); cond_syscall(sys_remap_file_pages); +cond_syscall(compat_sys_move_pages); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index e82726f..362a0cc 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -18,7 +18,6 @@ * Removed it and replaced it with older style, 03/23/00, Bill Wendling */ -#include #include #include #include @@ -59,6 +58,7 @@ #if defined(CONFIG_SYSCTL) extern int C_A_D; extern int sysctl_overcommit_memory; extern int sysctl_overcommit_ratio; +extern int sysctl_panic_on_oom; extern int max_threads; extern int sysrq_enabled; extern int core_uses_pid; @@ -72,6 +72,7 @@ extern int printk_ratelimit_burst; extern int pid_max_min, pid_max_max; extern int sysctl_drop_caches; extern int percpu_pagelist_fraction; +extern int compat_log; #if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86) int unknown_nmi_panic; @@ -131,6 +132,10 @@ #ifdef CONFIG_IA64 extern int no_unaligned_warning; #endif +#ifdef CONFIG_RT_MUTEXES +extern int max_lock_depth; +#endif + static int parse_table(int __user *, int, void __user *, size_t __user *, void __user *, size_t, ctl_table *, void **); static int proc_doutsstring(ctl_table *table, int write, struct file *filp, @@ -142,7 +147,6 @@ static struct ctl_table_header root_tabl static ctl_table kern_table[]; static ctl_table vm_table[]; -static ctl_table proc_table[]; static ctl_table fs_table[]; static ctl_table debug_table[]; static ctl_table dev_table[]; @@ -150,7 +154,7 @@ extern ctl_table random_table[]; #ifdef CONFIG_UNIX98_PTYS extern ctl_table pty_table[]; #endif -#ifdef CONFIG_INOTIFY +#ifdef CONFIG_INOTIFY_USER extern ctl_table inotify_table[]; #endif @@ -202,12 +206,6 @@ #ifdef CONFIG_NET }, #endif { - .ctl_name = CTL_PROC, - .procname = "proc", - .mode = 0555, - .child = proc_table, - }, - { .ctl_name = CTL_FS, .procname = "fs", .mode = 0555, @@ -398,7 +396,7 @@ #ifdef CONFIG_KMOD .strategy = &sysctl_string, }, #endif -#ifdef CONFIG_HOTPLUG +#if defined(CONFIG_HOTPLUG) && defined(CONFIG_NET) { .ctl_name = KERN_HOTPLUG, .procname = "hotplug", @@ -683,6 +681,27 @@ #ifdef CONFIG_IA64 .proc_handler = &proc_dointvec, }, #endif +#ifdef CONFIG_COMPAT + { + .ctl_name = KERN_COMPAT_LOG, + .procname = "compat-log", + .data = &compat_log, + .maxlen = sizeof (int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, +#endif +#ifdef CONFIG_RT_MUTEXES + { + .ctl_name = KERN_MAX_LOCK_DEPTH, + .procname = "max_lock_depth", + .data = &max_lock_depth, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, +#endif + { .ctl_name = 0 } }; @@ -702,6 +721,14 @@ static ctl_table vm_table[] = { .proc_handler = &proc_dointvec, }, { + .ctl_name = VM_PANIC_ON_OOM, + .procname = "panic_on_oom", + .data = &sysctl_panic_on_oom, + .maxlen = sizeof(sysctl_panic_on_oom), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = VM_OVERCOMMIT_RATIO, .procname = "overcommit_ratio", .data = &sysctl_overcommit_ratio, @@ -906,19 +933,29 @@ #ifdef CONFIG_NUMA .extra1 = &zero, }, { - .ctl_name = VM_ZONE_RECLAIM_INTERVAL, - .procname = "zone_reclaim_interval", - .data = &zone_reclaim_interval, - .maxlen = sizeof(zone_reclaim_interval), + .ctl_name = VM_MIN_UNMAPPED, + .procname = "min_unmapped_ratio", + .data = &sysctl_min_unmapped_ratio, + .maxlen = sizeof(sysctl_min_unmapped_ratio), .mode = 0644, - .proc_handler = &proc_dointvec_jiffies, - .strategy = &sysctl_jiffies, + .proc_handler = &sysctl_min_unmapped_ratio_sysctl_handler, + .strategy = &sysctl_intvec, + .extra1 = &zero, + .extra2 = &one_hundred, + }, +#endif +#ifdef CONFIG_X86_32 + { + .ctl_name = VM_VDSO_ENABLED, + .procname = "vdso_enabled", + .data = &vdso_enabled, + .maxlen = sizeof(vdso_enabled), + .mode = 0644, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + .extra1 = &zero, }, #endif - { .ctl_name = 0 } -}; - -static ctl_table proc_table[] = { { .ctl_name = 0 } }; @@ -1028,7 +1065,7 @@ #ifdef CONFIG_MMU .mode = 0644, .proc_handler = &proc_doulongvec_minmax, }, -#ifdef CONFIG_INOTIFY +#ifdef CONFIG_INOTIFY_USER { .ctl_name = FS_INOTIFY, .procname = "inotify", diff --git a/kernel/time.c b/kernel/time.c index b00ddc7..5bd4897 100644 --- a/kernel/time.c +++ b/kernel/time.c @@ -523,6 +523,7 @@ EXPORT_SYMBOL(do_gettimeofday); #else +#ifndef CONFIG_GENERIC_TIME /* * Simulate gettimeofday using do_gettimeofday which only allows a timeval * and therefore only yields usec accuracy @@ -537,6 +538,7 @@ void getnstimeofday(struct timespec *tv) } EXPORT_SYMBOL_GPL(getnstimeofday); #endif +#endif /* Converts Gregorian date to seconds since 1970-01-01 00:00:00. * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 diff --git a/kernel/time/Makefile b/kernel/time/Makefile new file mode 100644 index 0000000..e1dfd8e --- /dev/null +++ b/kernel/time/Makefile @@ -0,0 +1 @@ +obj-y += clocksource.o jiffies.o diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c new file mode 100644 index 0000000..74eca59 --- /dev/null +++ b/kernel/time/clocksource.c @@ -0,0 +1,349 @@ +/* + * linux/kernel/time/clocksource.c + * + * This file contains the functions which manage clocksource drivers. + * + * Copyright (C) 2004, 2005 IBM, John Stultz (johnstul@us.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * TODO WishList: + * o Allow clocksource drivers to be unregistered + * o get rid of clocksource_jiffies extern + */ + +#include +#include +#include +#include + +/* XXX - Would like a better way for initializing curr_clocksource */ +extern struct clocksource clocksource_jiffies; + +/*[Clocksource internal variables]--------- + * curr_clocksource: + * currently selected clocksource. Initialized to clocksource_jiffies. + * next_clocksource: + * pending next selected clocksource. + * clocksource_list: + * linked list with the registered clocksources + * clocksource_lock: + * protects manipulations to curr_clocksource and next_clocksource + * and the clocksource_list + * override_name: + * Name of the user-specified clocksource. + */ +static struct clocksource *curr_clocksource = &clocksource_jiffies; +static struct clocksource *next_clocksource; +static LIST_HEAD(clocksource_list); +static DEFINE_SPINLOCK(clocksource_lock); +static char override_name[32]; +static int finished_booting; + +/* clocksource_done_booting - Called near the end of bootup + * + * Hack to avoid lots of clocksource churn at boot time + */ +static int __init clocksource_done_booting(void) +{ + finished_booting = 1; + return 0; +} + +late_initcall(clocksource_done_booting); + +/** + * clocksource_get_next - Returns the selected clocksource + * + */ +struct clocksource *clocksource_get_next(void) +{ + unsigned long flags; + + spin_lock_irqsave(&clocksource_lock, flags); + if (next_clocksource && finished_booting) { + curr_clocksource = next_clocksource; + next_clocksource = NULL; + } + spin_unlock_irqrestore(&clocksource_lock, flags); + + return curr_clocksource; +} + +/** + * select_clocksource - Finds the best registered clocksource. + * + * Private function. Must hold clocksource_lock when called. + * + * Looks through the list of registered clocksources, returning + * the one with the highest rating value. If there is a clocksource + * name that matches the override string, it returns that clocksource. + */ +static struct clocksource *select_clocksource(void) +{ + struct clocksource *best = NULL; + struct list_head *tmp; + + list_for_each(tmp, &clocksource_list) { + struct clocksource *src; + + src = list_entry(tmp, struct clocksource, list); + if (!best) + best = src; + + /* check for override: */ + if (strlen(src->name) == strlen(override_name) && + !strcmp(src->name, override_name)) { + best = src; + break; + } + /* pick the highest rating: */ + if (src->rating > best->rating) + best = src; + } + + return best; +} + +/** + * is_registered_source - Checks if clocksource is registered + * @c: pointer to a clocksource + * + * Private helper function. Must hold clocksource_lock when called. + * + * Returns one if the clocksource is already registered, zero otherwise. + */ +static int is_registered_source(struct clocksource *c) +{ + int len = strlen(c->name); + struct list_head *tmp; + + list_for_each(tmp, &clocksource_list) { + struct clocksource *src; + + src = list_entry(tmp, struct clocksource, list); + if (strlen(src->name) == len && !strcmp(src->name, c->name)) + return 1; + } + + return 0; +} + +/** + * clocksource_register - Used to install new clocksources + * @t: clocksource to be registered + * + * Returns -EBUSY if registration fails, zero otherwise. + */ +int clocksource_register(struct clocksource *c) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&clocksource_lock, flags); + /* check if clocksource is already registered */ + if (is_registered_source(c)) { + printk("register_clocksource: Cannot register %s. " + "Already registered!", c->name); + ret = -EBUSY; + } else { + /* register it */ + list_add(&c->list, &clocksource_list); + /* scan the registered clocksources, and pick the best one */ + next_clocksource = select_clocksource(); + } + spin_unlock_irqrestore(&clocksource_lock, flags); + return ret; +} +EXPORT_SYMBOL(clocksource_register); + +/** + * clocksource_reselect - Rescan list for next clocksource + * + * A quick helper function to be used if a clocksource changes its + * rating. Forces the clocksource list to be re-scanned for the best + * clocksource. + */ +void clocksource_reselect(void) +{ + unsigned long flags; + + spin_lock_irqsave(&clocksource_lock, flags); + next_clocksource = select_clocksource(); + spin_unlock_irqrestore(&clocksource_lock, flags); +} +EXPORT_SYMBOL(clocksource_reselect); + +/** + * sysfs_show_current_clocksources - sysfs interface for current clocksource + * @dev: unused + * @buf: char buffer to be filled with clocksource list + * + * Provides sysfs interface for listing current clocksource. + */ +static ssize_t +sysfs_show_current_clocksources(struct sys_device *dev, char *buf) +{ + char *curr = buf; + + spin_lock_irq(&clocksource_lock); + curr += sprintf(curr, "%s ", curr_clocksource->name); + spin_unlock_irq(&clocksource_lock); + + curr += sprintf(curr, "\n"); + + return curr - buf; +} + +/** + * sysfs_override_clocksource - interface for manually overriding clocksource + * @dev: unused + * @buf: name of override clocksource + * @count: length of buffer + * + * Takes input from sysfs interface for manually overriding the default + * clocksource selction. + */ +static ssize_t sysfs_override_clocksource(struct sys_device *dev, + const char *buf, size_t count) +{ + size_t ret = count; + /* strings from sysfs write are not 0 terminated! */ + if (count >= sizeof(override_name)) + return -EINVAL; + + /* strip of \n: */ + if (buf[count-1] == '\n') + count--; + if (count < 1) + return -EINVAL; + + spin_lock_irq(&clocksource_lock); + + /* copy the name given: */ + memcpy(override_name, buf, count); + override_name[count] = 0; + + /* try to select it: */ + next_clocksource = select_clocksource(); + + spin_unlock_irq(&clocksource_lock); + + return ret; +} + +/** + * sysfs_show_available_clocksources - sysfs interface for listing clocksource + * @dev: unused + * @buf: char buffer to be filled with clocksource list + * + * Provides sysfs interface for listing registered clocksources + */ +static ssize_t +sysfs_show_available_clocksources(struct sys_device *dev, char *buf) +{ + struct list_head *tmp; + char *curr = buf; + + spin_lock_irq(&clocksource_lock); + list_for_each(tmp, &clocksource_list) { + struct clocksource *src; + + src = list_entry(tmp, struct clocksource, list); + curr += sprintf(curr, "%s ", src->name); + } + spin_unlock_irq(&clocksource_lock); + + curr += sprintf(curr, "\n"); + + return curr - buf; +} + +/* + * Sysfs setup bits: + */ +static SYSDEV_ATTR(current_clocksource, 0600, sysfs_show_current_clocksources, + sysfs_override_clocksource); + +static SYSDEV_ATTR(available_clocksource, 0600, + sysfs_show_available_clocksources, NULL); + +static struct sysdev_class clocksource_sysclass = { + set_kset_name("clocksource"), +}; + +static struct sys_device device_clocksource = { + .id = 0, + .cls = &clocksource_sysclass, +}; + +static int __init init_clocksource_sysfs(void) +{ + int error = sysdev_class_register(&clocksource_sysclass); + + if (!error) + error = sysdev_register(&device_clocksource); + if (!error) + error = sysdev_create_file( + &device_clocksource, + &attr_current_clocksource); + if (!error) + error = sysdev_create_file( + &device_clocksource, + &attr_available_clocksource); + return error; +} + +device_initcall(init_clocksource_sysfs); + +/** + * boot_override_clocksource - boot clock override + * @str: override name + * + * Takes a clocksource= boot argument and uses it + * as the clocksource override name. + */ +static int __init boot_override_clocksource(char* str) +{ + unsigned long flags; + spin_lock_irqsave(&clocksource_lock, flags); + if (str) + strlcpy(override_name, str, sizeof(override_name)); + spin_unlock_irqrestore(&clocksource_lock, flags); + return 1; +} + +__setup("clocksource=", boot_override_clocksource); + +/** + * boot_override_clock - Compatibility layer for deprecated boot option + * @str: override name + * + * DEPRECATED! Takes a clock= boot argument and uses it + * as the clocksource override name + */ +static int __init boot_override_clock(char* str) +{ + if (!strcmp(str, "pmtmr")) { + printk("Warning: clock=pmtmr is deprecated. " + "Use clocksource=acpi_pm.\n"); + return boot_override_clocksource("acpi_pm"); + } + printk("Warning! clock= boot option is deprecated. " + "Use clocksource=xyz\n"); + return boot_override_clocksource(str); +} + +__setup("clock=", boot_override_clock); diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c new file mode 100644 index 0000000..126bb30 --- /dev/null +++ b/kernel/time/jiffies.c @@ -0,0 +1,73 @@ +/*********************************************************************** +* linux/kernel/time/jiffies.c +* +* This file contains the jiffies based clocksource. +* +* Copyright (C) 2004, 2005 IBM, John Stultz (johnstul@us.ibm.com) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +************************************************************************/ +#include +#include +#include + +/* The Jiffies based clocksource is the lowest common + * denominator clock source which should function on + * all systems. It has the same coarse resolution as + * the timer interrupt frequency HZ and it suffers + * inaccuracies caused by missed or lost timer + * interrupts and the inability for the timer + * interrupt hardware to accuratly tick at the + * requested HZ value. It is also not reccomended + * for "tick-less" systems. + */ +#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/ACTHZ)) + +/* Since jiffies uses a simple NSEC_PER_JIFFY multiplier + * conversion, the .shift value could be zero. However + * this would make NTP adjustments impossible as they are + * in units of 1/2^.shift. Thus we use JIFFIES_SHIFT to + * shift both the nominator and denominator the same + * amount, and give ntp adjustments in units of 1/2^8 + * + * The value 8 is somewhat carefully chosen, as anything + * larger can result in overflows. NSEC_PER_JIFFY grows as + * HZ shrinks, so values greater then 8 overflow 32bits when + * HZ=100. + */ +#define JIFFIES_SHIFT 8 + +static cycle_t jiffies_read(void) +{ + return (cycle_t) jiffies; +} + +struct clocksource clocksource_jiffies = { + .name = "jiffies", + .rating = 0, /* lowest rating*/ + .read = jiffies_read, + .mask = 0xffffffff, /*32bits*/ + .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* details above */ + .shift = JIFFIES_SHIFT, + .is_continuous = 0, /* tick based, not free running */ +}; + +static int __init init_jiffies_clocksource(void) +{ + return clocksource_register(&clocksource_jiffies); +} + +module_init(init_jiffies_clocksource); diff --git a/kernel/timer.c b/kernel/timer.c index 9e49dee..396a3c0 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -146,7 +146,7 @@ static void internal_add_timer(tvec_base void fastcall init_timer(struct timer_list *timer) { timer->entry.next = NULL; - timer->base = per_cpu(tvec_bases, raw_smp_processor_id()); + timer->base = __raw_get_cpu_var(tvec_bases); } EXPORT_SYMBOL(init_timer); @@ -383,23 +383,19 @@ #endif static int cascade(tvec_base_t *base, tvec_t *tv, int index) { /* cascade all the timers from tv up one level */ - struct list_head *head, *curr; + struct timer_list *timer, *tmp; + struct list_head tv_list; + + list_replace_init(tv->vec + index, &tv_list); - head = tv->vec + index; - curr = head->next; /* - * We are removing _all_ timers from the list, so we don't have to - * detach them individually, just clear the list afterwards. + * We are removing _all_ timers from the list, so we + * don't have to detach them individually. */ - while (curr != head) { - struct timer_list *tmp; - - tmp = list_entry(curr, struct timer_list, entry); - BUG_ON(tmp->base != base); - curr = curr->next; - internal_add_timer(base, tmp); + list_for_each_entry_safe(timer, tmp, &tv_list, entry) { + BUG_ON(timer->base != base); + internal_add_timer(base, timer); } - INIT_LIST_HEAD(head); return index; } @@ -419,10 +415,10 @@ static inline void __run_timers(tvec_bas spin_lock_irq(&base->lock); while (time_after_eq(jiffies, base->timer_jiffies)) { - struct list_head work_list = LIST_HEAD_INIT(work_list); + struct list_head work_list; struct list_head *head = &work_list; int index = base->timer_jiffies & TVR_MASK; - + /* * Cascade timers: */ @@ -431,8 +427,8 @@ static inline void __run_timers(tvec_bas (!cascade(base, &base->tv3, INDEX(1))) && !cascade(base, &base->tv4, INDEX(2))) cascade(base, &base->tv5, INDEX(3)); - ++base->timer_jiffies; - list_splice_init(base->tv1.vec + index, &work_list); + ++base->timer_jiffies; + list_replace_init(base->tv1.vec + index, &work_list); while (!list_empty(head)) { void (*fn)(unsigned long); unsigned long data; @@ -601,7 +597,6 @@ long time_tolerance = MAXFREQ; /* frequ long time_precision = 1; /* clock precision (us) */ long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */ long time_esterror = NTP_PHASE_LIMIT; /* estimated error (us) */ -static long time_phase; /* phase offset (scaled us) */ long time_freq = (((NSEC_PER_SEC + HZ/2) % HZ - HZ/2) << SHIFT_USEC) / NSEC_PER_USEC; /* frequency offset (scaled ppm)*/ static long time_adj; /* tick adjust (scaled 1 / HZ) */ @@ -751,27 +746,14 @@ static long adjtime_adjustment(void) } /* in the NTP reference this is called "hardclock()" */ -static void update_wall_time_one_tick(void) +static void update_ntp_one_tick(void) { - long time_adjust_step, delta_nsec; + long time_adjust_step; time_adjust_step = adjtime_adjustment(); if (time_adjust_step) /* Reduce by this step the amount of time left */ time_adjust -= time_adjust_step; - delta_nsec = tick_nsec + time_adjust_step * 1000; - /* - * Advance the phase, once it gets to one microsecond, then - * advance the tick more. - */ - time_phase += time_adj; - if ((time_phase >= FINENSEC) || (time_phase <= -FINENSEC)) { - long ltemp = shift_right(time_phase, (SHIFT_SCALE - 10)); - time_phase -= ltemp << (SHIFT_SCALE - 10); - delta_nsec += ltemp; - } - xtime.tv_nsec += delta_nsec; - time_interpolator_update(delta_nsec); /* Changes by adjtime() do not take effect till next tick. */ if (time_next_adjust != 0) { @@ -784,36 +766,378 @@ static void update_wall_time_one_tick(vo * Return how long ticks are at the moment, that is, how much time * update_wall_time_one_tick will add to xtime next time we call it * (assuming no calls to do_adjtimex in the meantime). - * The return value is in fixed-point nanoseconds with SHIFT_SCALE-10 - * bits to the right of the binary point. + * The return value is in fixed-point nanoseconds shifted by the + * specified number of bits to the right of the binary point. * This function has no side-effects. */ u64 current_tick_length(void) { long delta_nsec; + u64 ret; + /* calculate the finest interval NTP will allow. + * ie: nanosecond value shifted by (SHIFT_SCALE - 10) + */ delta_nsec = tick_nsec + adjtime_adjustment() * 1000; - return ((u64) delta_nsec << (SHIFT_SCALE - 10)) + time_adj; + ret = (u64)delta_nsec << TICK_LENGTH_SHIFT; + ret += (s64)time_adj << (TICK_LENGTH_SHIFT - (SHIFT_SCALE - 10)); + + return ret; } -/* - * Using a loop looks inefficient, but "ticks" is - * usually just one (we shouldn't be losing ticks, - * we're doing this this way mainly for interrupt - * latency reasons, not because we think we'll - * have lots of lost timer ticks +/* XXX - all of this timekeeping code should be later moved to time.c */ +#include +static struct clocksource *clock; /* pointer to current clocksource */ + +#ifdef CONFIG_GENERIC_TIME +/** + * __get_nsec_offset - Returns nanoseconds since last call to periodic_hook + * + * private function, must hold xtime_lock lock when being + * called. Returns the number of nanoseconds since the + * last call to update_wall_time() (adjusted by NTP scaling) + */ +static inline s64 __get_nsec_offset(void) +{ + cycle_t cycle_now, cycle_delta; + s64 ns_offset; + + /* read clocksource: */ + cycle_now = clocksource_read(clock); + + /* calculate the delta since the last update_wall_time: */ + cycle_delta = (cycle_now - clock->cycle_last) & clock->mask; + + /* convert to nanoseconds: */ + ns_offset = cyc2ns(clock, cycle_delta); + + return ns_offset; +} + +/** + * __get_realtime_clock_ts - Returns the time of day in a timespec + * @ts: pointer to the timespec to be set + * + * Returns the time of day in a timespec. Used by + * do_gettimeofday() and get_realtime_clock_ts(). + */ +static inline void __get_realtime_clock_ts(struct timespec *ts) +{ + unsigned long seq; + s64 nsecs; + + do { + seq = read_seqbegin(&xtime_lock); + + *ts = xtime; + nsecs = __get_nsec_offset(); + + } while (read_seqretry(&xtime_lock, seq)); + + timespec_add_ns(ts, nsecs); +} + +/** + * getnstimeofday - Returns the time of day in a timespec + * @ts: pointer to the timespec to be set + * + * Returns the time of day in a timespec. + */ +void getnstimeofday(struct timespec *ts) +{ + __get_realtime_clock_ts(ts); +} + +EXPORT_SYMBOL(getnstimeofday); + +/** + * do_gettimeofday - Returns the time of day in a timeval + * @tv: pointer to the timeval to be set + * + * NOTE: Users should be converted to using get_realtime_clock_ts() + */ +void do_gettimeofday(struct timeval *tv) +{ + struct timespec now; + + __get_realtime_clock_ts(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; +} + +EXPORT_SYMBOL(do_gettimeofday); +/** + * do_settimeofday - Sets the time of day + * @tv: pointer to the timespec variable containing the new time + * + * Sets the time of day to the new time and update NTP and notify hrtimers + */ +int do_settimeofday(struct timespec *tv) +{ + unsigned long flags; + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irqsave(&xtime_lock, flags); + + nsec -= __get_nsec_offset(); + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + ntp_clear(); + + write_sequnlock_irqrestore(&xtime_lock, flags); + + /* signal hrtimers about time change */ + clock_was_set(); + + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + +/** + * change_clocksource - Swaps clocksources if a new one is available + * + * Accumulates current time interval and initializes new clocksource */ -static void update_wall_time(unsigned long ticks) +static int change_clocksource(void) { + struct clocksource *new; + cycle_t now; + u64 nsec; + new = clocksource_get_next(); + if (clock != new) { + now = clocksource_read(new); + nsec = __get_nsec_offset(); + timespec_add_ns(&xtime, nsec); + + clock = new; + clock->cycle_last = now; + printk(KERN_INFO "Time: %s clocksource has been installed.\n", + clock->name); + return 1; + } else if (clock->update_callback) { + return clock->update_callback(); + } + return 0; +} +#else +#define change_clocksource() (0) +#endif + +/** + * timeofday_is_continuous - check to see if timekeeping is free running + */ +int timekeeping_is_continuous(void) +{ + unsigned long seq; + int ret; + do { - ticks--; - update_wall_time_one_tick(); - if (xtime.tv_nsec >= 1000000000) { - xtime.tv_nsec -= 1000000000; + seq = read_seqbegin(&xtime_lock); + + ret = clock->is_continuous; + + } while (read_seqretry(&xtime_lock, seq)); + + return ret; +} + +/* + * timekeeping_init - Initializes the clocksource and common timekeeping values + */ +void __init timekeeping_init(void) +{ + unsigned long flags; + + write_seqlock_irqsave(&xtime_lock, flags); + clock = clocksource_get_next(); + clocksource_calculate_interval(clock, tick_nsec); + clock->cycle_last = clocksource_read(clock); + ntp_clear(); + write_sequnlock_irqrestore(&xtime_lock, flags); +} + + +/* + * timekeeping_resume - Resumes the generic timekeeping subsystem. + * @dev: unused + * + * This is for the generic clocksource timekeeping. + * xtime/wall_to_monotonic/jiffies/wall_jiffies/etc are + * still managed by arch specific suspend/resume code. + */ +static int timekeeping_resume(struct sys_device *dev) +{ + unsigned long flags; + + write_seqlock_irqsave(&xtime_lock, flags); + /* restart the last cycle value */ + clock->cycle_last = clocksource_read(clock); + write_sequnlock_irqrestore(&xtime_lock, flags); + return 0; +} + +/* sysfs resume/suspend bits for timekeeping */ +static struct sysdev_class timekeeping_sysclass = { + .resume = timekeeping_resume, + set_kset_name("timekeeping"), +}; + +static struct sys_device device_timer = { + .id = 0, + .cls = &timekeeping_sysclass, +}; + +static int __init timekeeping_init_device(void) +{ + int error = sysdev_class_register(&timekeeping_sysclass); + if (!error) + error = sysdev_register(&device_timer); + return error; +} + +device_initcall(timekeeping_init_device); + +/* + * If the error is already larger, we look ahead another tick, + * to compensate for late or lost adjustments. + */ +static __always_inline int clocksource_bigadjust(int sign, s64 error, s64 *interval, s64 *offset) +{ + int adj; + + /* + * As soon as the machine is synchronized to the external time + * source this should be the common case. + */ + error >>= 2; + if (likely(sign > 0 ? error <= *interval : error >= *interval)) + return sign; + + /* + * An extra look ahead dampens the effect of the current error, + * which can grow quite large with continously late updates, as + * it would dominate the adjustment value and can lead to + * oscillation. + */ + error += current_tick_length() >> (TICK_LENGTH_SHIFT - clock->shift + 1); + error -= clock->xtime_interval >> 1; + + adj = 0; + while (1) { + error >>= 1; + if (sign > 0 ? error <= *interval : error >= *interval) + break; + adj++; + } + + /* + * Add the current adjustments to the error and take the offset + * into account, the latter can cause the error to be hardly + * reduced at the next tick. Check the error again if there's + * room for another adjustment, thus further reducing the error + * which otherwise had to be corrected at the next update. + */ + error = (error << 1) - *interval + *offset; + if (sign > 0 ? error > *interval : error < *interval) + adj++; + + *interval <<= adj; + *offset <<= adj; + return sign << adj; +} + +/* + * Adjust the multiplier to reduce the error value, + * this is optimized for the most common adjustments of -1,0,1, + * for other values we can do a bit more work. + */ +static void clocksource_adjust(struct clocksource *clock, s64 offset) +{ + s64 error, interval = clock->cycle_interval; + int adj; + + error = clock->error >> (TICK_LENGTH_SHIFT - clock->shift - 1); + if (error > interval) { + adj = clocksource_bigadjust(1, error, &interval, &offset); + } else if (error < -interval) { + interval = -interval; + offset = -offset; + adj = clocksource_bigadjust(-1, error, &interval, &offset); + } else + return; + + clock->mult += adj; + clock->xtime_interval += interval; + clock->xtime_nsec -= offset; + clock->error -= (interval - offset) << (TICK_LENGTH_SHIFT - clock->shift); +} + +/* + * update_wall_time - Uses the current clocksource to increment the wall time + * + * Called from the timer interrupt, must hold a write on xtime_lock. + */ +static void update_wall_time(void) +{ + cycle_t offset; + + clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift; + +#ifdef CONFIG_GENERIC_TIME + offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask; +#else + offset = clock->cycle_interval; +#endif + + /* normally this loop will run just once, however in the + * case of lost or late ticks, it will accumulate correctly. + */ + while (offset >= clock->cycle_interval) { + /* accumulate one interval */ + clock->xtime_nsec += clock->xtime_interval; + clock->cycle_last += clock->cycle_interval; + offset -= clock->cycle_interval; + + if (clock->xtime_nsec >= (u64)NSEC_PER_SEC << clock->shift) { + clock->xtime_nsec -= (u64)NSEC_PER_SEC << clock->shift; xtime.tv_sec++; second_overflow(); } - } while (ticks); + + /* interpolator bits */ + time_interpolator_update(clock->xtime_interval + >> clock->shift); + /* increment the NTP state machine */ + update_ntp_one_tick(); + + /* accumulate error between NTP and clock interval */ + clock->error += current_tick_length(); + clock->error -= clock->xtime_interval << (TICK_LENGTH_SHIFT - clock->shift); + } + + /* correct the clock when NTP error is too big */ + clocksource_adjust(clock, offset); + + /* store full nanoseconds into xtime */ + xtime.tv_nsec = clock->xtime_nsec >> clock->shift; + clock->xtime_nsec -= (s64)xtime.tv_nsec << clock->shift; + + /* check to see if there is a new clocksource to use */ + if (change_clocksource()) { + clock->error = 0; + clock->xtime_nsec = 0; + clocksource_calculate_interval(clock, tick_nsec); + } } /* @@ -884,7 +1208,7 @@ unsigned long wall_jiffies = INITIAL_JIF * playing with xtime and avenrun. */ #ifndef ARCH_HAVE_XTIME_LOCK -seqlock_t xtime_lock __cacheline_aligned_in_smp = SEQLOCK_UNLOCKED; +__cacheline_aligned_in_smp DEFINE_SEQLOCK(xtime_lock); EXPORT_SYMBOL(xtime_lock); #endif @@ -919,10 +1243,8 @@ static inline void update_times(void) unsigned long ticks; ticks = jiffies - wall_jiffies; - if (ticks) { - wall_jiffies += ticks; - update_wall_time(ticks); - } + wall_jiffies += ticks; + update_wall_time(); calc_load(ticks); } @@ -1046,7 +1368,7 @@ #endif static void process_timeout(unsigned long __data) { - wake_up_process((task_t *)__data); + wake_up_process((struct task_struct *)__data); } /** @@ -1237,6 +1559,13 @@ asmlinkage long sys_sysinfo(struct sysin return 0; } +/* + * lockdep: we want to track each per-CPU base as a separate lock-class, + * but timer-bases are kmalloc()-ed, so we need to attach separate + * keys to them: + */ +static struct lock_class_key base_lock_keys[NR_CPUS]; + static int __devinit init_timers_cpu(int cpu) { int j; @@ -1272,6 +1601,8 @@ static int __devinit init_timers_cpu(int } spin_lock_init(&base->lock); + lockdep_set_class(&base->lock, base_lock_keys + cpu); + for (j = 0; j < TVN_SIZE; j++) { INIT_LIST_HEAD(base->tv5.vec + j); INIT_LIST_HEAD(base->tv4.vec + j); @@ -1330,7 +1661,7 @@ static void __devinit migrate_timers(int } #endif /* CONFIG_HOTPLUG_CPU */ -static int timer_cpu_notify(struct notifier_block *self, +static int __devinit timer_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { long cpu = (long)hcpu; @@ -1350,7 +1681,7 @@ #endif return NOTIFY_OK; } -static struct notifier_block timers_nb = { +static struct notifier_block __devinitdata timers_nb = { .notifier_call = timer_cpu_notify, }; diff --git a/kernel/unwind.c b/kernel/unwind.c new file mode 100644 index 0000000..f69c804 --- /dev/null +++ b/kernel/unwind.c @@ -0,0 +1,918 @@ +/* + * Copyright (C) 2002-2006 Novell, Inc. + * Jan Beulich + * This code is released under version 2 of the GNU GPL. + * + * A simple API for unwinding kernel stacks. This is used for + * debugging and error reporting purposes. The kernel doesn't need + * full-blown stack unwinding with all the bells and whistles, so there + * is not much point in implementing the full Dwarf2 unwind API. + */ + +#include +#include +#include +#include +#include +#include +#include + +extern char __start_unwind[], __end_unwind[]; + +#define MAX_STACK_DEPTH 8 + +#define EXTRA_INFO(f) { \ + BUILD_BUG_ON_ZERO(offsetof(struct unwind_frame_info, f) \ + % FIELD_SIZEOF(struct unwind_frame_info, f)) \ + + offsetof(struct unwind_frame_info, f) \ + / FIELD_SIZEOF(struct unwind_frame_info, f), \ + FIELD_SIZEOF(struct unwind_frame_info, f) \ + } +#define PTREGS_INFO(f) EXTRA_INFO(regs.f) + +static const struct { + unsigned offs:BITS_PER_LONG / 2; + unsigned width:BITS_PER_LONG / 2; +} reg_info[] = { + UNW_REGISTER_INFO +}; + +#undef PTREGS_INFO +#undef EXTRA_INFO + +#ifndef REG_INVALID +#define REG_INVALID(r) (reg_info[r].width == 0) +#endif + +#define DW_CFA_nop 0x00 +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_def_cfa_expression 0x0f +#define DW_CFA_expression 0x10 +#define DW_CFA_offset_extended_sf 0x11 +#define DW_CFA_def_cfa_sf 0x12 +#define DW_CFA_def_cfa_offset_sf 0x13 +#define DW_CFA_val_offset 0x14 +#define DW_CFA_val_offset_sf 0x15 +#define DW_CFA_val_expression 0x16 +#define DW_CFA_lo_user 0x1c +#define DW_CFA_GNU_window_save 0x2d +#define DW_CFA_GNU_args_size 0x2e +#define DW_CFA_GNU_negative_offset_extended 0x2f +#define DW_CFA_hi_user 0x3f + +#define DW_EH_PE_FORM 0x07 +#define DW_EH_PE_native 0x00 +#define DW_EH_PE_leb128 0x01 +#define DW_EH_PE_data2 0x02 +#define DW_EH_PE_data4 0x03 +#define DW_EH_PE_data8 0x04 +#define DW_EH_PE_signed 0x08 +#define DW_EH_PE_ADJUST 0x70 +#define DW_EH_PE_abs 0x00 +#define DW_EH_PE_pcrel 0x10 +#define DW_EH_PE_textrel 0x20 +#define DW_EH_PE_datarel 0x30 +#define DW_EH_PE_funcrel 0x40 +#define DW_EH_PE_aligned 0x50 +#define DW_EH_PE_indirect 0x80 +#define DW_EH_PE_omit 0xff + +typedef unsigned long uleb128_t; +typedef signed long sleb128_t; + +static struct unwind_table { + struct { + unsigned long pc; + unsigned long range; + } core, init; + const void *address; + unsigned long size; + struct unwind_table *link; + const char *name; +} root_table, *last_table; + +struct unwind_item { + enum item_location { + Nowhere, + Memory, + Register, + Value + } where; + uleb128_t value; +}; + +struct unwind_state { + uleb128_t loc, org; + const u8 *cieStart, *cieEnd; + uleb128_t codeAlign; + sleb128_t dataAlign; + struct cfa { + uleb128_t reg, offs; + } cfa; + struct unwind_item regs[ARRAY_SIZE(reg_info)]; + unsigned stackDepth:8; + unsigned version:8; + const u8 *label; + const u8 *stack[MAX_STACK_DEPTH]; +}; + +static const struct cfa badCFA = { ARRAY_SIZE(reg_info), 1 }; + +static struct unwind_table *find_table(unsigned long pc) +{ + struct unwind_table *table; + + for (table = &root_table; table; table = table->link) + if ((pc >= table->core.pc + && pc < table->core.pc + table->core.range) + || (pc >= table->init.pc + && pc < table->init.pc + table->init.range)) + break; + + return table; +} + +static void init_unwind_table(struct unwind_table *table, + const char *name, + const void *core_start, + unsigned long core_size, + const void *init_start, + unsigned long init_size, + const void *table_start, + unsigned long table_size) +{ + table->core.pc = (unsigned long)core_start; + table->core.range = core_size; + table->init.pc = (unsigned long)init_start; + table->init.range = init_size; + table->address = table_start; + table->size = table_size; + table->link = NULL; + table->name = name; +} + +void __init unwind_init(void) +{ + init_unwind_table(&root_table, "kernel", + _text, _end - _text, + NULL, 0, + __start_unwind, __end_unwind - __start_unwind); +} + +#ifdef CONFIG_MODULES + +/* Must be called with module_mutex held. */ +void *unwind_add_table(struct module *module, + const void *table_start, + unsigned long table_size) +{ + struct unwind_table *table; + + if (table_size <= 0) + return NULL; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return NULL; + + init_unwind_table(table, module->name, + module->module_core, module->core_size, + module->module_init, module->init_size, + table_start, table_size); + + if (last_table) + last_table->link = table; + else + root_table.link = table; + last_table = table; + + return table; +} + +struct unlink_table_info +{ + struct unwind_table *table; + int init_only; +}; + +static int unlink_table(void *arg) +{ + struct unlink_table_info *info = arg; + struct unwind_table *table = info->table, *prev; + + for (prev = &root_table; prev->link && prev->link != table; prev = prev->link) + ; + + if (prev->link) { + if (info->init_only) { + table->init.pc = 0; + table->init.range = 0; + info->table = NULL; + } else { + prev->link = table->link; + if (!prev->link) + last_table = prev; + } + } else + info->table = NULL; + + return 0; +} + +/* Must be called with module_mutex held. */ +void unwind_remove_table(void *handle, int init_only) +{ + struct unwind_table *table = handle; + struct unlink_table_info info; + + if (!table || table == &root_table) + return; + + if (init_only && table == last_table) { + table->init.pc = 0; + table->init.range = 0; + return; + } + + info.table = table; + info.init_only = init_only; + stop_machine_run(unlink_table, &info, NR_CPUS); + + if (info.table) + kfree(table); +} + +#endif /* CONFIG_MODULES */ + +static uleb128_t get_uleb128(const u8 **pcur, const u8 *end) +{ + const u8 *cur = *pcur; + uleb128_t value; + unsigned shift; + + for (shift = 0, value = 0; cur < end; shift += 7) { + if (shift + 7 > 8 * sizeof(value) + && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { + cur = end + 1; + break; + } + value |= (uleb128_t)(*cur & 0x7f) << shift; + if (!(*cur++ & 0x80)) + break; + } + *pcur = cur; + + return value; +} + +static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) +{ + const u8 *cur = *pcur; + sleb128_t value; + unsigned shift; + + for (shift = 0, value = 0; cur < end; shift += 7) { + if (shift + 7 > 8 * sizeof(value) + && (*cur & 0x7fU) >= (1U << (8 * sizeof(value) - shift))) { + cur = end + 1; + break; + } + value |= (sleb128_t)(*cur & 0x7f) << shift; + if (!(*cur & 0x80)) { + value |= -(*cur++ & 0x40) << shift; + break; + } + } + *pcur = cur; + + return value; +} + +static unsigned long read_pointer(const u8 **pLoc, + const void *end, + signed ptrType) +{ + unsigned long value = 0; + union { + const u8 *p8; + const u16 *p16u; + const s16 *p16s; + const u32 *p32u; + const s32 *p32s; + const unsigned long *pul; + } ptr; + + if (ptrType < 0 || ptrType == DW_EH_PE_omit) + return 0; + ptr.p8 = *pLoc; + switch(ptrType & DW_EH_PE_FORM) { + case DW_EH_PE_data2: + if (end < (const void *)(ptr.p16u + 1)) + return 0; + if(ptrType & DW_EH_PE_signed) + value = get_unaligned(ptr.p16s++); + else + value = get_unaligned(ptr.p16u++); + break; + case DW_EH_PE_data4: +#ifdef CONFIG_64BIT + if (end < (const void *)(ptr.p32u + 1)) + return 0; + if(ptrType & DW_EH_PE_signed) + value = get_unaligned(ptr.p32s++); + else + value = get_unaligned(ptr.p32u++); + break; + case DW_EH_PE_data8: + BUILD_BUG_ON(sizeof(u64) != sizeof(value)); +#else + BUILD_BUG_ON(sizeof(u32) != sizeof(value)); +#endif + case DW_EH_PE_native: + if (end < (const void *)(ptr.pul + 1)) + return 0; + value = get_unaligned(ptr.pul++); + break; + case DW_EH_PE_leb128: + BUILD_BUG_ON(sizeof(uleb128_t) > sizeof(value)); + value = ptrType & DW_EH_PE_signed + ? get_sleb128(&ptr.p8, end) + : get_uleb128(&ptr.p8, end); + if ((const void *)ptr.p8 > end) + return 0; + break; + default: + return 0; + } + switch(ptrType & DW_EH_PE_ADJUST) { + case DW_EH_PE_abs: + break; + case DW_EH_PE_pcrel: + value += (unsigned long)*pLoc; + break; + default: + return 0; + } + if ((ptrType & DW_EH_PE_indirect) + && __get_user(value, (unsigned long *)value)) + return 0; + *pLoc = ptr.p8; + + return value; +} + +static signed fde_pointer_type(const u32 *cie) +{ + const u8 *ptr = (const u8 *)(cie + 2); + unsigned version = *ptr; + + if (version != 1) + return -1; /* unsupported */ + if (*++ptr) { + const char *aug; + const u8 *end = (const u8 *)(cie + 1) + *cie; + uleb128_t len; + + /* check if augmentation size is first (and thus present) */ + if (*ptr != 'z') + return -1; + /* check if augmentation string is nul-terminated */ + if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) + return -1; + ++ptr; /* skip terminator */ + get_uleb128(&ptr, end); /* skip code alignment */ + get_sleb128(&ptr, end); /* skip data alignment */ + /* skip return address column */ + version <= 1 ? (void)++ptr : (void)get_uleb128(&ptr, end); + len = get_uleb128(&ptr, end); /* augmentation length */ + if (ptr + len < ptr || ptr + len > end) + return -1; + end = ptr + len; + while (*++aug) { + if (ptr >= end) + return -1; + switch(*aug) { + case 'L': + ++ptr; + break; + case 'P': { + signed ptrType = *ptr++; + + if (!read_pointer(&ptr, end, ptrType) || ptr > end) + return -1; + } + break; + case 'R': + return *ptr; + default: + return -1; + } + } + } + return DW_EH_PE_native|DW_EH_PE_abs; +} + +static int advance_loc(unsigned long delta, struct unwind_state *state) +{ + state->loc += delta * state->codeAlign; + + return delta > 0; +} + +static void set_rule(uleb128_t reg, + enum item_location where, + uleb128_t value, + struct unwind_state *state) +{ + if (reg < ARRAY_SIZE(state->regs)) { + state->regs[reg].where = where; + state->regs[reg].value = value; + } +} + +static int processCFI(const u8 *start, + const u8 *end, + unsigned long targetLoc, + signed ptrType, + struct unwind_state *state) +{ + union { + const u8 *p8; + const u16 *p16; + const u32 *p32; + } ptr; + int result = 1; + + if (start != state->cieStart) { + state->loc = state->org; + result = processCFI(state->cieStart, state->cieEnd, 0, ptrType, state); + if (targetLoc == 0 && state->label == NULL) + return result; + } + for (ptr.p8 = start; result && ptr.p8 < end; ) { + switch(*ptr.p8 >> 6) { + uleb128_t value; + + case 0: + switch(*ptr.p8++) { + case DW_CFA_nop: + break; + case DW_CFA_set_loc: + if ((state->loc = read_pointer(&ptr.p8, end, ptrType)) == 0) + result = 0; + break; + case DW_CFA_advance_loc1: + result = ptr.p8 < end && advance_loc(*ptr.p8++, state); + break; + case DW_CFA_advance_loc2: + result = ptr.p8 <= end + 2 + && advance_loc(*ptr.p16++, state); + break; + case DW_CFA_advance_loc4: + result = ptr.p8 <= end + 4 + && advance_loc(*ptr.p32++, state); + break; + case DW_CFA_offset_extended: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Memory, get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_val_offset: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Value, get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_offset_extended_sf: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Memory, get_sleb128(&ptr.p8, end), state); + break; + case DW_CFA_val_offset_sf: + value = get_uleb128(&ptr.p8, end); + set_rule(value, Value, get_sleb128(&ptr.p8, end), state); + break; + case DW_CFA_restore_extended: + case DW_CFA_undefined: + case DW_CFA_same_value: + set_rule(get_uleb128(&ptr.p8, end), Nowhere, 0, state); + break; + case DW_CFA_register: + value = get_uleb128(&ptr.p8, end); + set_rule(value, + Register, + get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_remember_state: + if (ptr.p8 == state->label) { + state->label = NULL; + return 1; + } + if (state->stackDepth >= MAX_STACK_DEPTH) + return 0; + state->stack[state->stackDepth++] = ptr.p8; + break; + case DW_CFA_restore_state: + if (state->stackDepth) { + const uleb128_t loc = state->loc; + const u8 *label = state->label; + + state->label = state->stack[state->stackDepth - 1]; + memcpy(&state->cfa, &badCFA, sizeof(state->cfa)); + memset(state->regs, 0, sizeof(state->regs)); + state->stackDepth = 0; + result = processCFI(start, end, 0, ptrType, state); + state->loc = loc; + state->label = label; + } else + return 0; + break; + case DW_CFA_def_cfa: + state->cfa.reg = get_uleb128(&ptr.p8, end); + /*nobreak*/ + case DW_CFA_def_cfa_offset: + state->cfa.offs = get_uleb128(&ptr.p8, end); + break; + case DW_CFA_def_cfa_sf: + state->cfa.reg = get_uleb128(&ptr.p8, end); + /*nobreak*/ + case DW_CFA_def_cfa_offset_sf: + state->cfa.offs = get_sleb128(&ptr.p8, end) + * state->dataAlign; + break; + case DW_CFA_def_cfa_register: + state->cfa.reg = get_uleb128(&ptr.p8, end); + break; + /*todo case DW_CFA_def_cfa_expression: */ + /*todo case DW_CFA_expression: */ + /*todo case DW_CFA_val_expression: */ + case DW_CFA_GNU_args_size: + get_uleb128(&ptr.p8, end); + break; + case DW_CFA_GNU_negative_offset_extended: + value = get_uleb128(&ptr.p8, end); + set_rule(value, + Memory, + (uleb128_t)0 - get_uleb128(&ptr.p8, end), state); + break; + case DW_CFA_GNU_window_save: + default: + result = 0; + break; + } + break; + case 1: + result = advance_loc(*ptr.p8++ & 0x3f, state); + break; + case 2: + value = *ptr.p8++ & 0x3f; + set_rule(value, Memory, get_uleb128(&ptr.p8, end), state); + break; + case 3: + set_rule(*ptr.p8++ & 0x3f, Nowhere, 0, state); + break; + } + if (ptr.p8 > end) + result = 0; + if (result && targetLoc != 0 && targetLoc < state->loc) + return 1; + } + + return result + && ptr.p8 == end + && (targetLoc == 0 + || (/*todo While in theory this should apply, gcc in practice omits + everything past the function prolog, and hence the location + never reaches the end of the function. + targetLoc < state->loc &&*/ state->label == NULL)); +} + +/* Unwind to previous to frame. Returns 0 if successful, negative + * number in case of an error. */ +int unwind(struct unwind_frame_info *frame) +{ +#define FRAME_REG(r, t) (((t *)frame)[reg_info[r].offs]) + const u32 *fde = NULL, *cie = NULL; + const u8 *ptr = NULL, *end = NULL; + unsigned long startLoc = 0, endLoc = 0, cfa; + unsigned i; + signed ptrType = -1; + uleb128_t retAddrReg = 0; + struct unwind_table *table; + struct unwind_state state; + + if (UNW_PC(frame) == 0) + return -EINVAL; + if ((table = find_table(UNW_PC(frame))) != NULL + && !(table->size & (sizeof(*fde) - 1))) { + unsigned long tableSize = table->size; + + for (fde = table->address; + tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; + tableSize -= sizeof(*fde) + *fde, + fde += 1 + *fde / sizeof(*fde)) { + if (!*fde || (*fde & (sizeof(*fde) - 1))) + break; + if (!fde[1]) + continue; /* this is a CIE */ + if ((fde[1] & (sizeof(*fde) - 1)) + || fde[1] > (unsigned long)(fde + 1) + - (unsigned long)table->address) + continue; /* this is not a valid FDE */ + cie = fde + 1 - fde[1] / sizeof(*fde); + if (*cie <= sizeof(*cie) + 4 + || *cie >= fde[1] - sizeof(*fde) + || (*cie & (sizeof(*cie) - 1)) + || cie[1] + || (ptrType = fde_pointer_type(cie)) < 0) { + cie = NULL; /* this is not a (valid) CIE */ + continue; + } + ptr = (const u8 *)(fde + 2); + startLoc = read_pointer(&ptr, + (const u8 *)(fde + 1) + *fde, + ptrType); + endLoc = startLoc + + read_pointer(&ptr, + (const u8 *)(fde + 1) + *fde, + ptrType & DW_EH_PE_indirect + ? ptrType + : ptrType & (DW_EH_PE_FORM|DW_EH_PE_signed)); + if (UNW_PC(frame) >= startLoc && UNW_PC(frame) < endLoc) + break; + cie = NULL; + } + } + if (cie != NULL) { + memset(&state, 0, sizeof(state)); + state.cieEnd = ptr; /* keep here temporarily */ + ptr = (const u8 *)(cie + 2); + end = (const u8 *)(cie + 1) + *cie; + if ((state.version = *ptr) != 1) + cie = NULL; /* unsupported version */ + else if (*++ptr) { + /* check if augmentation size is first (and thus present) */ + if (*ptr == 'z') { + /* check for ignorable (or already handled) + * nul-terminated augmentation string */ + while (++ptr < end && *ptr) + if (strchr("LPR", *ptr) == NULL) + break; + } + if (ptr >= end || *ptr) + cie = NULL; + } + ++ptr; + } + if (cie != NULL) { + /* get code aligment factor */ + state.codeAlign = get_uleb128(&ptr, end); + /* get data aligment factor */ + state.dataAlign = get_sleb128(&ptr, end); + if (state.codeAlign == 0 || state.dataAlign == 0 || ptr >= end) + cie = NULL; + else { + retAddrReg = state.version <= 1 ? *ptr++ : get_uleb128(&ptr, end); + /* skip augmentation */ + if (((const char *)(cie + 2))[1] == 'z') + ptr += get_uleb128(&ptr, end); + if (ptr > end + || retAddrReg >= ARRAY_SIZE(reg_info) + || REG_INVALID(retAddrReg) + || reg_info[retAddrReg].width != sizeof(unsigned long)) + cie = NULL; + } + } + if (cie != NULL) { + state.cieStart = ptr; + ptr = state.cieEnd; + state.cieEnd = end; + end = (const u8 *)(fde + 1) + *fde; + /* skip augmentation */ + if (((const char *)(cie + 2))[1] == 'z') { + uleb128_t augSize = get_uleb128(&ptr, end); + + if ((ptr += augSize) > end) + fde = NULL; + } + } + if (cie == NULL || fde == NULL) { +#ifdef CONFIG_FRAME_POINTER + unsigned long top, bottom; +#endif + +#ifdef CONFIG_FRAME_POINTER + top = STACK_TOP(frame->task); + bottom = STACK_BOTTOM(frame->task); +# if FRAME_RETADDR_OFFSET < 0 + if (UNW_SP(frame) < top + && UNW_FP(frame) <= UNW_SP(frame) + && bottom < UNW_FP(frame) +# else + if (UNW_SP(frame) > top + && UNW_FP(frame) >= UNW_SP(frame) + && bottom > UNW_FP(frame) +# endif + && !((UNW_SP(frame) | UNW_FP(frame)) + & (sizeof(unsigned long) - 1))) { + unsigned long link; + + if (!__get_user(link, + (unsigned long *)(UNW_FP(frame) + + FRAME_LINK_OFFSET)) +# if FRAME_RETADDR_OFFSET < 0 + && link > bottom && link < UNW_FP(frame) +# else + && link > UNW_FP(frame) && link < bottom +# endif + && !(link & (sizeof(link) - 1)) + && !__get_user(UNW_PC(frame), + (unsigned long *)(UNW_FP(frame) + + FRAME_RETADDR_OFFSET))) { + UNW_SP(frame) = UNW_FP(frame) + FRAME_RETADDR_OFFSET +# if FRAME_RETADDR_OFFSET < 0 + - +# else + + +# endif + sizeof(UNW_PC(frame)); + UNW_FP(frame) = link; + return 0; + } + } +#endif + return -ENXIO; + } + state.org = startLoc; + memcpy(&state.cfa, &badCFA, sizeof(state.cfa)); + /* process instructions */ + if (!processCFI(ptr, end, UNW_PC(frame), ptrType, &state) + || state.loc > endLoc + || state.regs[retAddrReg].where == Nowhere + || state.cfa.reg >= ARRAY_SIZE(reg_info) + || reg_info[state.cfa.reg].width != sizeof(unsigned long) + || state.cfa.offs % sizeof(unsigned long)) + return -EIO; + /* update frame */ + cfa = FRAME_REG(state.cfa.reg, unsigned long) + state.cfa.offs; + startLoc = min((unsigned long)UNW_SP(frame), cfa); + endLoc = max((unsigned long)UNW_SP(frame), cfa); + if (STACK_LIMIT(startLoc) != STACK_LIMIT(endLoc)) { + startLoc = min(STACK_LIMIT(cfa), cfa); + endLoc = max(STACK_LIMIT(cfa), cfa); + } +#ifndef CONFIG_64BIT +# define CASES CASE(8); CASE(16); CASE(32) +#else +# define CASES CASE(8); CASE(16); CASE(32); CASE(64) +#endif + for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { + if (REG_INVALID(i)) { + if (state.regs[i].where == Nowhere) + continue; + return -EIO; + } + switch(state.regs[i].where) { + default: + break; + case Register: + if (state.regs[i].value >= ARRAY_SIZE(reg_info) + || REG_INVALID(state.regs[i].value) + || reg_info[i].width > reg_info[state.regs[i].value].width) + return -EIO; + switch(reg_info[state.regs[i].value].width) { +#define CASE(n) \ + case sizeof(u##n): \ + state.regs[i].value = FRAME_REG(state.regs[i].value, \ + const u##n); \ + break + CASES; +#undef CASE + default: + return -EIO; + } + break; + } + } + for (i = 0; i < ARRAY_SIZE(state.regs); ++i) { + if (REG_INVALID(i)) + continue; + switch(state.regs[i].where) { + case Nowhere: + if (reg_info[i].width != sizeof(UNW_SP(frame)) + || &FRAME_REG(i, __typeof__(UNW_SP(frame))) + != &UNW_SP(frame)) + continue; + UNW_SP(frame) = cfa; + break; + case Register: + switch(reg_info[i].width) { +#define CASE(n) case sizeof(u##n): \ + FRAME_REG(i, u##n) = state.regs[i].value; \ + break + CASES; +#undef CASE + default: + return -EIO; + } + break; + case Value: + if (reg_info[i].width != sizeof(unsigned long)) + return -EIO; + FRAME_REG(i, unsigned long) = cfa + state.regs[i].value + * state.dataAlign; + break; + case Memory: { + unsigned long addr = cfa + state.regs[i].value + * state.dataAlign; + + if ((state.regs[i].value * state.dataAlign) + % sizeof(unsigned long) + || addr < startLoc + || addr + sizeof(unsigned long) < addr + || addr + sizeof(unsigned long) > endLoc) + return -EIO; + switch(reg_info[i].width) { +#define CASE(n) case sizeof(u##n): \ + __get_user(FRAME_REG(i, u##n), (u##n *)addr); \ + break + CASES; +#undef CASE + default: + return -EIO; + } + } + break; + } + } + + return 0; +#undef CASES +#undef FRAME_REG +} +EXPORT_SYMBOL(unwind); + +int unwind_init_frame_info(struct unwind_frame_info *info, + struct task_struct *tsk, + /*const*/ struct pt_regs *regs) +{ + info->task = tsk; + arch_unw_init_frame_info(info, regs); + + return 0; +} +EXPORT_SYMBOL(unwind_init_frame_info); + +/* + * Prepare to unwind a blocked task. + */ +int unwind_init_blocked(struct unwind_frame_info *info, + struct task_struct *tsk) +{ + info->task = tsk; + arch_unw_init_blocked(info); + + return 0; +} +EXPORT_SYMBOL(unwind_init_blocked); + +/* + * Prepare to unwind the currently running thread. + */ +int unwind_init_running(struct unwind_frame_info *info, + asmlinkage int (*callback)(struct unwind_frame_info *, + void *arg), + void *arg) +{ + info->task = current; + + return arch_unwind_init_running(info, callback, arg); +} +EXPORT_SYMBOL(unwind_init_running); + +/* + * Unwind until the return pointer is in user-land (or until an error + * occurs). Returns 0 if successful, negative number in case of + * error. + */ +int unwind_to_user(struct unwind_frame_info *info) +{ + while (!arch_unw_user_mode(info)) { + int err = unwind(info); + + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL(unwind_to_user); diff --git a/kernel/user.c b/kernel/user.c index 2116642..6408c04 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -140,7 +140,7 @@ struct user_struct * alloc_uid(uid_t uid atomic_set(&new->processes, 0); atomic_set(&new->files, 0); atomic_set(&new->sigpending, 0); -#ifdef CONFIG_INOTIFY +#ifdef CONFIG_INOTIFY_USER atomic_set(&new->inotify_watches, 0); atomic_set(&new->inotify_devs, 0); #endif @@ -148,7 +148,7 @@ #endif new->mq_bytes = 0; new->locked_shm = 0; - if (alloc_uid_keyring(new) < 0) { + if (alloc_uid_keyring(new, current) < 0) { kmem_cache_free(uid_cachep, new); return NULL; } diff --git a/kernel/wait.c b/kernel/wait.c index 791681c..a1d57ae 100644 --- a/kernel/wait.c +++ b/kernel/wait.c @@ -3,7 +3,6 @@ * * (C) 2004 William Irwin, Oracle */ -#include #include #include #include @@ -11,6 +10,10 @@ #include #include #include +struct lock_class_key waitqueue_lock_key; + +EXPORT_SYMBOL(waitqueue_lock_key); + void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 880fb41..eebb1d8 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -51,7 +51,7 @@ struct cpu_workqueue_struct { wait_queue_head_t work_done; struct workqueue_struct *wq; - task_t *thread; + struct task_struct *thread; int run_depth; /* Detect run_workqueue() recursion depth */ } ____cacheline_aligned; @@ -114,6 +114,7 @@ int fastcall queue_work(struct workqueue put_cpu(); return ret; } +EXPORT_SYMBOL_GPL(queue_work); static void delayed_work_timer_fn(unsigned long __data) { @@ -147,6 +148,29 @@ int fastcall queue_delayed_work(struct w } return ret; } +EXPORT_SYMBOL_GPL(queue_delayed_work); + +int queue_delayed_work_on(int cpu, struct workqueue_struct *wq, + struct work_struct *work, unsigned long delay) +{ + int ret = 0; + struct timer_list *timer = &work->timer; + + if (!test_and_set_bit(0, &work->pending)) { + BUG_ON(timer_pending(timer)); + BUG_ON(!list_empty(&work->entry)); + + /* This stores wq for the moment, for the timer_fn */ + work->wq_data = wq; + timer->expires = jiffies + delay; + timer->data = (unsigned long)work; + timer->function = delayed_work_timer_fn; + add_timer_on(timer, cpu); + ret = 1; + } + return ret; +} +EXPORT_SYMBOL_GPL(queue_delayed_work_on); static void run_workqueue(struct cpu_workqueue_struct *cwq) { @@ -281,6 +305,7 @@ void fastcall flush_workqueue(struct wor unlock_cpu_hotplug(); } } +EXPORT_SYMBOL_GPL(flush_workqueue); static struct task_struct *create_workqueue_thread(struct workqueue_struct *wq, int cpu) @@ -358,6 +383,7 @@ struct workqueue_struct *__create_workqu } return wq; } +EXPORT_SYMBOL_GPL(__create_workqueue); static void cleanup_workqueue_thread(struct workqueue_struct *wq, int cpu) { @@ -395,6 +421,7 @@ void destroy_workqueue(struct workqueue_ free_percpu(wq->cpu_wq); kfree(wq); } +EXPORT_SYMBOL_GPL(destroy_workqueue); static struct workqueue_struct *keventd_wq; @@ -402,48 +429,49 @@ int fastcall schedule_work(struct work_s { return queue_work(keventd_wq, work); } +EXPORT_SYMBOL(schedule_work); int fastcall schedule_delayed_work(struct work_struct *work, unsigned long delay) { return queue_delayed_work(keventd_wq, work, delay); } +EXPORT_SYMBOL(schedule_delayed_work); int schedule_delayed_work_on(int cpu, struct work_struct *work, unsigned long delay) { - int ret = 0; - struct timer_list *timer = &work->timer; - - if (!test_and_set_bit(0, &work->pending)) { - BUG_ON(timer_pending(timer)); - BUG_ON(!list_empty(&work->entry)); - /* This stores keventd_wq for the moment, for the timer_fn */ - work->wq_data = keventd_wq; - timer->expires = jiffies + delay; - timer->data = (unsigned long)work; - timer->function = delayed_work_timer_fn; - add_timer_on(timer, cpu); - ret = 1; - } - return ret; + return queue_delayed_work_on(cpu, keventd_wq, work, delay); } +EXPORT_SYMBOL(schedule_delayed_work_on); -int schedule_on_each_cpu(void (*func) (void *info), void *info) +/** + * schedule_on_each_cpu - call a function on each online CPU from keventd + * @func: the function to call + * @info: a pointer to pass to func() + * + * Returns zero on success. + * Returns -ve errno on failure. + * + * Appears to be racy against CPU hotplug. + * + * schedule_on_each_cpu() is very slow. + */ +int schedule_on_each_cpu(void (*func)(void *info), void *info) { int cpu; - struct work_struct *work; + struct work_struct *works; - work = kmalloc(NR_CPUS * sizeof(struct work_struct), GFP_KERNEL); - - if (!work) + works = alloc_percpu(struct work_struct); + if (!works) return -ENOMEM; + for_each_online_cpu(cpu) { - INIT_WORK(work + cpu, func, info); + INIT_WORK(per_cpu_ptr(works, cpu), func, info); __queue_work(per_cpu_ptr(keventd_wq->cpu_wq, cpu), - work + cpu); + per_cpu_ptr(works, cpu)); } flush_workqueue(keventd_wq); - kfree(work); + free_percpu(works); return 0; } @@ -451,6 +479,7 @@ void flush_scheduled_work(void) { flush_workqueue(keventd_wq); } +EXPORT_SYMBOL(flush_scheduled_work); /** * cancel_rearming_delayed_workqueue - reliably kill off a delayed @@ -531,11 +560,11 @@ #ifdef CONFIG_HOTPLUG_CPU static void take_over_work(struct workqueue_struct *wq, unsigned int cpu) { struct cpu_workqueue_struct *cwq = per_cpu_ptr(wq->cpu_wq, cpu); - LIST_HEAD(list); + struct list_head list; struct work_struct *work; spin_lock_irq(&cwq->lock); - list_splice_init(&cwq->worklist, &list); + list_replace_init(&cwq->worklist, &list); while (!list_empty(&list)) { printk("Taking work for %s\n", wq->name); @@ -547,7 +576,7 @@ static void take_over_work(struct workqu } /* We're holding the cpucontrol mutex here */ -static int workqueue_cpu_callback(struct notifier_block *nfb, +static int __devinit workqueue_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -578,6 +607,8 @@ static int workqueue_cpu_callback(struct case CPU_UP_CANCELED: list_for_each_entry(wq, &workqueues, list) { + if (!per_cpu_ptr(wq->cpu_wq, hotcpu)->thread) + continue; /* Unbind so it can run. */ kthread_bind(per_cpu_ptr(wq->cpu_wq, hotcpu)->thread, any_online_cpu(cpu_online_map)); @@ -605,13 +636,3 @@ void init_workqueues(void) BUG_ON(!keventd_wq); } -EXPORT_SYMBOL_GPL(__create_workqueue); -EXPORT_SYMBOL_GPL(queue_work); -EXPORT_SYMBOL_GPL(queue_delayed_work); -EXPORT_SYMBOL_GPL(flush_workqueue); -EXPORT_SYMBOL_GPL(destroy_workqueue); - -EXPORT_SYMBOL(schedule_work); -EXPORT_SYMBOL(schedule_delayed_work); -EXPORT_SYMBOL(schedule_delayed_work_on); -EXPORT_SYMBOL(flush_scheduled_work); diff --git a/lib/Kconfig b/lib/Kconfig index 3de9335..f629934 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -86,4 +86,10 @@ config TEXTSEARCH_BM config TEXTSEARCH_FSM tristate +# +# plist support is select#ed if needed +# +config PLIST + boolean + endmenu diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ccb0c1f..e5889b1 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -23,6 +23,22 @@ config MAGIC_SYSRQ keys are documented in . Don't say Y unless you really know what this hack does. +config UNUSED_SYMBOLS + bool "Enable unused/obsolete exported symbols" + default y if X86 + help + Unused but exported symbols make the kernel needlessly bigger. For + that reason most of these unused exports will soon be removed. This + option is provided temporarily to provide a transition period in case + some external kernel module needs one of these symbols anyway. If you + encounter such a case in your module, consider if you are actually + using the right API. (rationale: since nobody in the kernel is using + this in a module, there is a pretty good chance it's actually the + wrong interface to use). If you really need the symbol, please send a + mail to the linux kernel mailing list mentioning the symbol and why + you really need it, and what the merge plan to the mainline kernel for + your module is. + config DEBUG_KERNEL bool "Kernel debugging" help @@ -32,7 +48,7 @@ config DEBUG_KERNEL config LOG_BUF_SHIFT int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL range 12 21 - default 17 if S390 + default 17 if S390 || LOCKDEP default 16 if X86_NUMAQ || IA64 default 15 if SMP default 14 @@ -91,7 +107,7 @@ config DEBUG_SLAB_LEAK config DEBUG_PREEMPT bool "Debug preemptible kernel" - depends on DEBUG_KERNEL && PREEMPT + depends on DEBUG_KERNEL && PREEMPT && TRACE_IRQFLAGS_SUPPORT default y help If you say Y here then the kernel will use a debug variant of the @@ -99,16 +115,26 @@ config DEBUG_PREEMPT if kernel code uses it in a preemption-unsafe way. Also, the kernel will detect preemption count underflows. -config DEBUG_MUTEXES - bool "Mutex debugging, deadlock detection" - default n - depends on DEBUG_KERNEL +config DEBUG_RT_MUTEXES + bool "RT Mutex debugging, deadlock detection" + depends on DEBUG_KERNEL && RT_MUTEXES help - This allows mutex semantics violations and mutex related deadlocks - (lockups) to be detected and reported automatically. + This allows rt mutex semantics violations and rt mutex related + deadlocks (lockups) to be detected and reported automatically. + +config DEBUG_PI_LIST + bool + default y + depends on DEBUG_RT_MUTEXES + +config RT_MUTEX_TESTER + bool "Built-in scriptable tester for rt-mutexes" + depends on DEBUG_KERNEL && RT_MUTEXES + help + This option enables a rt-mutex tester. config DEBUG_SPINLOCK - bool "Spinlock debugging" + bool "Spinlock and rw-lock debugging: basic checks" depends on DEBUG_KERNEL help Say Y here and build SMP to catch missing spinlock initialization @@ -116,13 +142,122 @@ config DEBUG_SPINLOCK best used in conjunction with the NMI watchdog so that spinlock deadlocks are also debuggable. +config DEBUG_MUTEXES + bool "Mutex debugging: basic checks" + depends on DEBUG_KERNEL + help + This feature allows mutex semantics violations to be detected and + reported. + +config DEBUG_RWSEMS + bool "RW-sem debugging: basic checks" + depends on DEBUG_KERNEL + help + This feature allows read-write semaphore semantics violations to + be detected and reported. + +config DEBUG_LOCK_ALLOC + bool "Lock debugging: detect incorrect freeing of live locks" + depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + select DEBUG_SPINLOCK + select DEBUG_MUTEXES + select DEBUG_RWSEMS + select LOCKDEP + help + This feature will check whether any held lock (spinlock, rwlock, + mutex or rwsem) is incorrectly freed by the kernel, via any of the + memory-freeing routines (kfree(), kmem_cache_free(), free_pages(), + vfree(), etc.), whether a live lock is incorrectly reinitialized via + spin_lock_init()/mutex_init()/etc., or whether there is any lock + held during task exit. + +config PROVE_LOCKING + bool "Lock debugging: prove locking correctness" + depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + select LOCKDEP + select DEBUG_SPINLOCK + select DEBUG_MUTEXES + select DEBUG_RWSEMS + select DEBUG_LOCK_ALLOC + default n + help + This feature enables the kernel to prove that all locking + that occurs in the kernel runtime is mathematically + correct: that under no circumstance could an arbitrary (and + not yet triggered) combination of observed locking + sequences (on an arbitrary number of CPUs, running an + arbitrary number of tasks and interrupt contexts) cause a + deadlock. + + In short, this feature enables the kernel to report locking + related deadlocks before they actually occur. + + The proof does not depend on how hard and complex a + deadlock scenario would be to trigger: how many + participant CPUs, tasks and irq-contexts would be needed + for it to trigger. The proof also does not depend on + timing: if a race and a resulting deadlock is possible + theoretically (no matter how unlikely the race scenario + is), it will be proven so and will immediately be + reported by the kernel (once the event is observed that + makes the deadlock theoretically possible). + + If a deadlock is impossible (i.e. the locking rules, as + observed by the kernel, are mathematically correct), the + kernel reports nothing. + + NOTE: this feature can also be enabled for rwlocks, mutexes + and rwsems - in which case all dependencies between these + different locking variants are observed and mapped too, and + the proof of observed correctness is also maintained for an + arbitrary combination of these separate locking variants. + + For more details, see Documentation/lockdep-design.txt. + +config LOCKDEP + bool + depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && LOCKDEP_SUPPORT + select STACKTRACE + select FRAME_POINTER + select KALLSYMS + select KALLSYMS_ALL + +config DEBUG_LOCKDEP + bool "Lock dependency engine debugging" + depends on LOCKDEP + help + If you say Y here, the lock dependency engine will do + additional runtime checks to debug itself, at the price + of more runtime overhead. + +config TRACE_IRQFLAGS + bool + default y + depends on TRACE_IRQFLAGS_SUPPORT + depends on PROVE_LOCKING + config DEBUG_SPINLOCK_SLEEP - bool "Sleep-inside-spinlock checking" + bool "Spinlock debugging: sleep-inside-spinlock checking" depends on DEBUG_KERNEL help If you say Y here, various routines which may sleep will become very noisy if they are called with a spinlock held. +config DEBUG_LOCKING_API_SELFTESTS + bool "Locking API boot-time self-tests" + depends on DEBUG_KERNEL + help + Say Y here if you want the kernel to run a short self-test during + bootup. The self-test checks whether common types of locking bugs + are detected by debugging mechanisms or not. (if you disable + lock debugging then those bugs wont be detected of course.) + The following locking APIs are covered: spinlocks, rwlocks, + mutexes and rwsems. + +config STACKTRACE + bool + depends on STACKTRACE_SUPPORT + config DEBUG_KOBJECT bool "kobject debugging" depends on DEBUG_KERNEL @@ -178,7 +313,7 @@ config DEBUG_VM config FRAME_POINTER bool "Compile the kernel with frame pointers" - depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML) + depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML || S390) default y if DEBUG_INFO && UML help If you say Y here the resulting kernel image will be slightly larger @@ -188,14 +323,22 @@ config FRAME_POINTER config UNWIND_INFO bool "Compile the kernel with frame unwind information" - depends on !IA64 - depends on !MODULES || !(MIPS || PARISC || PPC || SUPERH || V850) + depends on !IA64 && !PARISC + depends on !MODULES || !(MIPS || PPC || SUPERH || V850) help If you say Y here the resulting kernel image will be slightly larger but not slower, and it will give very useful debugging information. If you don't debug the kernel, you can say N, but we may not be able to solve problems without frame unwind information or frame pointers. +config STACK_UNWIND + bool "Stack unwind support" + depends on UNWIND_INFO + depends on X86 + help + This enables more precise stack traces, omitting all unrelated + occurrences of pointers into kernel code from the dump. + config FORCED_INLINING bool "Force gcc to inline functions marked 'inline'" depends on DEBUG_KERNEL diff --git a/lib/Makefile b/lib/Makefile index b830c9a..be9719a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -11,13 +11,14 @@ lib-$(CONFIG_SMP) += cpumask.o lib-y += kobject.o kref.o kobject_uevent.o klist.o -obj-y += sort.o parser.o halfmd4.o iomap_copy.o +obj-y += sort.o parser.o halfmd4.o iomap_copy.o debug_locks.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG CFLAGS_kobject_uevent.o += -DDEBUG endif +obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o @@ -25,6 +26,7 @@ lib-$(CONFIG_SEMAPHORE_SLEEPERS) += sema lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o lib-$(CONFIG_GENERIC_HWEIGHT) += hweight.o obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o +obj-$(CONFIG_PLIST) += plist.o obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o ifneq ($(CONFIG_HAVE_DEC_LOCK),y) @@ -46,6 +48,7 @@ obj-$(CONFIG_TEXTSEARCH) += textsearch.o obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o obj-$(CONFIG_TEXTSEARCH_BM) += ts_bm.o obj-$(CONFIG_TEXTSEARCH_FSM) += ts_fsm.o +obj-$(CONFIG_SMP) += percpu_counter.o obj-$(CONFIG_SWIOTLB) += swiotlb.o diff --git a/lib/bitmap.c b/lib/bitmap.c index ed2ae3b..d71e38c 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c @@ -317,16 +317,16 @@ EXPORT_SYMBOL(bitmap_scnprintf); /** * bitmap_parse - convert an ASCII hex string into a bitmap. - * @buf: pointer to buffer in user space containing string. - * @buflen: buffer size in bytes. If string is smaller than this + * @ubuf: pointer to buffer in user space containing string. + * @ubuflen: buffer size in bytes. If string is smaller than this * then it must be terminated with a \0. * @maskp: pointer to bitmap array that will contain result. * @nmaskbits: size of bitmap, in bits. * * Commas group hex digits into chunks. Each chunk defines exactly 32 * bits of the resultant bitmask. No chunk may specify a value larger - * than 32 bits (-EOVERFLOW), and if a chunk specifies a smaller value - * then leading 0-bits are prepended. -EINVAL is returned for illegal + * than 32 bits (%-EOVERFLOW), and if a chunk specifies a smaller value + * then leading 0-bits are prepended. %-EINVAL is returned for illegal * characters and for grouping errors such as "1,,5", ",44", "," and "". * Leading and trailing whitespace accepted, but not embedded whitespace. */ @@ -452,8 +452,8 @@ EXPORT_SYMBOL(bitmap_scnlistprintf); /** * bitmap_parselist - convert list format ASCII string to bitmap - * @buf: read nul-terminated user string from this buffer - * @mask: write resulting mask here + * @bp: read nul-terminated user string from this buffer + * @maskp: write resulting mask here * @nmaskbits: number of bits in mask to be written * * Input format is a comma-separated list of decimal numbers and @@ -461,10 +461,11 @@ EXPORT_SYMBOL(bitmap_scnlistprintf); * decimal numbers, the smallest and largest bit numbers set in * the range. * - * Returns 0 on success, -errno on invalid input strings: - * -EINVAL: second number in range smaller than first - * -EINVAL: invalid character in string - * -ERANGE: bit number specified too large for mask + * Returns 0 on success, -errno on invalid input strings. + * Error values: + * %-EINVAL: second number in range smaller than first + * %-EINVAL: invalid character in string + * %-ERANGE: bit number specified too large for mask */ int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) { @@ -625,10 +626,10 @@ EXPORT_SYMBOL(bitmap_remap); /** * bitmap_bitremap - Apply map defined by a pair of bitmaps to a single bit - * @oldbit - bit position to be mapped - * @old: defines domain of map - * @new: defines range of map - * @bits: number of bits in each of these bitmaps + * @oldbit: bit position to be mapped + * @old: defines domain of map + * @new: defines range of map + * @bits: number of bits in each of these bitmaps * * Let @old and @new define a mapping of bit positions, such that * whatever position is held by the n-th set bit in @old is mapped @@ -790,7 +791,7 @@ EXPORT_SYMBOL(bitmap_release_region); * * Allocate (set bits in) a specified region of a bitmap. * - * Return 0 on success, or -EBUSY if specified region wasn't + * Return 0 on success, or %-EBUSY if specified region wasn't * free (not all bits were zero). */ int bitmap_allocate_region(unsigned long *bitmap, int pos, int order) diff --git a/lib/bust_spinlocks.c b/lib/bust_spinlocks.c index 6bb7319..a2055bc 100644 --- a/lib/bust_spinlocks.c +++ b/lib/bust_spinlocks.c @@ -7,7 +7,6 @@ * and panic() information from reaching the user. */ -#include #include #include #include diff --git a/lib/crc-ccitt.c b/lib/crc-ccitt.c index 115d149..7f6dd68 100644 --- a/lib/crc-ccitt.c +++ b/lib/crc-ccitt.c @@ -53,9 +53,9 @@ EXPORT_SYMBOL(crc_ccitt_table); /** * crc_ccitt - recompute the CRC for the data buffer - * @crc - previous CRC value - * @buffer - data pointer - * @len - number of bytes in the buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer */ u16 crc_ccitt(u16 crc, u8 const *buffer, size_t len) { diff --git a/lib/crc16.c b/lib/crc16.c index 011fe57..8737b08 100644 --- a/lib/crc16.c +++ b/lib/crc16.c @@ -47,12 +47,12 @@ u16 const crc16_table[256] = { EXPORT_SYMBOL(crc16_table); /** - * Compute the CRC-16 for the data buffer + * crc16 - compute the CRC-16 for the data buffer + * @crc: previous CRC value + * @buffer: data pointer + * @len: number of bytes in the buffer * - * @param crc previous CRC value - * @param buffer data pointer - * @param len number of bytes in the buffer - * @return the updated CRC value + * Returns the updated CRC value. */ u16 crc16(u16 crc, u8 const *buffer, size_t len) { diff --git a/lib/crc32.c b/lib/crc32.c index 065198f..285fd9b 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -42,20 +42,21 @@ MODULE_AUTHOR("Matt Domsch > 16) | (x << 16); diff --git a/lib/debug_locks.c b/lib/debug_locks.c new file mode 100644 index 0000000..0ef01d1 --- /dev/null +++ b/lib/debug_locks.c @@ -0,0 +1,45 @@ +/* + * lib/debug_locks.c + * + * Generic place for common debugging facilities for various locks: + * spinlocks, rwlocks, mutexes and rwsems. + * + * Started by Ingo Molnar: + * + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + */ +#include +#include +#include +#include +#include + +/* + * We want to turn all lock-debugging facilities on/off at once, + * via a global flag. The reason is that once a single bug has been + * detected and reported, there might be cascade of followup bugs + * that would just muddy the log. So we report the first one and + * shut up after that. + */ +int debug_locks = 1; + +/* + * The locking-testsuite uses to get a + * 'silent failure': nothing is printed to the console when + * a locking bug is detected. + */ +int debug_locks_silent; + +/* + * Generic 'turn off all lock debugging' function: + */ +int debug_locks_off(void) +{ + if (xchg(&debug_locks, 0)) { + if (!debug_locks_silent) { + console_verbose(); + return 1; + } + } + return 0; +} diff --git a/lib/extable.c b/lib/extable.c index 01c08b5..463f456 100644 --- a/lib/extable.c +++ b/lib/extable.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/lib/genalloc.c b/lib/genalloc.c index 9ce0a6a..71338b4 100644 --- a/lib/genalloc.c +++ b/lib/genalloc.c @@ -4,10 +4,6 @@ * Uses for this includes on-device special memory, uncached memory * etc. * - * This code is based on the buddy allocator found in the sym53c8xx_2 - * driver Copyright (C) 1999-2001 Gerard Roudier , - * and adapted for general purpose use. - * * Copyright 2005 (C) Jes Sorensen * * This source code is licensed under the GNU General Public License, @@ -15,172 +11,155 @@ */ #include -#include -#include -#include -#include -#include -#include -#include #include -#include - -struct gen_pool *gen_pool_create(int nr_chunks, int max_chunk_shift, - unsigned long (*fp)(struct gen_pool *), - unsigned long data) +/* + * Create a new special memory pool. + * + * @min_alloc_order: log base 2 of number of bytes each bitmap bit represents + * @nid: node id of the node the pool structure should be allocated on, or -1 + */ +struct gen_pool *gen_pool_create(int min_alloc_order, int nid) { - struct gen_pool *poolp; - unsigned long tmp; - int i; - - /* - * This is really an arbitrary limit, +10 is enough for - * IA64_GRANULE_SHIFT, aka 16MB. If anyone needs a large limit - * this can be increased without problems. - */ - if ((max_chunk_shift > (PAGE_SHIFT + 10)) || - ((max_chunk_shift < ALLOC_MIN_SHIFT) && max_chunk_shift)) - return NULL; - - if (!max_chunk_shift) - max_chunk_shift = PAGE_SHIFT; - - poolp = kmalloc(sizeof(struct gen_pool), GFP_KERNEL); - if (!poolp) - return NULL; - memset(poolp, 0, sizeof(struct gen_pool)); - poolp->h = kmalloc(sizeof(struct gen_pool_link) * - (max_chunk_shift - ALLOC_MIN_SHIFT + 1), - GFP_KERNEL); - if (!poolp->h) { - printk(KERN_WARNING "gen_pool_alloc() failed to allocate\n"); - kfree(poolp); - return NULL; - } - memset(poolp->h, 0, sizeof(struct gen_pool_link) * - (max_chunk_shift - ALLOC_MIN_SHIFT + 1)); - - spin_lock_init(&poolp->lock); - poolp->get_new_chunk = fp; - poolp->max_chunk_shift = max_chunk_shift; - poolp->private = data; - - for (i = 0; i < nr_chunks; i++) { - tmp = poolp->get_new_chunk(poolp); - printk(KERN_INFO "allocated %lx\n", tmp); - if (!tmp) - break; - gen_pool_free(poolp, tmp, (1 << poolp->max_chunk_shift)); - } + struct gen_pool *pool; - return poolp; + pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid); + if (pool != NULL) { + rwlock_init(&pool->lock); + INIT_LIST_HEAD(&pool->chunks); + pool->min_alloc_order = min_alloc_order; + } + return pool; } EXPORT_SYMBOL(gen_pool_create); /* - * Simple power of two buddy-like generic allocator. - * Provides naturally aligned memory chunks. + * Add a new chunk of memory to the specified pool. + * + * @pool: pool to add new memory chunk to + * @addr: starting address of memory chunk to add to pool + * @size: size in bytes of the memory chunk to add to pool + * @nid: node id of the node the chunk structure and bitmap should be + * allocated on, or -1 */ -unsigned long gen_pool_alloc(struct gen_pool *poolp, int size) +int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size, + int nid) { - int j, i, s, max_chunk_size; - unsigned long a, flags; - struct gen_pool_link *h = poolp->h; + struct gen_pool_chunk *chunk; + int nbits = size >> pool->min_alloc_order; + int nbytes = sizeof(struct gen_pool_chunk) + + (nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE; - max_chunk_size = 1 << poolp->max_chunk_shift; + chunk = kmalloc_node(nbytes, GFP_KERNEL, nid); + if (unlikely(chunk == NULL)) + return -1; - if (size > max_chunk_size) - return 0; + memset(chunk, 0, nbytes); + spin_lock_init(&chunk->lock); + chunk->start_addr = addr; + chunk->end_addr = addr + size; - size = max(size, 1 << ALLOC_MIN_SHIFT); - i = fls(size - 1); - s = 1 << i; - j = i -= ALLOC_MIN_SHIFT; - - spin_lock_irqsave(&poolp->lock, flags); - while (!h[j].next) { - if (s == max_chunk_size) { - struct gen_pool_link *ptr; - spin_unlock_irqrestore(&poolp->lock, flags); - ptr = (struct gen_pool_link *)poolp->get_new_chunk(poolp); - spin_lock_irqsave(&poolp->lock, flags); - h[j].next = ptr; - if (h[j].next) - h[j].next->next = NULL; - break; - } - j++; - s <<= 1; - } - a = (unsigned long) h[j].next; - if (a) { - h[j].next = h[j].next->next; - /* - * This should be split into a seperate function doing - * the chunk split in order to support custom - * handling memory not physically accessible by host - */ - while (j > i) { - j -= 1; - s >>= 1; - h[j].next = (struct gen_pool_link *) (a + s); - h[j].next->next = NULL; - } - } - spin_unlock_irqrestore(&poolp->lock, flags); - return a; + write_lock(&pool->lock); + list_add(&chunk->next_chunk, &pool->chunks); + write_unlock(&pool->lock); + + return 0; } -EXPORT_SYMBOL(gen_pool_alloc); +EXPORT_SYMBOL(gen_pool_add); /* - * Counter-part of the generic allocator. + * Allocate the requested number of bytes from the specified pool. + * Uses a first-fit algorithm. + * + * @pool: pool to allocate from + * @size: number of bytes to allocate from the pool */ -void gen_pool_free(struct gen_pool *poolp, unsigned long ptr, int size) +unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size) { - struct gen_pool_link *q; - struct gen_pool_link *h = poolp->h; - unsigned long a, b, flags; - int i, s, max_chunk_size; - - max_chunk_size = 1 << poolp->max_chunk_shift; + struct list_head *_chunk; + struct gen_pool_chunk *chunk; + unsigned long addr, flags; + int order = pool->min_alloc_order; + int nbits, bit, start_bit, end_bit; - if (size > max_chunk_size) - return; - - size = max(size, 1 << ALLOC_MIN_SHIFT); - i = fls(size - 1); - s = 1 << i; - i -= ALLOC_MIN_SHIFT; - - a = ptr; + if (size == 0) + return 0; - spin_lock_irqsave(&poolp->lock, flags); - while (1) { - if (s == max_chunk_size) { - ((struct gen_pool_link *)a)->next = h[i].next; - h[i].next = (struct gen_pool_link *)a; - break; + nbits = (size + (1UL << order) - 1) >> order; + + read_lock(&pool->lock); + list_for_each(_chunk, &pool->chunks) { + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + + end_bit = (chunk->end_addr - chunk->start_addr) >> order; + end_bit -= nbits + 1; + + spin_lock_irqsave(&chunk->lock, flags); + bit = -1; + while (bit + 1 < end_bit) { + bit = find_next_zero_bit(chunk->bits, end_bit, bit + 1); + if (bit >= end_bit) + break; + + start_bit = bit; + if (nbits > 1) { + bit = find_next_bit(chunk->bits, bit + nbits, + bit + 1); + if (bit - start_bit < nbits) + continue; + } + + addr = chunk->start_addr + + ((unsigned long)start_bit << order); + while (nbits--) + __set_bit(start_bit++, &chunk->bits); + spin_unlock_irqrestore(&chunk->lock, flags); + read_unlock(&pool->lock); + return addr; } - b = a ^ s; - q = &h[i]; + spin_unlock_irqrestore(&chunk->lock, flags); + } + read_unlock(&pool->lock); + return 0; +} +EXPORT_SYMBOL(gen_pool_alloc); - while (q->next && q->next != (struct gen_pool_link *)b) - q = q->next; - if (!q->next) { - ((struct gen_pool_link *)a)->next = h[i].next; - h[i].next = (struct gen_pool_link *)a; +/* + * Free the specified memory back to the specified pool. + * + * @pool: pool to free to + * @addr: starting address of memory to free back to pool + * @size: size in bytes of memory to free + */ +void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size) +{ + struct list_head *_chunk; + struct gen_pool_chunk *chunk; + unsigned long flags; + int order = pool->min_alloc_order; + int bit, nbits; + + nbits = (size + (1UL << order) - 1) >> order; + + read_lock(&pool->lock); + list_for_each(_chunk, &pool->chunks) { + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk); + + if (addr >= chunk->start_addr && addr < chunk->end_addr) { + BUG_ON(addr + size > chunk->end_addr); + spin_lock_irqsave(&chunk->lock, flags); + bit = (addr - chunk->start_addr) >> order; + while (nbits--) + __clear_bit(bit++, &chunk->bits); + spin_unlock_irqrestore(&chunk->lock, flags); break; } - q->next = q->next->next; - a = a & b; - s <<= 1; - i++; } - spin_unlock_irqrestore(&poolp->lock, flags); + BUG_ON(nbits > 0); + read_unlock(&pool->lock); } EXPORT_SYMBOL(gen_pool_free); diff --git a/lib/idr.c b/lib/idr.c index d226259..4d09681 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -29,6 +29,7 @@ #include #include #include #endif +#include #include #include @@ -48,15 +49,21 @@ static struct idr_layer *alloc_layer(str return(p); } +/* only called when idp->lock is held */ +static void __free_layer(struct idr *idp, struct idr_layer *p) +{ + p->ary[0] = idp->id_free; + idp->id_free = p; + idp->id_free_cnt++; +} + static void free_layer(struct idr *idp, struct idr_layer *p) { /* * Depends on the return element being zeroed. */ spin_lock(&idp->lock); - p->ary[0] = idp->id_free; - idp->id_free = p; - idp->id_free_cnt++; + __free_layer(idp, p); spin_unlock(&idp->lock); } @@ -184,12 +191,14 @@ build_up: * The allocation failed. If we built part of * the structure tear it down. */ + spin_lock(&idp->lock); for (new = p; p && p != idp->top; new = p) { p = p->ary[0]; new->ary[0] = NULL; new->bitmap = new->count = 0; - free_layer(idp, new); + __free_layer(idp, new); } + spin_unlock(&idp->lock); return -1; } new->ary[0] = p; @@ -390,6 +399,48 @@ void *idr_find(struct idr *idp, int id) } EXPORT_SYMBOL(idr_find); +/** + * idr_replace - replace pointer for given id + * @idp: idr handle + * @ptr: pointer you want associated with the id + * @id: lookup key + * + * Replace the pointer registered with an id and return the old value. + * A -ENOENT return indicates that @id was not found. + * A -EINVAL return indicates that @id was not within valid constraints. + * + * The caller must serialize vs idr_find(), idr_get_new(), and idr_remove(). + */ +void *idr_replace(struct idr *idp, void *ptr, int id) +{ + int n; + struct idr_layer *p, *old_p; + + n = idp->layers * IDR_BITS; + p = idp->top; + + id &= MAX_ID_MASK; + + if (id >= (1 << n)) + return ERR_PTR(-EINVAL); + + n -= IDR_BITS; + while ((n > 0) && p) { + p = p->ary[(id >> n) & IDR_MASK]; + n -= IDR_BITS; + } + + n = id & IDR_MASK; + if (unlikely(p == NULL || !test_bit(n, &p->bitmap))) + return ERR_PTR(-ENOENT); + + old_p = p->ary[n]; + p->ary[n] = ptr; + + return old_p; +} +EXPORT_SYMBOL(idr_replace); + static void idr_cache_ctor(void * idr_layer, kmem_cache_t *idr_layer_cache, unsigned long flags) { diff --git a/lib/iomap_copy.c b/lib/iomap_copy.c index 351045f..864fc5e 100644 --- a/lib/iomap_copy.c +++ b/lib/iomap_copy.c @@ -40,3 +40,31 @@ void __attribute__((weak)) __iowrite32_c __raw_writel(*src++, dst++); } EXPORT_SYMBOL_GPL(__iowrite32_copy); + +/** + * __iowrite64_copy - copy data to MMIO space, in 64-bit or 32-bit units + * @to: destination, in MMIO space (must be 64-bit aligned) + * @from: source (must be 64-bit aligned) + * @count: number of 64-bit quantities to copy + * + * Copy data from kernel space to MMIO space, in units of 32 or 64 bits at a + * time. Order of access is not guaranteed, nor is a memory barrier + * performed afterwards. + */ +void __attribute__((weak)) __iowrite64_copy(void __iomem *to, + const void *from, + size_t count) +{ +#ifdef CONFIG_64BIT + u64 __iomem *dst = to; + const u64 *src = from; + const u64 *end = src + count; + + while (src < end) + __raw_writeq(*src++, dst++); +#else + __iowrite32_copy(to, from, count * 2); +#endif +} + +EXPORT_SYMBOL_GPL(__iowrite64_copy); diff --git a/lib/kernel_lock.c b/lib/kernel_lock.c index cb5490e..e0fdfdd 100644 --- a/lib/kernel_lock.c +++ b/lib/kernel_lock.c @@ -14,7 +14,7 @@ #ifdef CONFIG_PREEMPT_BKL * The 'big kernel semaphore' * * This mutex is taken and released recursively by lock_kernel() - * and unlock_kernel(). It is transparently dropped and reaquired + * and unlock_kernel(). It is transparently dropped and reacquired * over schedule(). It is used to protect legacy code that hasn't * been migrated to a proper locking design yet. * @@ -92,7 +92,7 @@ #else * The 'big kernel lock' * * This spinlock is taken and released recursively by lock_kernel() - * and unlock_kernel(). It is transparently dropped and reaquired + * and unlock_kernel(). It is transparently dropped and reacquired * over schedule(). It is used to protect legacy code that hasn't * been migrated to a proper locking design yet. * @@ -177,7 +177,12 @@ #endif static inline void __unlock_kernel(void) { - spin_unlock(&kernel_flag); + /* + * the BKL is not covered by lockdep, so we open-code the + * unlocking sequence (and thus avoid the dep-chain ops): + */ + _raw_spin_unlock(&kernel_flag); + preempt_enable(); } /* diff --git a/lib/kobject.c b/lib/kobject.c index 687ab41..8e7c719 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -198,14 +198,14 @@ int kobject_add(struct kobject * kobj) /* be noisy on error issues */ if (error == -EEXIST) - pr_debug("kobject_add failed for %s with -EEXIST, " + printk("kobject_add failed for %s with -EEXIST, " "don't try to register things with the " "same name in the same directory.\n", kobject_name(kobj)); else - pr_debug("kobject_add failed for %s (%d)\n", + printk("kobject_add failed for %s (%d)\n", kobject_name(kobj), error); - /* dump_stack(); */ + dump_stack(); } return error; diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 7f20e7b..2b1530f 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -50,6 +50,10 @@ static char *action_to_string(enum kobje return "offline"; case KOBJ_ONLINE: return "online"; + case KOBJ_DOCK: + return "dock"; + case KOBJ_UNDOCK: + return "undock"; default: return NULL; } diff --git a/lib/libcrc32c.c b/lib/libcrc32c.c index 52b6dc1..60f4680 100644 --- a/lib/libcrc32c.c +++ b/lib/libcrc32c.c @@ -88,7 +88,7 @@ #else * reflect output bytes = true */ -static u32 crc32c_table[256] = { +static const u32 crc32c_table[256] = { 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, diff --git a/lib/locking-selftest-hardirq.h b/lib/locking-selftest-hardirq.h new file mode 100644 index 0000000..10d4a15 --- /dev/null +++ b/lib/locking-selftest-hardirq.h @@ -0,0 +1,9 @@ +#undef IRQ_DISABLE +#undef IRQ_ENABLE +#undef IRQ_ENTER +#undef IRQ_EXIT + +#define IRQ_ENABLE HARDIRQ_ENABLE +#define IRQ_DISABLE HARDIRQ_DISABLE +#define IRQ_ENTER HARDIRQ_ENTER +#define IRQ_EXIT HARDIRQ_EXIT diff --git a/lib/locking-selftest-mutex.h b/lib/locking-selftest-mutex.h new file mode 100644 index 0000000..68601b6 --- /dev/null +++ b/lib/locking-selftest-mutex.h @@ -0,0 +1,11 @@ +#undef LOCK +#define LOCK ML + +#undef UNLOCK +#define UNLOCK MU + +#undef RLOCK +#undef WLOCK + +#undef INIT +#define INIT MI diff --git a/lib/locking-selftest-rlock-hardirq.h b/lib/locking-selftest-rlock-hardirq.h new file mode 100644 index 0000000..9f517eb --- /dev/null +++ b/lib/locking-selftest-rlock-hardirq.h @@ -0,0 +1,2 @@ +#include "locking-selftest-rlock.h" +#include "locking-selftest-hardirq.h" diff --git a/lib/locking-selftest-rlock-softirq.h b/lib/locking-selftest-rlock-softirq.h new file mode 100644 index 0000000..981455d --- /dev/null +++ b/lib/locking-selftest-rlock-softirq.h @@ -0,0 +1,2 @@ +#include "locking-selftest-rlock.h" +#include "locking-selftest-softirq.h" diff --git a/lib/locking-selftest-rlock.h b/lib/locking-selftest-rlock.h new file mode 100644 index 0000000..6789044 --- /dev/null +++ b/lib/locking-selftest-rlock.h @@ -0,0 +1,14 @@ +#undef LOCK +#define LOCK RL + +#undef UNLOCK +#define UNLOCK RU + +#undef RLOCK +#define RLOCK RL + +#undef WLOCK +#define WLOCK WL + +#undef INIT +#define INIT RWI diff --git a/lib/locking-selftest-rsem.h b/lib/locking-selftest-rsem.h new file mode 100644 index 0000000..62da886 --- /dev/null +++ b/lib/locking-selftest-rsem.h @@ -0,0 +1,14 @@ +#undef LOCK +#define LOCK RSL + +#undef UNLOCK +#define UNLOCK RSU + +#undef RLOCK +#define RLOCK RSL + +#undef WLOCK +#define WLOCK WSL + +#undef INIT +#define INIT RWSI diff --git a/lib/locking-selftest-softirq.h b/lib/locking-selftest-softirq.h new file mode 100644 index 0000000..a83de2a --- /dev/null +++ b/lib/locking-selftest-softirq.h @@ -0,0 +1,9 @@ +#undef IRQ_DISABLE +#undef IRQ_ENABLE +#undef IRQ_ENTER +#undef IRQ_EXIT + +#define IRQ_DISABLE SOFTIRQ_DISABLE +#define IRQ_ENABLE SOFTIRQ_ENABLE +#define IRQ_ENTER SOFTIRQ_ENTER +#define IRQ_EXIT SOFTIRQ_EXIT diff --git a/lib/locking-selftest-spin-hardirq.h b/lib/locking-selftest-spin-hardirq.h new file mode 100644 index 0000000..693198d --- /dev/null +++ b/lib/locking-selftest-spin-hardirq.h @@ -0,0 +1,2 @@ +#include "locking-selftest-spin.h" +#include "locking-selftest-hardirq.h" diff --git a/lib/locking-selftest-spin-softirq.h b/lib/locking-selftest-spin-softirq.h new file mode 100644 index 0000000..c472e2a --- /dev/null +++ b/lib/locking-selftest-spin-softirq.h @@ -0,0 +1,2 @@ +#include "locking-selftest-spin.h" +#include "locking-selftest-softirq.h" diff --git a/lib/locking-selftest-spin.h b/lib/locking-selftest-spin.h new file mode 100644 index 0000000..ccd1b4b --- /dev/null +++ b/lib/locking-selftest-spin.h @@ -0,0 +1,11 @@ +#undef LOCK +#define LOCK L + +#undef UNLOCK +#define UNLOCK U + +#undef RLOCK +#undef WLOCK + +#undef INIT +#define INIT SI diff --git a/lib/locking-selftest-wlock-hardirq.h b/lib/locking-selftest-wlock-hardirq.h new file mode 100644 index 0000000..2dd2e51 --- /dev/null +++ b/lib/locking-selftest-wlock-hardirq.h @@ -0,0 +1,2 @@ +#include "locking-selftest-wlock.h" +#include "locking-selftest-hardirq.h" diff --git a/lib/locking-selftest-wlock-softirq.h b/lib/locking-selftest-wlock-softirq.h new file mode 100644 index 0000000..cb80d1c --- /dev/null +++ b/lib/locking-selftest-wlock-softirq.h @@ -0,0 +1,2 @@ +#include "locking-selftest-wlock.h" +#include "locking-selftest-softirq.h" diff --git a/lib/locking-selftest-wlock.h b/lib/locking-selftest-wlock.h new file mode 100644 index 0000000..0815322 --- /dev/null +++ b/lib/locking-selftest-wlock.h @@ -0,0 +1,14 @@ +#undef LOCK +#define LOCK WL + +#undef UNLOCK +#define UNLOCK WU + +#undef RLOCK +#define RLOCK RL + +#undef WLOCK +#define WLOCK WL + +#undef INIT +#define INIT RWI diff --git a/lib/locking-selftest-wsem.h b/lib/locking-selftest-wsem.h new file mode 100644 index 0000000..b88c5f2 --- /dev/null +++ b/lib/locking-selftest-wsem.h @@ -0,0 +1,14 @@ +#undef LOCK +#define LOCK WSL + +#undef UNLOCK +#define UNLOCK WSU + +#undef RLOCK +#define RLOCK RSL + +#undef WLOCK +#define WLOCK WSL + +#undef INIT +#define INIT RWSI diff --git a/lib/locking-selftest.c b/lib/locking-selftest.c new file mode 100644 index 0000000..7945787 --- /dev/null +++ b/lib/locking-selftest.c @@ -0,0 +1,1216 @@ +/* + * lib/locking-selftest.c + * + * Testsuite for various locking APIs: spinlocks, rwlocks, + * mutexes and rw-semaphores. + * + * It is checking both false positives and false negatives. + * + * Started by Ingo Molnar: + * + * Copyright (C) 2006 Red Hat, Inc., Ingo Molnar + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Change this to 1 if you want to see the failure printouts: + */ +static unsigned int debug_locks_verbose; + +static int __init setup_debug_locks_verbose(char *str) +{ + get_option(&str, &debug_locks_verbose); + + return 1; +} + +__setup("debug_locks_verbose=", setup_debug_locks_verbose); + +#define FAILURE 0 +#define SUCCESS 1 + +#define LOCKTYPE_SPIN 0x1 +#define LOCKTYPE_RWLOCK 0x2 +#define LOCKTYPE_MUTEX 0x4 +#define LOCKTYPE_RWSEM 0x8 + +/* + * Normal standalone locks, for the circular and irq-context + * dependency tests: + */ +static DEFINE_SPINLOCK(lock_A); +static DEFINE_SPINLOCK(lock_B); +static DEFINE_SPINLOCK(lock_C); +static DEFINE_SPINLOCK(lock_D); + +static DEFINE_RWLOCK(rwlock_A); +static DEFINE_RWLOCK(rwlock_B); +static DEFINE_RWLOCK(rwlock_C); +static DEFINE_RWLOCK(rwlock_D); + +static DEFINE_MUTEX(mutex_A); +static DEFINE_MUTEX(mutex_B); +static DEFINE_MUTEX(mutex_C); +static DEFINE_MUTEX(mutex_D); + +static DECLARE_RWSEM(rwsem_A); +static DECLARE_RWSEM(rwsem_B); +static DECLARE_RWSEM(rwsem_C); +static DECLARE_RWSEM(rwsem_D); + +/* + * Locks that we initialize dynamically as well so that + * e.g. X1 and X2 becomes two instances of the same class, + * but X* and Y* are different classes. We do this so that + * we do not trigger a real lockup: + */ +static DEFINE_SPINLOCK(lock_X1); +static DEFINE_SPINLOCK(lock_X2); +static DEFINE_SPINLOCK(lock_Y1); +static DEFINE_SPINLOCK(lock_Y2); +static DEFINE_SPINLOCK(lock_Z1); +static DEFINE_SPINLOCK(lock_Z2); + +static DEFINE_RWLOCK(rwlock_X1); +static DEFINE_RWLOCK(rwlock_X2); +static DEFINE_RWLOCK(rwlock_Y1); +static DEFINE_RWLOCK(rwlock_Y2); +static DEFINE_RWLOCK(rwlock_Z1); +static DEFINE_RWLOCK(rwlock_Z2); + +static DEFINE_MUTEX(mutex_X1); +static DEFINE_MUTEX(mutex_X2); +static DEFINE_MUTEX(mutex_Y1); +static DEFINE_MUTEX(mutex_Y2); +static DEFINE_MUTEX(mutex_Z1); +static DEFINE_MUTEX(mutex_Z2); + +static DECLARE_RWSEM(rwsem_X1); +static DECLARE_RWSEM(rwsem_X2); +static DECLARE_RWSEM(rwsem_Y1); +static DECLARE_RWSEM(rwsem_Y2); +static DECLARE_RWSEM(rwsem_Z1); +static DECLARE_RWSEM(rwsem_Z2); + +/* + * non-inlined runtime initializers, to let separate locks share + * the same lock-class: + */ +#define INIT_CLASS_FUNC(class) \ +static noinline void \ +init_class_##class(spinlock_t *lock, rwlock_t *rwlock, struct mutex *mutex, \ + struct rw_semaphore *rwsem) \ +{ \ + spin_lock_init(lock); \ + rwlock_init(rwlock); \ + mutex_init(mutex); \ + init_rwsem(rwsem); \ +} + +INIT_CLASS_FUNC(X) +INIT_CLASS_FUNC(Y) +INIT_CLASS_FUNC(Z) + +static void init_shared_classes(void) +{ + init_class_X(&lock_X1, &rwlock_X1, &mutex_X1, &rwsem_X1); + init_class_X(&lock_X2, &rwlock_X2, &mutex_X2, &rwsem_X2); + + init_class_Y(&lock_Y1, &rwlock_Y1, &mutex_Y1, &rwsem_Y1); + init_class_Y(&lock_Y2, &rwlock_Y2, &mutex_Y2, &rwsem_Y2); + + init_class_Z(&lock_Z1, &rwlock_Z1, &mutex_Z1, &rwsem_Z1); + init_class_Z(&lock_Z2, &rwlock_Z2, &mutex_Z2, &rwsem_Z2); +} + +/* + * For spinlocks and rwlocks we also do hardirq-safe / softirq-safe tests. + * The following functions use a lock from a simulated hardirq/softirq + * context, causing the locks to be marked as hardirq-safe/softirq-safe: + */ + +#define HARDIRQ_DISABLE local_irq_disable +#define HARDIRQ_ENABLE local_irq_enable + +#define HARDIRQ_ENTER() \ + local_irq_disable(); \ + irq_enter(); \ + WARN_ON(!in_irq()); + +#define HARDIRQ_EXIT() \ + __irq_exit(); \ + local_irq_enable(); + +#define SOFTIRQ_DISABLE local_bh_disable +#define SOFTIRQ_ENABLE local_bh_enable + +#define SOFTIRQ_ENTER() \ + local_bh_disable(); \ + local_irq_disable(); \ + trace_softirq_enter(); \ + WARN_ON(!in_softirq()); + +#define SOFTIRQ_EXIT() \ + trace_softirq_exit(); \ + local_irq_enable(); \ + local_bh_enable(); + +/* + * Shortcuts for lock/unlock API variants, to keep + * the testcases compact: + */ +#define L(x) spin_lock(&lock_##x) +#define U(x) spin_unlock(&lock_##x) +#define LU(x) L(x); U(x) +#define SI(x) spin_lock_init(&lock_##x) + +#define WL(x) write_lock(&rwlock_##x) +#define WU(x) write_unlock(&rwlock_##x) +#define WLU(x) WL(x); WU(x) + +#define RL(x) read_lock(&rwlock_##x) +#define RU(x) read_unlock(&rwlock_##x) +#define RLU(x) RL(x); RU(x) +#define RWI(x) rwlock_init(&rwlock_##x) + +#define ML(x) mutex_lock(&mutex_##x) +#define MU(x) mutex_unlock(&mutex_##x) +#define MI(x) mutex_init(&mutex_##x) + +#define WSL(x) down_write(&rwsem_##x) +#define WSU(x) up_write(&rwsem_##x) + +#define RSL(x) down_read(&rwsem_##x) +#define RSU(x) up_read(&rwsem_##x) +#define RWSI(x) init_rwsem(&rwsem_##x) + +#define LOCK_UNLOCK_2(x,y) LOCK(x); LOCK(y); UNLOCK(y); UNLOCK(x) + +/* + * Generate different permutations of the same testcase, using + * the same basic lock-dependency/state events: + */ + +#define GENERATE_TESTCASE(name) \ + \ +static void name(void) { E(); } + +#define GENERATE_PERMUTATIONS_2_EVENTS(name) \ + \ +static void name##_12(void) { E1(); E2(); } \ +static void name##_21(void) { E2(); E1(); } + +#define GENERATE_PERMUTATIONS_3_EVENTS(name) \ + \ +static void name##_123(void) { E1(); E2(); E3(); } \ +static void name##_132(void) { E1(); E3(); E2(); } \ +static void name##_213(void) { E2(); E1(); E3(); } \ +static void name##_231(void) { E2(); E3(); E1(); } \ +static void name##_312(void) { E3(); E1(); E2(); } \ +static void name##_321(void) { E3(); E2(); E1(); } + +/* + * AA deadlock: + */ + +#define E() \ + \ + LOCK(X1); \ + LOCK(X2); /* this one should fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(AA_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(AA_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(AA_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(AA_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(AA_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(AA_rsem) + +#undef E + +/* + * Special-case for read-locking, they are + * allowed to recurse on the same lock class: + */ +static void rlock_AA1(void) +{ + RL(X1); + RL(X1); // this one should NOT fail +} + +static void rlock_AA1B(void) +{ + RL(X1); + RL(X2); // this one should NOT fail +} + +static void rsem_AA1(void) +{ + RSL(X1); + RSL(X1); // this one should fail +} + +static void rsem_AA1B(void) +{ + RSL(X1); + RSL(X2); // this one should fail +} +/* + * The mixing of read and write locks is not allowed: + */ +static void rlock_AA2(void) +{ + RL(X1); + WL(X2); // this one should fail +} + +static void rsem_AA2(void) +{ + RSL(X1); + WSL(X2); // this one should fail +} + +static void rlock_AA3(void) +{ + WL(X1); + RL(X2); // this one should fail +} + +static void rsem_AA3(void) +{ + WSL(X1); + RSL(X2); // this one should fail +} + +/* + * ABBA deadlock: + */ + +#define E() \ + \ + LOCK_UNLOCK_2(A, B); \ + LOCK_UNLOCK_2(B, A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(ABBA_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(ABBA_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(ABBA_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(ABBA_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(ABBA_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(ABBA_rsem) + +#undef E + +/* + * AB BC CA deadlock: + */ + +#define E() \ + \ + LOCK_UNLOCK_2(A, B); \ + LOCK_UNLOCK_2(B, C); \ + LOCK_UNLOCK_2(C, A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(ABBCCA_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(ABBCCA_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(ABBCCA_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(ABBCCA_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(ABBCCA_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(ABBCCA_rsem) + +#undef E + +/* + * AB CA BC deadlock: + */ + +#define E() \ + \ + LOCK_UNLOCK_2(A, B); \ + LOCK_UNLOCK_2(C, A); \ + LOCK_UNLOCK_2(B, C); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(ABCABC_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(ABCABC_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(ABCABC_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(ABCABC_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(ABCABC_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(ABCABC_rsem) + +#undef E + +/* + * AB BC CD DA deadlock: + */ + +#define E() \ + \ + LOCK_UNLOCK_2(A, B); \ + LOCK_UNLOCK_2(B, C); \ + LOCK_UNLOCK_2(C, D); \ + LOCK_UNLOCK_2(D, A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(ABBCCDDA_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(ABBCCDDA_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(ABBCCDDA_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(ABBCCDDA_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(ABBCCDDA_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(ABBCCDDA_rsem) + +#undef E + +/* + * AB CD BD DA deadlock: + */ +#define E() \ + \ + LOCK_UNLOCK_2(A, B); \ + LOCK_UNLOCK_2(C, D); \ + LOCK_UNLOCK_2(B, D); \ + LOCK_UNLOCK_2(D, A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(ABCDBDDA_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(ABCDBDDA_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(ABCDBDDA_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(ABCDBDDA_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(ABCDBDDA_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(ABCDBDDA_rsem) + +#undef E + +/* + * AB CD BC DA deadlock: + */ +#define E() \ + \ + LOCK_UNLOCK_2(A, B); \ + LOCK_UNLOCK_2(C, D); \ + LOCK_UNLOCK_2(B, C); \ + LOCK_UNLOCK_2(D, A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(ABCDBCDA_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(ABCDBCDA_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(ABCDBCDA_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(ABCDBCDA_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(ABCDBCDA_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(ABCDBCDA_rsem) + +#undef E + +/* + * Double unlock: + */ +#define E() \ + \ + LOCK(A); \ + UNLOCK(A); \ + UNLOCK(A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(double_unlock_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(double_unlock_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(double_unlock_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(double_unlock_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(double_unlock_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(double_unlock_rsem) + +#undef E + +/* + * Bad unlock ordering: + */ +#define E() \ + \ + LOCK(A); \ + LOCK(B); \ + UNLOCK(A); /* fail */ \ + UNLOCK(B); + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(bad_unlock_order_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(bad_unlock_order_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(bad_unlock_order_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(bad_unlock_order_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(bad_unlock_order_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(bad_unlock_order_rsem) + +#undef E + +/* + * initializing a held lock: + */ +#define E() \ + \ + LOCK(A); \ + INIT(A); /* fail */ + +/* + * 6 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_TESTCASE(init_held_spin) +#include "locking-selftest-wlock.h" +GENERATE_TESTCASE(init_held_wlock) +#include "locking-selftest-rlock.h" +GENERATE_TESTCASE(init_held_rlock) +#include "locking-selftest-mutex.h" +GENERATE_TESTCASE(init_held_mutex) +#include "locking-selftest-wsem.h" +GENERATE_TESTCASE(init_held_wsem) +#include "locking-selftest-rsem.h" +GENERATE_TESTCASE(init_held_rsem) + +#undef E + +/* + * locking an irq-safe lock with irqs enabled: + */ +#define E1() \ + \ + IRQ_ENTER(); \ + LOCK(A); \ + UNLOCK(A); \ + IRQ_EXIT(); + +#define E2() \ + \ + LOCK(A); \ + UNLOCK(A); + +/* + * Generate 24 testcases: + */ +#include "locking-selftest-spin-hardirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_spin) + +#include "locking-selftest-rlock-hardirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_rlock) + +#include "locking-selftest-wlock-hardirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_wlock) + +#include "locking-selftest-spin-softirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_spin) + +#include "locking-selftest-rlock-softirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_rlock) + +#include "locking-selftest-wlock-softirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_wlock) + +#undef E1 +#undef E2 + +/* + * Enabling hardirqs with a softirq-safe lock held: + */ +#define E1() \ + \ + SOFTIRQ_ENTER(); \ + LOCK(A); \ + UNLOCK(A); \ + SOFTIRQ_EXIT(); + +#define E2() \ + \ + HARDIRQ_DISABLE(); \ + LOCK(A); \ + HARDIRQ_ENABLE(); \ + UNLOCK(A); + +/* + * Generate 12 testcases: + */ +#include "locking-selftest-spin.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2A_spin) + +#include "locking-selftest-wlock.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2A_wlock) + +#include "locking-selftest-rlock.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2A_rlock) + +#undef E1 +#undef E2 + +/* + * Enabling irqs with an irq-safe lock held: + */ +#define E1() \ + \ + IRQ_ENTER(); \ + LOCK(A); \ + UNLOCK(A); \ + IRQ_EXIT(); + +#define E2() \ + \ + IRQ_DISABLE(); \ + LOCK(A); \ + IRQ_ENABLE(); \ + UNLOCK(A); + +/* + * Generate 24 testcases: + */ +#include "locking-selftest-spin-hardirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_spin) + +#include "locking-selftest-rlock-hardirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_rlock) + +#include "locking-selftest-wlock-hardirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_wlock) + +#include "locking-selftest-spin-softirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_spin) + +#include "locking-selftest-rlock-softirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_rlock) + +#include "locking-selftest-wlock-softirq.h" +GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_wlock) + +#undef E1 +#undef E2 + +/* + * Acquiring a irq-unsafe lock while holding an irq-safe-lock: + */ +#define E1() \ + \ + LOCK(A); \ + LOCK(B); \ + UNLOCK(B); \ + UNLOCK(A); \ + +#define E2() \ + \ + LOCK(B); \ + UNLOCK(B); + +#define E3() \ + \ + IRQ_ENTER(); \ + LOCK(A); \ + UNLOCK(A); \ + IRQ_EXIT(); + +/* + * Generate 36 testcases: + */ +#include "locking-selftest-spin-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_spin) + +#include "locking-selftest-rlock-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_rlock) + +#include "locking-selftest-wlock-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_wlock) + +#include "locking-selftest-spin-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_spin) + +#include "locking-selftest-rlock-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_rlock) + +#include "locking-selftest-wlock-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_wlock) + +#undef E1 +#undef E2 +#undef E3 + +/* + * If a lock turns into softirq-safe, but earlier it took + * a softirq-unsafe lock: + */ + +#define E1() \ + IRQ_DISABLE(); \ + LOCK(A); \ + LOCK(B); \ + UNLOCK(B); \ + UNLOCK(A); \ + IRQ_ENABLE(); + +#define E2() \ + LOCK(B); \ + UNLOCK(B); + +#define E3() \ + IRQ_ENTER(); \ + LOCK(A); \ + UNLOCK(A); \ + IRQ_EXIT(); + +/* + * Generate 36 testcases: + */ +#include "locking-selftest-spin-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_spin) + +#include "locking-selftest-rlock-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_rlock) + +#include "locking-selftest-wlock-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_wlock) + +#include "locking-selftest-spin-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_spin) + +#include "locking-selftest-rlock-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_rlock) + +#include "locking-selftest-wlock-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_wlock) + +#undef E1 +#undef E2 +#undef E3 + +/* + * read-lock / write-lock irq inversion. + * + * Deadlock scenario: + * + * CPU#1 is at #1, i.e. it has write-locked A, but has not + * taken B yet. + * + * CPU#2 is at #2, i.e. it has locked B. + * + * Hardirq hits CPU#2 at point #2 and is trying to read-lock A. + * + * The deadlock occurs because CPU#1 will spin on B, and CPU#2 + * will spin on A. + */ + +#define E1() \ + \ + IRQ_DISABLE(); \ + WL(A); \ + LOCK(B); \ + UNLOCK(B); \ + WU(A); \ + IRQ_ENABLE(); + +#define E2() \ + \ + LOCK(B); \ + UNLOCK(B); + +#define E3() \ + \ + IRQ_ENTER(); \ + RL(A); \ + RU(A); \ + IRQ_EXIT(); + +/* + * Generate 36 testcases: + */ +#include "locking-selftest-spin-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_hard_spin) + +#include "locking-selftest-rlock-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_hard_rlock) + +#include "locking-selftest-wlock-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_hard_wlock) + +#include "locking-selftest-spin-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_soft_spin) + +#include "locking-selftest-rlock-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_soft_rlock) + +#include "locking-selftest-wlock-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_inversion_soft_wlock) + +#undef E1 +#undef E2 +#undef E3 + +/* + * read-lock / write-lock recursion that is actually safe. + */ + +#define E1() \ + \ + IRQ_DISABLE(); \ + WL(A); \ + WU(A); \ + IRQ_ENABLE(); + +#define E2() \ + \ + RL(A); \ + RU(A); \ + +#define E3() \ + \ + IRQ_ENTER(); \ + RL(A); \ + L(B); \ + U(B); \ + RU(A); \ + IRQ_EXIT(); + +/* + * Generate 12 testcases: + */ +#include "locking-selftest-hardirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_hard) + +#include "locking-selftest-softirq.h" +GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion_soft) + +#undef E1 +#undef E2 +#undef E3 + +/* + * read-lock / write-lock recursion that is unsafe. + */ + +#define E1() \ + \ + IRQ_DISABLE(); \ + L(B); \ + WL(A); \ + WU(A); \ + U(B); \ + IRQ_ENABLE(); + +#define E2() \ + \ + RL(A); \ + RU(A); \ + +#define E3() \ + \ + IRQ_ENTER(); \ + L(B); \ + U(B); \ + IRQ_EXIT(); + +/* + * Generate 12 testcases: + */ +#include "locking-selftest-hardirq.h" +// GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion2_hard) + +#include "locking-selftest-softirq.h" +// GENERATE_PERMUTATIONS_3_EVENTS(irq_read_recursion2_soft) + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +# define I_SPINLOCK(x) lockdep_reset_lock(&lock_##x.dep_map) +# define I_RWLOCK(x) lockdep_reset_lock(&rwlock_##x.dep_map) +# define I_MUTEX(x) lockdep_reset_lock(&mutex_##x.dep_map) +# define I_RWSEM(x) lockdep_reset_lock(&rwsem_##x.dep_map) +#else +# define I_SPINLOCK(x) +# define I_RWLOCK(x) +# define I_MUTEX(x) +# define I_RWSEM(x) +#endif + +#define I1(x) \ + do { \ + I_SPINLOCK(x); \ + I_RWLOCK(x); \ + I_MUTEX(x); \ + I_RWSEM(x); \ + } while (0) + +#define I2(x) \ + do { \ + spin_lock_init(&lock_##x); \ + rwlock_init(&rwlock_##x); \ + mutex_init(&mutex_##x); \ + init_rwsem(&rwsem_##x); \ + } while (0) + +static void reset_locks(void) +{ + local_irq_disable(); + I1(A); I1(B); I1(C); I1(D); + I1(X1); I1(X2); I1(Y1); I1(Y2); I1(Z1); I1(Z2); + lockdep_reset(); + I2(A); I2(B); I2(C); I2(D); + init_shared_classes(); + local_irq_enable(); +} + +#undef I + +static int testcase_total; +static int testcase_successes; +static int expected_testcase_failures; +static int unexpected_testcase_failures; + +static void dotest(void (*testcase_fn)(void), int expected, int lockclass_mask) +{ + unsigned long saved_preempt_count = preempt_count(); + int expected_failure = 0; + + WARN_ON(irqs_disabled()); + + testcase_fn(); + /* + * Filter out expected failures: + */ +#ifndef CONFIG_PROVE_LOCKING + if ((lockclass_mask & LOCKTYPE_SPIN) && debug_locks != expected) + expected_failure = 1; + if ((lockclass_mask & LOCKTYPE_RWLOCK) && debug_locks != expected) + expected_failure = 1; + if ((lockclass_mask & LOCKTYPE_MUTEX) && debug_locks != expected) + expected_failure = 1; + if ((lockclass_mask & LOCKTYPE_RWSEM) && debug_locks != expected) + expected_failure = 1; +#endif + if (debug_locks != expected) { + if (expected_failure) { + expected_testcase_failures++; + printk("failed|"); + } else { + unexpected_testcase_failures++; + printk("FAILED|"); + } + } else { + testcase_successes++; + printk(" ok |"); + } + testcase_total++; + + if (debug_locks_verbose) + printk(" lockclass mask: %x, debug_locks: %d, expected: %d\n", + lockclass_mask, debug_locks, expected); + /* + * Some tests (e.g. double-unlock) might corrupt the preemption + * count, so restore it: + */ + preempt_count() = saved_preempt_count; +#ifdef CONFIG_TRACE_IRQFLAGS + if (softirq_count()) + current->softirqs_enabled = 0; + else + current->softirqs_enabled = 1; +#endif + + reset_locks(); +} + +static inline void print_testname(const char *testname) +{ + printk("%33s:", testname); +} + +#define DO_TESTCASE_1(desc, name, nr) \ + print_testname(desc"/"#nr); \ + dotest(name##_##nr, SUCCESS, LOCKTYPE_RWLOCK); \ + printk("\n"); + +#define DO_TESTCASE_1B(desc, name, nr) \ + print_testname(desc"/"#nr); \ + dotest(name##_##nr, FAILURE, LOCKTYPE_RWLOCK); \ + printk("\n"); + +#define DO_TESTCASE_3(desc, name, nr) \ + print_testname(desc"/"#nr); \ + dotest(name##_spin_##nr, FAILURE, LOCKTYPE_SPIN); \ + dotest(name##_wlock_##nr, FAILURE, LOCKTYPE_RWLOCK); \ + dotest(name##_rlock_##nr, SUCCESS, LOCKTYPE_RWLOCK); \ + printk("\n"); + +#define DO_TESTCASE_3RW(desc, name, nr) \ + print_testname(desc"/"#nr); \ + dotest(name##_spin_##nr, FAILURE, LOCKTYPE_SPIN|LOCKTYPE_RWLOCK);\ + dotest(name##_wlock_##nr, FAILURE, LOCKTYPE_RWLOCK); \ + dotest(name##_rlock_##nr, SUCCESS, LOCKTYPE_RWLOCK); \ + printk("\n"); + +#define DO_TESTCASE_6(desc, name) \ + print_testname(desc); \ + dotest(name##_spin, FAILURE, LOCKTYPE_SPIN); \ + dotest(name##_wlock, FAILURE, LOCKTYPE_RWLOCK); \ + dotest(name##_rlock, FAILURE, LOCKTYPE_RWLOCK); \ + dotest(name##_mutex, FAILURE, LOCKTYPE_MUTEX); \ + dotest(name##_wsem, FAILURE, LOCKTYPE_RWSEM); \ + dotest(name##_rsem, FAILURE, LOCKTYPE_RWSEM); \ + printk("\n"); + +#define DO_TESTCASE_6_SUCCESS(desc, name) \ + print_testname(desc); \ + dotest(name##_spin, SUCCESS, LOCKTYPE_SPIN); \ + dotest(name##_wlock, SUCCESS, LOCKTYPE_RWLOCK); \ + dotest(name##_rlock, SUCCESS, LOCKTYPE_RWLOCK); \ + dotest(name##_mutex, SUCCESS, LOCKTYPE_MUTEX); \ + dotest(name##_wsem, SUCCESS, LOCKTYPE_RWSEM); \ + dotest(name##_rsem, SUCCESS, LOCKTYPE_RWSEM); \ + printk("\n"); + +/* + * 'read' variant: rlocks must not trigger. + */ +#define DO_TESTCASE_6R(desc, name) \ + print_testname(desc); \ + dotest(name##_spin, FAILURE, LOCKTYPE_SPIN); \ + dotest(name##_wlock, FAILURE, LOCKTYPE_RWLOCK); \ + dotest(name##_rlock, SUCCESS, LOCKTYPE_RWLOCK); \ + dotest(name##_mutex, FAILURE, LOCKTYPE_MUTEX); \ + dotest(name##_wsem, FAILURE, LOCKTYPE_RWSEM); \ + dotest(name##_rsem, FAILURE, LOCKTYPE_RWSEM); \ + printk("\n"); + +#define DO_TESTCASE_2I(desc, name, nr) \ + DO_TESTCASE_1("hard-"desc, name##_hard, nr); \ + DO_TESTCASE_1("soft-"desc, name##_soft, nr); + +#define DO_TESTCASE_2IB(desc, name, nr) \ + DO_TESTCASE_1B("hard-"desc, name##_hard, nr); \ + DO_TESTCASE_1B("soft-"desc, name##_soft, nr); + +#define DO_TESTCASE_6I(desc, name, nr) \ + DO_TESTCASE_3("hard-"desc, name##_hard, nr); \ + DO_TESTCASE_3("soft-"desc, name##_soft, nr); + +#define DO_TESTCASE_6IRW(desc, name, nr) \ + DO_TESTCASE_3RW("hard-"desc, name##_hard, nr); \ + DO_TESTCASE_3RW("soft-"desc, name##_soft, nr); + +#define DO_TESTCASE_2x3(desc, name) \ + DO_TESTCASE_3(desc, name, 12); \ + DO_TESTCASE_3(desc, name, 21); + +#define DO_TESTCASE_2x6(desc, name) \ + DO_TESTCASE_6I(desc, name, 12); \ + DO_TESTCASE_6I(desc, name, 21); + +#define DO_TESTCASE_6x2(desc, name) \ + DO_TESTCASE_2I(desc, name, 123); \ + DO_TESTCASE_2I(desc, name, 132); \ + DO_TESTCASE_2I(desc, name, 213); \ + DO_TESTCASE_2I(desc, name, 231); \ + DO_TESTCASE_2I(desc, name, 312); \ + DO_TESTCASE_2I(desc, name, 321); + +#define DO_TESTCASE_6x2B(desc, name) \ + DO_TESTCASE_2IB(desc, name, 123); \ + DO_TESTCASE_2IB(desc, name, 132); \ + DO_TESTCASE_2IB(desc, name, 213); \ + DO_TESTCASE_2IB(desc, name, 231); \ + DO_TESTCASE_2IB(desc, name, 312); \ + DO_TESTCASE_2IB(desc, name, 321); + +#define DO_TESTCASE_6x6(desc, name) \ + DO_TESTCASE_6I(desc, name, 123); \ + DO_TESTCASE_6I(desc, name, 132); \ + DO_TESTCASE_6I(desc, name, 213); \ + DO_TESTCASE_6I(desc, name, 231); \ + DO_TESTCASE_6I(desc, name, 312); \ + DO_TESTCASE_6I(desc, name, 321); + +#define DO_TESTCASE_6x6RW(desc, name) \ + DO_TESTCASE_6IRW(desc, name, 123); \ + DO_TESTCASE_6IRW(desc, name, 132); \ + DO_TESTCASE_6IRW(desc, name, 213); \ + DO_TESTCASE_6IRW(desc, name, 231); \ + DO_TESTCASE_6IRW(desc, name, 312); \ + DO_TESTCASE_6IRW(desc, name, 321); + + +void locking_selftest(void) +{ + /* + * Got a locking failure before the selftest ran? + */ + if (!debug_locks) { + printk("----------------------------------\n"); + printk("| Locking API testsuite disabled |\n"); + printk("----------------------------------\n"); + return; + } + + /* + * Run the testsuite: + */ + printk("------------------------\n"); + printk("| Locking API testsuite:\n"); + printk("----------------------------------------------------------------------------\n"); + printk(" | spin |wlock |rlock |mutex | wsem | rsem |\n"); + printk(" --------------------------------------------------------------------------\n"); + + init_shared_classes(); + debug_locks_silent = !debug_locks_verbose; + + DO_TESTCASE_6R("A-A deadlock", AA); + DO_TESTCASE_6R("A-B-B-A deadlock", ABBA); + DO_TESTCASE_6R("A-B-B-C-C-A deadlock", ABBCCA); + DO_TESTCASE_6R("A-B-C-A-B-C deadlock", ABCABC); + DO_TESTCASE_6R("A-B-B-C-C-D-D-A deadlock", ABBCCDDA); + DO_TESTCASE_6R("A-B-C-D-B-D-D-A deadlock", ABCDBDDA); + DO_TESTCASE_6R("A-B-C-D-B-C-D-A deadlock", ABCDBCDA); + DO_TESTCASE_6("double unlock", double_unlock); + DO_TESTCASE_6("initialize held", init_held); + DO_TESTCASE_6_SUCCESS("bad unlock order", bad_unlock_order); + + printk(" --------------------------------------------------------------------------\n"); + print_testname("recursive read-lock"); + printk(" |"); + dotest(rlock_AA1, SUCCESS, LOCKTYPE_RWLOCK); + printk(" |"); + dotest(rsem_AA1, FAILURE, LOCKTYPE_RWSEM); + printk("\n"); + + print_testname("recursive read-lock #2"); + printk(" |"); + dotest(rlock_AA1B, SUCCESS, LOCKTYPE_RWLOCK); + printk(" |"); + dotest(rsem_AA1B, FAILURE, LOCKTYPE_RWSEM); + printk("\n"); + + print_testname("mixed read-write-lock"); + printk(" |"); + dotest(rlock_AA2, FAILURE, LOCKTYPE_RWLOCK); + printk(" |"); + dotest(rsem_AA2, FAILURE, LOCKTYPE_RWSEM); + printk("\n"); + + print_testname("mixed write-read-lock"); + printk(" |"); + dotest(rlock_AA3, FAILURE, LOCKTYPE_RWLOCK); + printk(" |"); + dotest(rsem_AA3, FAILURE, LOCKTYPE_RWSEM); + printk("\n"); + + printk(" --------------------------------------------------------------------------\n"); + + /* + * irq-context testcases: + */ + DO_TESTCASE_2x6("irqs-on + irq-safe-A", irqsafe1); + DO_TESTCASE_2x3("sirq-safe-A => hirqs-on", irqsafe2A); + DO_TESTCASE_2x6("safe-A + irqs-on", irqsafe2B); + DO_TESTCASE_6x6("safe-A + unsafe-B #1", irqsafe3); + DO_TESTCASE_6x6("safe-A + unsafe-B #2", irqsafe4); + DO_TESTCASE_6x6RW("irq lock-inversion", irq_inversion); + + DO_TESTCASE_6x2("irq read-recursion", irq_read_recursion); +// DO_TESTCASE_6x2B("irq read-recursion #2", irq_read_recursion2); + + if (unexpected_testcase_failures) { + printk("-----------------------------------------------------------------\n"); + debug_locks = 0; + printk("BUG: %3d unexpected failures (out of %3d) - debugging disabled! |\n", + unexpected_testcase_failures, testcase_total); + printk("-----------------------------------------------------------------\n"); + } else if (expected_testcase_failures && testcase_successes) { + printk("--------------------------------------------------------\n"); + printk("%3d out of %3d testcases failed, as expected. |\n", + expected_testcase_failures, testcase_total); + printk("----------------------------------------------------\n"); + debug_locks = 1; + } else if (expected_testcase_failures && !testcase_successes) { + printk("--------------------------------------------------------\n"); + printk("All %3d testcases failed, as expected. |\n", + expected_testcase_failures); + printk("----------------------------------------\n"); + debug_locks = 1; + } else { + printk("-------------------------------------------------------\n"); + printk("Good, all %3d testcases passed! |\n", + testcase_successes); + printk("---------------------------------\n"); + debug_locks = 1; + } + debug_locks_silent = 0; +} diff --git a/lib/percpu_counter.c b/lib/percpu_counter.c new file mode 100644 index 0000000..8504490 --- /dev/null +++ b/lib/percpu_counter.c @@ -0,0 +1,46 @@ +/* + * Fast batching percpu counters. + */ + +#include +#include + +void percpu_counter_mod(struct percpu_counter *fbc, s32 amount) +{ + long count; + s32 *pcount; + int cpu = get_cpu(); + + pcount = per_cpu_ptr(fbc->counters, cpu); + count = *pcount + amount; + if (count >= FBC_BATCH || count <= -FBC_BATCH) { + spin_lock(&fbc->lock); + fbc->count += count; + *pcount = 0; + spin_unlock(&fbc->lock); + } else { + *pcount = count; + } + put_cpu(); +} +EXPORT_SYMBOL(percpu_counter_mod); + +/* + * Add up all the per-cpu counts, return the result. This is a more accurate + * but much slower version of percpu_counter_read_positive() + */ +s64 percpu_counter_sum(struct percpu_counter *fbc) +{ + s64 ret; + int cpu; + + spin_lock(&fbc->lock); + ret = fbc->count; + for_each_possible_cpu(cpu) { + s32 *pcount = per_cpu_ptr(fbc->counters, cpu); + ret += *pcount; + } + spin_unlock(&fbc->lock); + return ret < 0 ? 0 : ret; +} +EXPORT_SYMBOL(percpu_counter_sum); diff --git a/lib/plist.c b/lib/plist.c new file mode 100644 index 0000000..3074a02 --- /dev/null +++ b/lib/plist.c @@ -0,0 +1,118 @@ +/* + * lib/plist.c + * + * Descending-priority-sorted double-linked list + * + * (C) 2002-2003 Intel Corp + * Inaky Perez-Gonzalez . + * + * 2001-2005 (c) MontaVista Software, Inc. + * Daniel Walker + * + * (C) 2005 Thomas Gleixner + * + * Simplifications of the original code by + * Oleg Nesterov + * + * Licensed under the FSF's GNU Public License v2 or later. + * + * Based on simple lists (include/linux/list.h). + * + * This file contains the add / del functions which are considered to + * be too large to inline. See include/linux/plist.h for further + * information. + */ + +#include +#include + +#ifdef CONFIG_DEBUG_PI_LIST + +static void plist_check_prev_next(struct list_head *t, struct list_head *p, + struct list_head *n) +{ + if (n->prev != p || p->next != n) { + printk("top: %p, n: %p, p: %p\n", t, t->next, t->prev); + printk("prev: %p, n: %p, p: %p\n", p, p->next, p->prev); + printk("next: %p, n: %p, p: %p\n", n, n->next, n->prev); + WARN_ON(1); + } +} + +static void plist_check_list(struct list_head *top) +{ + struct list_head *prev = top, *next = top->next; + + plist_check_prev_next(top, prev, next); + while (next != top) { + prev = next; + next = prev->next; + plist_check_prev_next(top, prev, next); + } +} + +static void plist_check_head(struct plist_head *head) +{ + WARN_ON(!head->lock); + if (head->lock) + WARN_ON_SMP(!spin_is_locked(head->lock)); + plist_check_list(&head->prio_list); + plist_check_list(&head->node_list); +} + +#else +# define plist_check_head(h) do { } while (0) +#endif + +/** + * plist_add - add @node to @head + * + * @node: &struct plist_node pointer + * @head: &struct plist_head pointer + */ +void plist_add(struct plist_node *node, struct plist_head *head) +{ + struct plist_node *iter; + + plist_check_head(head); + WARN_ON(!plist_node_empty(node)); + + list_for_each_entry(iter, &head->prio_list, plist.prio_list) { + if (node->prio < iter->prio) + goto lt_prio; + else if (node->prio == iter->prio) { + iter = list_entry(iter->plist.prio_list.next, + struct plist_node, plist.prio_list); + goto eq_prio; + } + } + +lt_prio: + list_add_tail(&node->plist.prio_list, &iter->plist.prio_list); +eq_prio: + list_add_tail(&node->plist.node_list, &iter->plist.node_list); + + plist_check_head(head); +} + +/** + * plist_del - Remove a @node from plist. + * + * @node: &struct plist_node pointer - entry to be removed + * @head: &struct plist_head pointer - list head + */ +void plist_del(struct plist_node *node, struct plist_head *head) +{ + plist_check_head(head); + + if (!list_empty(&node->plist.prio_list)) { + struct plist_node *next = plist_first(&node->plist); + + list_move_tail(&next->plist.prio_list, &node->plist.prio_list); + list_del_init(&node->plist.prio_list); + } + + list_del_init(&node->plist.node_list); + + plist_check_head(head); +} diff --git a/lib/radix-tree.c b/lib/radix-tree.c index 7097bb2..637d556 100644 --- a/lib/radix-tree.c +++ b/lib/radix-tree.c @@ -33,7 +33,7 @@ #include #ifdef __KERNEL__ -#define RADIX_TREE_MAP_SHIFT 6 +#define RADIX_TREE_MAP_SHIFT (CONFIG_BASE_SMALL ? 4 : 6) #else #define RADIX_TREE_MAP_SHIFT 3 /* For more stressful testing */ #endif @@ -74,6 +74,11 @@ struct radix_tree_preload { }; DEFINE_PER_CPU(struct radix_tree_preload, radix_tree_preloads) = { 0, }; +static inline gfp_t root_gfp_mask(struct radix_tree_root *root) +{ + return root->gfp_mask & __GFP_BITS_MASK; +} + /* * This assumes that the caller has performed appropriate preallocation, and * that the caller has pinned this thread of control to the current CPU. @@ -82,9 +87,10 @@ static struct radix_tree_node * radix_tree_node_alloc(struct radix_tree_root *root) { struct radix_tree_node *ret; + gfp_t gfp_mask = root_gfp_mask(root); - ret = kmem_cache_alloc(radix_tree_node_cachep, root->gfp_mask); - if (ret == NULL && !(root->gfp_mask & __GFP_WAIT)) { + ret = kmem_cache_alloc(radix_tree_node_cachep, gfp_mask); + if (ret == NULL && !(gfp_mask & __GFP_WAIT)) { struct radix_tree_preload *rtp; rtp = &__get_cpu_var(radix_tree_preloads); @@ -152,6 +158,27 @@ static inline int tag_get(struct radix_t return test_bit(offset, node->tags[tag]); } +static inline void root_tag_set(struct radix_tree_root *root, unsigned int tag) +{ + root->gfp_mask |= (1 << (tag + __GFP_BITS_SHIFT)); +} + + +static inline void root_tag_clear(struct radix_tree_root *root, unsigned int tag) +{ + root->gfp_mask &= ~(1 << (tag + __GFP_BITS_SHIFT)); +} + +static inline void root_tag_clear_all(struct radix_tree_root *root) +{ + root->gfp_mask &= __GFP_BITS_MASK; +} + +static inline int root_tag_get(struct radix_tree_root *root, unsigned int tag) +{ + return root->gfp_mask & (1 << (tag + __GFP_BITS_SHIFT)); +} + /* * Returns 1 if any slot in the node has this tag set. * Otherwise returns 0. @@ -182,7 +209,6 @@ static int radix_tree_extend(struct radi { struct radix_tree_node *node; unsigned int height; - char tags[RADIX_TREE_MAX_TAGS]; int tag; /* Figure out what the height should be. */ @@ -195,16 +221,6 @@ static int radix_tree_extend(struct radi goto out; } - /* - * Prepare the tag status of the top-level node for propagation - * into the newly-pushed top-level node(s) - */ - for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { - tags[tag] = 0; - if (any_tag_set(root->rnode, tag)) - tags[tag] = 1; - } - do { if (!(node = radix_tree_node_alloc(root))) return -ENOMEM; @@ -214,7 +230,7 @@ static int radix_tree_extend(struct radi /* Propagate the aggregated tag info into the new root */ for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { - if (tags[tag]) + if (root_tag_get(root, tag)) tag_set(node, tag, 0); } @@ -243,8 +259,7 @@ int radix_tree_insert(struct radix_tree_ int error; /* Make sure the tree is high enough. */ - if ((!index && !root->rnode) || - index > radix_tree_maxindex(root->height)) { + if (index > radix_tree_maxindex(root->height)) { error = radix_tree_extend(root, index); if (error) return error; @@ -255,7 +270,7 @@ int radix_tree_insert(struct radix_tree_ shift = (height-1) * RADIX_TREE_MAP_SHIFT; offset = 0; /* uninitialised var warning */ - do { + while (height > 0) { if (slot == NULL) { /* Have to add a child node. */ if (!(slot = radix_tree_node_alloc(root))) @@ -273,16 +288,21 @@ int radix_tree_insert(struct radix_tree_ slot = node->slots[offset]; shift -= RADIX_TREE_MAP_SHIFT; height--; - } while (height > 0); + } if (slot != NULL) return -EEXIST; - BUG_ON(!node); - node->count++; - node->slots[offset] = item; - BUG_ON(tag_get(node, 0, offset)); - BUG_ON(tag_get(node, 1, offset)); + if (node) { + node->count++; + node->slots[offset] = item; + BUG_ON(tag_get(node, 0, offset)); + BUG_ON(tag_get(node, 1, offset)); + } else { + root->rnode = item; + BUG_ON(root_tag_get(root, 0)); + BUG_ON(root_tag_get(root, 1)); + } return 0; } @@ -295,9 +315,13 @@ static inline void **__lookup_slot(struc struct radix_tree_node **slot; height = root->height; + if (index > radix_tree_maxindex(height)) return NULL; + if (height == 0 && root->rnode) + return (void **)&root->rnode; + shift = (height-1) * RADIX_TREE_MAP_SHIFT; slot = &root->rnode; @@ -365,11 +389,10 @@ void *radix_tree_tag_set(struct radix_tr struct radix_tree_node *slot; height = root->height; - if (index > radix_tree_maxindex(height)) - return NULL; + BUG_ON(index > radix_tree_maxindex(height)); - shift = (height - 1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; while (height > 0) { int offset; @@ -383,6 +406,10 @@ void *radix_tree_tag_set(struct radix_tr height--; } + /* set the root's tag bit */ + if (slot && !root_tag_get(root, tag)) + root_tag_set(root, tag); + return slot; } EXPORT_SYMBOL(radix_tree_tag_set); @@ -405,9 +432,8 @@ void *radix_tree_tag_clear(struct radix_ unsigned long index, unsigned int tag) { struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; - struct radix_tree_node *slot; + struct radix_tree_node *slot = NULL; unsigned int height, shift; - void *ret = NULL; height = root->height; if (index > radix_tree_maxindex(height)) @@ -432,20 +458,24 @@ void *radix_tree_tag_clear(struct radix_ height--; } - ret = slot; - if (ret == NULL) + if (slot == NULL) goto out; - do { + while (pathp->node) { if (!tag_get(pathp->node, tag, pathp->offset)) goto out; tag_clear(pathp->node, tag, pathp->offset); if (any_tag_set(pathp->node, tag)) goto out; pathp--; - } while (pathp->node); + } + + /* clear the root's tag bit */ + if (root_tag_get(root, tag)) + root_tag_clear(root, tag); + out: - return ret; + return slot; } EXPORT_SYMBOL(radix_tree_tag_clear); @@ -458,9 +488,8 @@ #ifndef __KERNEL__ /* Only the test harn * * Return values: * - * 0: tag not present - * 1: tag present, set - * -1: tag present, unset + * 0: tag not present or not set + * 1: tag set */ int radix_tree_tag_get(struct radix_tree_root *root, unsigned long index, unsigned int tag) @@ -473,6 +502,13 @@ int radix_tree_tag_get(struct radix_tree if (index > radix_tree_maxindex(height)) return 0; + /* check the root's tag bit */ + if (!root_tag_get(root, tag)) + return 0; + + if (height == 0) + return 1; + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; @@ -494,7 +530,7 @@ int radix_tree_tag_get(struct radix_tree int ret = tag_get(slot, tag, offset); BUG_ON(ret && saw_unset_tag); - return ret ? 1 : -1; + return !!ret; } slot = slot->slots[offset]; shift -= RADIX_TREE_MAP_SHIFT; @@ -514,8 +550,11 @@ __lookup(struct radix_tree_root *root, v unsigned long i; height = root->height; - if (height == 0) + if (height == 0) { + if (root->rnode && index == 0) + results[nr_found++] = root->rnode; goto out; + } shift = (height-1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; @@ -603,10 +642,16 @@ __lookup_tag(struct radix_tree_root *roo unsigned int height = root->height; struct radix_tree_node *slot; + if (height == 0) { + if (root->rnode && index == 0) + results[nr_found++] = root->rnode; + goto out; + } + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; slot = root->rnode; - while (height > 0) { + do { unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK; for ( ; i < RADIX_TREE_MAP_SIZE; i++) { @@ -637,7 +682,7 @@ __lookup_tag(struct radix_tree_root *roo } shift -= RADIX_TREE_MAP_SHIFT; slot = slot->slots[i]; - } + } while (height > 0); out: *next_index = index; return nr_found; @@ -665,6 +710,10 @@ radix_tree_gang_lookup_tag(struct radix_ unsigned long cur_index = first_index; unsigned int ret = 0; + /* check the root's tag bit */ + if (!root_tag_get(root, tag)) + return 0; + while (ret < max_items) { unsigned int nr_found; unsigned long next_index; /* Index of next search */ @@ -689,7 +738,7 @@ EXPORT_SYMBOL(radix_tree_gang_lookup_tag static inline void radix_tree_shrink(struct radix_tree_root *root) { /* try to shrink tree height */ - while (root->height > 1 && + while (root->height > 0 && root->rnode->count == 1 && root->rnode->slots[0]) { struct radix_tree_node *to_free = root->rnode; @@ -717,12 +766,8 @@ static inline void radix_tree_shrink(str void *radix_tree_delete(struct radix_tree_root *root, unsigned long index) { struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path; - struct radix_tree_path *orig_pathp; - struct radix_tree_node *slot; + struct radix_tree_node *slot = NULL; unsigned int height, shift; - void *ret = NULL; - char tags[RADIX_TREE_MAX_TAGS]; - int nr_cleared_tags; int tag; int offset; @@ -730,11 +775,17 @@ void *radix_tree_delete(struct radix_tre if (index > radix_tree_maxindex(height)) goto out; + slot = root->rnode; + if (height == 0 && root->rnode) { + root_tag_clear_all(root); + root->rnode = NULL; + goto out; + } + shift = (height - 1) * RADIX_TREE_MAP_SHIFT; pathp->node = NULL; - slot = root->rnode; - for ( ; height > 0; height--) { + do { if (slot == NULL) goto out; @@ -744,44 +795,22 @@ void *radix_tree_delete(struct radix_tre pathp->node = slot; slot = slot->slots[offset]; shift -= RADIX_TREE_MAP_SHIFT; - } + height--; + } while (height > 0); - ret = slot; - if (ret == NULL) + if (slot == NULL) goto out; - orig_pathp = pathp; - /* * Clear all tags associated with the just-deleted item */ - nr_cleared_tags = 0; for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { - tags[tag] = 1; - if (tag_get(pathp->node, tag, pathp->offset)) { - tag_clear(pathp->node, tag, pathp->offset); - if (!any_tag_set(pathp->node, tag)) { - tags[tag] = 0; - nr_cleared_tags++; - } - } - } - - for (pathp--; nr_cleared_tags && pathp->node; pathp--) { - for (tag = 0; tag < RADIX_TREE_MAX_TAGS; tag++) { - if (tags[tag]) - continue; - - tag_clear(pathp->node, tag, pathp->offset); - if (any_tag_set(pathp->node, tag)) { - tags[tag] = 1; - nr_cleared_tags--; - } - } + if (tag_get(pathp->node, tag, pathp->offset)) + radix_tree_tag_clear(root, index, tag); } /* Now free the nodes we do not need anymore */ - for (pathp = orig_pathp; pathp->node; pathp--) { + while (pathp->node) { pathp->node->slots[pathp->offset] = NULL; pathp->node->count--; @@ -793,11 +822,15 @@ void *radix_tree_delete(struct radix_tre /* Node with zero slots in use so free it */ radix_tree_node_free(pathp->node); + + pathp--; } - root->rnode = NULL; + root_tag_clear_all(root); root->height = 0; + root->rnode = NULL; + out: - return ret; + return slot; } EXPORT_SYMBOL(radix_tree_delete); @@ -808,11 +841,7 @@ EXPORT_SYMBOL(radix_tree_delete); */ int radix_tree_tagged(struct radix_tree_root *root, unsigned int tag) { - struct radix_tree_node *rnode; - rnode = root->rnode; - if (!rnode) - return 0; - return any_tag_set(rnode, tag); + return root_tag_get(root, tag); } EXPORT_SYMBOL(radix_tree_tagged); diff --git a/lib/rbtree.c b/lib/rbtree.c index 14b791a..1e55ba1 100644 --- a/lib/rbtree.c +++ b/lib/rbtree.c @@ -26,60 +26,66 @@ #include static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) { struct rb_node *right = node->rb_right; + struct rb_node *parent = rb_parent(node); if ((node->rb_right = right->rb_left)) - right->rb_left->rb_parent = node; + rb_set_parent(right->rb_left, node); right->rb_left = node; - if ((right->rb_parent = node->rb_parent)) + rb_set_parent(right, parent); + + if (parent) { - if (node == node->rb_parent->rb_left) - node->rb_parent->rb_left = right; + if (node == parent->rb_left) + parent->rb_left = right; else - node->rb_parent->rb_right = right; + parent->rb_right = right; } else root->rb_node = right; - node->rb_parent = right; + rb_set_parent(node, right); } static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) { struct rb_node *left = node->rb_left; + struct rb_node *parent = rb_parent(node); if ((node->rb_left = left->rb_right)) - left->rb_right->rb_parent = node; + rb_set_parent(left->rb_right, node); left->rb_right = node; - if ((left->rb_parent = node->rb_parent)) + rb_set_parent(left, parent); + + if (parent) { - if (node == node->rb_parent->rb_right) - node->rb_parent->rb_right = left; + if (node == parent->rb_right) + parent->rb_right = left; else - node->rb_parent->rb_left = left; + parent->rb_left = left; } else root->rb_node = left; - node->rb_parent = left; + rb_set_parent(node, left); } void rb_insert_color(struct rb_node *node, struct rb_root *root) { struct rb_node *parent, *gparent; - while ((parent = node->rb_parent) && parent->rb_color == RB_RED) + while ((parent = rb_parent(node)) && rb_is_red(parent)) { - gparent = parent->rb_parent; + gparent = rb_parent(parent); if (parent == gparent->rb_left) { { register struct rb_node *uncle = gparent->rb_right; - if (uncle && uncle->rb_color == RB_RED) + if (uncle && rb_is_red(uncle)) { - uncle->rb_color = RB_BLACK; - parent->rb_color = RB_BLACK; - gparent->rb_color = RB_RED; + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); node = gparent; continue; } @@ -94,17 +100,17 @@ void rb_insert_color(struct rb_node *nod node = tmp; } - parent->rb_color = RB_BLACK; - gparent->rb_color = RB_RED; + rb_set_black(parent); + rb_set_red(gparent); __rb_rotate_right(gparent, root); } else { { register struct rb_node *uncle = gparent->rb_left; - if (uncle && uncle->rb_color == RB_RED) + if (uncle && rb_is_red(uncle)) { - uncle->rb_color = RB_BLACK; - parent->rb_color = RB_BLACK; - gparent->rb_color = RB_RED; + rb_set_black(uncle); + rb_set_black(parent); + rb_set_red(gparent); node = gparent; continue; } @@ -119,13 +125,13 @@ void rb_insert_color(struct rb_node *nod node = tmp; } - parent->rb_color = RB_BLACK; - gparent->rb_color = RB_RED; + rb_set_black(parent); + rb_set_red(gparent); __rb_rotate_left(gparent, root); } } - root->rb_node->rb_color = RB_BLACK; + rb_set_black(root->rb_node); } EXPORT_SYMBOL(rb_insert_color); @@ -134,43 +140,40 @@ static void __rb_erase_color(struct rb_n { struct rb_node *other; - while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) + while ((!node || rb_is_black(node)) && node != root->rb_node) { if (parent->rb_left == node) { other = parent->rb_right; - if (other->rb_color == RB_RED) + if (rb_is_red(other)) { - other->rb_color = RB_BLACK; - parent->rb_color = RB_RED; + rb_set_black(other); + rb_set_red(parent); __rb_rotate_left(parent, root); other = parent->rb_right; } - if ((!other->rb_left || - other->rb_left->rb_color == RB_BLACK) - && (!other->rb_right || - other->rb_right->rb_color == RB_BLACK)) + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) { - other->rb_color = RB_RED; + rb_set_red(other); node = parent; - parent = node->rb_parent; + parent = rb_parent(node); } else { - if (!other->rb_right || - other->rb_right->rb_color == RB_BLACK) + if (!other->rb_right || rb_is_black(other->rb_right)) { - register struct rb_node *o_left; + struct rb_node *o_left; if ((o_left = other->rb_left)) - o_left->rb_color = RB_BLACK; - other->rb_color = RB_RED; + rb_set_black(o_left); + rb_set_red(other); __rb_rotate_right(other, root); other = parent->rb_right; } - other->rb_color = parent->rb_color; - parent->rb_color = RB_BLACK; + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); if (other->rb_right) - other->rb_right->rb_color = RB_BLACK; + rb_set_black(other->rb_right); __rb_rotate_left(parent, root); node = root->rb_node; break; @@ -179,38 +182,35 @@ static void __rb_erase_color(struct rb_n else { other = parent->rb_left; - if (other->rb_color == RB_RED) + if (rb_is_red(other)) { - other->rb_color = RB_BLACK; - parent->rb_color = RB_RED; + rb_set_black(other); + rb_set_red(parent); __rb_rotate_right(parent, root); other = parent->rb_left; } - if ((!other->rb_left || - other->rb_left->rb_color == RB_BLACK) - && (!other->rb_right || - other->rb_right->rb_color == RB_BLACK)) + if ((!other->rb_left || rb_is_black(other->rb_left)) && + (!other->rb_right || rb_is_black(other->rb_right))) { - other->rb_color = RB_RED; + rb_set_red(other); node = parent; - parent = node->rb_parent; + parent = rb_parent(node); } else { - if (!other->rb_left || - other->rb_left->rb_color == RB_BLACK) + if (!other->rb_left || rb_is_black(other->rb_left)) { register struct rb_node *o_right; if ((o_right = other->rb_right)) - o_right->rb_color = RB_BLACK; - other->rb_color = RB_RED; + rb_set_black(o_right); + rb_set_red(other); __rb_rotate_left(other, root); other = parent->rb_left; } - other->rb_color = parent->rb_color; - parent->rb_color = RB_BLACK; + rb_set_color(other, rb_color(parent)); + rb_set_black(parent); if (other->rb_left) - other->rb_left->rb_color = RB_BLACK; + rb_set_black(other->rb_left); __rb_rotate_right(parent, root); node = root->rb_node; break; @@ -218,7 +218,7 @@ static void __rb_erase_color(struct rb_n } } if (node) - node->rb_color = RB_BLACK; + rb_set_black(node); } void rb_erase(struct rb_node *node, struct rb_root *root) @@ -238,48 +238,41 @@ void rb_erase(struct rb_node *node, stru while ((left = node->rb_left) != NULL) node = left; child = node->rb_right; - parent = node->rb_parent; - color = node->rb_color; + parent = rb_parent(node); + color = rb_color(node); if (child) - child->rb_parent = parent; - if (parent) - { - if (parent->rb_left == node) - parent->rb_left = child; - else - parent->rb_right = child; - } - else - root->rb_node = child; - - if (node->rb_parent == old) + rb_set_parent(child, parent); + if (parent == old) { + parent->rb_right = child; parent = node; - node->rb_parent = old->rb_parent; - node->rb_color = old->rb_color; + } else + parent->rb_left = child; + + node->rb_parent_color = old->rb_parent_color; node->rb_right = old->rb_right; node->rb_left = old->rb_left; - if (old->rb_parent) + if (rb_parent(old)) { - if (old->rb_parent->rb_left == old) - old->rb_parent->rb_left = node; + if (rb_parent(old)->rb_left == old) + rb_parent(old)->rb_left = node; else - old->rb_parent->rb_right = node; + rb_parent(old)->rb_right = node; } else root->rb_node = node; - old->rb_left->rb_parent = node; + rb_set_parent(old->rb_left, node); if (old->rb_right) - old->rb_right->rb_parent = node; + rb_set_parent(old->rb_right, node); goto color; } - parent = node->rb_parent; - color = node->rb_color; + parent = rb_parent(node); + color = rb_color(node); if (child) - child->rb_parent = parent; + rb_set_parent(child, parent); if (parent) { if (parent->rb_left == node) @@ -327,6 +320,8 @@ EXPORT_SYMBOL(rb_last); struct rb_node *rb_next(struct rb_node *node) { + struct rb_node *parent; + /* If we have a right-hand child, go down and then left as far as we can. */ if (node->rb_right) { @@ -342,15 +337,17 @@ struct rb_node *rb_next(struct rb_node * ancestor is a right-hand child of its parent, keep going up. First time it's a left-hand child of its parent, said parent is our 'next' node. */ - while (node->rb_parent && node == node->rb_parent->rb_right) - node = node->rb_parent; + while ((parent = rb_parent(node)) && node == parent->rb_right) + node = parent; - return node->rb_parent; + return parent; } EXPORT_SYMBOL(rb_next); struct rb_node *rb_prev(struct rb_node *node) { + struct rb_node *parent; + /* If we have a left-hand child, go down and then right as far as we can. */ if (node->rb_left) { @@ -362,17 +359,17 @@ struct rb_node *rb_prev(struct rb_node * /* No left-hand children. Go up till we find an ancestor which is a right-hand child of its parent */ - while (node->rb_parent && node == node->rb_parent->rb_left) - node = node->rb_parent; + while ((parent = rb_parent(node)) && node == parent->rb_left) + node = parent; - return node->rb_parent; + return parent; } EXPORT_SYMBOL(rb_prev); void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) { - struct rb_node *parent = victim->rb_parent; + struct rb_node *parent = rb_parent(victim); /* Set the surrounding nodes to point to the replacement */ if (parent) { @@ -384,9 +381,9 @@ void rb_replace_node(struct rb_node *vic root->rb_node = new; } if (victim->rb_left) - victim->rb_left->rb_parent = new; + rb_set_parent(victim->rb_left, new); if (victim->rb_right) - victim->rb_right->rb_parent = new; + rb_set_parent(victim->rb_right, new); /* Copy the pointers/colour from the victim to the replacement */ *new = *victim; diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c index f8ac9fa..2cc11fa 100644 --- a/lib/reed_solomon/reed_solomon.c +++ b/lib/reed_solomon/reed_solomon.c @@ -54,7 +54,6 @@ static DEFINE_MUTEX(rslistlock); /** * rs_init - Initialize a Reed-Solomon codec - * * @symsize: symbol size, bits (1-8) * @gfpoly: Field generator polynomial coefficients * @fcr: first root of RS code generator polynomial, index form @@ -62,7 +61,7 @@ static DEFINE_MUTEX(rslistlock); * @nroots: RS code generator polynomial degree (number of roots) * * Allocate a control structure and the polynom arrays for faster - * en/decoding. Fill the arrays according to the given parameters + * en/decoding. Fill the arrays according to the given parameters. */ static struct rs_control *rs_init(int symsize, int gfpoly, int fcr, int prim, int nroots) @@ -155,8 +154,7 @@ errrs: /** - * free_rs - Free the rs control structure, if its not longer used - * + * free_rs - Free the rs control structure, if it is no longer used * @rs: the control structure which is not longer used by the * caller */ @@ -176,7 +174,6 @@ void free_rs(struct rs_control *rs) /** * init_rs - Find a matching or allocate a new rs control structure - * * @symsize: the symbol size (number of bits) * @gfpoly: the extended Galois field generator polynomial coefficients, * with the 0th coefficient in the low order bit. The polynomial @@ -236,7 +233,6 @@ out: #ifdef CONFIG_REED_SOLOMON_ENC8 /** * encode_rs8 - Calculate the parity for data values (8bit data width) - * * @rs: the rs control structure * @data: data field of a given type * @len: data length @@ -258,7 +254,6 @@ #endif #ifdef CONFIG_REED_SOLOMON_DEC8 /** * decode_rs8 - Decode codeword (8bit data width) - * * @rs: the rs control structure * @data: data field of a given type * @par: received parity data field @@ -285,7 +280,6 @@ #endif #ifdef CONFIG_REED_SOLOMON_ENC16 /** * encode_rs16 - Calculate the parity for data values (16bit data width) - * * @rs: the rs control structure * @data: data field of a given type * @len: data length @@ -305,7 +299,6 @@ #endif #ifdef CONFIG_REED_SOLOMON_DEC16 /** * decode_rs16 - Decode codeword (16bit data width) - * * @rs: the rs control structure * @data: data field of a given type * @par: received parity data field diff --git a/lib/rwsem-spinlock.c b/lib/rwsem-spinlock.c index 40ffde9..db4fed7 100644 --- a/lib/rwsem-spinlock.c +++ b/lib/rwsem-spinlock.c @@ -17,27 +17,22 @@ #define RWSEM_WAITING_FOR_READ 0x0000000 #define RWSEM_WAITING_FOR_WRITE 0x00000002 }; -#if RWSEM_DEBUG -void rwsemtrace(struct rw_semaphore *sem, const char *str) -{ - if (sem->debug) - printk("[%d] %s({%d,%d})\n", - current->pid, str, sem->activity, - list_empty(&sem->wait_list) ? 0 : 1); -} -#endif - /* * initialise the semaphore */ -void fastcall init_rwsem(struct rw_semaphore *sem) +void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key) { +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held semaphore: + */ + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key); +#endif sem->activity = 0; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); -#if RWSEM_DEBUG - sem->debug = 0; -#endif } /* @@ -56,8 +51,6 @@ __rwsem_do_wake(struct rw_semaphore *sem struct task_struct *tsk; int woken; - rwsemtrace(sem, "Entering __rwsem_do_wake"); - waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); if (!wakewrite) { @@ -104,7 +97,6 @@ __rwsem_do_wake(struct rw_semaphore *sem sem->activity += woken; out: - rwsemtrace(sem, "Leaving __rwsem_do_wake"); return sem; } @@ -138,8 +130,6 @@ void fastcall __sched __down_read(struct struct rwsem_waiter waiter; struct task_struct *tsk; - rwsemtrace(sem, "Entering __down_read"); - spin_lock_irq(&sem->wait_lock); if (sem->activity >= 0 && list_empty(&sem->wait_list)) { @@ -171,9 +161,8 @@ void fastcall __sched __down_read(struct } tsk->state = TASK_RUNNING; - out: - rwsemtrace(sem, "Leaving __down_read"); + ; } /* @@ -184,7 +173,6 @@ int fastcall __down_read_trylock(struct unsigned long flags; int ret = 0; - rwsemtrace(sem, "Entering __down_read_trylock"); spin_lock_irqsave(&sem->wait_lock, flags); @@ -196,7 +184,6 @@ int fastcall __down_read_trylock(struct spin_unlock_irqrestore(&sem->wait_lock, flags); - rwsemtrace(sem, "Leaving __down_read_trylock"); return ret; } @@ -204,13 +191,11 @@ int fastcall __down_read_trylock(struct * get a write lock on the semaphore * - we increment the waiting count anyway to indicate an exclusive lock */ -void fastcall __sched __down_write(struct rw_semaphore *sem) +void fastcall __sched __down_write_nested(struct rw_semaphore *sem, int subclass) { struct rwsem_waiter waiter; struct task_struct *tsk; - rwsemtrace(sem, "Entering __down_write"); - spin_lock_irq(&sem->wait_lock); if (sem->activity == 0 && list_empty(&sem->wait_list)) { @@ -242,9 +227,13 @@ void fastcall __sched __down_write(struc } tsk->state = TASK_RUNNING; - out: - rwsemtrace(sem, "Leaving __down_write"); + ; +} + +void fastcall __sched __down_write(struct rw_semaphore *sem) +{ + __down_write_nested(sem, 0); } /* @@ -255,8 +244,6 @@ int fastcall __down_write_trylock(struct unsigned long flags; int ret = 0; - rwsemtrace(sem, "Entering __down_write_trylock"); - spin_lock_irqsave(&sem->wait_lock, flags); if (sem->activity == 0 && list_empty(&sem->wait_list)) { @@ -267,7 +254,6 @@ int fastcall __down_write_trylock(struct spin_unlock_irqrestore(&sem->wait_lock, flags); - rwsemtrace(sem, "Leaving __down_write_trylock"); return ret; } @@ -278,16 +264,12 @@ void fastcall __up_read(struct rw_semaph { unsigned long flags; - rwsemtrace(sem, "Entering __up_read"); - spin_lock_irqsave(&sem->wait_lock, flags); if (--sem->activity == 0 && !list_empty(&sem->wait_list)) sem = __rwsem_wake_one_writer(sem); spin_unlock_irqrestore(&sem->wait_lock, flags); - - rwsemtrace(sem, "Leaving __up_read"); } /* @@ -297,8 +279,6 @@ void fastcall __up_write(struct rw_semap { unsigned long flags; - rwsemtrace(sem, "Entering __up_write"); - spin_lock_irqsave(&sem->wait_lock, flags); sem->activity = 0; @@ -306,8 +286,6 @@ void fastcall __up_write(struct rw_semap sem = __rwsem_do_wake(sem, 1); spin_unlock_irqrestore(&sem->wait_lock, flags); - - rwsemtrace(sem, "Leaving __up_write"); } /* @@ -318,8 +296,6 @@ void fastcall __downgrade_write(struct r { unsigned long flags; - rwsemtrace(sem, "Entering __downgrade_write"); - spin_lock_irqsave(&sem->wait_lock, flags); sem->activity = 1; @@ -327,18 +303,14 @@ void fastcall __downgrade_write(struct r sem = __rwsem_do_wake(sem, 0); spin_unlock_irqrestore(&sem->wait_lock, flags); - - rwsemtrace(sem, "Leaving __downgrade_write"); } -EXPORT_SYMBOL(init_rwsem); +EXPORT_SYMBOL(__init_rwsem); EXPORT_SYMBOL(__down_read); EXPORT_SYMBOL(__down_read_trylock); +EXPORT_SYMBOL(__down_write_nested); EXPORT_SYMBOL(__down_write); EXPORT_SYMBOL(__down_write_trylock); EXPORT_SYMBOL(__up_read); EXPORT_SYMBOL(__up_write); EXPORT_SYMBOL(__downgrade_write); -#if RWSEM_DEBUG -EXPORT_SYMBOL(rwsemtrace); -#endif diff --git a/lib/rwsem.c b/lib/rwsem.c index 62fa4eb..b322421 100644 --- a/lib/rwsem.c +++ b/lib/rwsem.c @@ -8,6 +8,26 @@ #include #include #include +/* + * Initialize an rwsem: + */ +void __init_rwsem(struct rw_semaphore *sem, const char *name, + struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held semaphore: + */ + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key); +#endif + sem->count = RWSEM_UNLOCKED_VALUE; + spin_lock_init(&sem->wait_lock); + INIT_LIST_HEAD(&sem->wait_list); +} + +EXPORT_SYMBOL(__init_rwsem); + struct rwsem_waiter { struct list_head list; struct task_struct *task; @@ -16,17 +36,6 @@ #define RWSEM_WAITING_FOR_READ 0x0000000 #define RWSEM_WAITING_FOR_WRITE 0x00000002 }; -#if RWSEM_DEBUG -#undef rwsemtrace -void rwsemtrace(struct rw_semaphore *sem, const char *str) -{ - printk("sem=%p\n", sem); - printk("(sem)=%08lx\n", sem->count); - if (sem->debug) - printk("[%d] %s({%08lx})\n", current->pid, str, sem->count); -} -#endif - /* * handle the lock release when processes blocked on it that can now run * - if we come here from up_xxxx(), then: @@ -45,8 +54,6 @@ __rwsem_do_wake(struct rw_semaphore *sem struct list_head *next; signed long oldcount, woken, loop; - rwsemtrace(sem, "Entering __rwsem_do_wake"); - if (downgrading) goto dont_wake_writers; @@ -127,7 +134,6 @@ __rwsem_do_wake(struct rw_semaphore *sem next->prev = &sem->wait_list; out: - rwsemtrace(sem, "Leaving __rwsem_do_wake"); return sem; /* undo the change to count, but check for a transition 1->0 */ @@ -186,13 +192,9 @@ rwsem_down_read_failed(struct rw_semapho { struct rwsem_waiter waiter; - rwsemtrace(sem, "Entering rwsem_down_read_failed"); - waiter.flags = RWSEM_WAITING_FOR_READ; rwsem_down_failed_common(sem, &waiter, RWSEM_WAITING_BIAS - RWSEM_ACTIVE_BIAS); - - rwsemtrace(sem, "Leaving rwsem_down_read_failed"); return sem; } @@ -204,12 +206,9 @@ rwsem_down_write_failed(struct rw_semaph { struct rwsem_waiter waiter; - rwsemtrace(sem, "Entering rwsem_down_write_failed"); - waiter.flags = RWSEM_WAITING_FOR_WRITE; rwsem_down_failed_common(sem, &waiter, -RWSEM_ACTIVE_BIAS); - rwsemtrace(sem, "Leaving rwsem_down_write_failed"); return sem; } @@ -221,8 +220,6 @@ struct rw_semaphore fastcall *rwsem_wake { unsigned long flags; - rwsemtrace(sem, "Entering rwsem_wake"); - spin_lock_irqsave(&sem->wait_lock, flags); /* do nothing if list empty */ @@ -231,8 +228,6 @@ struct rw_semaphore fastcall *rwsem_wake spin_unlock_irqrestore(&sem->wait_lock, flags); - rwsemtrace(sem, "Leaving rwsem_wake"); - return sem; } @@ -245,8 +240,6 @@ struct rw_semaphore fastcall *rwsem_down { unsigned long flags; - rwsemtrace(sem, "Entering rwsem_downgrade_wake"); - spin_lock_irqsave(&sem->wait_lock, flags); /* do nothing if list empty */ @@ -255,7 +248,6 @@ struct rw_semaphore fastcall *rwsem_down spin_unlock_irqrestore(&sem->wait_lock, flags); - rwsemtrace(sem, "Leaving rwsem_downgrade_wake"); return sem; } @@ -263,6 +255,3 @@ EXPORT_SYMBOL(rwsem_down_read_failed); EXPORT_SYMBOL(rwsem_down_write_failed); EXPORT_SYMBOL(rwsem_wake); EXPORT_SYMBOL(rwsem_downgrade_wake); -#if RWSEM_DEBUG -EXPORT_SYMBOL(rwsemtrace); -#endif diff --git a/lib/semaphore-sleepers.c b/lib/semaphore-sleepers.c index 4d5f188..1281805 100644 --- a/lib/semaphore-sleepers.c +++ b/lib/semaphore-sleepers.c @@ -12,7 +12,6 @@ * * rw semaphores implemented November 1999 by Benjamin LaHaise */ -#include #include #include #include diff --git a/lib/spinlock_debug.c b/lib/spinlock_debug.c index d8b6bb4..3d9c4dc 100644 --- a/lib/spinlock_debug.c +++ b/lib/spinlock_debug.c @@ -6,41 +6,73 @@ * DEBUG_SPINLOCK. */ -#include #include #include +#include #include +#include + +void __spin_lock_init(spinlock_t *lock, const char *name, + struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held lock: + */ + debug_check_no_locks_freed((void *)lock, sizeof(*lock)); + lockdep_init_map(&lock->dep_map, name, key); +#endif + lock->raw_lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED; + lock->magic = SPINLOCK_MAGIC; + lock->owner = SPINLOCK_OWNER_INIT; + lock->owner_cpu = -1; +} + +EXPORT_SYMBOL(__spin_lock_init); + +void __rwlock_init(rwlock_t *lock, const char *name, + struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + /* + * Make sure we are not reinitializing a held lock: + */ + debug_check_no_locks_freed((void *)lock, sizeof(*lock)); + lockdep_init_map(&lock->dep_map, name, key); +#endif + lock->raw_lock = (raw_rwlock_t) __RAW_RW_LOCK_UNLOCKED; + lock->magic = RWLOCK_MAGIC; + lock->owner = SPINLOCK_OWNER_INIT; + lock->owner_cpu = -1; +} + +EXPORT_SYMBOL(__rwlock_init); static void spin_bug(spinlock_t *lock, const char *msg) { - static long print_once = 1; struct task_struct *owner = NULL; - if (xchg(&print_once, 0)) { - if (lock->owner && lock->owner != SPINLOCK_OWNER_INIT) - owner = lock->owner; - printk(KERN_EMERG "BUG: spinlock %s on CPU#%d, %s/%d\n", - msg, raw_smp_processor_id(), - current->comm, current->pid); - printk(KERN_EMERG " lock: %p, .magic: %08x, .owner: %s/%d, " - ".owner_cpu: %d\n", - lock, lock->magic, - owner ? owner->comm : "", - owner ? owner->pid : -1, - lock->owner_cpu); - dump_stack(); -#ifdef CONFIG_SMP - /* - * We cannot continue on SMP: - */ -// panic("bad locking"); -#endif - } + if (!debug_locks_off()) + return; + + if (lock->owner && lock->owner != SPINLOCK_OWNER_INIT) + owner = lock->owner; + printk(KERN_EMERG "BUG: spinlock %s on CPU#%d, %s/%d\n", + msg, raw_smp_processor_id(), + current->comm, current->pid); + printk(KERN_EMERG " lock: %p, .magic: %08x, .owner: %s/%d, " + ".owner_cpu: %d\n", + lock, lock->magic, + owner ? owner->comm : "", + owner ? owner->pid : -1, + lock->owner_cpu); + dump_stack(); } #define SPIN_BUG_ON(cond, lock, msg) if (unlikely(cond)) spin_bug(lock, msg) -static inline void debug_spin_lock_before(spinlock_t *lock) +static inline void +debug_spin_lock_before(spinlock_t *lock) { SPIN_BUG_ON(lock->magic != SPINLOCK_MAGIC, lock, "bad magic"); SPIN_BUG_ON(lock->owner == current, lock, "recursion"); @@ -119,20 +151,13 @@ void _raw_spin_unlock(spinlock_t *lock) static void rwlock_bug(rwlock_t *lock, const char *msg) { - static long print_once = 1; - - if (xchg(&print_once, 0)) { - printk(KERN_EMERG "BUG: rwlock %s on CPU#%d, %s/%d, %p\n", - msg, raw_smp_processor_id(), current->comm, - current->pid, lock); - dump_stack(); -#ifdef CONFIG_SMP - /* - * We cannot continue on SMP: - */ - panic("bad locking"); -#endif - } + if (!debug_locks_off()) + return; + + printk(KERN_EMERG "BUG: rwlock %s on CPU#%d, %s/%d, %p\n", + msg, raw_smp_processor_id(), current->comm, + current->pid, lock); + dump_stack(); } #define RWLOCK_BUG_ON(cond, lock, msg) if (unlikely(cond)) rwlock_bug(lock, msg) diff --git a/lib/string.c b/lib/string.c index 064f631..6307726 100644 --- a/lib/string.c +++ b/lib/string.c @@ -301,6 +301,36 @@ char *strnchr(const char *s, size_t coun EXPORT_SYMBOL(strnchr); #endif +/** + * strstrip - Removes leading and trailing whitespace from @s. + * @s: The string to be stripped. + * + * Note that the first trailing whitespace is replaced with a %NUL-terminator + * in the given string @s. Returns a pointer to the first non-whitespace + * character in @s. + */ +char *strstrip(char *s) +{ + size_t size; + char *end; + + size = strlen(s); + + if (!size) + return s; + + end = s + size - 1; + while (end != s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + while (*s && isspace(*s)) + s++; + + return s; +} +EXPORT_SYMBOL(strstrip); + #ifndef __HAVE_ARCH_STRLEN /** * strlen - Find the length of a string diff --git a/lib/textsearch.c b/lib/textsearch.c index 6f3093e..2cb4a43 100644 --- a/lib/textsearch.c +++ b/lib/textsearch.c @@ -93,7 +93,6 @@ * ========================================================================== */ -#include #include #include #include diff --git a/lib/ts_bm.c b/lib/ts_bm.c index c4c1ac5..0110e44 100644 --- a/lib/ts_bm.c +++ b/lib/ts_bm.c @@ -35,7 +35,6 @@ * matchings spread over multiple fragments, then go BM. */ -#include #include #include #include diff --git a/lib/ts_fsm.c b/lib/ts_fsm.c index ca32112..87847c2 100644 --- a/lib/ts_fsm.c +++ b/lib/ts_fsm.c @@ -26,7 +26,6 @@ * however while in strict mode the average runtime can be better. */ -#include #include #include #include diff --git a/lib/ts_kmp.c b/lib/ts_kmp.c index 7fd4545..3ced628 100644 --- a/lib/ts_kmp.c +++ b/lib/ts_kmp.c @@ -30,7 +30,6 @@ * [2] See finite automation theory */ -#include #include #include #include diff --git a/lib/vsprintf.c b/lib/vsprintf.c index b07db5c..bed7229 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -187,49 +187,49 @@ static char * number(char * buf, char * size -= precision; if (!(type&(ZEROPAD+LEFT))) { while(size-->0) { - if (buf <= end) + if (buf < end) *buf = ' '; ++buf; } } if (sign) { - if (buf <= end) + if (buf < end) *buf = sign; ++buf; } if (type & SPECIAL) { if (base==8) { - if (buf <= end) + if (buf < end) *buf = '0'; ++buf; } else if (base==16) { - if (buf <= end) + if (buf < end) *buf = '0'; ++buf; - if (buf <= end) + if (buf < end) *buf = digits[33]; ++buf; } } if (!(type & LEFT)) { while (size-- > 0) { - if (buf <= end) + if (buf < end) *buf = c; ++buf; } } while (i < precision--) { - if (buf <= end) + if (buf < end) *buf = '0'; ++buf; } while (i-- > 0) { - if (buf <= end) + if (buf < end) *buf = tmp[i]; ++buf; } while (size-- > 0) { - if (buf <= end) + if (buf < end) *buf = ' '; ++buf; } @@ -272,7 +272,8 @@ int vsnprintf(char *buf, size_t size, co /* 'z' changed to 'Z' --davidm 1/25/99 */ /* 't' added for ptrdiff_t */ - /* Reject out-of-range values early */ + /* Reject out-of-range values early. Large positive sizes are + used for unknown buffer sizes. */ if (unlikely((int) size < 0)) { /* There can be only one.. */ static int warn = 1; @@ -282,16 +283,17 @@ int vsnprintf(char *buf, size_t size, co } str = buf; - end = buf + size - 1; + end = buf + size; - if (end < buf - 1) { - end = ((void *) -1); - size = end - buf + 1; + /* Make sure end is always >= buf */ + if (end < buf) { + end = ((void *)-1); + size = end - buf; } for (; *fmt ; ++fmt) { if (*fmt != '%') { - if (str <= end) + if (str < end) *str = *fmt; ++str; continue; @@ -357,17 +359,17 @@ int vsnprintf(char *buf, size_t size, co case 'c': if (!(flags & LEFT)) { while (--field_width > 0) { - if (str <= end) + if (str < end) *str = ' '; ++str; } } c = (unsigned char) va_arg(args, int); - if (str <= end) + if (str < end) *str = c; ++str; while (--field_width > 0) { - if (str <= end) + if (str < end) *str = ' '; ++str; } @@ -382,18 +384,18 @@ int vsnprintf(char *buf, size_t size, co if (!(flags & LEFT)) { while (len < field_width--) { - if (str <= end) + if (str < end) *str = ' '; ++str; } } for (i = 0; i < len; ++i) { - if (str <= end) + if (str < end) *str = *s; ++str; ++s; } while (len < field_width--) { - if (str <= end) + if (str < end) *str = ' '; ++str; } @@ -426,7 +428,7 @@ int vsnprintf(char *buf, size_t size, co continue; case '%': - if (str <= end) + if (str < end) *str = '%'; ++str; continue; @@ -449,11 +451,11 @@ int vsnprintf(char *buf, size_t size, co break; default: - if (str <= end) + if (str < end) *str = '%'; ++str; if (*fmt) { - if (str <= end) + if (str < end) *str = *fmt; ++str; } else { @@ -483,14 +485,13 @@ int vsnprintf(char *buf, size_t size, co str = number(str, end, num, base, field_width, precision, flags); } - if (str <= end) - *str = '\0'; - else if (size > 0) - /* don't write out a null byte if the buf size is zero */ - *end = '\0'; - /* the trailing null byte doesn't count towards the total - * ++str; - */ + if (size > 0) { + if (str < end) + *str = '\0'; + else + end[-1] = '\0'; + } + /* the trailing null byte doesn't count towards the total */ return str-buf; } @@ -848,3 +849,26 @@ int sscanf(const char * buf, const char } EXPORT_SYMBOL(sscanf); + + +/* Simplified asprintf. */ +char *kasprintf(gfp_t gfp, const char *fmt, ...) +{ + va_list ap; + unsigned int len; + char *p; + + va_start(ap, fmt); + len = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + p = kmalloc(len+1, gfp); + if (!p) + return NULL; + va_start(ap, fmt); + vsnprintf(p, len+1, fmt, ap); + va_end(ap); + return p; +} + +EXPORT_SYMBOL(kasprintf); diff --git a/lib/zlib_deflate/deflate.c b/lib/zlib_deflate/deflate.c index 1653dd9..c3e4a2b 100644 --- a/lib/zlib_deflate/deflate.c +++ b/lib/zlib_deflate/deflate.c @@ -164,34 +164,17 @@ #define CLEAR_HASH(s) \ memset((char *)s->head, 0, (unsigned)(s->hash_size-1)*sizeof(*s->head)); /* ========================================================================= */ -int zlib_deflateInit_( - z_streamp strm, - int level, - const char *version, - int stream_size -) -{ - return zlib_deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, version, stream_size); - /* To do: ignore strm->next_in if we use it as window */ -} - -/* ========================================================================= */ -int zlib_deflateInit2_( +int zlib_deflateInit2( z_streamp strm, int level, int method, int windowBits, int memLevel, - int strategy, - const char *version, - int stream_size + int strategy ) { deflate_state *s; int noheader = 0; - static char* my_version = ZLIB_VERSION; deflate_workspace *mem; ush *overlay; @@ -199,10 +182,6 @@ int zlib_deflateInit2_( * output size for (length,distance) codes is <= 24 bits. */ - if (version == NULL || version[0] != my_version[0] || - stream_size != sizeof(z_stream)) { - return Z_VERSION_ERROR; - } if (strm == NULL) return Z_STREAM_ERROR; strm->msg = NULL; diff --git a/lib/zlib_deflate/deflate_syms.c b/lib/zlib_deflate/deflate_syms.c index 767b573..ccfe25f 100644 --- a/lib/zlib_deflate/deflate_syms.c +++ b/lib/zlib_deflate/deflate_syms.c @@ -12,8 +12,7 @@ #include EXPORT_SYMBOL(zlib_deflate_workspacesize); EXPORT_SYMBOL(zlib_deflate); -EXPORT_SYMBOL(zlib_deflateInit_); -EXPORT_SYMBOL(zlib_deflateInit2_); +EXPORT_SYMBOL(zlib_deflateInit2); EXPORT_SYMBOL(zlib_deflateEnd); EXPORT_SYMBOL(zlib_deflateReset); MODULE_LICENSE("GPL"); diff --git a/lib/zlib_inflate/Makefile b/lib/zlib_inflate/Makefile index 221c139..bf06548 100644 --- a/lib/zlib_inflate/Makefile +++ b/lib/zlib_inflate/Makefile @@ -15,5 +15,5 @@ # obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate.o -zlib_inflate-objs := infblock.o infcodes.o inffast.o inflate.o \ - inflate_sync.o inftrees.o infutil.o inflate_syms.o +zlib_inflate-objs := inffast.o inflate.o \ + inftrees.o inflate_syms.o diff --git a/lib/zlib_inflate/infblock.c b/lib/zlib_inflate/infblock.c deleted file mode 100644 index c16cdef..0000000 --- a/lib/zlib_inflate/infblock.c +++ /dev/null @@ -1,365 +0,0 @@ -/* infblock.c -- interpret and process block types to last block - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include -#include "infblock.h" -#include "inftrees.h" -#include "infcodes.h" -#include "infutil.h" - -struct inflate_codes_state; - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - -/* Table for deflate from PKZIP's appnote.txt. */ -static const uInt border[] = { /* Order of the bit length code lengths */ - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - -/* - Notes beyond the 1.93a appnote.txt: - - 1. Distance pointers never point before the beginning of the output - stream. - 2. Distance pointers can point back across blocks, up to 32k away. - 3. There is an implied maximum of 7 bits for the bit length table and - 15 bits for the actual data. - 4. If only one code exists, then it is encoded using one bit. (Zero - would be more efficient, but perhaps a little confusing.) If two - codes exist, they are coded using one bit each (0 and 1). - 5. There is no way of sending zero distance codes--a dummy must be - sent if there are none. (History: a pre 2.0 version of PKZIP would - store blocks with no distance codes, but this was discovered to be - too harsh a criterion.) Valid only for 1.93a. 2.04c does allow - zero distance codes, which is sent as one code of zero bits in - length. - 6. There are up to 286 literal/length codes. Code 256 represents the - end-of-block. Note however that the static length tree defines - 288 codes just to fill out the Huffman codes. Codes 286 and 287 - cannot be used though, since there is no length base or extra bits - defined for them. Similarily, there are up to 30 distance codes. - However, static trees define 32 codes (all 5 bits) to fill out the - Huffman codes, but the last two had better not show up in the data. - 7. Unzip can check dynamic Huffman blocks for complete code sets. - The exception is that a single code would not be complete (see #4). - 8. The five bits following the block type is really the number of - literal codes sent minus 257. - 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits - (1+6+6). Therefore, to output three times the length, you output - three codes (1+1+1), whereas to output four times the same length, - you only need two codes (1+3). Hmm. - 10. In the tree reconstruction algorithm, Code = Code + Increment - only if BitLength(i) is not zero. (Pretty obvious.) - 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) - 12. Note: length code 284 can represent 227-258, but length code 285 - really is 258. The last length deserves its own, short code - since it gets used a lot in very redundant files. The length - 258 is special since 258 - 3 (the min match length) is 255. - 13. The literal/length and distance code bit lengths are read as a - single stream of lengths. It is possible (and advantageous) for - a repeat code (16, 17, or 18) to go across the boundary between - the two sets of lengths. - */ - - -void zlib_inflate_blocks_reset( - inflate_blocks_statef *s, - z_streamp z, - uLong *c -) -{ - if (c != NULL) - *c = s->check; - if (s->mode == CODES) - zlib_inflate_codes_free(s->sub.decode.codes, z); - s->mode = TYPE; - s->bitk = 0; - s->bitb = 0; - s->read = s->write = s->window; - if (s->checkfn != NULL) - z->adler = s->check = (*s->checkfn)(0L, NULL, 0); -} - -inflate_blocks_statef *zlib_inflate_blocks_new( - z_streamp z, - check_func c, - uInt w -) -{ - inflate_blocks_statef *s; - - s = &WS(z)->working_blocks_state; - s->hufts = WS(z)->working_hufts; - s->window = WS(z)->working_window; - s->end = s->window + w; - s->checkfn = c; - s->mode = TYPE; - zlib_inflate_blocks_reset(s, z, NULL); - return s; -} - - -int zlib_inflate_blocks( - inflate_blocks_statef *s, - z_streamp z, - int r -) -{ - uInt t; /* temporary storage */ - uLong b; /* bit buffer */ - uInt k; /* bits in bit buffer */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - - /* copy input/output information to locals (UPDATE macro restores) */ - LOAD - - /* process input based on current state */ - while (1) switch (s->mode) - { - case TYPE: - NEEDBITS(3) - t = (uInt)b & 7; - s->last = t & 1; - switch (t >> 1) - { - case 0: /* stored */ - DUMPBITS(3) - t = k & 7; /* go to byte boundary */ - DUMPBITS(t) - s->mode = LENS; /* get length of stored block */ - break; - case 1: /* fixed */ - { - uInt bl, bd; - inflate_huft *tl, *td; - - zlib_inflate_trees_fixed(&bl, &bd, &tl, &td, s->hufts, z); - s->sub.decode.codes = zlib_inflate_codes_new(bl, bd, tl, td, z); - if (s->sub.decode.codes == NULL) - { - r = Z_MEM_ERROR; - LEAVE - } - } - DUMPBITS(3) - s->mode = CODES; - break; - case 2: /* dynamic */ - DUMPBITS(3) - s->mode = TABLE; - break; - case 3: /* illegal */ - DUMPBITS(3) - s->mode = B_BAD; - z->msg = (char*)"invalid block type"; - r = Z_DATA_ERROR; - LEAVE - } - break; - case LENS: - NEEDBITS(32) - if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) - { - s->mode = B_BAD; - z->msg = (char*)"invalid stored block lengths"; - r = Z_DATA_ERROR; - LEAVE - } - s->sub.left = (uInt)b & 0xffff; - b = k = 0; /* dump bits */ - s->mode = s->sub.left ? STORED : (s->last ? DRY : TYPE); - break; - case STORED: - if (n == 0) - LEAVE - NEEDOUT - t = s->sub.left; - if (t > n) t = n; - if (t > m) t = m; - memcpy(q, p, t); - p += t; n -= t; - q += t; m -= t; - if ((s->sub.left -= t) != 0) - break; - s->mode = s->last ? DRY : TYPE; - break; - case TABLE: - NEEDBITS(14) - s->sub.trees.table = t = (uInt)b & 0x3fff; -#ifndef PKZIP_BUG_WORKAROUND - if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) - { - s->mode = B_BAD; - z->msg = (char*)"too many length or distance symbols"; - r = Z_DATA_ERROR; - LEAVE - } -#endif - { - s->sub.trees.blens = WS(z)->working_blens; - } - DUMPBITS(14) - s->sub.trees.index = 0; - s->mode = BTREE; - case BTREE: - while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) - { - NEEDBITS(3) - s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; - DUMPBITS(3) - } - while (s->sub.trees.index < 19) - s->sub.trees.blens[border[s->sub.trees.index++]] = 0; - s->sub.trees.bb = 7; - t = zlib_inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, - &s->sub.trees.tb, s->hufts, z); - if (t != Z_OK) - { - r = t; - if (r == Z_DATA_ERROR) - s->mode = B_BAD; - LEAVE - } - s->sub.trees.index = 0; - s->mode = DTREE; - case DTREE: - while (t = s->sub.trees.table, - s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) - { - inflate_huft *h; - uInt i, j, c; - - t = s->sub.trees.bb; - NEEDBITS(t) - h = s->sub.trees.tb + ((uInt)b & zlib_inflate_mask[t]); - t = h->bits; - c = h->base; - if (c < 16) - { - DUMPBITS(t) - s->sub.trees.blens[s->sub.trees.index++] = c; - } - else /* c == 16..18 */ - { - i = c == 18 ? 7 : c - 14; - j = c == 18 ? 11 : 3; - NEEDBITS(t + i) - DUMPBITS(t) - j += (uInt)b & zlib_inflate_mask[i]; - DUMPBITS(i) - i = s->sub.trees.index; - t = s->sub.trees.table; - if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || - (c == 16 && i < 1)) - { - s->mode = B_BAD; - z->msg = (char*)"invalid bit length repeat"; - r = Z_DATA_ERROR; - LEAVE - } - c = c == 16 ? s->sub.trees.blens[i - 1] : 0; - do { - s->sub.trees.blens[i++] = c; - } while (--j); - s->sub.trees.index = i; - } - } - s->sub.trees.tb = NULL; - { - uInt bl, bd; - inflate_huft *tl, *td; - inflate_codes_statef *c; - - bl = 9; /* must be <= 9 for lookahead assumptions */ - bd = 6; /* must be <= 9 for lookahead assumptions */ - t = s->sub.trees.table; - t = zlib_inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), - s->sub.trees.blens, &bl, &bd, &tl, &td, - s->hufts, z); - if (t != Z_OK) - { - if (t == (uInt)Z_DATA_ERROR) - s->mode = B_BAD; - r = t; - LEAVE - } - if ((c = zlib_inflate_codes_new(bl, bd, tl, td, z)) == NULL) - { - r = Z_MEM_ERROR; - LEAVE - } - s->sub.decode.codes = c; - } - s->mode = CODES; - case CODES: - UPDATE - if ((r = zlib_inflate_codes(s, z, r)) != Z_STREAM_END) - return zlib_inflate_flush(s, z, r); - r = Z_OK; - zlib_inflate_codes_free(s->sub.decode.codes, z); - LOAD - if (!s->last) - { - s->mode = TYPE; - break; - } - s->mode = DRY; - case DRY: - FLUSH - if (s->read != s->write) - LEAVE - s->mode = B_DONE; - case B_DONE: - r = Z_STREAM_END; - LEAVE - case B_BAD: - r = Z_DATA_ERROR; - LEAVE - default: - r = Z_STREAM_ERROR; - LEAVE - } -} - - -int zlib_inflate_blocks_free( - inflate_blocks_statef *s, - z_streamp z -) -{ - zlib_inflate_blocks_reset(s, z, NULL); - return Z_OK; -} - - -#if 0 -void zlib_inflate_set_dictionary( - inflate_blocks_statef *s, - const Byte *d, - uInt n -) -{ - memcpy(s->window, d, n); - s->read = s->write = s->window + n; -} -#endif /* 0 */ - - -/* Returns true if inflate is currently at the end of a block generated - * by Z_SYNC_FLUSH or Z_FULL_FLUSH. - * IN assertion: s != NULL - */ -#if 0 -int zlib_inflate_blocks_sync_point( - inflate_blocks_statef *s -) -{ - return s->mode == LENS; -} -#endif /* 0 */ diff --git a/lib/zlib_inflate/infblock.h b/lib/zlib_inflate/infblock.h deleted file mode 100644 index ceee60b..0000000 --- a/lib/zlib_inflate/infblock.h +++ /dev/null @@ -1,48 +0,0 @@ -/* infblock.h -- header to use infblock.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -#ifndef _INFBLOCK_H -#define _INFBLOCK_H - -struct inflate_blocks_state; -typedef struct inflate_blocks_state inflate_blocks_statef; - -extern inflate_blocks_statef * zlib_inflate_blocks_new ( - z_streamp z, - check_func c, /* check function */ - uInt w); /* window size */ - -extern int zlib_inflate_blocks ( - inflate_blocks_statef *, - z_streamp , - int); /* initial return code */ - -extern void zlib_inflate_blocks_reset ( - inflate_blocks_statef *, - z_streamp , - uLong *); /* check value on output */ - -extern int zlib_inflate_blocks_free ( - inflate_blocks_statef *, - z_streamp); - -#if 0 -extern void zlib_inflate_set_dictionary ( - inflate_blocks_statef *s, - const Byte *d, /* dictionary */ - uInt n); /* dictionary length */ -#endif /* 0 */ - -#if 0 -extern int zlib_inflate_blocks_sync_point ( - inflate_blocks_statef *s); -#endif /* 0 */ - -#endif /* _INFBLOCK_H */ diff --git a/lib/zlib_inflate/infcodes.c b/lib/zlib_inflate/infcodes.c deleted file mode 100644 index 07cd759..0000000 --- a/lib/zlib_inflate/infcodes.c +++ /dev/null @@ -1,202 +0,0 @@ -/* infcodes.c -- process literals and length/distance pairs - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include -#include "inftrees.h" -#include "infblock.h" -#include "infcodes.h" -#include "infutil.h" -#include "inffast.h" - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - -inflate_codes_statef *zlib_inflate_codes_new( - uInt bl, - uInt bd, - inflate_huft *tl, - inflate_huft *td, /* need separate declaration for Borland C++ */ - z_streamp z -) -{ - inflate_codes_statef *c; - - c = &WS(z)->working_state; - { - c->mode = START; - c->lbits = (Byte)bl; - c->dbits = (Byte)bd; - c->ltree = tl; - c->dtree = td; - } - return c; -} - - -int zlib_inflate_codes( - inflate_blocks_statef *s, - z_streamp z, - int r -) -{ - uInt j; /* temporary storage */ - inflate_huft *t; /* temporary pointer */ - uInt e; /* extra bits or operation */ - uLong b; /* bit buffer */ - uInt k; /* bits in bit buffer */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - Byte *f; /* pointer to copy strings from */ - inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ - - /* copy input/output information to locals (UPDATE macro restores) */ - LOAD - - /* process input and output based on current state */ - while (1) switch (c->mode) - { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ - case START: /* x: set up for LEN */ -#ifndef SLOW - if (m >= 258 && n >= 10) - { - UPDATE - r = zlib_inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); - LOAD - if (r != Z_OK) - { - c->mode = r == Z_STREAM_END ? WASH : BADCODE; - break; - } - } -#endif /* !SLOW */ - c->sub.code.need = c->lbits; - c->sub.code.tree = c->ltree; - c->mode = LEN; - case LEN: /* i: get length/literal/eob next */ - j = c->sub.code.need; - NEEDBITS(j) - t = c->sub.code.tree + ((uInt)b & zlib_inflate_mask[j]); - DUMPBITS(t->bits) - e = (uInt)(t->exop); - if (e == 0) /* literal */ - { - c->sub.lit = t->base; - c->mode = LIT; - break; - } - if (e & 16) /* length */ - { - c->sub.copy.get = e & 15; - c->len = t->base; - c->mode = LENEXT; - break; - } - if ((e & 64) == 0) /* next table */ - { - c->sub.code.need = e; - c->sub.code.tree = t + t->base; - break; - } - if (e & 32) /* end of block */ - { - c->mode = WASH; - break; - } - c->mode = BADCODE; /* invalid code */ - z->msg = (char*)"invalid literal/length code"; - r = Z_DATA_ERROR; - LEAVE - case LENEXT: /* i: getting length extra (have base) */ - j = c->sub.copy.get; - NEEDBITS(j) - c->len += (uInt)b & zlib_inflate_mask[j]; - DUMPBITS(j) - c->sub.code.need = c->dbits; - c->sub.code.tree = c->dtree; - c->mode = DIST; - case DIST: /* i: get distance next */ - j = c->sub.code.need; - NEEDBITS(j) - t = c->sub.code.tree + ((uInt)b & zlib_inflate_mask[j]); - DUMPBITS(t->bits) - e = (uInt)(t->exop); - if (e & 16) /* distance */ - { - c->sub.copy.get = e & 15; - c->sub.copy.dist = t->base; - c->mode = DISTEXT; - break; - } - if ((e & 64) == 0) /* next table */ - { - c->sub.code.need = e; - c->sub.code.tree = t + t->base; - break; - } - c->mode = BADCODE; /* invalid code */ - z->msg = (char*)"invalid distance code"; - r = Z_DATA_ERROR; - LEAVE - case DISTEXT: /* i: getting distance extra */ - j = c->sub.copy.get; - NEEDBITS(j) - c->sub.copy.dist += (uInt)b & zlib_inflate_mask[j]; - DUMPBITS(j) - c->mode = COPY; - case COPY: /* o: copying bytes in window, waiting for space */ - f = q - c->sub.copy.dist; - while (f < s->window) /* modulo window size-"while" instead */ - f += s->end - s->window; /* of "if" handles invalid distances */ - while (c->len) - { - NEEDOUT - OUTBYTE(*f++) - if (f == s->end) - f = s->window; - c->len--; - } - c->mode = START; - break; - case LIT: /* o: got literal, waiting for output space */ - NEEDOUT - OUTBYTE(c->sub.lit) - c->mode = START; - break; - case WASH: /* o: got eob, possibly more output */ - if (k > 7) /* return unused byte, if any */ - { - k -= 8; - n++; - p--; /* can always return one */ - } - FLUSH - if (s->read != s->write) - LEAVE - c->mode = END; - case END: - r = Z_STREAM_END; - LEAVE - case BADCODE: /* x: got error */ - r = Z_DATA_ERROR; - LEAVE - default: - r = Z_STREAM_ERROR; - LEAVE - } -#ifdef NEED_DUMMY_RETURN - return Z_STREAM_ERROR; /* Some dumb compilers complain without this */ -#endif -} - - -void zlib_inflate_codes_free( - inflate_codes_statef *c, - z_streamp z -) -{ -} diff --git a/lib/zlib_inflate/infcodes.h b/lib/zlib_inflate/infcodes.h deleted file mode 100644 index 5cff417..0000000 --- a/lib/zlib_inflate/infcodes.h +++ /dev/null @@ -1,33 +0,0 @@ -/* infcodes.h -- header to use infcodes.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* WARNING: this file should *not* be used by applications. It is - part of the implementation of the compression library and is - subject to change. Applications should only use zlib.h. - */ - -#ifndef _INFCODES_H -#define _INFCODES_H - -#include "infblock.h" - -struct inflate_codes_state; -typedef struct inflate_codes_state inflate_codes_statef; - -extern inflate_codes_statef *zlib_inflate_codes_new ( - uInt, uInt, - inflate_huft *, inflate_huft *, - z_streamp ); - -extern int zlib_inflate_codes ( - inflate_blocks_statef *, - z_streamp , - int); - -extern void zlib_inflate_codes_free ( - inflate_codes_statef *, - z_streamp ); - -#endif /* _INFCODES_H */ diff --git a/lib/zlib_inflate/inffast.c b/lib/zlib_inflate/inffast.c index 0bd7623..d84560c 100644 --- a/lib/zlib_inflate/inffast.c +++ b/lib/zlib_inflate/inffast.c @@ -1,176 +1,312 @@ -/* inffast.c -- process literals and length/distance pairs fast - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ #include #include "inftrees.h" -#include "infblock.h" -#include "infcodes.h" -#include "infutil.h" +#include "inflate.h" #include "inffast.h" -struct inflate_codes_state; - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - -/* macros for bit input with no checking and for returning unused bytes */ -#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} - -/* Called with number of bytes left to write in window at least 258 - (the maximum string length) and number of input bytes available - at least ten. The ten bytes are six bytes for the longest length/ - distance pair plus four bytes for overloading the bit buffer. */ - -int zlib_inflate_fast( - uInt bl, - uInt bd, - inflate_huft *tl, - inflate_huft *td, /* need separate declaration for Borland C++ */ - inflate_blocks_statef *s, - z_streamp z -) +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + + - @start: inflate()'s starting value for strm->avail_out + */ +void inflate_fast(z_streamp strm, unsigned start) { - inflate_huft *t; /* temporary pointer */ - uInt e; /* extra bits or operation */ - uLong b; /* bit buffer */ - uInt k; /* bits in bit buffer */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - uInt ml; /* mask for literal/length tree */ - uInt md; /* mask for distance tree */ - uInt c; /* bytes to copy */ - uInt d; /* distance back to copy from */ - Byte *r; /* copy source pointer */ - - /* load input, output, bit values */ - LOAD - - /* initialize masks */ - ml = zlib_inflate_mask[bl]; - md = zlib_inflate_mask[bd]; - - /* do until not enough input or output space for fast loop */ - do { /* assume called with m >= 258 && n >= 10 */ - /* get literal/length code */ - GRABBITS(20) /* max bits for literal/length code */ - if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) - { - DUMPBITS(t->bits) - *q++ = (Byte)t->base; - m--; - continue; - } + struct inflate_state *state; + unsigned char *in; /* local strm->next_in */ + unsigned char *last; /* while in < last, enough input available */ + unsigned char *out; /* local strm->next_out */ + unsigned char *beg; /* inflate()'s initial strm->next_out */ + unsigned char *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const *lcode; /* local strm->lencode */ + code const *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ do { - DUMPBITS(t->bits) - if (e & 16) - { - /* get extra bits for length */ - e &= 15; - c = t->base + ((uInt)b & zlib_inflate_mask[e]); - DUMPBITS(e) - - /* decode distance base of block to copy */ - GRABBITS(15); /* max bits for distance code */ - e = (t = td + ((uInt)b & md))->exop; - do { - DUMPBITS(t->bits) - if (e & 16) - { - /* get extra bits to add to distance base */ - e &= 15; - GRABBITS(e) /* get extra bits (up to 13) */ - d = t->base + ((uInt)b & zlib_inflate_mask[e]); - DUMPBITS(e) - - /* do the copy */ - m -= c; - r = q - d; - if (r < s->window) /* wrap if needed */ - { - do { - r += s->end - s->window; /* force pointer in window */ - } while (r < s->window); /* covers invalid distances */ - e = s->end - r; - if (c > e) - { - c -= e; /* wrapped copy */ - do { - *q++ = *r++; - } while (--e); - r = s->window; - do { - *q++ = *r++; - } while (--c); - } - else /* normal copy */ - { - *q++ = *r++; c--; - *q++ = *r++; c--; - do { - *q++ = *r++; - } while (--c); - } + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } } - else /* normal copy */ - { - *q++ = *r++; c--; - *q++ = *r++; c--; - do { - *q++ = *r++; - } while (--c); + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + state->mode = TYPE; break; - } - else if ((e & 64) == 0) - { - t += t->base; - e = (t += ((uInt)b & zlib_inflate_mask[e]))->exop; - } - else - { - z->msg = (char*)"invalid distance code"; - UNGRAB - UPDATE - return Z_DATA_ERROR; - } - } while (1); - break; - } - if ((e & 64) == 0) - { - t += t->base; - if ((e = (t += ((uInt)b & zlib_inflate_mask[e]))->exop) == 0) - { - DUMPBITS(t->bits) - *q++ = (Byte)t->base; - m--; - break; } - } - else if (e & 32) - { - UNGRAB - UPDATE - return Z_STREAM_END; - } - else - { - z->msg = (char*)"invalid literal/length code"; - UNGRAB - UPDATE - return Z_DATA_ERROR; - } - } while (1); - } while (m >= 258 && n >= 10); - - /* not enough input or output--restore pointers and return */ - UNGRAB - UPDATE - return Z_OK; + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; } + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/lib/zlib_inflate/inffast.h b/lib/zlib_inflate/inffast.h index fc720f0..40315d9 100644 --- a/lib/zlib_inflate/inffast.h +++ b/lib/zlib_inflate/inffast.h @@ -1,6 +1,6 @@ /* inffast.h -- header to use inffast.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is @@ -8,10 +8,4 @@ subject to change. Applications should only use zlib.h. */ -extern int zlib_inflate_fast ( - uInt, - uInt, - inflate_huft *, - inflate_huft *, - inflate_blocks_statef *, - z_streamp ); +void inflate_fast (z_streamp strm, unsigned start); diff --git a/lib/zlib_inflate/inffixed.h b/lib/zlib_inflate/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/lib/zlib_inflate/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/lib/zlib_inflate/inflate.c b/lib/zlib_inflate/inflate.c index 31b9e90..7f922dc 100644 --- a/lib/zlib_inflate/inflate.c +++ b/lib/zlib_inflate/inflate.c @@ -1,89 +1,148 @@ -/* inflate.c -- zlib interface to inflate modules - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Based on zlib 1.2.3 but modified for the Linux Kernel by + * Richard Purdie + * + * Changes mainly for static instead of dynamic memory allocation + * */ #include -#include "infblock.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" #include "infutil.h" int zlib_inflate_workspacesize(void) { - return sizeof(struct inflate_workspace); + return sizeof(struct inflate_workspace); } +int zlib_inflateReset(z_streamp strm) +{ + struct inflate_state *state; + + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; -int zlib_inflateReset( - z_streamp z -) + /* Initialise Window */ + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + + return Z_OK; +} + +#if 0 +int zlib_inflatePrime(z_streamp strm, int bits, int value) { - if (z == NULL || z->state == NULL || z->workspace == NULL) - return Z_STREAM_ERROR; - z->total_in = z->total_out = 0; - z->msg = NULL; - z->state->mode = z->state->nowrap ? BLOCKS : METHOD; - zlib_inflate_blocks_reset(z->state->blocks, z, NULL); - return Z_OK; + struct inflate_state *state; + + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; } +#endif + +int zlib_inflateInit2(z_streamp strm, int windowBits) +{ + struct inflate_state *state; + + if (strm == NULL) return Z_STREAM_ERROR; + strm->msg = NULL; /* in case we return an error */ + + state = &WS(strm)->inflate_state; + strm->state = (struct internal_state *)state; + + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; + } + if (windowBits < 8 || windowBits > 15) { + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = &WS(strm)->working_window[0]; + return zlib_inflateReset(strm); +} -int zlib_inflateEnd( - z_streamp z -) +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. This returns fixed tables from inffixed.h. + */ +static void zlib_fixedtables(struct inflate_state *state) { - if (z == NULL || z->state == NULL || z->workspace == NULL) - return Z_STREAM_ERROR; - if (z->state->blocks != NULL) - zlib_inflate_blocks_free(z->state->blocks, z); - z->state = NULL; - return Z_OK; +# include "inffixed.h" + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; } -int zlib_inflateInit2_( - z_streamp z, - int w, - const char *version, - int stream_size -) +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. This is only called when a window is already in use, or when + output has been written during this inflate call, but the end of the deflate + stream has not been reached yet. It is also called to window dictionary data + when a dictionary is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +static void zlib_updatewindow(z_streamp strm, unsigned out) { - if (version == NULL || version[0] != ZLIB_VERSION[0] || - stream_size != sizeof(z_stream) || z->workspace == NULL) - return Z_VERSION_ERROR; - - /* initialize state */ - z->msg = NULL; - z->state = &WS(z)->internal_state; - z->state->blocks = NULL; - - /* handle undocumented nowrap option (no zlib header or check) */ - z->state->nowrap = 0; - if (w < 0) - { - w = - w; - z->state->nowrap = 1; - } - - /* set window size */ - if (w < 8 || w > 15) - { - zlib_inflateEnd(z); - return Z_STREAM_ERROR; - } - z->state->wbits = (uInt)w; - - /* create inflate_blocks state */ - if ((z->state->blocks = - zlib_inflate_blocks_new(z, z->state->nowrap ? NULL : zlib_adler32, (uInt)1 << w)) - == NULL) - { - zlib_inflateEnd(z); - return Z_MEM_ERROR; - } - - /* reset state */ - zlib_inflateReset(z); - return Z_OK; + struct inflate_state *state; + unsigned copy, dist; + + state = (struct inflate_state *)strm->state; + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + memcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + memcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + memcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } } @@ -91,157 +150,764 @@ int zlib_inflateInit2_( * At the end of a Deflate-compressed PPP packet, we expect to have seen * a `stored' block type value but not the (zero) length bytes. */ -static int zlib_inflate_packet_flush(inflate_blocks_statef *s) +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +static int zlib_inflateSyncPacket(z_streamp strm) { - if (s->mode != LENS) - return Z_DATA_ERROR; - s->mode = TYPE; + struct inflate_state *state; + + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + + if (state->mode == STORED && state->bits == 0) { + state->mode = TYPE; + return Z_OK; + } + return Z_DATA_ERROR; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#define UPDATE(check, buf, len) zlib_adler32(check, buf, len) + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int zlib_inflate(z_streamp strm, int flush) +{ + struct inflate_state *state; + unsigned char *next; /* next input */ + unsigned char *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == NULL || strm->state == NULL || strm->next_out == NULL || + (strm->next_in == NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state *)strm->state; + + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); + if ( + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + strm->adler = state->check = zlib_adler32(0L, NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = zlib_adler32(0L, NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + state->mode = STORED; + break; + case 1: /* fixed block */ + zlib_fixedtables(state); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + memcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const *)(state->next); + state->lenbits = 7; + ret = zlib_inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const *)(state->next); + state->lenbits = 9; + ret = zlib_inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const *)(state->next); + state->distbits = 6; + ret = zlib_inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + state->mode = LIT; + break; + } + if (this.op & 32) { + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + } + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call zlib_updatewindow() to create and/or update the window state. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + zlib_updatewindow(strm, out); + + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + + if (flush == Z_PACKET_FLUSH && ret == Z_OK && + (strm->avail_out != 0 || strm->avail_in == 0)) + return zlib_inflateSyncPacket(strm); + return ret; +} + +int zlib_inflateEnd(z_streamp strm) +{ + if (strm == NULL || strm->state == NULL) + return Z_STREAM_ERROR; return Z_OK; } +#if 0 +int zlib_inflateSetDictionary(z_streamp strm, const Byte *dictionary, + uInt dictLength) +{ + struct inflate_state *state; + unsigned long id; + + /* check state */ + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = zlib_adler32(0L, NULL, 0); + id = zlib_adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + zlib_updatewindow(strm, strm->avail_out); -int zlib_inflateInit_( - z_streamp z, - const char *version, - int stream_size -) + if (dictLength > state->wsize) { + memcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + memcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + return Z_OK; +} +#endif + +#if 0 +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, zlib_syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +static unsigned zlib_syncsearch(unsigned *have, unsigned char *buf, + unsigned len) { - return zlib_inflateInit2_(z, DEF_WBITS, version, stream_size); + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; } +#endif -#undef NEEDBYTE -#undef NEXTBYTE -#define NEEDBYTE {if(z->avail_in==0)goto empty;r=trv;} -#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) +#if 0 +int zlib_inflateSync(z_streamp strm) +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state *state; + + /* check parameters */ + if (strm == NULL || strm->state == NULL) return Z_STREAM_ERROR; + state = (struct inflate_state *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + zlib_syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = zlib_syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + zlib_inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} +#endif -int zlib_inflate( - z_streamp z, - int f -) +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output but this should always be the case. The state must + * be waiting on the start of a block (i.e. mode == TYPE or HEAD). On exit, + * the output will also be caught up, and the checksum will have been updated + * if need be. + */ +int zlib_inflateIncomp(z_stream *z) { - int r, trv; - uInt b; - - if (z == NULL || z->state == NULL || z->next_in == NULL) - return Z_STREAM_ERROR; - trv = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; - r = Z_BUF_ERROR; - while (1) switch (z->state->mode) - { - case METHOD: - NEEDBYTE - if (((z->state->sub.method = NEXTBYTE) & 0xf) != Z_DEFLATED) - { - z->state->mode = I_BAD; - z->msg = (char*)"unknown compression method"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - if ((z->state->sub.method >> 4) + 8 > z->state->wbits) - { - z->state->mode = I_BAD; - z->msg = (char*)"invalid window size"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - z->state->mode = FLAG; - case FLAG: - NEEDBYTE - b = NEXTBYTE; - if (((z->state->sub.method << 8) + b) % 31) - { - z->state->mode = I_BAD; - z->msg = (char*)"incorrect header check"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - if (!(b & PRESET_DICT)) - { - z->state->mode = BLOCKS; - break; - } - z->state->mode = DICT4; - case DICT4: - NEEDBYTE - z->state->sub.check.need = (uLong)NEXTBYTE << 24; - z->state->mode = DICT3; - case DICT3: - NEEDBYTE - z->state->sub.check.need += (uLong)NEXTBYTE << 16; - z->state->mode = DICT2; - case DICT2: - NEEDBYTE - z->state->sub.check.need += (uLong)NEXTBYTE << 8; - z->state->mode = DICT1; - case DICT1: - NEEDBYTE - z->state->sub.check.need += (uLong)NEXTBYTE; - z->adler = z->state->sub.check.need; - z->state->mode = DICT0; - return Z_NEED_DICT; - case DICT0: - z->state->mode = I_BAD; - z->msg = (char*)"need dictionary"; - z->state->sub.marker = 0; /* can try inflateSync */ - return Z_STREAM_ERROR; - case BLOCKS: - r = zlib_inflate_blocks(z->state->blocks, z, r); - if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) - r = zlib_inflate_packet_flush(z->state->blocks); - if (r == Z_DATA_ERROR) - { - z->state->mode = I_BAD; - z->state->sub.marker = 0; /* can try inflateSync */ - break; - } - if (r == Z_OK) - r = trv; - if (r != Z_STREAM_END) - return r; - r = trv; - zlib_inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); - if (z->state->nowrap) - { - z->state->mode = I_DONE; - break; - } - z->state->mode = CHECK4; - case CHECK4: - NEEDBYTE - z->state->sub.check.need = (uLong)NEXTBYTE << 24; - z->state->mode = CHECK3; - case CHECK3: - NEEDBYTE - z->state->sub.check.need += (uLong)NEXTBYTE << 16; - z->state->mode = CHECK2; - case CHECK2: - NEEDBYTE - z->state->sub.check.need += (uLong)NEXTBYTE << 8; - z->state->mode = CHECK1; - case CHECK1: - NEEDBYTE - z->state->sub.check.need += (uLong)NEXTBYTE; - - if (z->state->sub.check.was != z->state->sub.check.need) - { - z->state->mode = I_BAD; - z->msg = (char*)"incorrect data check"; - z->state->sub.marker = 5; /* can't try inflateSync */ - break; - } - z->state->mode = I_DONE; - case I_DONE: - return Z_STREAM_END; - case I_BAD: - return Z_DATA_ERROR; - default: - return Z_STREAM_ERROR; - } - empty: - if (f != Z_PACKET_FLUSH) - return r; - z->state->mode = I_BAD; - z->msg = (char *)"need more for packet flush"; - z->state->sub.marker = 0; /* can try inflateSync */ - return Z_DATA_ERROR; + struct inflate_state *state = (struct inflate_state *)z->state; + Byte *saved_no = z->next_out; + uInt saved_ao = z->avail_out; + + if (state->mode != TYPE && state->mode != HEAD) + return Z_DATA_ERROR; + + /* Setup some variables to allow misuse of updateWindow */ + z->avail_out = 0; + z->next_out = z->next_in + z->avail_in; + + zlib_updatewindow(z, z->avail_in); + + /* Restore saved variables */ + z->avail_out = saved_ao; + z->next_out = saved_no; + + z->adler = state->check = + UPDATE(state->check, z->next_in, z->avail_in); + + z->total_out += z->avail_in; + z->total_in += z->avail_in; + z->next_in += z->avail_in; + state->total += z->avail_in; + z->avail_in = 0; + + return Z_OK; } diff --git a/lib/zlib_inflate/inflate.h b/lib/zlib_inflate/inflate.h new file mode 100644 index 0000000..df8a6c9 --- /dev/null +++ b/lib/zlib_inflate/inflate.h @@ -0,0 +1,107 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + /* gz_headerp head; */ /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const *lencode; /* starting table for length/literal codes */ + code const *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/lib/zlib_inflate/inflate_syms.c b/lib/zlib_inflate/inflate_syms.c index ef49738..2061d4f 100644 --- a/lib/zlib_inflate/inflate_syms.c +++ b/lib/zlib_inflate/inflate_syms.c @@ -12,8 +12,7 @@ #include EXPORT_SYMBOL(zlib_inflate_workspacesize); EXPORT_SYMBOL(zlib_inflate); -EXPORT_SYMBOL(zlib_inflateInit_); -EXPORT_SYMBOL(zlib_inflateInit2_); +EXPORT_SYMBOL(zlib_inflateInit2); EXPORT_SYMBOL(zlib_inflateEnd); EXPORT_SYMBOL(zlib_inflateReset); EXPORT_SYMBOL(zlib_inflateIncomp); diff --git a/lib/zlib_inflate/inflate_sync.c b/lib/zlib_inflate/inflate_sync.c deleted file mode 100644 index 61411ff..0000000 --- a/lib/zlib_inflate/inflate_sync.c +++ /dev/null @@ -1,152 +0,0 @@ -/* inflate.c -- zlib interface to inflate modules - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include -#include "infblock.h" -#include "infutil.h" - -#if 0 -int zlib_inflateSync( - z_streamp z -) -{ - uInt n; /* number of bytes to look at */ - Byte *p; /* pointer to bytes */ - uInt m; /* number of marker bytes found in a row */ - uLong r, w; /* temporaries to save total_in and total_out */ - - /* set up */ - if (z == NULL || z->state == NULL) - return Z_STREAM_ERROR; - if (z->state->mode != I_BAD) - { - z->state->mode = I_BAD; - z->state->sub.marker = 0; - } - if ((n = z->avail_in) == 0) - return Z_BUF_ERROR; - p = z->next_in; - m = z->state->sub.marker; - - /* search */ - while (n && m < 4) - { - static const Byte mark[4] = {0, 0, 0xff, 0xff}; - if (*p == mark[m]) - m++; - else if (*p) - m = 0; - else - m = 4 - m; - p++, n--; - } - - /* restore */ - z->total_in += p - z->next_in; - z->next_in = p; - z->avail_in = n; - z->state->sub.marker = m; - - /* return no joy or set up to restart on a new block */ - if (m != 4) - return Z_DATA_ERROR; - r = z->total_in; w = z->total_out; - zlib_inflateReset(z); - z->total_in = r; z->total_out = w; - z->state->mode = BLOCKS; - return Z_OK; -} -#endif /* 0 */ - - -/* Returns true if inflate is currently at the end of a block generated - * by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP - * implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH - * but removes the length bytes of the resulting empty stored block. When - * decompressing, PPP checks that at the end of input packet, inflate is - * waiting for these length bytes. - */ -#if 0 -int zlib_inflateSyncPoint( - z_streamp z -) -{ - if (z == NULL || z->state == NULL || z->state->blocks == NULL) - return Z_STREAM_ERROR; - return zlib_inflate_blocks_sync_point(z->state->blocks); -} -#endif /* 0 */ - -/* - * This subroutine adds the data at next_in/avail_in to the output history - * without performing any output. The output buffer must be "caught up"; - * i.e. no pending output (hence s->read equals s->write), and the state must - * be BLOCKS (i.e. we should be willing to see the start of a series of - * BLOCKS). On exit, the output will also be caught up, and the checksum - * will have been updated if need be. - */ -static int zlib_inflate_addhistory(inflate_blocks_statef *s, - z_stream *z) -{ - uLong b; /* bit buffer */ /* NOT USED HERE */ - uInt k; /* bits in bit buffer */ /* NOT USED HERE */ - uInt t; /* temporary storage */ - Byte *p; /* input data pointer */ - uInt n; /* bytes available there */ - Byte *q; /* output window write pointer */ - uInt m; /* bytes to end of window or read pointer */ - - if (s->read != s->write) - return Z_STREAM_ERROR; - if (s->mode != TYPE) - return Z_DATA_ERROR; - - /* we're ready to rock */ - LOAD - /* while there is input ready, copy to output buffer, moving - * pointers as needed. - */ - while (n) { - t = n; /* how many to do */ - /* is there room until end of buffer? */ - if (t > m) t = m; - /* update check information */ - if (s->checkfn != NULL) - s->check = (*s->checkfn)(s->check, q, t); - memcpy(q, p, t); - q += t; - p += t; - n -= t; - z->total_out += t; - s->read = q; /* drag read pointer forward */ -/* WWRAP */ /* expand WWRAP macro by hand to handle s->read */ - if (q == s->end) { - s->read = q = s->window; - m = WAVAIL; - } - } - UPDATE - return Z_OK; -} - - -/* - * This subroutine adds the data at next_in/avail_in to the output history - * without performing any output. The output buffer must be "caught up"; - * i.e. no pending output (hence s->read equals s->write), and the state must - * be BLOCKS (i.e. we should be willing to see the start of a series of - * BLOCKS). On exit, the output will also be caught up, and the checksum - * will have been updated if need be. - */ - -int zlib_inflateIncomp( - z_stream *z - -) -{ - if (z->state->mode != BLOCKS) - return Z_DATA_ERROR; - return zlib_inflate_addhistory(z->state->blocks, z); -} diff --git a/lib/zlib_inflate/inftrees.c b/lib/zlib_inflate/inftrees.c index 874950e..3fe6ce5 100644 --- a/lib/zlib_inflate/inftrees.c +++ b/lib/zlib_inflate/inftrees.c @@ -1,412 +1,315 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ #include #include "inftrees.h" -#include "infutil.h" -static const char inflate_copyright[] __attribute_used__ = - " inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +#define MAXBITS 15 + /* - If you use the zlib library in a product, an acknowledgment is welcome - in the documentation of your product. If for some reason you cannot - include such an acknowledgment, I would appreciate that you keep this - copyright string in the executable of your product. + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. */ -struct internal_state; - -/* simplify the use of the inflate_huft type with some defines */ -#define exop word.what.Exop -#define bits word.what.Bits - - -static int huft_build ( - uInt *, /* code lengths in bits */ - uInt, /* number of codes */ - uInt, /* number of "simple" codes */ - const uInt *, /* list of base values for non-simple codes */ - const uInt *, /* list of extra bits for non-simple codes */ - inflate_huft **, /* result: starting table */ - uInt *, /* maximum lookup bits (returns actual) */ - inflate_huft *, /* space for trees */ - uInt *, /* hufts used in space */ - uInt * ); /* space for values */ - -/* Tables for deflate from PKZIP's appnote.txt. */ -static const uInt cplens[31] = { /* Copy lengths for literal codes 257..285 */ +int zlib_inflate_table(codetype type, unsigned short *lens, unsigned codes, + code **table, unsigned *bits, unsigned short *work) +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code *next; /* next available space in table */ + const unsigned short *base; /* base value table to use */ + const unsigned short *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; - /* see note #13 above about 258 */ -static const uInt cplext[31] = { /* Extra bits for literal codes 257..285 */ - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, - 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; /* 112==invalid */ -static const uInt cpdist[30] = { /* Copy offsets for distance codes 0..29 */ + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, - 8193, 12289, 16385, 24577}; -static const uInt cpdext[30] = { /* Extra bits for distance codes */ - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, - 12, 12, 13, 13}; - -/* - Huffman code decoding is performed using a multi-level table lookup. - The fastest way to decode is to simply build a lookup table whose - size is determined by the longest code. However, the time it takes - to build this table can also be a factor if the data being decoded - is not very long. The most common codes are necessarily the - shortest codes, so those codes dominate the decoding time, and hence - the speed. The idea is you can have a shorter table that decodes the - shorter, more probable codes, and then point to subsidiary tables for - the longer codes. The time it costs to decode the longer codes is - then traded against the time it takes to make longer tables. - - This results of this trade are in the variables lbits and dbits - below. lbits is the number of bits the first level table for literal/ - length codes can decode in one step, and dbits is the same thing for - the distance codes. Subsequent tables are also less than or equal to - those sizes. These values may be adjusted either when all of the - codes are shorter than that, in which case the longest code length in - bits is used, or when the shortest code is *longer* than the requested - table size, in which case the length of the shortest code in bits is - used. - - There are two different values for the two tables, since they code a - different number of possibilities each. The literal/length table - codes 286 possible values, or in a flat code, a little over eight - bits. The distance table codes 30 possible values, or a little less - than five bits, flat. The optimum values for speed end up being - about one bit more than those, so lbits is 8+1 and dbits is 5+1. - The optimum values may differ though from machine to machine, and - possibly even between compilers. Your mileage may vary. - */ - - -/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ -#define BMAX 15 /* maximum bit length of any code */ - -static int huft_build( - uInt *b, /* code lengths in bits (all assumed <= BMAX) */ - uInt n, /* number of codes (assumed <= 288) */ - uInt s, /* number of simple-valued codes (0..s-1) */ - const uInt *d, /* list of base values for non-simple codes */ - const uInt *e, /* list of extra bits for non-simple codes */ - inflate_huft **t, /* result: starting table */ - uInt *m, /* maximum lookup bits, returns actual */ - inflate_huft *hp, /* space for trees */ - uInt *hn, /* hufts used in space */ - uInt *v /* working area: values in order of bit length */ -) -/* Given a list of code lengths and a maximum table size, make a set of - tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR - if the given code set is incomplete (the tables are still built in this - case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of - lengths), or Z_MEM_ERROR if not enough memory. */ -{ + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } - uInt a; /* counter for codes of length k */ - uInt c[BMAX+1]; /* bit length count table */ - uInt f; /* i repeats in table every f entries */ - int g; /* maximum code length */ - int h; /* table level */ - register uInt i; /* counter, current code */ - register uInt j; /* counter */ - register int k; /* number of bits in current code */ - int l; /* bits per table (returned in m) */ - uInt mask; /* (1 << w) - 1, to avoid cc -O bug on HP */ - register uInt *p; /* pointer into c[], b[], or v[] */ - inflate_huft *q; /* points to current table */ - struct inflate_huft_s r; /* table entry for structure assignment */ - inflate_huft *u[BMAX]; /* table stack */ - register int w; /* bits before this table == (l * h) */ - uInt x[BMAX+1]; /* bit offsets, then code stack */ - uInt *xp; /* pointer into x */ - int y; /* number of dummy codes added */ - uInt z; /* number of entries in current table */ - - - /* Generate counts for each bit length */ - p = c; -#define C0 *p++ = 0; -#define C2 C0 C0 C0 C0 -#define C4 C2 C2 C2 C2 - C4 /* clear c[]--assume BMAX+1 is 16 */ - p = b; i = n; - do { - c[*p++]++; /* assume all entries <= BMAX */ - } while (--i); - if (c[0] == n) /* null input--all zero length codes */ - { - *t = NULL; - *m = 0; - return Z_OK; - } - - - /* Find minimum and maximum length, bound *m by those */ - l = *m; - for (j = 1; j <= BMAX; j++) - if (c[j]) - break; - k = j; /* minimum code length */ - if ((uInt)l < j) - l = j; - for (i = BMAX; i; i--) - if (c[i]) - break; - g = i; /* maximum code length */ - if ((uInt)l > i) - l = i; - *m = l; - - - /* Adjust last length count to fill out codes, if needed */ - for (y = 1 << j; j < i; j++, y <<= 1) - if ((y -= c[j]) < 0) - return Z_DATA_ERROR; - if ((y -= c[i]) < 0) - return Z_DATA_ERROR; - c[i] += y; - - - /* Generate starting offsets into the value table for each length */ - x[1] = j = 0; - p = c + 1; xp = x + 2; - while (--i) { /* note that i == g from above */ - *xp++ = (j += *p++); - } - - - /* Make a table of values in order of bit lengths */ - p = b; i = 0; - do { - if ((j = *p++) != 0) - v[x[j]++] = i; - } while (++i < n); - n = x[g]; /* set n to length of v */ - - - /* Generate the Huffman codes and for each, make the table entries */ - x[0] = i = 0; /* first Huffman code is zero */ - p = v; /* grab values in bit order */ - h = -1; /* no tables yet--level -1 */ - w = -l; /* bits decoded == (l * h) */ - u[0] = NULL; /* just to keep compilers happy */ - q = NULL; /* ditto */ - z = 0; /* ditto */ - - /* go through the bit lengths (k already is bits in shortest code) */ - for (; k <= g; k++) - { - a = c[k]; - while (a--) - { - /* here i is the Huffman code of length k bits for value *p */ - /* make tables up to required level */ - while (k > w + l) - { - h++; - w += l; /* previous table always l bits */ - - /* compute minimum size table less than or equal to l bits */ - z = g - w; - z = z > (uInt)l ? l : z; /* table size upper limit */ - if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ - { /* too few codes for k-w bit table */ - f -= a + 1; /* deduct codes from patterns left */ - xp = c + k; - if (j < z) - while (++j < z) /* try smaller tables up to z bits */ - { - if ((f <<= 1) <= *++xp) - break; /* enough codes to use up j bits */ - f -= *xp; /* else deduct codes from patterns */ - } + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; } - z = 1 << j; /* table entries for j-bit table */ - - /* allocate new table */ - if (*hn + z > MANY) /* (note: doesn't matter for fixed) */ - return Z_DATA_ERROR; /* overflow of MANY */ - u[h] = q = hp + *hn; - *hn += z; - - /* connect to last table, if there is one */ - if (h) - { - x[h] = i; /* save pattern for backing up */ - r.bits = (Byte)l; /* bits to dump before this table */ - r.exop = (Byte)j; /* bits in this table */ - j = i >> (w - l); - r.base = (uInt)(q - u[h-1] - j); /* offset to this table */ - u[h-1][j] = r; /* connect to last table */ + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; } - else - *t = q; /* first table is returned result */ - } - - /* set up table entry in r */ - r.bits = (Byte)(k - w); - if (p >= v + n) - r.exop = 128 + 64; /* out of values--invalid code */ - else if (*p < s) - { - r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ - r.base = *p++; /* simple code is just the value */ - } - else - { - r.exop = (Byte)(e[*p - s] + 16 + 64);/* non-simple--look up in lists */ - r.base = d[*p++ - s]; - } - - /* fill code-like entries with r */ - f = 1 << (k - w); - for (j = i >> w; j < z; j += f) - q[j] = r; - - /* backwards increment the k-bit code i */ - for (j = 1 << (k - 1); i & j; j >>= 1) - i ^= j; - i ^= j; - - /* backup over finished tables */ - mask = (1 << w) - 1; /* needed on HP, cc -O bug */ - while ((i & mask) != x[h]) - { - h--; /* don't need to update q */ - w -= l; - mask = (1 << w) - 1; - } - } - } + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; - /* Return Z_BUF_ERROR if we were given an incomplete table */ - return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; -} + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } -int zlib_inflate_trees_bits( - uInt *c, /* 19 code lengths */ - uInt *bb, /* bits tree desired/actual depth */ - inflate_huft **tb, /* bits tree result */ - inflate_huft *hp, /* space for trees */ - z_streamp z /* for messages */ -) -{ - int r; - uInt hn = 0; /* hufts used in space */ - uInt *v; /* work area for huft_build */ - - v = WS(z)->tree_work_area_1; - r = huft_build(c, 19, 19, NULL, NULL, tb, bb, hp, &hn, v); - if (r == Z_DATA_ERROR) - z->msg = (char*)"oversubscribed dynamic bit lengths tree"; - else if (r == Z_BUF_ERROR || *bb == 0) - { - z->msg = (char*)"incomplete dynamic bit lengths tree"; - r = Z_DATA_ERROR; - } - return r; -} + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; -int zlib_inflate_trees_dynamic( - uInt nl, /* number of literal/length codes */ - uInt nd, /* number of distance codes */ - uInt *c, /* that many (total) code lengths */ - uInt *bl, /* literal desired/actual bit depth */ - uInt *bd, /* distance desired/actual bit depth */ - inflate_huft **tl, /* literal/length tree result */ - inflate_huft **td, /* distance tree result */ - inflate_huft *hp, /* space for trees */ - z_streamp z /* for messages */ -) -{ - int r; - uInt hn = 0; /* hufts used in space */ - uInt *v; /* work area for huft_build */ - - /* allocate work area */ - v = WS(z)->tree_work_area_2; - - /* build literal/length tree */ - r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); - if (r != Z_OK || *bl == 0) - { - if (r == Z_DATA_ERROR) - z->msg = (char*)"oversubscribed literal/length tree"; - else if (r != Z_MEM_ERROR) - { - z->msg = (char*)"incomplete literal/length tree"; - r = Z_DATA_ERROR; - } - return r; - } - - /* build distance tree */ - r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); - if (r != Z_OK || (*bd == 0 && nl > 257)) - { - if (r == Z_DATA_ERROR) - z->msg = (char*)"oversubscribed distance tree"; - else if (r == Z_BUF_ERROR) { -#ifdef PKZIP_BUG_WORKAROUND - r = Z_OK; - } -#else - z->msg = (char*)"incomplete distance tree"; - r = Z_DATA_ERROR; - } - else if (r != Z_MEM_ERROR) - { - z->msg = (char*)"empty distance tree with lengths"; - r = Z_DATA_ERROR; + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } } - return r; -#endif - } - /* done */ - return Z_OK; -} + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + /* put invalid code marker in table */ + next[huff >> drop] = this; -int zlib_inflate_trees_fixed( - uInt *bl, /* literal desired/actual bit depth */ - uInt *bd, /* distance desired/actual bit depth */ - inflate_huft **tl, /* literal/length tree result */ - inflate_huft **td, /* distance tree result */ - inflate_huft *hp, /* space for trees */ - z_streamp z /* for memory allocation */ -) -{ - int i; /* temporary variable */ - unsigned l[288]; /* length list for huft_build */ - uInt *v; /* work area for huft_build */ - - /* set up literal table */ - for (i = 0; i < 144; i++) - l[i] = 8; - for (; i < 256; i++) - l[i] = 9; - for (; i < 280; i++) - l[i] = 7; - for (; i < 288; i++) /* make a complete, but wrong code set */ - l[i] = 8; - *bl = 9; - v = WS(z)->tree_work_area_1; - if ((i = huft_build(l, 288, 257, cplens, cplext, tl, bl, hp, &i, v)) != 0) - return i; - - /* set up distance table */ - for (i = 0; i < 30; i++) /* make an incomplete code set */ - l[i] = 5; - *bd = 5; - if ((i = huft_build(l, 30, 0, cpdist, cpdext, td, bd, hp, &i, v)) > 1) - return i; - - return Z_OK; + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; } diff --git a/lib/zlib_inflate/inftrees.h b/lib/zlib_inflate/inftrees.h index e37705a..5f5219b 100644 --- a/lib/zlib_inflate/inftrees.h +++ b/lib/zlib_inflate/inftrees.h @@ -1,6 +1,6 @@ /* inftrees.h -- header to use inftrees.c - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h */ /* WARNING: this file should *not* be used by applications. It is @@ -8,57 +8,48 @@ subject to change. Applications should only use zlib.h. */ -/* Huffman code lookup table entry--this entry is four bytes for machines - that have 16-bit pointers (e.g. PC's in the small or medium model). */ - -#ifndef _INFTREES_H -#define _INFTREES_H - -typedef struct inflate_huft_s inflate_huft; - -struct inflate_huft_s { - union { - struct { - Byte Exop; /* number of extra bits or operation */ - Byte Bits; /* number of bits in this code or subcode */ - } what; - uInt pad; /* pad structure to a power of 2 (4 bytes for */ - } word; /* 16-bit, 8 bytes for 32-bit int's) */ - uInt base; /* literal, length base, distance base, - or table offset */ -}; +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ /* Maximum size of dynamic tree. The maximum found in a long but non- - exhaustive search was 1004 huft structures (850 for length/literals - and 154 for distances, the latter actually the result of an - exhaustive search). The actual maximum is not known, but the - value below is more than safe. */ -#define MANY 1440 - -extern int zlib_inflate_trees_bits ( - uInt *, /* 19 code lengths */ - uInt *, /* bits tree desired/actual depth */ - inflate_huft **, /* bits tree result */ - inflate_huft *, /* space for trees */ - z_streamp); /* for messages */ - -extern int zlib_inflate_trees_dynamic ( - uInt, /* number of literal/length codes */ - uInt, /* number of distance codes */ - uInt *, /* that many (total) code lengths */ - uInt *, /* literal desired/actual bit depth */ - uInt *, /* distance desired/actual bit depth */ - inflate_huft **, /* literal/length tree result */ - inflate_huft **, /* distance tree result */ - inflate_huft *, /* space for trees */ - z_streamp); /* for messages */ - -extern int zlib_inflate_trees_fixed ( - uInt *, /* literal desired/actual bit depth */ - uInt *, /* distance desired/actual bit depth */ - inflate_huft **, /* literal/length tree result */ - inflate_huft **, /* distance tree result */ - inflate_huft *, /* space for trees */ - z_streamp); /* for memory allocation */ - -#endif /* _INFTREES_H */ + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int zlib_inflate_table (codetype type, unsigned short *lens, + unsigned codes, code **table, + unsigned *bits, unsigned short *work); diff --git a/lib/zlib_inflate/infutil.c b/lib/zlib_inflate/infutil.c deleted file mode 100644 index 00202b3..0000000 --- a/lib/zlib_inflate/infutil.c +++ /dev/null @@ -1,88 +0,0 @@ -/* inflate_util.c -- data and routines common to blocks and codes - * Copyright (C) 1995-1998 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -#include -#include "infblock.h" -#include "inftrees.h" -#include "infcodes.h" -#include "infutil.h" - -struct inflate_codes_state; - -/* And'ing with mask[n] masks the lower n bits */ -uInt zlib_inflate_mask[17] = { - 0x0000, - 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, - 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff -}; - - -/* copy as much as possible from the sliding window to the output area */ -int zlib_inflate_flush( - inflate_blocks_statef *s, - z_streamp z, - int r -) -{ - uInt n; - Byte *p; - Byte *q; - - /* local copies of source and destination pointers */ - p = z->next_out; - q = s->read; - - /* compute number of bytes to copy as far as end of window */ - n = (uInt)((q <= s->write ? s->write : s->end) - q); - if (n > z->avail_out) n = z->avail_out; - if (n && r == Z_BUF_ERROR) r = Z_OK; - - /* update counters */ - z->avail_out -= n; - z->total_out += n; - - /* update check information */ - if (s->checkfn != NULL) - z->adler = s->check = (*s->checkfn)(s->check, q, n); - - /* copy as far as end of window */ - memcpy(p, q, n); - p += n; - q += n; - - /* see if more to copy at beginning of window */ - if (q == s->end) - { - /* wrap pointers */ - q = s->window; - if (s->write == s->end) - s->write = s->window; - - /* compute bytes to copy */ - n = (uInt)(s->write - q); - if (n > z->avail_out) n = z->avail_out; - if (n && r == Z_BUF_ERROR) r = Z_OK; - - /* update counters */ - z->avail_out -= n; - z->total_out += n; - - /* update check information */ - if (s->checkfn != NULL) - z->adler = s->check = (*s->checkfn)(s->check, q, n); - - /* copy */ - memcpy(p, q, n); - p += n; - q += n; - } - - /* update pointers */ - z->next_out = p; - s->read = q; - - /* done */ - return r; -} diff --git a/lib/zlib_inflate/infutil.h b/lib/zlib_inflate/infutil.h index a15875f..eb1a900 100644 --- a/lib/zlib_inflate/infutil.h +++ b/lib/zlib_inflate/infutil.h @@ -11,184 +11,12 @@ #ifndef _INFUTIL_H #define _INFUTIL_H -#include -#include "inftrees.h" -#include "infcodes.h" - -typedef enum { - TYPE, /* get type bits (3, including end bit) */ - LENS, /* get lengths for stored */ - STORED, /* processing stored block */ - TABLE, /* get table lengths */ - BTREE, /* get bit lengths tree for a dynamic block */ - DTREE, /* get length, distance trees for a dynamic block */ - CODES, /* processing fixed or dynamic block */ - DRY, /* output remaining window bytes */ - B_DONE, /* finished last block, done */ - B_BAD} /* got a data error--stuck here */ -inflate_block_mode; - -/* inflate blocks semi-private state */ -struct inflate_blocks_state { - - /* mode */ - inflate_block_mode mode; /* current inflate_block mode */ - - /* mode dependent information */ - union { - uInt left; /* if STORED, bytes left to copy */ - struct { - uInt table; /* table lengths (14 bits) */ - uInt index; /* index into blens (or border) */ - uInt *blens; /* bit lengths of codes */ - uInt bb; /* bit length tree depth */ - inflate_huft *tb; /* bit length decoding tree */ - } trees; /* if DTREE, decoding info for trees */ - struct { - inflate_codes_statef - *codes; - } decode; /* if CODES, current state */ - } sub; /* submode */ - uInt last; /* true if this block is the last block */ - - /* mode independent information */ - uInt bitk; /* bits in bit buffer */ - uLong bitb; /* bit buffer */ - inflate_huft *hufts; /* single malloc for tree space */ - Byte *window; /* sliding window */ - Byte *end; /* one byte after sliding window */ - Byte *read; /* window read pointer */ - Byte *write; /* window write pointer */ - check_func checkfn; /* check function */ - uLong check; /* check on output */ - -}; - - -/* defines for inflate input/output */ -/* update pointers and return */ -#define UPDBITS {s->bitb=b;s->bitk=k;} -#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} -#define UPDOUT {s->write=q;} -#define UPDATE {UPDBITS UPDIN UPDOUT} -#define LEAVE {UPDATE return zlib_inflate_flush(s,z,r);} -/* get bytes and bits */ -#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} -#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} -#define NEXTBYTE (n--,*p++) -#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} -/* output bytes */ -#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) -#define LOADOUT {q=s->write;m=(uInt)WAVAIL;} -#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} -#define FLUSH {UPDOUT r=zlib_inflate_flush(s,z,r); LOADOUT} -#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} -#define OUTBYTE(a) {*q++=(Byte)(a);m--;} -/* load local pointers */ -#define LOAD {LOADIN LOADOUT} - -/* masks for lower bits (size given to avoid silly warnings with Visual C++) */ -extern uInt zlib_inflate_mask[17]; - -/* copy as much as possible from the sliding window to the output area */ -extern int zlib_inflate_flush ( - inflate_blocks_statef *, - z_streamp , - int); - -/* inflate private state */ -typedef enum { - METHOD, /* waiting for method byte */ - FLAG, /* waiting for flag byte */ - DICT4, /* four dictionary check bytes to go */ - DICT3, /* three dictionary check bytes to go */ - DICT2, /* two dictionary check bytes to go */ - DICT1, /* one dictionary check byte to go */ - DICT0, /* waiting for inflateSetDictionary */ - BLOCKS, /* decompressing blocks */ - CHECK4, /* four check bytes to go */ - CHECK3, /* three check bytes to go */ - CHECK2, /* two check bytes to go */ - CHECK1, /* one check byte to go */ - I_DONE, /* finished check, done */ - I_BAD} /* got an error--stay here */ -inflate_mode; - -struct internal_state { - - /* mode */ - inflate_mode mode; /* current inflate mode */ - - /* mode dependent information */ - union { - uInt method; /* if FLAGS, method byte */ - struct { - uLong was; /* computed check value */ - uLong need; /* stream check value */ - } check; /* if CHECK, check values to compare */ - uInt marker; /* if BAD, inflateSync's marker bytes count */ - } sub; /* submode */ - - /* mode independent information */ - int nowrap; /* flag for no wrapper */ - uInt wbits; /* log2(window size) (8..15, defaults to 15) */ - inflate_blocks_statef - *blocks; /* current inflate_blocks state */ - -}; - -/* inflate codes private state */ -typedef enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ - START, /* x: set up for LEN */ - LEN, /* i: get length/literal/eob next */ - LENEXT, /* i: getting length extra (have base) */ - DIST, /* i: get distance next */ - DISTEXT, /* i: getting distance extra */ - COPY, /* o: copying bytes in window, waiting for space */ - LIT, /* o: got literal, waiting for output space */ - WASH, /* o: got eob, possibly still output waiting */ - END, /* x: got eob and all data flushed */ - BADCODE} /* x: got error */ -inflate_codes_mode; - -struct inflate_codes_state { - - /* mode */ - inflate_codes_mode mode; /* current inflate_codes mode */ - - /* mode dependent information */ - uInt len; - union { - struct { - inflate_huft *tree; /* pointer into tree */ - uInt need; /* bits needed */ - } code; /* if LEN or DIST, where in tree */ - uInt lit; /* if LIT, literal */ - struct { - uInt get; /* bits to get for extra */ - uInt dist; /* distance back to copy from */ - } copy; /* if EXT or COPY, where and how much */ - } sub; /* submode */ - - /* mode independent information */ - Byte lbits; /* ltree bits decoded per branch */ - Byte dbits; /* dtree bits decoder per branch */ - inflate_huft *ltree; /* literal/length/eob tree */ - inflate_huft *dtree; /* distance tree */ - -}; +#include /* memory allocation for inflation */ struct inflate_workspace { - inflate_codes_statef working_state; - struct inflate_blocks_state working_blocks_state; - struct internal_state internal_state; - unsigned int tree_work_area_1[19]; - unsigned int tree_work_area_2[288]; - unsigned working_blens[258 + 0x1f + 0x1f]; - inflate_huft working_hufts[MANY]; + struct inflate_state inflate_state; unsigned char working_window[1 << MAX_WBITS]; }; diff --git a/mm/Kconfig b/mm/Kconfig index 332f5c2..8f5b456 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -115,7 +115,8 @@ config SPARSEMEM_EXTREME # eventually, we can have this option just 'select SPARSEMEM' config MEMORY_HOTPLUG bool "Allow for memory hot-add" - depends on SPARSEMEM && HOTPLUG && !SOFTWARE_SUSPEND + depends on SPARSEMEM && HOTPLUG && !SOFTWARE_SUSPEND && ARCH_ENABLE_MEMORY_HOTPLUG + depends on (IA64 || X86 || PPC64) comment "Memory hotplug is currently incompatible with Software Suspend" depends on SPARSEMEM && HOTPLUG && SOFTWARE_SUSPEND @@ -138,10 +139,16 @@ # support for page migration # config MIGRATION bool "Page migration" - def_bool y if NUMA - depends on SWAP && NUMA + def_bool y + depends on NUMA help Allows the migration of the physical location of pages of processes while the virtual addresses are not changed. This is useful for example on NUMA systems to put pages nearer to the processors accessing the page. + +config RESOURCES_64BIT + bool "64 bit Memory and IO resources (EXPERIMENTAL)" if (!64BIT && EXPERIMENTAL) + default 64BIT + help + This option allows memory and IO resources to be 64 bit. diff --git a/mm/Makefile b/mm/Makefile index 0b8f73f..9dd824c 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -10,7 +10,7 @@ mmu-$(CONFIG_MMU) := fremap.o highmem.o obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o \ - prio_tree.o util.o mmzone.o $(mmu-y) + prio_tree.o util.o mmzone.o vmstat.o $(mmu-y) obj-$(CONFIG_SWAP) += page_io.o swap_state.o swapfile.o thrash.o obj-$(CONFIG_HUGETLBFS) += hugetlb.o diff --git a/mm/filemap.c b/mm/filemap.c index fd57442..d087fc3 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -9,11 +9,11 @@ * most "normal" filesystems (but you don't /have/ to use this: * the NFS filesystem used to do this differently, for example) */ -#include #include #include #include #include +#include #include #include #include @@ -38,7 +38,6 @@ #include "internal.h" */ #include /* for generic_osync_inode */ -#include #include static ssize_t @@ -120,7 +119,7 @@ void __remove_from_page_cache(struct pag radix_tree_delete(&mapping->page_tree, page->index); page->mapping = NULL; mapping->nrpages--; - pagecache_acct(-1); + __dec_zone_page_state(page, NR_FILE_PAGES); } void remove_from_page_cache(struct page *page) @@ -171,15 +170,17 @@ static int sync_page(void *word) } /** - * filemap_fdatawrite_range - start writeback against all of a mapping's - * dirty pages that lie within the byte offsets + * __filemap_fdatawrite_range - start writeback on mapping dirty pages in range * @mapping: address space structure to write * @start: offset in bytes where the range starts * @end: offset in bytes where the range ends (inclusive) * @sync_mode: enable synchronous operation * + * Start writeback against all of a mapping's dirty pages that lie + * within the byte offsets inclusive. + * * If sync_mode is WB_SYNC_ALL then this is a "data integrity" operation, as - * opposed to a regular memory * cleansing writeback. The difference between + * opposed to a regular memory cleansing writeback. The difference between * these two operations is that if a dirty page/buffer is encountered, it must * be waited upon, and not just skipped over. */ @@ -190,8 +191,8 @@ int __filemap_fdatawrite_range(struct ad struct writeback_control wbc = { .sync_mode = sync_mode, .nr_to_write = mapping->nrpages * 2, - .start = start, - .end = end, + .range_start = start, + .range_end = end, }; if (!mapping_cap_writeback_dirty(mapping)) @@ -204,7 +205,7 @@ int __filemap_fdatawrite_range(struct ad static inline int __filemap_fdatawrite(struct address_space *mapping, int sync_mode) { - return __filemap_fdatawrite_range(mapping, 0, 0, sync_mode); + return __filemap_fdatawrite_range(mapping, 0, LLONG_MAX, sync_mode); } int filemap_fdatawrite(struct address_space *mapping) @@ -219,7 +220,10 @@ static int filemap_fdatawrite_range(stru return __filemap_fdatawrite_range(mapping, start, end, WB_SYNC_ALL); } -/* +/** + * filemap_flush - mostly a non-blocking flush + * @mapping: target address_space + * * This is a mostly non-blocking flush. Not suitable for data-integrity * purposes - I/O may not be started against all dirty pages. */ @@ -229,7 +233,12 @@ int filemap_flush(struct address_space * } EXPORT_SYMBOL(filemap_flush); -/* +/** + * wait_on_page_writeback_range - wait for writeback to complete + * @mapping: target address_space + * @start: beginning page index + * @end: ending page index + * * Wait for writeback to complete against pages indexed by start->end * inclusive */ @@ -276,7 +285,13 @@ int wait_on_page_writeback_range(struct return ret; } -/* +/** + * sync_page_range - write and wait on all pages in the passed range + * @inode: target inode + * @mapping: target address_space + * @pos: beginning offset in pages to write + * @count: number of bytes to write + * * Write and wait upon all the pages in the passed range. This is a "data * integrity" operation. It waits upon in-flight writeout before starting and * waiting upon new writeout. If there was an IO error, return it. @@ -305,7 +320,13 @@ int sync_page_range(struct inode *inode, } EXPORT_SYMBOL(sync_page_range); -/* +/** + * sync_page_range_nolock + * @inode: target inode + * @mapping: target address_space + * @pos: beginning offset in pages to write + * @count: number of bytes to write + * * Note: Holding i_mutex across sync_page_range_nolock is not a good idea * as it forces O_SYNC writers to different parts of the same file * to be serialised right until io completion. @@ -329,10 +350,11 @@ int sync_page_range_nolock(struct inode EXPORT_SYMBOL(sync_page_range_nolock); /** - * filemap_fdatawait - walk the list of under-writeback pages of the given - * address space and wait for all of them. - * + * filemap_fdatawait - wait for all under-writeback pages to complete * @mapping: address space structure to wait for + * + * Walk the list of under-writeback pages of the given address space + * and wait for all of them. */ int filemap_fdatawait(struct address_space *mapping) { @@ -368,7 +390,12 @@ int filemap_write_and_wait(struct addres } EXPORT_SYMBOL(filemap_write_and_wait); -/* +/** + * filemap_write_and_wait_range - write out & wait on a file range + * @mapping: the address_space for the pages + * @lstart: offset in bytes where the range starts + * @lend: offset in bytes where the range ends (inclusive) + * * Write out and wait upon file offsets lstart->lend, inclusive. * * Note that `lend' is inclusive (describes the last byte to be written) so @@ -394,8 +421,14 @@ int filemap_write_and_wait_range(struct return err; } -/* - * This function is used to add newly allocated pagecache pages: +/** + * add_to_page_cache - add newly allocated pagecache pages + * @page: page to add + * @mapping: the page's address_space + * @offset: page index + * @gfp_mask: page allocation mode + * + * This function is used to add newly allocated pagecache pages; * the page is new, so we can just run SetPageLocked() against it. * The other page state flags were set by rmqueue(). * @@ -415,14 +448,13 @@ int add_to_page_cache(struct page *page, page->mapping = mapping; page->index = offset; mapping->nrpages++; - pagecache_acct(1); + __inc_zone_page_state(page, NR_FILE_PAGES); } write_unlock_irq(&mapping->tree_lock); radix_tree_preload_end(); } return error; } - EXPORT_SYMBOL(add_to_page_cache); int add_to_page_cache_lru(struct page *page, struct address_space *mapping, @@ -489,8 +521,7 @@ void fastcall wait_on_page_bit(struct pa EXPORT_SYMBOL(wait_on_page_bit); /** - * unlock_page() - unlock a locked page - * + * unlock_page - unlock a locked page * @page: the page * * Unlocks the page and wakes up sleepers in ___wait_on_page_locked(). @@ -513,8 +544,9 @@ void fastcall unlock_page(struct page *p } EXPORT_SYMBOL(unlock_page); -/* - * End writeback against a page. +/** + * end_page_writeback - end writeback against a page + * @page: the page */ void end_page_writeback(struct page *page) { @@ -527,10 +559,11 @@ void end_page_writeback(struct page *pag } EXPORT_SYMBOL(end_page_writeback); -/* - * Get a lock on the page, assuming we need to sleep to get it. +/** + * __lock_page - get a lock on the page, assuming we need to sleep to get it + * @page: the page to lock * - * Ugly: running sync_page() in state TASK_UNINTERRUPTIBLE is scary. If some + * Ugly. Running sync_page() in state TASK_UNINTERRUPTIBLE is scary. If some * random driver's requestfn sets TASK_RUNNING, we could busywait. However * chances are that on the second loop, the block layer's plug list is empty, * so sync_page() will then return in state TASK_UNINTERRUPTIBLE. @@ -544,8 +577,12 @@ void fastcall __lock_page(struct page *p } EXPORT_SYMBOL(__lock_page); -/* - * a rather lightweight function, finding and getting a reference to a +/** + * find_get_page - find and get a page reference + * @mapping: the address_space to search + * @offset: the page index + * + * A rather lightweight function, finding and getting a reference to a * hashed page atomically. */ struct page * find_get_page(struct address_space *mapping, unsigned long offset) @@ -559,11 +596,14 @@ struct page * find_get_page(struct addre read_unlock_irq(&mapping->tree_lock); return page; } - EXPORT_SYMBOL(find_get_page); -/* - * Same as above, but trylock it instead of incrementing the count. +/** + * find_trylock_page - find and lock a page + * @mapping: the address_space to search + * @offset: the page index + * + * Same as find_get_page(), but trylock it instead of incrementing the count. */ struct page *find_trylock_page(struct address_space *mapping, unsigned long offset) { @@ -576,12 +616,10 @@ struct page *find_trylock_page(struct ad read_unlock_irq(&mapping->tree_lock); return page; } - EXPORT_SYMBOL(find_trylock_page); /** * find_lock_page - locate, pin and lock a pagecache page - * * @mapping: the address_space to search * @offset: the page index * @@ -617,12 +655,10 @@ repeat: read_unlock_irq(&mapping->tree_lock); return page; } - EXPORT_SYMBOL(find_lock_page); /** * find_or_create_page - locate or add a pagecache page - * * @mapping: the page's address_space * @index: the page's index into the mapping * @gfp_mask: page allocation mode @@ -663,7 +699,6 @@ repeat: page_cache_release(cached_page); return page; } - EXPORT_SYMBOL(find_or_create_page); /** @@ -729,9 +764,16 @@ unsigned find_get_pages_contig(struct ad return i; } -/* +/** + * find_get_pages_tag - find and return pages that match @tag + * @mapping: the address_space to search + * @index: the starting page index + * @tag: the tag index + * @nr_pages: the maximum number of pages + * @pages: where the resulting pages are placed + * * Like find_get_pages, except we only return pages which are tagged with - * `tag'. We update *index to index the next page for the traversal. + * @tag. We update @index to index the next page for the traversal. */ unsigned find_get_pages_tag(struct address_space *mapping, pgoff_t *index, int tag, unsigned int nr_pages, struct page **pages) @@ -750,7 +792,11 @@ unsigned find_get_pages_tag(struct addre return ret; } -/* +/** + * grab_cache_page_nowait - returns locked page at given index in given cache + * @mapping: target address_space + * @index: the page index + * * Same as grab_cache_page, but do not wait if the page is unavailable. * This is intended for speculative data generators, where the data can * be regenerated if the page couldn't be grabbed. This routine should @@ -779,19 +825,51 @@ grab_cache_page_nowait(struct address_sp } return page; } - EXPORT_SYMBOL(grab_cache_page_nowait); /* + * CD/DVDs are error prone. When a medium error occurs, the driver may fail + * a _large_ part of the i/o request. Imagine the worst scenario: + * + * ---R__________________________________________B__________ + * ^ reading here ^ bad block(assume 4k) + * + * read(R) => miss => readahead(R...B) => media error => frustrating retries + * => failing the whole request => read(R) => read(R+1) => + * readahead(R+1...B+1) => bang => read(R+2) => read(R+3) => + * readahead(R+3...B+2) => bang => read(R+3) => read(R+4) => + * readahead(R+4...B+3) => bang => read(R+4) => read(R+5) => ...... + * + * It is going insane. Fix it by quickly scaling down the readahead size. + */ +static void shrink_readahead_size_eio(struct file *filp, + struct file_ra_state *ra) +{ + if (!ra->ra_pages) + return; + + ra->ra_pages /= 4; + printk(KERN_WARNING "Reducing readahead size to %luK\n", + ra->ra_pages << (PAGE_CACHE_SHIFT - 10)); +} + +/** + * do_generic_mapping_read - generic file read routine + * @mapping: address_space to be read + * @_ra: file's readahead state + * @filp: the file to read + * @ppos: current file position + * @desc: read_descriptor + * @actor: read method + * * This is a generic file read routine, and uses the - * mapping->a_ops->readpage() function for the actual low-level - * stuff. + * mapping->a_ops->readpage() function for the actual low-level stuff. * * This is really ugly. But the goto's actually try to clarify some * of the logic when it comes to error handling etc. * - * Note the struct file* is only passed for the use of readpage. It may be - * NULL. + * Note the struct file* is only passed for the use of readpage. + * It may be NULL. */ void do_generic_mapping_read(struct address_space *mapping, struct file_ra_state *_ra, @@ -932,6 +1010,7 @@ readpage: } unlock_page(page); error = -EIO; + shrink_readahead_size_eio(filp, &ra); goto readpage_error; } unlock_page(page); @@ -1004,7 +1083,6 @@ out: if (filp) file_accessed(filp); } - EXPORT_SYMBOL(do_generic_mapping_read); int file_read_actor(read_descriptor_t *desc, struct page *page, @@ -1045,7 +1123,13 @@ success: return size; } -/* +/** + * __generic_file_aio_read - generic filesystem read routine + * @iocb: kernel I/O control block + * @iov: io vector request + * @nr_segs: number of segments in the iovec + * @ppos: current file position + * * This is the "read()" routine for all filesystems * that can use the page cache directly. */ @@ -1124,7 +1208,6 @@ __generic_file_aio_read(struct kiocb *io out: return retval; } - EXPORT_SYMBOL(__generic_file_aio_read); ssize_t @@ -1135,7 +1218,6 @@ generic_file_aio_read(struct kiocb *iocb BUG_ON(iocb->ki_pos != pos); return __generic_file_aio_read(iocb, &local_iov, 1, &iocb->ki_pos); } - EXPORT_SYMBOL(generic_file_aio_read); ssize_t @@ -1151,7 +1233,6 @@ generic_file_read(struct file *filp, cha ret = wait_on_sync_kiocb(&kiocb); return ret; } - EXPORT_SYMBOL(generic_file_read); int file_send_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size) @@ -1192,7 +1273,6 @@ ssize_t generic_file_sendfile(struct fil return desc.written; return desc.error; } - EXPORT_SYMBOL(generic_file_sendfile); static ssize_t @@ -1228,11 +1308,15 @@ asmlinkage ssize_t sys_readahead(int fd, } #ifdef CONFIG_MMU -/* +static int FASTCALL(page_cache_read(struct file * file, unsigned long offset)); +/** + * page_cache_read - adds requested page to the page cache if not already there + * @file: file to read + * @offset: page index + * * This adds the requested page to the page cache if it isn't already there, * and schedules an I/O to read in its contents from disk. */ -static int FASTCALL(page_cache_read(struct file * file, unsigned long offset)); static int fastcall page_cache_read(struct file * file, unsigned long offset) { struct address_space *mapping = file->f_mapping; @@ -1259,7 +1343,12 @@ static int fastcall page_cache_read(stru #define MMAP_LOTSAMISS (100) -/* +/** + * filemap_nopage - read in file data for page fault handling + * @area: the applicable vm_area + * @address: target address to read in + * @type: returned with VM_FAULT_{MINOR,MAJOR} if not %NULL + * * filemap_nopage() is invoked via the vma operations vector for a * mapped memory region to read in file data during a page fault. * @@ -1326,7 +1415,7 @@ retry_find: */ if (!did_readaround) { majmin = VM_FAULT_MAJOR; - inc_page_state(pgmajfault); + count_vm_event(PGMAJFAULT); } did_readaround = 1; ra_pages = max_sane_readahead(file->f_ra.ra_pages); @@ -1397,7 +1486,7 @@ no_cached_page: page_not_uptodate: if (!did_readaround) { majmin = VM_FAULT_MAJOR; - inc_page_state(pgmajfault); + count_vm_event(PGMAJFAULT); } lock_page(page); @@ -1459,10 +1548,10 @@ page_not_uptodate: * Things didn't work out. Return zero to tell the * mm layer so, possibly freeing the page cache page first. */ + shrink_readahead_size_eio(file, ra); page_cache_release(page); return NULL; } - EXPORT_SYMBOL(filemap_nopage); static struct page * filemap_getpage(struct file *file, unsigned long pgoff, @@ -1716,7 +1805,13 @@ repeat: return page; } -/* +/** + * read_cache_page - read into page cache, fill it if needed + * @mapping: the page's address_space + * @index: the page index + * @filler: function to perform the read + * @data: destination for read data + * * Read into the page cache. If a page already exists, * and PageUptodate() is not set, try to fill the page. */ @@ -1754,7 +1849,6 @@ retry: out: return page; } - EXPORT_SYMBOL(read_cache_page); /* @@ -1825,7 +1919,7 @@ int remove_suid(struct dentry *dentry) EXPORT_SYMBOL(remove_suid); size_t -__filemap_copy_from_user_iovec(char *vaddr, +__filemap_copy_from_user_iovec_inatomic(char *vaddr, const struct iovec *iov, size_t base, size_t bytes) { size_t copied = 0, left = 0; @@ -1835,18 +1929,14 @@ __filemap_copy_from_user_iovec(char *vad int copy = min(bytes, iov->iov_len - base); base = 0; - left = __copy_from_user_inatomic(vaddr, buf, copy); + left = __copy_from_user_inatomic_nocache(vaddr, buf, copy); copied += copy; bytes -= copy; vaddr += copy; iov++; - if (unlikely(left)) { - /* zero the rest of the target like __copy_from_user */ - if (bytes) - memset(vaddr, 0, bytes); + if (unlikely(left)) break; - } } return copied - left; } @@ -1854,7 +1944,7 @@ __filemap_copy_from_user_iovec(char *vad /* * Performs necessary checks before doing a write * - * Can adjust writing position aor amount of bytes to write. + * Can adjust writing position or amount of bytes to write. * Returns appropriate error code that caller should return or * zero in case that write should be allowed. */ @@ -1978,7 +2068,7 @@ generic_file_buffered_write(struct kiocb { struct file *file = iocb->ki_filp; struct address_space * mapping = file->f_mapping; - struct address_space_operations *a_ops = mapping->a_ops; + const struct address_space_operations *a_ops = mapping->a_ops; struct inode *inode = mapping->host; long status = 0; struct page *page; @@ -2004,14 +2094,21 @@ generic_file_buffered_write(struct kiocb do { unsigned long index; unsigned long offset; - unsigned long maxlen; size_t copied; offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */ index = pos >> PAGE_CACHE_SHIFT; bytes = PAGE_CACHE_SIZE - offset; - if (bytes > count) - bytes = count; + + /* Limit the size of the copy to the caller's write size */ + bytes = min(bytes, count); + + /* + * Limit the size of the copy to that of the current segment, + * because fault_in_pages_readable() doesn't know how to walk + * segments. + */ + bytes = min(bytes, cur_iov->iov_len - iov_base); /* * Bring in the user page that we will copy from _first_. @@ -2019,10 +2116,7 @@ generic_file_buffered_write(struct kiocb * same page as we're writing to, without it being marked * up-to-date. */ - maxlen = cur_iov->iov_len - iov_base; - if (maxlen > bytes) - maxlen = bytes; - fault_in_pages_readable(buf, maxlen); + fault_in_pages_readable(buf, bytes); page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec); if (!page) { @@ -2030,6 +2124,12 @@ generic_file_buffered_write(struct kiocb break; } + if (unlikely(bytes == 0)) { + status = 0; + copied = 0; + goto zero_length_segment; + } + status = a_ops->prepare_write(file, page, offset, offset+bytes); if (unlikely(status)) { loff_t isize = i_size_read(inode); @@ -2059,7 +2159,8 @@ generic_file_buffered_write(struct kiocb page_cache_release(page); continue; } - if (likely(copied > 0)) { +zero_length_segment: + if (likely(copied >= 0)) { if (!status) status = copied; @@ -2124,7 +2225,7 @@ __generic_file_aio_write_nolock(struct k unsigned long nr_segs, loff_t *ppos) { struct file *file = iocb->ki_filp; - struct address_space * mapping = file->f_mapping; + const struct address_space * mapping = file->f_mapping; size_t ocount; /* original count */ size_t count; /* after file limit checks */ struct inode *inode = mapping->host; diff --git a/mm/filemap.h b/mm/filemap.h index 13793ba..3f2a343 100644 --- a/mm/filemap.h +++ b/mm/filemap.h @@ -13,18 +13,26 @@ #include #include #include #include -#include +#include size_t -__filemap_copy_from_user_iovec(char *vaddr, - const struct iovec *iov, - size_t base, - size_t bytes); +__filemap_copy_from_user_iovec_inatomic(char *vaddr, + const struct iovec *iov, + size_t base, + size_t bytes); /* * Copy as much as we can into the page and return the number of bytes which * were sucessfully copied. If a fault is encountered then clear the page * out to (offset+bytes) and return the number of bytes which were copied. + * + * NOTE: For this to work reliably we really want copy_from_user_inatomic_nocache + * to *NOT* zero any tail of the buffer that it failed to copy. If it does, + * and if the following non-atomic copy succeeds, then there is a small window + * where the target page contains neither the data before the write, nor the + * data after the write (it contains zero). A read at this time will see + * data that is inconsistent with any ordering of the read and the write. + * (This has been detected in practice). */ static inline size_t filemap_copy_from_user(struct page *page, unsigned long offset, @@ -34,13 +42,13 @@ filemap_copy_from_user(struct page *page int left; kaddr = kmap_atomic(page, KM_USER0); - left = __copy_from_user_inatomic(kaddr + offset, buf, bytes); + left = __copy_from_user_inatomic_nocache(kaddr + offset, buf, bytes); kunmap_atomic(kaddr, KM_USER0); if (left != 0) { /* Do it the slow way */ kaddr = kmap(page); - left = __copy_from_user(kaddr + offset, buf, bytes); + left = __copy_from_user_nocache(kaddr + offset, buf, bytes); kunmap(page); } return bytes - left; @@ -60,13 +68,15 @@ filemap_copy_from_user_iovec(struct page size_t copied; kaddr = kmap_atomic(page, KM_USER0); - copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, - base, bytes); + copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset, iov, + base, bytes); kunmap_atomic(kaddr, KM_USER0); if (copied != bytes) { kaddr = kmap(page); - copied = __filemap_copy_from_user_iovec(kaddr + offset, iov, - base, bytes); + copied = __filemap_copy_from_user_iovec_inatomic(kaddr + offset, iov, + base, bytes); + if (bytes - copied) + memset(kaddr + offset + copied, 0, bytes - copied); kunmap(page); } return copied; @@ -78,7 +88,7 @@ filemap_set_next_iovec(const struct iove const struct iovec *iov = *iovp; size_t base = *basep; - while (bytes) { + do { int copy = min(bytes, iov->iov_len - base); bytes -= copy; @@ -87,7 +97,7 @@ filemap_set_next_iovec(const struct iove iov++; base = 0; } - } + } while (bytes); *iovp = iov; *basep = base; } diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index b960ac8..b4fd0d7 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -273,7 +273,7 @@ __xip_file_write(struct file *filp, cons size_t count, loff_t pos, loff_t *ppos) { struct address_space * mapping = filp->f_mapping; - struct address_space_operations *a_ops = mapping->a_ops; + const struct address_space_operations *a_ops = mapping->a_ops; struct inode *inode = mapping->host; long status = 0; struct page *page; diff --git a/mm/fremap.c b/mm/fremap.c index 9f381e5..21b7d0c 100644 --- a/mm/fremap.c +++ b/mm/fremap.c @@ -83,6 +83,7 @@ int install_page(struct mm_struct *mm, s page_add_file_rmap(page); pte_val = *pte; update_mmu_cache(vma, addr, pte_val); + lazy_mmu_prot_update(pte_val); err = 0; unlock: pte_unmap_unlock(pte, ptl); @@ -114,7 +115,13 @@ int install_file_pte(struct mm_struct *m set_pte_at(mm, addr, pte, pgoff_to_pte(pgoff)); pte_val = *pte; - update_mmu_cache(vma, addr, pte_val); + /* + * We don't need to run update_mmu_cache() here because the "file pte" + * being installed by install_file_pte() is not a real pte - it's a + * non-present entry (like a swap entry), noting what file offset should + * be mapped there when there's a fault (in a non-linear vma where + * that's not obvious). + */ pte_unmap_unlock(pte, ptl); err = 0; out: diff --git a/mm/highmem.c b/mm/highmem.c index 9b274fd..9b2a540 100644 --- a/mm/highmem.c +++ b/mm/highmem.c @@ -315,8 +315,8 @@ static void bounce_end_io(struct bio *bi if (bvec->bv_page == org_vec->bv_page) continue; - mempool_free(bvec->bv_page, pool); - dec_page_state(nr_bounce); + dec_zone_page_state(bvec->bv_page, NR_BOUNCE); + mempool_free(bvec->bv_page, pool); } bio_endio(bio_orig, bio_orig->bi_size, err); @@ -397,7 +397,7 @@ static void __blk_queue_bounce(request_q to->bv_page = mempool_alloc(pool, q->bounce_gfp); to->bv_len = from->bv_len; to->bv_offset = from->bv_offset; - inc_page_state(nr_bounce); + inc_zone_page_state(to->bv_page, NR_BOUNCE); if (rw == WRITE) { char *vto, *vfrom; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 832f676..df49997 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -22,7 +22,7 @@ #include #include "internal.h" const unsigned long hugetlb_zero = 0, hugetlb_infinity = ~0UL; -static unsigned long nr_huge_pages, free_huge_pages, reserved_huge_pages; +static unsigned long nr_huge_pages, free_huge_pages, resv_huge_pages; unsigned long max_huge_pages; static struct list_head hugepage_freelists[MAX_NUMNODES]; static unsigned int nr_huge_pages_node[MAX_NUMNODES]; @@ -123,39 +123,13 @@ static int alloc_fresh_huge_page(void) static struct page *alloc_huge_page(struct vm_area_struct *vma, unsigned long addr) { - struct inode *inode = vma->vm_file->f_dentry->d_inode; struct page *page; - int use_reserve = 0; - unsigned long idx; spin_lock(&hugetlb_lock); - - if (vma->vm_flags & VM_MAYSHARE) { - - /* idx = radix tree index, i.e. offset into file in - * HPAGE_SIZE units */ - idx = ((addr - vma->vm_start) >> HPAGE_SHIFT) - + (vma->vm_pgoff >> (HPAGE_SHIFT - PAGE_SHIFT)); - - /* The hugetlbfs specific inode info stores the number - * of "guaranteed available" (huge) pages. That is, - * the first 'prereserved_hpages' pages of the inode - * are either already instantiated, or have been - * pre-reserved (by hugetlb_reserve_for_inode()). Here - * we're in the process of instantiating the page, so - * we use this to determine whether to draw from the - * pre-reserved pool or the truly free pool. */ - if (idx < HUGETLBFS_I(inode)->prereserved_hpages) - use_reserve = 1; - } - - if (!use_reserve) { - if (free_huge_pages <= reserved_huge_pages) - goto fail; - } else { - BUG_ON(reserved_huge_pages == 0); - reserved_huge_pages--; - } + if (vma->vm_flags & VM_MAYSHARE) + resv_huge_pages--; + else if (free_huge_pages <= resv_huge_pages) + goto fail; page = dequeue_huge_page(vma, addr); if (!page) @@ -165,96 +139,11 @@ static struct page *alloc_huge_page(stru set_page_refcounted(page); return page; - fail: - WARN_ON(use_reserve); /* reserved allocations shouldn't fail */ +fail: spin_unlock(&hugetlb_lock); return NULL; } -/* hugetlb_extend_reservation() - * - * Ensure that at least 'atleast' hugepages are, and will remain, - * available to instantiate the first 'atleast' pages of the given - * inode. If the inode doesn't already have this many pages reserved - * or instantiated, set aside some hugepages in the reserved pool to - * satisfy later faults (or fail now if there aren't enough, rather - * than getting the SIGBUS later). - */ -int hugetlb_extend_reservation(struct hugetlbfs_inode_info *info, - unsigned long atleast) -{ - struct inode *inode = &info->vfs_inode; - unsigned long change_in_reserve = 0; - int ret = 0; - - spin_lock(&hugetlb_lock); - read_lock_irq(&inode->i_mapping->tree_lock); - - if (info->prereserved_hpages >= atleast) - goto out; - - /* Because we always call this on shared mappings, none of the - * pages beyond info->prereserved_hpages can have been - * instantiated, so we need to reserve all of them now. */ - change_in_reserve = atleast - info->prereserved_hpages; - - if ((reserved_huge_pages + change_in_reserve) > free_huge_pages) { - ret = -ENOMEM; - goto out; - } - - reserved_huge_pages += change_in_reserve; - info->prereserved_hpages = atleast; - - out: - read_unlock_irq(&inode->i_mapping->tree_lock); - spin_unlock(&hugetlb_lock); - - return ret; -} - -/* hugetlb_truncate_reservation() - * - * This returns pages reserved for the given inode to the general free - * hugepage pool. If the inode has any pages prereserved, but not - * instantiated, beyond offset (atmost << HPAGE_SIZE), then release - * them. - */ -void hugetlb_truncate_reservation(struct hugetlbfs_inode_info *info, - unsigned long atmost) -{ - struct inode *inode = &info->vfs_inode; - struct address_space *mapping = inode->i_mapping; - unsigned long idx; - unsigned long change_in_reserve = 0; - struct page *page; - - spin_lock(&hugetlb_lock); - read_lock_irq(&inode->i_mapping->tree_lock); - - if (info->prereserved_hpages <= atmost) - goto out; - - /* Count pages which were reserved, but not instantiated, and - * which we can now release. */ - for (idx = atmost; idx < info->prereserved_hpages; idx++) { - page = radix_tree_lookup(&mapping->page_tree, idx); - if (!page) - /* Pages which are already instantiated can't - * be unreserved (and in fact have already - * been removed from the reserved pool) */ - change_in_reserve++; - } - - BUG_ON(reserved_huge_pages < change_in_reserve); - reserved_huge_pages -= change_in_reserve; - info->prereserved_hpages = atmost; - - out: - read_unlock_irq(&inode->i_mapping->tree_lock); - spin_unlock(&hugetlb_lock); -} - static int __init hugetlb_init(void) { unsigned long i; @@ -334,7 +223,7 @@ static unsigned long set_max_huge_pages( return nr_huge_pages; spin_lock(&hugetlb_lock); - count = max(count, reserved_huge_pages); + count = max(count, resv_huge_pages); try_to_free_low(count); while (count < nr_huge_pages) { struct page *page = dequeue_huge_page(NULL, 0); @@ -361,11 +250,11 @@ int hugetlb_report_meminfo(char *buf) return sprintf(buf, "HugePages_Total: %5lu\n" "HugePages_Free: %5lu\n" - "HugePages_Rsvd: %5lu\n" + "HugePages_Rsvd: %5lu\n" "Hugepagesize: %5lu kB\n", nr_huge_pages, free_huge_pages, - reserved_huge_pages, + resv_huge_pages, HPAGE_SIZE/1024); } @@ -754,3 +643,156 @@ void hugetlb_change_protection(struct vm flush_tlb_range(vma, start, end); } +struct file_region { + struct list_head link; + long from; + long to; +}; + +static long region_add(struct list_head *head, long f, long t) +{ + struct file_region *rg, *nrg, *trg; + + /* Locate the region we are either in or before. */ + list_for_each_entry(rg, head, link) + if (f <= rg->to) + break; + + /* Round our left edge to the current segment if it encloses us. */ + if (f > rg->from) + f = rg->from; + + /* Check for and consume any regions we now overlap with. */ + nrg = rg; + list_for_each_entry_safe(rg, trg, rg->link.prev, link) { + if (&rg->link == head) + break; + if (rg->from > t) + break; + + /* If this area reaches higher then extend our area to + * include it completely. If this is not the first area + * which we intend to reuse, free it. */ + if (rg->to > t) + t = rg->to; + if (rg != nrg) { + list_del(&rg->link); + kfree(rg); + } + } + nrg->from = f; + nrg->to = t; + return 0; +} + +static long region_chg(struct list_head *head, long f, long t) +{ + struct file_region *rg, *nrg; + long chg = 0; + + /* Locate the region we are before or in. */ + list_for_each_entry(rg, head, link) + if (f <= rg->to) + break; + + /* If we are below the current region then a new region is required. + * Subtle, allocate a new region at the position but make it zero + * size such that we can guarentee to record the reservation. */ + if (&rg->link == head || t < rg->from) { + nrg = kmalloc(sizeof(*nrg), GFP_KERNEL); + if (nrg == 0) + return -ENOMEM; + nrg->from = f; + nrg->to = f; + INIT_LIST_HEAD(&nrg->link); + list_add(&nrg->link, rg->link.prev); + + return t - f; + } + + /* Round our left edge to the current segment if it encloses us. */ + if (f > rg->from) + f = rg->from; + chg = t - f; + + /* Check for and consume any regions we now overlap with. */ + list_for_each_entry(rg, rg->link.prev, link) { + if (&rg->link == head) + break; + if (rg->from > t) + return chg; + + /* We overlap with this area, if it extends futher than + * us then we must extend ourselves. Account for its + * existing reservation. */ + if (rg->to > t) { + chg += rg->to - t; + t = rg->to; + } + chg -= rg->to - rg->from; + } + return chg; +} + +static long region_truncate(struct list_head *head, long end) +{ + struct file_region *rg, *trg; + long chg = 0; + + /* Locate the region we are either in or before. */ + list_for_each_entry(rg, head, link) + if (end <= rg->to) + break; + if (&rg->link == head) + return 0; + + /* If we are in the middle of a region then adjust it. */ + if (end > rg->from) { + chg = rg->to - end; + rg->to = end; + rg = list_entry(rg->link.next, typeof(*rg), link); + } + + /* Drop any remaining regions. */ + list_for_each_entry_safe(rg, trg, rg->link.prev, link) { + if (&rg->link == head) + break; + chg += rg->to - rg->from; + list_del(&rg->link); + kfree(rg); + } + return chg; +} + +static int hugetlb_acct_memory(long delta) +{ + int ret = -ENOMEM; + + spin_lock(&hugetlb_lock); + if ((delta + resv_huge_pages) <= free_huge_pages) { + resv_huge_pages += delta; + ret = 0; + } + spin_unlock(&hugetlb_lock); + return ret; +} + +int hugetlb_reserve_pages(struct inode *inode, long from, long to) +{ + long ret, chg; + + chg = region_chg(&inode->i_mapping->private_list, from, to); + if (chg < 0) + return chg; + ret = hugetlb_acct_memory(chg); + if (ret < 0) + return ret; + region_add(&inode->i_mapping->private_list, from, to); + return 0; +} + +void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed) +{ + long chg = region_truncate(&inode->i_mapping->private_list, offset); + hugetlb_acct_memory(freed - chg); +} diff --git a/mm/memory.c b/mm/memory.c index 0ec7bc6..c1e14c9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -126,7 +126,7 @@ static void free_pte_range(struct mmu_ga pmd_clear(pmd); pte_lock_deinit(page); pte_free_tlb(tlb, page); - dec_page_state(nr_page_table_pages); + dec_zone_page_state(page, NR_PAGETABLE); tlb->mm->nr_ptes--; } @@ -311,7 +311,7 @@ int __pte_alloc(struct mm_struct *mm, pm pte_free(new); } else { mm->nr_ptes++; - inc_page_state(nr_page_table_pages); + inc_zone_page_state(new, NR_PAGETABLE); pmd_populate(mm, pmd, new); } spin_unlock(&mm->page_table_lock); @@ -434,7 +434,9 @@ copy_one_pte(struct mm_struct *dst_mm, s /* pte contains position in swap or file, so copy. */ if (unlikely(!pte_present(pte))) { if (!pte_file(pte)) { - swap_duplicate(pte_to_swp_entry(pte)); + swp_entry_t entry = pte_to_swp_entry(pte); + + swap_duplicate(entry); /* make sure dst_mm is on swapoff's mmlist. */ if (unlikely(list_empty(&dst_mm->mmlist))) { spin_lock(&mmlist_lock); @@ -443,6 +445,16 @@ copy_one_pte(struct mm_struct *dst_mm, s &src_mm->mmlist); spin_unlock(&mmlist_lock); } + if (is_write_migration_entry(entry) && + is_cow_mapping(vm_flags)) { + /* + * COW mappings require pages in both parent + * and child to be set to read. + */ + make_migration_entry_read(&entry); + pte = swp_entry_to_pte(entry); + set_pte_at(src_mm, addr, src_pte, pte); + } } goto out_set_pte; } @@ -491,7 +503,7 @@ again: return -ENOMEM; src_pte = pte_offset_map_nested(src_pmd, addr); src_ptl = pte_lockptr(src_mm, src_pmd); - spin_lock(src_ptl); + spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING); do { /* @@ -1445,25 +1457,60 @@ static int do_wp_page(struct mm_struct * { struct page *old_page, *new_page; pte_t entry; - int ret = VM_FAULT_MINOR; + int reuse, ret = VM_FAULT_MINOR; old_page = vm_normal_page(vma, address, orig_pte); if (!old_page) goto gotten; - if (PageAnon(old_page) && !TestSetPageLocked(old_page)) { - int reuse = can_share_swap_page(old_page); - unlock_page(old_page); - if (reuse) { - flush_cache_page(vma, address, pte_pfn(orig_pte)); - entry = pte_mkyoung(orig_pte); - entry = maybe_mkwrite(pte_mkdirty(entry), vma); - ptep_set_access_flags(vma, address, page_table, entry, 1); - update_mmu_cache(vma, address, entry); - lazy_mmu_prot_update(entry); - ret |= VM_FAULT_WRITE; - goto unlock; + if (unlikely((vma->vm_flags & (VM_SHARED|VM_WRITE)) == + (VM_SHARED|VM_WRITE))) { + if (vma->vm_ops && vma->vm_ops->page_mkwrite) { + /* + * Notify the address space that the page is about to + * become writable so that it can prohibit this or wait + * for the page to get into an appropriate state. + * + * We do this without the lock held, so that it can + * sleep if it needs to. + */ + page_cache_get(old_page); + pte_unmap_unlock(page_table, ptl); + + if (vma->vm_ops->page_mkwrite(vma, old_page) < 0) + goto unwritable_page; + + page_cache_release(old_page); + + /* + * Since we dropped the lock we need to revalidate + * the PTE as someone else may have changed it. If + * they did, we just return, as we can count on the + * MMU to tell us if they didn't also make it writable. + */ + page_table = pte_offset_map_lock(mm, pmd, address, + &ptl); + if (!pte_same(*page_table, orig_pte)) + goto unlock; } + + reuse = 1; + } else if (PageAnon(old_page) && !TestSetPageLocked(old_page)) { + reuse = can_share_swap_page(old_page); + unlock_page(old_page); + } else { + reuse = 0; + } + + if (reuse) { + flush_cache_page(vma, address, pte_pfn(orig_pte)); + entry = pte_mkyoung(orig_pte); + entry = maybe_mkwrite(pte_mkdirty(entry), vma); + ptep_set_access_flags(vma, address, page_table, entry, 1); + update_mmu_cache(vma, address, entry); + lazy_mmu_prot_update(entry); + ret |= VM_FAULT_WRITE; + goto unlock; } /* @@ -1523,6 +1570,10 @@ oom: if (old_page) page_cache_release(old_page); return VM_FAULT_OOM; + +unwritable_page: + page_cache_release(old_page); + return VM_FAULT_SIGBUS; } /* @@ -1879,7 +1930,10 @@ static int do_swap_page(struct mm_struct goto out; entry = pte_to_swp_entry(orig_pte); -again: + if (is_migration_entry(entry)) { + migration_entry_wait(mm, pmd, address); + goto out; + } page = lookup_swap_cache(entry); if (!page) { swapin_readahead(entry, address, vma); @@ -1897,18 +1951,12 @@ again: /* Had to read the page from swap area: Major fault */ ret = VM_FAULT_MAJOR; - inc_page_state(pgmajfault); + count_vm_event(PGMAJFAULT); grab_swap_token(); } mark_page_accessed(page); lock_page(page); - if (!PageSwapCache(page)) { - /* Page migration has occured */ - unlock_page(page); - page_cache_release(page); - goto again; - } /* * Back out if somebody else already faulted in this pte. @@ -2074,18 +2122,31 @@ retry: /* * Should we do an early C-O-W break? */ - if (write_access && !(vma->vm_flags & VM_SHARED)) { - struct page *page; + if (write_access) { + if (!(vma->vm_flags & VM_SHARED)) { + struct page *page; - if (unlikely(anon_vma_prepare(vma))) - goto oom; - page = alloc_page_vma(GFP_HIGHUSER, vma, address); - if (!page) - goto oom; - copy_user_highpage(page, new_page, address); - page_cache_release(new_page); - new_page = page; - anon = 1; + if (unlikely(anon_vma_prepare(vma))) + goto oom; + page = alloc_page_vma(GFP_HIGHUSER, vma, address); + if (!page) + goto oom; + copy_user_highpage(page, new_page, address); + page_cache_release(new_page); + new_page = page; + anon = 1; + + } else { + /* if the page will be shareable, see if the backing + * address space wants to know that the page is about + * to become writable */ + if (vma->vm_ops->page_mkwrite && + vma->vm_ops->page_mkwrite(vma, new_page) < 0 + ) { + page_cache_release(new_page); + return VM_FAULT_SIGBUS; + } + } } page_table = pte_offset_map_lock(mm, pmd, address, &ptl); @@ -2263,7 +2324,7 @@ int __handle_mm_fault(struct mm_struct * __set_current_state(TASK_RUNNING); - inc_page_state(pgfault); + count_vm_event(PGFAULT); if (unlikely(is_vm_hugetlb_page(vma))) return hugetlb_fault(mm, vma, address, write_access); diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 70df5c0..01c9fb9 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -4,7 +4,6 @@ * Copyright (C) */ -#include #include #include #include @@ -21,12 +20,13 @@ #include #include #include #include +#include #include extern void zonetable_add(struct zone *zone, int nid, int zid, unsigned long pfn, unsigned long size); -static void __add_zone(struct zone *zone, unsigned long phys_start_pfn) +static int __add_zone(struct zone *zone, unsigned long phys_start_pfn) { struct pglist_data *pgdat = zone->zone_pgdat; int nr_pages = PAGES_PER_SECTION; @@ -34,8 +34,15 @@ static void __add_zone(struct zone *zone int zone_type; zone_type = zone - pgdat->node_zones; + if (!populated_zone(zone)) { + int ret = 0; + ret = init_currently_empty_zone(zone, phys_start_pfn, nr_pages); + if (ret < 0) + return ret; + } memmap_init_zone(nr_pages, nid, zone_type, phys_start_pfn); zonetable_add(zone, nid, zone_type, phys_start_pfn, nr_pages); + return 0; } extern int sparse_add_one_section(struct zone *zone, unsigned long start_pfn, @@ -50,7 +57,11 @@ static int __add_section(struct zone *zo if (ret < 0) return ret; - __add_zone(zone, phys_start_pfn); + ret = __add_zone(zone, phys_start_pfn); + + if (ret < 0) + return ret; + return register_new_memory(__pfn_to_section(phys_start_pfn)); } @@ -115,7 +126,11 @@ int online_pages(unsigned long pfn, unsi unsigned long i; unsigned long flags; unsigned long onlined_pages = 0; + struct resource res; + u64 section_end; + unsigned long start_pfn; struct zone *zone; + int need_zonelists_rebuild = 0; /* * This doesn't need a lock to do pfn_to_page(). @@ -128,15 +143,140 @@ int online_pages(unsigned long pfn, unsi grow_pgdat_span(zone->zone_pgdat, pfn, pfn + nr_pages); pgdat_resize_unlock(zone->zone_pgdat, &flags); - for (i = 0; i < nr_pages; i++) { - struct page *page = pfn_to_page(pfn + i); - online_page(page); - onlined_pages++; + /* + * If this zone is not populated, then it is not in zonelist. + * This means the page allocator ignores this zone. + * So, zonelist must be updated after online. + */ + if (!populated_zone(zone)) + need_zonelists_rebuild = 1; + + res.start = (u64)pfn << PAGE_SHIFT; + res.end = res.start + ((u64)nr_pages << PAGE_SHIFT) - 1; + res.flags = IORESOURCE_MEM; /* we just need system ram */ + section_end = res.end; + + while (find_next_system_ram(&res) >= 0) { + start_pfn = (unsigned long)(res.start >> PAGE_SHIFT); + nr_pages = (unsigned long) + ((res.end + 1 - res.start) >> PAGE_SHIFT); + + if (PageReserved(pfn_to_page(start_pfn))) { + /* this region's page is not onlined now */ + for (i = 0; i < nr_pages; i++) { + struct page *page = pfn_to_page(start_pfn + i); + online_page(page); + onlined_pages++; + } + } + + res.start = res.end + 1; + res.end = section_end; } zone->present_pages += onlined_pages; zone->zone_pgdat->node_present_pages += onlined_pages; setup_per_zone_pages_min(); + if (need_zonelists_rebuild) + build_all_zonelists(); + vm_total_pages = nr_free_pagecache_pages(); return 0; } + +static pg_data_t *hotadd_new_pgdat(int nid, u64 start) +{ + struct pglist_data *pgdat; + unsigned long zones_size[MAX_NR_ZONES] = {0}; + unsigned long zholes_size[MAX_NR_ZONES] = {0}; + unsigned long start_pfn = start >> PAGE_SHIFT; + + pgdat = arch_alloc_nodedata(nid); + if (!pgdat) + return NULL; + + arch_refresh_nodedata(nid, pgdat); + + /* we can use NODE_DATA(nid) from here */ + + /* init node's zones as empty zones, we don't have any present pages.*/ + free_area_init_node(nid, pgdat, zones_size, start_pfn, zholes_size); + + return pgdat; +} + +static void rollback_node_hotadd(int nid, pg_data_t *pgdat) +{ + arch_refresh_nodedata(nid, NULL); + arch_free_nodedata(pgdat); + return; +} + +/* add this memory to iomem resource */ +static void register_memory_resource(u64 start, u64 size) +{ + struct resource *res; + + res = kzalloc(sizeof(struct resource), GFP_KERNEL); + BUG_ON(!res); + + res->name = "System RAM"; + res->start = start; + res->end = start + size - 1; + res->flags = IORESOURCE_MEM; + if (request_resource(&iomem_resource, res) < 0) { + printk("System RAM resource %llx - %llx cannot be added\n", + (unsigned long long)res->start, (unsigned long long)res->end); + kfree(res); + } +} + + + +int add_memory(int nid, u64 start, u64 size) +{ + pg_data_t *pgdat = NULL; + int new_pgdat = 0; + int ret; + + if (!node_online(nid)) { + pgdat = hotadd_new_pgdat(nid, start); + if (!pgdat) + return -ENOMEM; + new_pgdat = 1; + ret = kswapd_run(nid); + if (ret) + goto error; + } + + /* call arch's memory hotadd */ + ret = arch_add_memory(nid, start, size); + + if (ret < 0) + goto error; + + /* we online node here. we can't roll back from here. */ + node_set_online(nid); + + if (new_pgdat) { + ret = register_one_node(nid); + /* + * If sysfs file of new node can't create, cpu on the node + * can't be hot-added. There is no rollback way now. + * So, check by BUG_ON() to catch it reluctantly.. + */ + BUG_ON(ret); + } + + /* register this memory as resource */ + register_memory_resource(start, size); + + return ret; +error: + /* rollback pgdat allocation and others */ + if (new_pgdat) + rollback_node_hotadd(nid, pgdat); + + return ret; +} +EXPORT_SYMBOL_GPL(add_memory); diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 8778f58..e07e27e 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -87,6 +87,8 @@ #include #include #include #include +#include +#include #include #include @@ -587,6 +589,11 @@ static void migrate_page_add(struct page isolate_lru_page(page, pagelist); } +static struct page *new_node_page(struct page *page, unsigned long node, int **x) +{ + return alloc_pages_node(node, GFP_HIGHUSER, 0); +} + /* * Migrate pages from one node to a target node. * Returns error or the number of pages not migrated. @@ -603,11 +610,9 @@ int migrate_to_node(struct mm_struct *mm check_range(mm, mm->mmap->vm_start, TASK_SIZE, &nmask, flags | MPOL_MF_DISCONTIG_OK, &pagelist); - if (!list_empty(&pagelist)) { - err = migrate_pages_to(&pagelist, NULL, dest); - if (!list_empty(&pagelist)) - putback_lru_pages(&pagelist); - } + if (!list_empty(&pagelist)) + err = migrate_pages(&pagelist, new_node_page, dest); + return err; } @@ -627,6 +632,10 @@ int do_migrate_pages(struct mm_struct *m down_read(&mm->mmap_sem); + err = migrate_vmas(mm, from_nodes, to_nodes, flags); + if (err) + goto out; + /* * Find a 'source' bit set in 'tmp' whose corresponding 'dest' * bit in 'to' is not also set in 'tmp'. Clear the found 'source' @@ -686,7 +695,7 @@ int do_migrate_pages(struct mm_struct *m if (err < 0) break; } - +out: up_read(&mm->mmap_sem); if (err < 0) return err; @@ -694,6 +703,12 @@ int do_migrate_pages(struct mm_struct *m } +static struct page *new_vma_page(struct page *page, unsigned long private, int **x) +{ + struct vm_area_struct *vma = (struct vm_area_struct *)private; + + return alloc_page_vma(GFP_HIGHUSER, vma, page_address_in_vma(page, vma)); +} #else static void migrate_page_add(struct page *page, struct list_head *pagelist, @@ -706,6 +721,11 @@ int do_migrate_pages(struct mm_struct *m { return -ENOSYS; } + +static struct page *new_vma_page(struct page *page, unsigned long private) +{ + return NULL; +} #endif long do_mbind(unsigned long start, unsigned long len, @@ -767,15 +787,13 @@ long do_mbind(unsigned long start, unsig err = mbind_range(vma, start, end, new); if (!list_empty(&pagelist)) - nr_failed = migrate_pages_to(&pagelist, vma, -1); + nr_failed = migrate_pages(&pagelist, new_vma_page, + (unsigned long)vma); if (!err && nr_failed && (flags & MPOL_MF_STRICT)) err = -EIO; } - if (!list_empty(&pagelist)) - putback_lru_pages(&pagelist); - up_write(&mm->mmap_sem); mpol_free(new); return err; @@ -929,6 +947,10 @@ asmlinkage long sys_migrate_pages(pid_t goto out; } + err = security_task_movememory(task); + if (err) + goto out; + err = do_migrate_pages(mm, &old, &new, capable(CAP_SYS_NICE) ? MPOL_MF_MOVE_ALL : MPOL_MF_MOVE); out: @@ -1187,10 +1209,8 @@ static struct page *alloc_page_interleav zl = NODE_DATA(nid)->node_zonelists + gfp_zone(gfp); page = __alloc_pages(gfp, order, zl); - if (page && page_zone(page) == zl->zones[0]) { - zone_pcp(zl->zones[0],get_cpu())->interleave_hit++; - put_cpu(); - } + if (page && page_zone(page) == zl->zones[0]) + inc_zone_page_state(page, NUMA_INTERLEAVE_HIT); return page; } @@ -1799,7 +1819,7 @@ #endif int show_numa_map(struct seq_file *m, void *v) { - struct task_struct *task = m->private; + struct proc_maps_private *priv = m->private; struct vm_area_struct *vma = v; struct numa_maps *md; struct file *file = vma->vm_file; @@ -1815,7 +1835,7 @@ int show_numa_map(struct seq_file *m, vo return 0; mpol_to_str(buffer, sizeof(buffer), - get_vma_policy(task, vma, vma->vm_start)); + get_vma_policy(priv->task, vma, vma->vm_start)); seq_printf(m, "%08lx %s", vma->vm_start, buffer); @@ -1869,7 +1889,7 @@ out: kfree(md); if (m->count < m->size) - m->version = (vma != get_gate_vma(task)) ? vma->vm_start : 0; + m->version = (vma != priv->tail_vma) ? vma->vm_start : 0; return 0; } diff --git a/mm/migrate.c b/mm/migrate.c index 1c25040..3f1e0c2 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -23,13 +24,13 @@ #include #include #include #include -#include +#include +#include +#include +#include #include "internal.h" -/* The maximum number of pages to take off the LRU for migration */ -#define MIGRATE_CHUNK_SIZE 256 - #define lru_to_page(_head) (list_entry((_head)->prev, struct page, lru)) /* @@ -64,16 +65,11 @@ int isolate_lru_page(struct page *page, } /* - * migrate_prep() needs to be called after we have compiled the list of pages - * to be migrated using isolate_lru_page() but before we begin a series of calls - * to migrate_pages(). + * migrate_prep() needs to be called before we start compiling a list of pages + * to be migrated using isolate_lru_page(). */ int migrate_prep(void) { - /* Must have swap device for migration */ - if (nr_swap_pages <= 0) - return -ENODEV; - /* * Clear the LRU lists so pages can be isolated. * Note that pages may be moved off the LRU after we have @@ -87,7 +83,6 @@ int migrate_prep(void) static inline void move_to_lru(struct page *page) { - list_del(&page->lru); if (PageActive(page)) { /* * lru_cache_add_active checks that @@ -113,113 +108,200 @@ int putback_lru_pages(struct list_head * int count = 0; list_for_each_entry_safe(page, page2, l, lru) { + list_del(&page->lru); move_to_lru(page); count++; } return count; } -/* - * Non migratable page - */ -int fail_migrate_page(struct page *newpage, struct page *page) +static inline int is_swap_pte(pte_t pte) { - return -EIO; + return !pte_none(pte) && !pte_present(pte) && !pte_file(pte); } -EXPORT_SYMBOL(fail_migrate_page); /* - * swapout a single page - * page is locked upon entry, unlocked on exit + * Restore a potential migration pte to a working pte entry */ -static int swap_page(struct page *page) +static void remove_migration_pte(struct vm_area_struct *vma, + struct page *old, struct page *new) { - struct address_space *mapping = page_mapping(page); + struct mm_struct *mm = vma->vm_mm; + swp_entry_t entry; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep, pte; + spinlock_t *ptl; + unsigned long addr = page_address_in_vma(new, vma); + + if (addr == -EFAULT) + return; + + pgd = pgd_offset(mm, addr); + if (!pgd_present(*pgd)) + return; + + pud = pud_offset(pgd, addr); + if (!pud_present(*pud)) + return; + + pmd = pmd_offset(pud, addr); + if (!pmd_present(*pmd)) + return; + + ptep = pte_offset_map(pmd, addr); + + if (!is_swap_pte(*ptep)) { + pte_unmap(ptep); + return; + } - if (page_mapped(page) && mapping) - if (try_to_unmap(page, 1) != SWAP_SUCCESS) - goto unlock_retry; + ptl = pte_lockptr(mm, pmd); + spin_lock(ptl); + pte = *ptep; + if (!is_swap_pte(pte)) + goto out; - if (PageDirty(page)) { - /* Page is dirty, try to write it out here */ - switch(pageout(page, mapping)) { - case PAGE_KEEP: - case PAGE_ACTIVATE: - goto unlock_retry; + entry = pte_to_swp_entry(pte); - case PAGE_SUCCESS: - goto retry; + if (!is_migration_entry(entry) || migration_entry_to_page(entry) != old) + goto out; - case PAGE_CLEAN: - ; /* try to free the page below */ - } - } + get_page(new); + pte = pte_mkold(mk_pte(new, vma->vm_page_prot)); + if (is_write_migration_entry(entry)) + pte = pte_mkwrite(pte); + set_pte_at(mm, addr, ptep, pte); - if (PagePrivate(page)) { - if (!try_to_release_page(page, GFP_KERNEL) || - (!mapping && page_count(page) == 1)) - goto unlock_retry; - } + if (PageAnon(new)) + page_add_anon_rmap(new, vma, addr); + else + page_add_file_rmap(new); - if (remove_mapping(mapping, page)) { - /* Success */ - unlock_page(page); - return 0; - } + /* No need to invalidate - it was non-present before */ + update_mmu_cache(vma, addr, pte); + lazy_mmu_prot_update(pte); -unlock_retry: - unlock_page(page); +out: + pte_unmap_unlock(ptep, ptl); +} -retry: - return -EAGAIN; +/* + * Note that remove_file_migration_ptes will only work on regular mappings, + * Nonlinear mappings do not use migration entries. + */ +static void remove_file_migration_ptes(struct page *old, struct page *new) +{ + struct vm_area_struct *vma; + struct address_space *mapping = page_mapping(new); + struct prio_tree_iter iter; + pgoff_t pgoff = new->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); + + if (!mapping) + return; + + spin_lock(&mapping->i_mmap_lock); + + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) + remove_migration_pte(vma, old, new); + + spin_unlock(&mapping->i_mmap_lock); } /* - * Remove references for a page and establish the new page with the correct - * basic settings to be able to stop accesses to the page. + * Must hold mmap_sem lock on at least one of the vmas containing + * the page so that the anon_vma cannot vanish. */ -int migrate_page_remove_references(struct page *newpage, - struct page *page, int nr_refs) +static void remove_anon_migration_ptes(struct page *old, struct page *new) { - struct address_space *mapping = page_mapping(page); - struct page **radix_pointer; + struct anon_vma *anon_vma; + struct vm_area_struct *vma; + unsigned long mapping; - /* - * Avoid doing any of the following work if the page count - * indicates that the page is in use or truncate has removed - * the page. - */ - if (!mapping || page_mapcount(page) + nr_refs != page_count(page)) - return -EAGAIN; + mapping = (unsigned long)new->mapping; - /* - * Establish swap ptes for anonymous pages or destroy pte - * maps for files. - * - * In order to reestablish file backed mappings the fault handlers - * will take the radix tree_lock which may then be used to stop - * processses from accessing this page until the new page is ready. - * - * A process accessing via a swap pte (an anonymous page) will take a - * page_lock on the old page which will block the process until the - * migration attempt is complete. At that time the PageSwapCache bit - * will be examined. If the page was migrated then the PageSwapCache - * bit will be clear and the operation to retrieve the page will be - * retried which will find the new page in the radix tree. Then a new - * direct mapping may be generated based on the radix tree contents. - * - * If the page was not migrated then the PageSwapCache bit - * is still set and the operation may continue. - */ - if (try_to_unmap(page, 1) == SWAP_FAIL) - /* A vma has VM_LOCKED set -> permanent failure */ - return -EPERM; + if (!mapping || (mapping & PAGE_MAPPING_ANON) == 0) + return; /* - * Give up if we were unable to remove all mappings. + * We hold the mmap_sem lock. So no need to call page_lock_anon_vma. */ - if (page_mapcount(page)) - return -EAGAIN; + anon_vma = (struct anon_vma *) (mapping - PAGE_MAPPING_ANON); + spin_lock(&anon_vma->lock); + + list_for_each_entry(vma, &anon_vma->head, anon_vma_node) + remove_migration_pte(vma, old, new); + + spin_unlock(&anon_vma->lock); +} + +/* + * Get rid of all migration entries and replace them by + * references to the indicated page. + */ +static void remove_migration_ptes(struct page *old, struct page *new) +{ + if (PageAnon(new)) + remove_anon_migration_ptes(old, new); + else + remove_file_migration_ptes(old, new); +} + +/* + * Something used the pte of a page under migration. We need to + * get to the page and wait until migration is finished. + * When we return from this function the fault will be retried. + * + * This function is called from do_swap_page(). + */ +void migration_entry_wait(struct mm_struct *mm, pmd_t *pmd, + unsigned long address) +{ + pte_t *ptep, pte; + spinlock_t *ptl; + swp_entry_t entry; + struct page *page; + + ptep = pte_offset_map_lock(mm, pmd, address, &ptl); + pte = *ptep; + if (!is_swap_pte(pte)) + goto out; + + entry = pte_to_swp_entry(pte); + if (!is_migration_entry(entry)) + goto out; + + page = migration_entry_to_page(entry); + + get_page(page); + pte_unmap_unlock(ptep, ptl); + wait_on_page_locked(page); + put_page(page); + return; +out: + pte_unmap_unlock(ptep, ptl); +} + +/* + * Replace the page in the mapping. + * + * The number of remaining references must be: + * 1 for anonymous pages without a mapping + * 2 for pages with a mapping + * 3 for pages with a mapping and PagePrivate set. + */ +static int migrate_page_move_mapping(struct address_space *mapping, + struct page *newpage, struct page *page) +{ + struct page **radix_pointer; + + if (!mapping) { + /* Anonymous page */ + if (page_count(page) != 1) + return -EAGAIN; + return 0; + } write_lock_irq(&mapping->tree_lock); @@ -227,7 +309,7 @@ int migrate_page_remove_references(struc &mapping->page_tree, page_index(page)); - if (!page_mapping(page) || page_count(page) != nr_refs || + if (page_count(page) != 2 + !!PagePrivate(page) || *radix_pointer != page) { write_unlock_irq(&mapping->tree_lock); return -EAGAIN; @@ -235,19 +317,14 @@ int migrate_page_remove_references(struc /* * Now we know that no one else is looking at the page. - * - * Certain minimal information about a page must be available - * in order for other subsystems to properly handle the page if they - * find it through the radix tree update before we are finished - * copying the page. */ get_page(newpage); - newpage->index = page->index; - newpage->mapping = page->mapping; +#ifdef CONFIG_SWAP if (PageSwapCache(page)) { SetPageSwapCache(newpage); set_page_private(newpage, page_private(page)); } +#endif *radix_pointer = newpage; __put_page(page); @@ -255,12 +332,11 @@ int migrate_page_remove_references(struc return 0; } -EXPORT_SYMBOL(migrate_page_remove_references); /* * Copy the page to its new location */ -void migrate_page_copy(struct page *newpage, struct page *page) +static void migrate_page_copy(struct page *newpage, struct page *page) { copy_highpage(newpage, page); @@ -282,7 +358,9 @@ void migrate_page_copy(struct page *newp set_page_dirty(newpage); } +#ifdef CONFIG_SWAP ClearPageSwapCache(page); +#endif ClearPageActive(page); ClearPagePrivate(page); set_page_private(page, 0); @@ -295,7 +373,18 @@ void migrate_page_copy(struct page *newp if (PageWriteback(newpage)) end_page_writeback(newpage); } -EXPORT_SYMBOL(migrate_page_copy); + +/************************************************************ + * Migration functions + ***********************************************************/ + +/* Always fail migration. Used for mappings that are not movable */ +int fail_migrate_page(struct address_space *mapping, + struct page *newpage, struct page *page) +{ + return -EIO; +} +EXPORT_SYMBOL(fail_migrate_page); /* * Common logic to directly migrate a single page suitable for @@ -303,51 +392,284 @@ EXPORT_SYMBOL(migrate_page_copy); * * Pages are locked upon entry and exit. */ -int migrate_page(struct page *newpage, struct page *page) +int migrate_page(struct address_space *mapping, + struct page *newpage, struct page *page) { int rc; BUG_ON(PageWriteback(page)); /* Writeback must be complete */ - rc = migrate_page_remove_references(newpage, page, 2); + rc = migrate_page_move_mapping(mapping, newpage, page); + + if (rc) + return rc; + + migrate_page_copy(newpage, page); + return 0; +} +EXPORT_SYMBOL(migrate_page); + +/* + * Migration function for pages with buffers. This function can only be used + * if the underlying filesystem guarantees that no other references to "page" + * exist. + */ +int buffer_migrate_page(struct address_space *mapping, + struct page *newpage, struct page *page) +{ + struct buffer_head *bh, *head; + int rc; + + if (!page_has_buffers(page)) + return migrate_page(mapping, newpage, page); + + head = page_buffers(page); + + rc = migrate_page_move_mapping(mapping, newpage, page); if (rc) return rc; + bh = head; + do { + get_bh(bh); + lock_buffer(bh); + bh = bh->b_this_page; + + } while (bh != head); + + ClearPagePrivate(page); + set_page_private(newpage, page_private(page)); + set_page_private(page, 0); + put_page(page); + get_page(newpage); + + bh = head; + do { + set_bh_page(bh, newpage, bh_offset(bh)); + bh = bh->b_this_page; + + } while (bh != head); + + SetPagePrivate(newpage); + migrate_page_copy(newpage, page); + bh = head; + do { + unlock_buffer(bh); + put_bh(bh); + bh = bh->b_this_page; + + } while (bh != head); + + return 0; +} +EXPORT_SYMBOL(buffer_migrate_page); + +/* + * Writeback a page to clean the dirty state + */ +static int writeout(struct address_space *mapping, struct page *page) +{ + struct writeback_control wbc = { + .sync_mode = WB_SYNC_NONE, + .nr_to_write = 1, + .range_start = 0, + .range_end = LLONG_MAX, + .nonblocking = 1, + .for_reclaim = 1 + }; + int rc; + + if (!mapping->a_ops->writepage) + /* No write method for the address space */ + return -EINVAL; + + if (!clear_page_dirty_for_io(page)) + /* Someone else already triggered a write */ + return -EAGAIN; + /* - * Remove auxiliary swap entries and replace - * them with real ptes. - * - * Note that a real pte entry will allow processes that are not - * waiting on the page lock to use the new page via the page tables - * before the new page is unlocked. + * A dirty page may imply that the underlying filesystem has + * the page on some queue. So the page must be clean for + * migration. Writeout may mean we loose the lock and the + * page state is no longer what we checked for earlier. + * At this point we know that the migration attempt cannot + * be successful. */ - remove_from_swap(newpage); - return 0; + remove_migration_ptes(page, page); + + rc = mapping->a_ops->writepage(page, &wbc); + if (rc < 0) + /* I/O Error writing */ + return -EIO; + + if (rc != AOP_WRITEPAGE_ACTIVATE) + /* unlocked. Relock */ + lock_page(page); + + return -EAGAIN; +} + +/* + * Default handling if a filesystem does not provide a migration function. + */ +static int fallback_migrate_page(struct address_space *mapping, + struct page *newpage, struct page *page) +{ + if (PageDirty(page)) + return writeout(mapping, page); + + /* + * Buffers may be managed in a filesystem specific way. + * We must have no buffers or drop them. + */ + if (page_has_buffers(page) && + !try_to_release_page(page, GFP_KERNEL)) + return -EAGAIN; + + return migrate_page(mapping, newpage, page); +} + +/* + * Move a page to a newly allocated page + * The page is locked and all ptes have been successfully removed. + * + * The new page will have replaced the old page if this function + * is successful. + */ +static int move_to_new_page(struct page *newpage, struct page *page) +{ + struct address_space *mapping; + int rc; + + /* + * Block others from accessing the page when we get around to + * establishing additional references. We are the only one + * holding a reference to the new page at this point. + */ + if (TestSetPageLocked(newpage)) + BUG(); + + /* Prepare mapping for the new page.*/ + newpage->index = page->index; + newpage->mapping = page->mapping; + + mapping = page_mapping(page); + if (!mapping) + rc = migrate_page(mapping, newpage, page); + else if (mapping->a_ops->migratepage) + /* + * Most pages have a mapping and most filesystems + * should provide a migration function. Anonymous + * pages are part of swap space which also has its + * own migration function. This is the most common + * path for page migration. + */ + rc = mapping->a_ops->migratepage(mapping, + newpage, page); + else + rc = fallback_migrate_page(mapping, newpage, page); + + if (!rc) + remove_migration_ptes(page, newpage); + else + newpage->mapping = NULL; + + unlock_page(newpage); + + return rc; +} + +/* + * Obtain the lock on page, remove all ptes and migrate the page + * to the newly allocated page in newpage. + */ +static int unmap_and_move(new_page_t get_new_page, unsigned long private, + struct page *page, int force) +{ + int rc = 0; + int *result = NULL; + struct page *newpage = get_new_page(page, private, &result); + + if (!newpage) + return -ENOMEM; + + if (page_count(page) == 1) + /* page was freed from under us. So we are done. */ + goto move_newpage; + + rc = -EAGAIN; + if (TestSetPageLocked(page)) { + if (!force) + goto move_newpage; + lock_page(page); + } + + if (PageWriteback(page)) { + if (!force) + goto unlock; + wait_on_page_writeback(page); + } + + /* + * Establish migration ptes or remove ptes + */ + try_to_unmap(page, 1); + if (!page_mapped(page)) + rc = move_to_new_page(newpage, page); + + if (rc) + remove_migration_ptes(page, page); + +unlock: + unlock_page(page); + + if (rc != -EAGAIN) { + /* + * A page that has been migrated has all references + * removed and will be freed. A page that has not been + * migrated will have kepts its references and be + * restored. + */ + list_del(&page->lru); + move_to_lru(page); + } + +move_newpage: + /* + * Move the new page to the LRU. If migration was not successful + * then this will free the page. + */ + move_to_lru(newpage); + if (result) { + if (rc) + *result = rc; + else + *result = page_to_nid(newpage); + } + return rc; } -EXPORT_SYMBOL(migrate_page); /* * migrate_pages * - * Two lists are passed to this function. The first list - * contains the pages isolated from the LRU to be migrated. - * The second list contains new pages that the pages isolated - * can be moved to. If the second list is NULL then all - * pages are swapped out. + * The function takes one list of pages to migrate and a function + * that determines from the page to be migrated and the private data + * the target of the move and allocates the page. * * The function returns after 10 attempts or if no pages * are movable anymore because to has become empty - * or no retryable pages exist anymore. + * or no retryable pages exist anymore. All pages will be + * retruned to the LRU or freed. * - * Return: Number of pages not migrated when "to" ran empty. + * Return: Number of pages not migrated or error code. */ -int migrate_pages(struct list_head *from, struct list_head *to, - struct list_head *moved, struct list_head *failed) +int migrate_pages(struct list_head *from, + new_page_t get_new_page, unsigned long private) { - int retry; + int retry = 1; int nr_failed = 0; int pass = 0; struct page *page; @@ -358,305 +680,317 @@ int migrate_pages(struct list_head *from if (!swapwrite) current->flags |= PF_SWAPWRITE; -redo: - retry = 0; + for(pass = 0; pass < 10 && retry; pass++) { + retry = 0; + + list_for_each_entry_safe(page, page2, from, lru) { + cond_resched(); + + rc = unmap_and_move(get_new_page, private, + page, pass > 2); + + switch(rc) { + case -ENOMEM: + goto out; + case -EAGAIN: + retry++; + break; + case 0: + break; + default: + /* Permanent failure */ + nr_failed++; + break; + } + } + } + rc = 0; +out: + if (!swapwrite) + current->flags &= ~PF_SWAPWRITE; + + putback_lru_pages(from); + + if (rc) + return rc; - list_for_each_entry_safe(page, page2, from, lru) { - struct page *newpage = NULL; - struct address_space *mapping; + return nr_failed + retry; +} - cond_resched(); +#ifdef CONFIG_NUMA +/* + * Move a list of individual pages + */ +struct page_to_node { + unsigned long addr; + struct page *page; + int node; + int status; +}; - rc = 0; - if (page_count(page) == 1) - /* page was freed from under us. So we are done. */ - goto next; +static struct page *new_page_node(struct page *p, unsigned long private, + int **result) +{ + struct page_to_node *pm = (struct page_to_node *)private; - if (to && list_empty(to)) - break; + while (pm->node != MAX_NUMNODES && pm->page != p) + pm++; - /* - * Skip locked pages during the first two passes to give the - * functions holding the lock time to release the page. Later we - * use lock_page() to have a higher chance of acquiring the - * lock. - */ - rc = -EAGAIN; - if (pass > 2) - lock_page(page); - else - if (TestSetPageLocked(page)) - goto next; + if (pm->node == MAX_NUMNODES) + return NULL; - /* - * Only wait on writeback if we have already done a pass where - * we we may have triggered writeouts for lots of pages. - */ - if (pass > 0) { - wait_on_page_writeback(page); - } else { - if (PageWriteback(page)) - goto unlock_page; - } + *result = &pm->status; - /* - * Anonymous pages must have swap cache references otherwise - * the information contained in the page maps cannot be - * preserved. - */ - if (PageAnon(page) && !PageSwapCache(page)) { - if (!add_to_swap(page, GFP_KERNEL)) { - rc = -ENOMEM; - goto unlock_page; - } - } + return alloc_pages_node(pm->node, GFP_HIGHUSER, 0); +} - if (!to) { - rc = swap_page(page); - goto next; - } +/* + * Move a set of pages as indicated in the pm array. The addr + * field must be set to the virtual address of the page to be moved + * and the node number must contain a valid target node. + */ +static int do_move_pages(struct mm_struct *mm, struct page_to_node *pm, + int migrate_all) +{ + int err; + struct page_to_node *pp; + LIST_HEAD(pagelist); + + down_read(&mm->mmap_sem); - newpage = lru_to_page(to); - lock_page(newpage); + /* + * Build a list of pages to migrate + */ + migrate_prep(); + for (pp = pm; pp->node != MAX_NUMNODES; pp++) { + struct vm_area_struct *vma; + struct page *page; /* - * Pages are properly locked and writeback is complete. - * Try to migrate the page. + * A valid page pointer that will not match any of the + * pages that will be moved. */ - mapping = page_mapping(page); - if (!mapping) - goto unlock_both; + pp->page = ZERO_PAGE(0); - if (mapping->a_ops->migratepage) { - /* - * Most pages have a mapping and most filesystems - * should provide a migration function. Anonymous - * pages are part of swap space which also has its - * own migration function. This is the most common - * path for page migration. - */ - rc = mapping->a_ops->migratepage(newpage, page); - goto unlock_both; - } - - /* Make sure the dirty bit is up to date */ - if (try_to_unmap(page, 1) == SWAP_FAIL) { - rc = -EPERM; - goto unlock_both; - } + err = -EFAULT; + vma = find_vma(mm, pp->addr); + if (!vma) + goto set_status; - if (page_mapcount(page)) { - rc = -EAGAIN; - goto unlock_both; - } + page = follow_page(vma, pp->addr, FOLL_GET); + err = -ENOENT; + if (!page) + goto set_status; - /* - * Default handling if a filesystem does not provide - * a migration function. We can only migrate clean - * pages so try to write out any dirty pages first. - */ - if (PageDirty(page)) { - switch (pageout(page, mapping)) { - case PAGE_KEEP: - case PAGE_ACTIVATE: - goto unlock_both; - - case PAGE_SUCCESS: - unlock_page(newpage); - goto next; - - case PAGE_CLEAN: - ; /* try to migrate the page below */ - } - } + if (PageReserved(page)) /* Check for zero page */ + goto put_and_set; - /* - * Buffers are managed in a filesystem specific way. - * We must have no buffers or drop them. - */ - if (!page_has_buffers(page) || - try_to_release_page(page, GFP_KERNEL)) { - rc = migrate_page(newpage, page); - goto unlock_both; - } + pp->page = page; + err = page_to_nid(page); - /* - * On early passes with mapped pages simply - * retry. There may be a lock held for some - * buffers that may go away. Later - * swap them out. - */ - if (pass > 4) { + if (err == pp->node) /* - * Persistently unable to drop buffers..... As a - * measure of last resort we fall back to - * swap_page(). + * Node already in the right place */ - unlock_page(newpage); - newpage = NULL; - rc = swap_page(page); - goto next; - } + goto put_and_set; -unlock_both: - unlock_page(newpage); - -unlock_page: - unlock_page(page); - -next: - if (rc == -EAGAIN) { - retry++; - } else if (rc) { - /* Permanent failure */ - list_move(&page->lru, failed); - nr_failed++; - } else { - if (newpage) { - /* Successful migration. Return page to LRU */ - move_to_lru(newpage); - } - list_move(&page->lru, moved); - } + err = -EACCES; + if (page_mapcount(page) > 1 && + !migrate_all) + goto put_and_set; + + err = isolate_lru_page(page, &pagelist); +put_and_set: + /* + * Either remove the duplicate refcount from + * isolate_lru_page() or drop the page ref if it was + * not isolated. + */ + put_page(page); +set_status: + pp->status = err; } - if (retry && pass++ < 10) - goto redo; - if (!swapwrite) - current->flags &= ~PF_SWAPWRITE; + if (!list_empty(&pagelist)) + err = migrate_pages(&pagelist, new_page_node, + (unsigned long)pm); + else + err = -ENOENT; - return nr_failed + retry; + up_read(&mm->mmap_sem); + return err; } /* - * Migration function for pages with buffers. This function can only be used - * if the underlying filesystem guarantees that no other references to "page" - * exist. + * Determine the nodes of a list of pages. The addr in the pm array + * must have been set to the virtual address of which we want to determine + * the node number. */ -int buffer_migrate_page(struct page *newpage, struct page *page) +static int do_pages_stat(struct mm_struct *mm, struct page_to_node *pm) { - struct address_space *mapping = page->mapping; - struct buffer_head *bh, *head; - int rc; + down_read(&mm->mmap_sem); + + for ( ; pm->node != MAX_NUMNODES; pm++) { + struct vm_area_struct *vma; + struct page *page; + int err; + + err = -EFAULT; + vma = find_vma(mm, pm->addr); + if (!vma) + goto set_status; + + page = follow_page(vma, pm->addr, 0); + err = -ENOENT; + /* Use PageReserved to check for zero page */ + if (!page || PageReserved(page)) + goto set_status; + + err = page_to_nid(page); +set_status: + pm->status = err; + } - if (!mapping) - return -EAGAIN; + up_read(&mm->mmap_sem); + return 0; +} - if (!page_has_buffers(page)) - return migrate_page(newpage, page); +/* + * Move a list of pages in the address space of the currently executing + * process. + */ +asmlinkage long sys_move_pages(pid_t pid, unsigned long nr_pages, + const void __user * __user *pages, + const int __user *nodes, + int __user *status, int flags) +{ + int err = 0; + int i; + struct task_struct *task; + nodemask_t task_nodes; + struct mm_struct *mm; + struct page_to_node *pm = NULL; - head = page_buffers(page); + /* Check flags */ + if (flags & ~(MPOL_MF_MOVE|MPOL_MF_MOVE_ALL)) + return -EINVAL; - rc = migrate_page_remove_references(newpage, page, 3); + if ((flags & MPOL_MF_MOVE_ALL) && !capable(CAP_SYS_NICE)) + return -EPERM; - if (rc) - return rc; + /* Find the mm_struct */ + read_lock(&tasklist_lock); + task = pid ? find_task_by_pid(pid) : current; + if (!task) { + read_unlock(&tasklist_lock); + return -ESRCH; + } + mm = get_task_mm(task); + read_unlock(&tasklist_lock); - bh = head; - do { - get_bh(bh); - lock_buffer(bh); - bh = bh->b_this_page; + if (!mm) + return -EINVAL; - } while (bh != head); + /* + * Check if this process has the right to modify the specified + * process. The right exists if the process has administrative + * capabilities, superuser privileges or the same + * userid as the target process. + */ + if ((current->euid != task->suid) && (current->euid != task->uid) && + (current->uid != task->suid) && (current->uid != task->uid) && + !capable(CAP_SYS_NICE)) { + err = -EPERM; + goto out2; + } - ClearPagePrivate(page); - set_page_private(newpage, page_private(page)); - set_page_private(page, 0); - put_page(page); - get_page(newpage); + err = security_task_movememory(task); + if (err) + goto out2; - bh = head; - do { - set_bh_page(bh, newpage, bh_offset(bh)); - bh = bh->b_this_page; - } while (bh != head); + task_nodes = cpuset_mems_allowed(task); - SetPagePrivate(newpage); + /* Limit nr_pages so that the multiplication may not overflow */ + if (nr_pages >= ULONG_MAX / sizeof(struct page_to_node) - 1) { + err = -E2BIG; + goto out2; + } - migrate_page_copy(newpage, page); + pm = vmalloc((nr_pages + 1) * sizeof(struct page_to_node)); + if (!pm) { + err = -ENOMEM; + goto out2; + } - bh = head; - do { - unlock_buffer(bh); - put_bh(bh); - bh = bh->b_this_page; + /* + * Get parameters from user space and initialize the pm + * array. Return various errors if the user did something wrong. + */ + for (i = 0; i < nr_pages; i++) { + const void *p; - } while (bh != head); + err = -EFAULT; + if (get_user(p, pages + i)) + goto out; - return 0; -} -EXPORT_SYMBOL(buffer_migrate_page); + pm[i].addr = (unsigned long)p; + if (nodes) { + int node; -/* - * Migrate the list 'pagelist' of pages to a certain destination. - * - * Specify destination with either non-NULL vma or dest_node >= 0 - * Return the number of pages not migrated or error code - */ -int migrate_pages_to(struct list_head *pagelist, - struct vm_area_struct *vma, int dest) -{ - LIST_HEAD(newlist); - LIST_HEAD(moved); - LIST_HEAD(failed); - int err = 0; - unsigned long offset = 0; - int nr_pages; - struct page *page; - struct list_head *p; + if (get_user(node, nodes + i)) + goto out; -redo: - nr_pages = 0; - list_for_each(p, pagelist) { - if (vma) { - /* - * The address passed to alloc_page_vma is used to - * generate the proper interleave behavior. We fake - * the address here by an increasing offset in order - * to get the proper distribution of pages. - * - * No decision has been made as to which page - * a certain old page is moved to so we cannot - * specify the correct address. - */ - page = alloc_page_vma(GFP_HIGHUSER, vma, - offset + vma->vm_start); - offset += PAGE_SIZE; - } - else - page = alloc_pages_node(dest, GFP_HIGHUSER, 0); + err = -ENODEV; + if (!node_online(node)) + goto out; - if (!page) { - err = -ENOMEM; - goto out; + err = -EACCES; + if (!node_isset(node, task_nodes)) + goto out; + + pm[i].node = node; } - list_add_tail(&page->lru, &newlist); - nr_pages++; - if (nr_pages > MIGRATE_CHUNK_SIZE) - break; } - err = migrate_pages(pagelist, &newlist, &moved, &failed); + /* End marker */ + pm[nr_pages].node = MAX_NUMNODES; + + if (nodes) + err = do_move_pages(mm, pm, flags & MPOL_MF_MOVE_ALL); + else + err = do_pages_stat(mm, pm); - putback_lru_pages(&moved); /* Call release pages instead ?? */ + if (err >= 0) + /* Return status information */ + for (i = 0; i < nr_pages; i++) + if (put_user(pm[i].status, status + i)) + err = -EFAULT; - if (err >= 0 && list_empty(&newlist) && !list_empty(pagelist)) - goto redo; out: - /* Return leftover allocated pages */ - while (!list_empty(&newlist)) { - page = list_entry(newlist.next, struct page, lru); - list_del(&page->lru); - __free_page(page); - } - list_splice(&failed, pagelist); - if (err < 0) - return err; - - /* Calculate number of leftover pages */ - nr_pages = 0; - list_for_each(p, pagelist) - nr_pages++; - return nr_pages; + vfree(pm); +out2: + mmput(mm); + return err; +} +#endif + +/* + * Call migration functions in the vma_ops that may prepare + * memory in a vm for migration. migration functions may perform + * the migration for vmas that do not have an underlying page struct. + */ +int migrate_vmas(struct mm_struct *mm, const nodemask_t *to, + const nodemask_t *from, unsigned long flags) +{ + struct vm_area_struct *vma; + int err = 0; + + for(vma = mm->mmap; vma->vm_next && !err; vma = vma->vm_next) { + if (vma->vm_ops && vma->vm_ops->migrate) { + err = vma->vm_ops->migrate(vma, to, from, flags); + if (err) + break; + } + } + return err; } diff --git a/mm/mmap.c b/mm/mmap.c index e6ee123..c1868ec 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -96,7 +96,7 @@ int __vm_enough_memory(long pages, int c if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) { unsigned long n; - free = get_page_cache_size(); + free = global_page_state(NR_FILE_PAGES); free += nr_swap_pages; /* @@ -1065,7 +1065,8 @@ munmap_back: vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_flags = vm_flags; - vma->vm_page_prot = protection_map[vm_flags & 0x0f]; + vma->vm_page_prot = protection_map[vm_flags & + (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]; vma->vm_pgoff = pgoff; if (file) { @@ -1089,6 +1090,12 @@ munmap_back: goto free_vma; } + /* Don't make the VMA automatically writable if it's shared, but the + * backer wishes to know when pages are first written to */ + if (vma->vm_ops && vma->vm_ops->page_mkwrite) + vma->vm_page_prot = + protection_map[vm_flags & (VM_READ|VM_WRITE|VM_EXEC)]; + /* We set VM_ACCOUNT in a shared mapping's vm_flags, to inform * shmem_zero_setup (perhaps called through /dev/zero's ->mmap) * that memory reservation must be checked; but that reservation @@ -1921,7 +1928,8 @@ unsigned long do_brk(unsigned long addr, vma->vm_end = addr + len; vma->vm_pgoff = pgoff; vma->vm_flags = flags; - vma->vm_page_prot = protection_map[flags & 0x0f]; + vma->vm_page_prot = protection_map[flags & + (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)]; vma_link(mm, vma, prev, rb_link, rb_parent); out: mm->total_vm += len >> PAGE_SHIFT; diff --git a/mm/mmzone.c b/mm/mmzone.c index b022370..0959ee1 100644 --- a/mm/mmzone.c +++ b/mm/mmzone.c @@ -5,7 +5,6 @@ */ -#include #include #include #include diff --git a/mm/mprotect.c b/mm/mprotect.c index 4c14d42..638edab 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -19,7 +19,8 @@ #include #include #include #include - +#include +#include #include #include #include @@ -28,12 +29,13 @@ #include static void change_pte_range(struct mm_struct *mm, pmd_t *pmd, unsigned long addr, unsigned long end, pgprot_t newprot) { - pte_t *pte; + pte_t *pte, oldpte; spinlock_t *ptl; pte = pte_offset_map_lock(mm, pmd, addr, &ptl); do { - if (pte_present(*pte)) { + oldpte = *pte; + if (pte_present(oldpte)) { pte_t ptent; /* Avoid an SMP race with hardware updated dirty/clean @@ -43,7 +45,22 @@ static void change_pte_range(struct mm_s ptent = pte_modify(ptep_get_and_clear(mm, addr, pte), newprot); set_pte_at(mm, addr, pte, ptent); lazy_mmu_prot_update(ptent); +#ifdef CONFIG_MIGRATION + } else if (!pte_file(oldpte)) { + swp_entry_t entry = pte_to_swp_entry(oldpte); + + if (is_write_migration_entry(entry)) { + /* + * A protection check is difficult so + * just be safe and disable write + */ + make_migration_entry_read(&entry); + set_pte_at(mm, addr, pte, + swp_entry_to_pte(entry)); + } +#endif } + } while (pte++, addr += PAGE_SIZE, addr != end); pte_unmap_unlock(pte - 1, ptl); } @@ -106,6 +123,7 @@ mprotect_fixup(struct vm_area_struct *vm unsigned long oldflags = vma->vm_flags; long nrpages = (end - start) >> PAGE_SHIFT; unsigned long charged = 0; + unsigned int mask; pgprot_t newprot; pgoff_t pgoff; int error; @@ -132,8 +150,6 @@ mprotect_fixup(struct vm_area_struct *vm } } - newprot = protection_map[newflags & 0xf]; - /* * First try to merge with previous and/or next vma. */ @@ -160,6 +176,14 @@ mprotect_fixup(struct vm_area_struct *vm } success: + /* Don't make the VMA automatically writable if it's shared, but the + * backer wishes to know when pages are first written to */ + mask = VM_READ|VM_WRITE|VM_EXEC|VM_SHARED; + if (vma->vm_ops && vma->vm_ops->page_mkwrite) + mask &= ~VM_SHARED; + + newprot = protection_map[newflags & mask]; + /* * vm_flags and vm_page_prot are protected by the mmap_sem * held in write mode. @@ -205,8 +229,7 @@ sys_mprotect(unsigned long start, size_t /* * Does the application expect PROT_READ to imply PROT_EXEC: */ - if (unlikely((prot & PROT_READ) && - (current->personality & READ_IMPLIES_EXEC))) + if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC)) prot |= PROT_EXEC; vm_flags = calc_vm_prot_bits(prot); diff --git a/mm/mremap.c b/mm/mremap.c index 1903bdf..7c15cf3 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -97,7 +97,7 @@ static void move_ptes(struct vm_area_str new_pte = pte_offset_map_nested(new_pmd, new_addr); new_ptl = pte_lockptr(mm, new_pmd); if (new_ptl != old_ptl) - spin_lock(new_ptl); + spin_lock_nested(new_ptl, SINGLE_DEPTH_NESTING); for (; old_addr < old_end; old_pte++, old_addr += PAGE_SIZE, new_pte++, new_addr += PAGE_SIZE) { diff --git a/mm/msync.c b/mm/msync.c index bc6c953..d083544 100644 --- a/mm/msync.c +++ b/mm/msync.c @@ -170,8 +170,6 @@ asmlinkage long sys_msync(unsigned long * just ignore them, but return -ENOMEM at the end. */ down_read(¤t->mm->mmap_sem); - if (flags & MS_SYNC) - current->flags |= PF_SYNCWRITE; vma = find_vma(current->mm, start); if (!vma) { error = -ENOMEM; @@ -228,7 +226,6 @@ asmlinkage long sys_msync(unsigned long } } while (vma && !done); out_unlock: - current->flags &= ~PF_SYNCWRITE; up_read(¤t->mm->mmap_sem); out: return error; diff --git a/mm/nommu.c b/mm/nommu.c index 029fada..5151c44 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1122,7 +1122,7 @@ int __vm_enough_memory(long pages, int c if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) { unsigned long n; - free = get_page_cache_size(); + free = global_page_state(NR_FILE_PAGES); free += nr_swap_pages; /* diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 042e643..b9af136 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -22,10 +22,11 @@ #include #include #include +int sysctl_panic_on_oom; /* #define DEBUG */ /** - * oom_badness - calculate a numeric value for how bad this task has been + * badness - calculate a numeric value for how bad this task has been * @p: task struct of which task we should calculate * @uptime: current uptime in seconds * @@ -200,7 +201,7 @@ static struct task_struct *select_bad_pr continue; /* - * This is in the process of releasing memory so for wait it + * This is in the process of releasing memory so wait for it * to finish before killing some other task by mistake. */ releasing = test_tsk_thread_flag(p, TIF_MEMDIE) || @@ -224,7 +225,7 @@ static struct task_struct *select_bad_pr * CAP_SYS_RAW_IO set, send SIGTERM instead (but it's unlikely that * we select a process with CAP_SYS_RAW_IO set). */ -static void __oom_kill_task(task_t *p, const char *message) +static void __oom_kill_task(struct task_struct *p, const char *message) { if (p->pid == 1) { WARN_ON(1); @@ -254,10 +255,10 @@ static void __oom_kill_task(task_t *p, c force_sig(SIGKILL, p); } -static int oom_kill_task(task_t *p, const char *message) +static int oom_kill_task(struct task_struct *p, const char *message) { struct mm_struct *mm; - task_t * g, * q; + struct task_struct *g, *q; mm = p->mm; @@ -306,7 +307,7 @@ static int oom_kill_process(struct task_ } /** - * oom_kill - kill the "best" process when we run out of memory + * out_of_memory - kill the "best" process when we run out of memory * * If we run out of memory, we have the choice between either * killing a random task (bad), letting the system crash (worse) @@ -315,7 +316,7 @@ static int oom_kill_process(struct task_ */ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order) { - task_t *p; + struct task_struct *p; unsigned long points = 0; if (printk_ratelimit()) { @@ -344,6 +345,8 @@ void out_of_memory(struct zonelist *zone break; case CONSTRAINT_NONE: + if (sysctl_panic_on_oom) + panic("out of memory. panic_on_oom is selected\n"); retry: /* * Rambo mode: Shoot down a process and hope it solves whatever diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 75d7f48..e630188 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -99,22 +99,6 @@ EXPORT_SYMBOL(laptop_mode); static void background_writeout(unsigned long _min_pages); -struct writeback_state -{ - unsigned long nr_dirty; - unsigned long nr_unstable; - unsigned long nr_mapped; - unsigned long nr_writeback; -}; - -static void get_writeback_state(struct writeback_state *wbs) -{ - wbs->nr_dirty = read_page_state(nr_dirty); - wbs->nr_unstable = read_page_state(nr_unstable); - wbs->nr_mapped = read_page_state(nr_mapped); - wbs->nr_writeback = read_page_state(nr_writeback); -} - /* * Work out the current dirty-memory clamping and background writeout * thresholds. @@ -133,8 +117,8 @@ static void get_writeback_state(struct w * clamping level. */ static void -get_dirty_limits(struct writeback_state *wbs, long *pbackground, long *pdirty, - struct address_space *mapping) +get_dirty_limits(long *pbackground, long *pdirty, + struct address_space *mapping) { int background_ratio; /* Percentages */ int dirty_ratio; @@ -144,8 +128,6 @@ get_dirty_limits(struct writeback_state unsigned long available_memory = total_pages; struct task_struct *tsk; - get_writeback_state(wbs); - #ifdef CONFIG_HIGHMEM /* * If this mapping can only allocate from low memory, @@ -156,7 +138,9 @@ #ifdef CONFIG_HIGHMEM #endif - unmapped_ratio = 100 - (wbs->nr_mapped * 100) / total_pages; + unmapped_ratio = 100 - ((global_page_state(NR_FILE_MAPPED) + + global_page_state(NR_ANON_PAGES)) * 100) / + total_pages; dirty_ratio = vm_dirty_ratio; if (dirty_ratio > unmapped_ratio / 2) @@ -189,7 +173,6 @@ #endif */ static void balance_dirty_pages(struct address_space *mapping) { - struct writeback_state wbs; long nr_reclaimable; long background_thresh; long dirty_thresh; @@ -204,13 +187,15 @@ static void balance_dirty_pages(struct a .sync_mode = WB_SYNC_NONE, .older_than_this = NULL, .nr_to_write = write_chunk, + .range_cyclic = 1, }; - get_dirty_limits(&wbs, &background_thresh, - &dirty_thresh, mapping); - nr_reclaimable = wbs.nr_dirty + wbs.nr_unstable; - if (nr_reclaimable + wbs.nr_writeback <= dirty_thresh) - break; + get_dirty_limits(&background_thresh, &dirty_thresh, mapping); + nr_reclaimable = global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS); + if (nr_reclaimable + global_page_state(NR_WRITEBACK) <= + dirty_thresh) + break; if (!dirty_exceeded) dirty_exceeded = 1; @@ -223,11 +208,14 @@ static void balance_dirty_pages(struct a */ if (nr_reclaimable) { writeback_inodes(&wbc); - get_dirty_limits(&wbs, &background_thresh, - &dirty_thresh, mapping); - nr_reclaimable = wbs.nr_dirty + wbs.nr_unstable; - if (nr_reclaimable + wbs.nr_writeback <= dirty_thresh) - break; + get_dirty_limits(&background_thresh, + &dirty_thresh, mapping); + nr_reclaimable = global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS); + if (nr_reclaimable + + global_page_state(NR_WRITEBACK) + <= dirty_thresh) + break; pages_written += write_chunk - wbc.nr_to_write; if (pages_written >= write_chunk) break; /* We've done our duty */ @@ -235,8 +223,9 @@ static void balance_dirty_pages(struct a blk_congestion_wait(WRITE, HZ/10); } - if (nr_reclaimable + wbs.nr_writeback <= dirty_thresh && dirty_exceeded) - dirty_exceeded = 0; + if (nr_reclaimable + global_page_state(NR_WRITEBACK) + <= dirty_thresh && dirty_exceeded) + dirty_exceeded = 0; if (writeback_in_progress(bdi)) return; /* pdflush is already working this queue */ @@ -298,12 +287,11 @@ EXPORT_SYMBOL(balance_dirty_pages_rateli void throttle_vm_writeout(void) { - struct writeback_state wbs; long background_thresh; long dirty_thresh; for ( ; ; ) { - get_dirty_limits(&wbs, &background_thresh, &dirty_thresh, NULL); + get_dirty_limits(&background_thresh, &dirty_thresh, NULL); /* * Boost the allowable dirty threshold a bit for page @@ -311,8 +299,9 @@ void throttle_vm_writeout(void) */ dirty_thresh += dirty_thresh / 10; /* wheeee... */ - if (wbs.nr_unstable + wbs.nr_writeback <= dirty_thresh) - break; + if (global_page_state(NR_UNSTABLE_NFS) + + global_page_state(NR_WRITEBACK) <= dirty_thresh) + break; blk_congestion_wait(WRITE, HZ/10); } } @@ -331,15 +320,16 @@ static void background_writeout(unsigned .older_than_this = NULL, .nr_to_write = 0, .nonblocking = 1, + .range_cyclic = 1, }; for ( ; ; ) { - struct writeback_state wbs; long background_thresh; long dirty_thresh; - get_dirty_limits(&wbs, &background_thresh, &dirty_thresh, NULL); - if (wbs.nr_dirty + wbs.nr_unstable < background_thresh + get_dirty_limits(&background_thresh, &dirty_thresh, NULL); + if (global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS) < background_thresh && min_pages <= 0) break; wbc.encountered_congestion = 0; @@ -363,12 +353,9 @@ static void background_writeout(unsigned */ int wakeup_pdflush(long nr_pages) { - if (nr_pages == 0) { - struct writeback_state wbs; - - get_writeback_state(&wbs); - nr_pages = wbs.nr_dirty + wbs.nr_unstable; - } + if (nr_pages == 0) + nr_pages = global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS); return pdflush_operation(background_writeout, nr_pages); } @@ -399,7 +386,6 @@ static void wb_kupdate(unsigned long arg unsigned long start_jif; unsigned long next_jif; long nr_to_write; - struct writeback_state wbs; struct writeback_control wbc = { .bdi = NULL, .sync_mode = WB_SYNC_NONE, @@ -407,15 +393,16 @@ static void wb_kupdate(unsigned long arg .nr_to_write = 0, .nonblocking = 1, .for_kupdate = 1, + .range_cyclic = 1, }; sync_supers(); - get_writeback_state(&wbs); oldest_jif = jiffies - dirty_expire_interval; start_jif = jiffies; next_jif = start_jif + dirty_writeback_interval; - nr_to_write = wbs.nr_dirty + wbs.nr_unstable + + nr_to_write = global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS) + (inodes_stat.nr_inodes - inodes_stat.nr_unused); while (nr_to_write > 0) { wbc.encountered_congestion = 0; @@ -513,14 +500,14 @@ static void set_ratelimit(void) ratelimit_pages = (4096 * 1024) / PAGE_CACHE_SIZE; } -static int +static int __cpuinit ratelimit_handler(struct notifier_block *self, unsigned long u, void *v) { set_ratelimit(); return 0; } -static struct notifier_block ratelimit_nb = { +static struct notifier_block __cpuinitdata ratelimit_nb = { .notifier_call = ratelimit_handler, .next = NULL, }; @@ -637,7 +624,8 @@ int __set_page_dirty_nobuffers(struct pa if (mapping2) { /* Race with truncate? */ BUG_ON(mapping2 != mapping); if (mapping_cap_account_dirty(mapping)) - inc_page_state(nr_dirty); + __inc_zone_page_state(page, + NR_FILE_DIRTY); radix_tree_tag_set(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); } @@ -724,9 +712,9 @@ int test_clear_page_dirty(struct page *p radix_tree_tag_clear(&mapping->page_tree, page_index(page), PAGECACHE_TAG_DIRTY); - write_unlock_irqrestore(&mapping->tree_lock, flags); if (mapping_cap_account_dirty(mapping)) - dec_page_state(nr_dirty); + __dec_zone_page_state(page, NR_FILE_DIRTY); + write_unlock_irqrestore(&mapping->tree_lock, flags); return 1; } write_unlock_irqrestore(&mapping->tree_lock, flags); @@ -757,7 +745,7 @@ int clear_page_dirty_for_io(struct page if (mapping) { if (TestClearPageDirty(page)) { if (mapping_cap_account_dirty(mapping)) - dec_page_state(nr_dirty); + dec_zone_page_state(page, NR_FILE_DIRTY); return 1; } return 0; diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 253a450..54a4f53 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -14,7 +14,6 @@ * (lots of bits borrowed from Ingo Molnar & Andrew Morton) */ -#include #include #include #include @@ -37,6 +36,7 @@ #include #include #include #include +#include #include #include @@ -83,8 +83,8 @@ EXPORT_SYMBOL(zone_table); static char *zone_names[MAX_NR_ZONES] = { "DMA", "DMA32", "Normal", "HighMem" }; int min_free_kbytes = 1024; -unsigned long __initdata nr_kernel_pages; -unsigned long __initdata nr_all_pages; +unsigned long __meminitdata nr_kernel_pages; +unsigned long __meminitdata nr_all_pages; #ifdef CONFIG_DEBUG_VM static int page_outside_zone_boundaries(struct zone *zone, struct page *page) @@ -265,7 +265,7 @@ static inline void rmv_page_order(struct * satisfies the following equation: * P = B & ~(1 << O) * - * Assumption: *_mem_map is contigious at least up to MAX_ORDER + * Assumption: *_mem_map is contiguous at least up to MAX_ORDER */ static inline struct page * __page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order) @@ -286,22 +286,27 @@ __find_combined_index(unsigned long page * we can do coalesce a page and its buddy if * (a) the buddy is not in a hole && * (b) the buddy is in the buddy system && - * (c) a page and its buddy have the same order. + * (c) a page and its buddy have the same order && + * (d) a page and its buddy are in the same zone. * * For recording whether a page is in the buddy system, we use PG_buddy. * Setting, clearing, and testing PG_buddy is serialized by zone->lock. * * For recording page's order, we use page_private(page). */ -static inline int page_is_buddy(struct page *page, int order) +static inline int page_is_buddy(struct page *page, struct page *buddy, + int order) { #ifdef CONFIG_HOLES_IN_ZONE - if (!pfn_valid(page_to_pfn(page))) + if (!pfn_valid(page_to_pfn(buddy))) return 0; #endif - if (PageBuddy(page) && page_order(page) == order) { - BUG_ON(page_count(page) != 0); + if (page_zone_id(page) != page_zone_id(buddy)) + return 0; + + if (PageBuddy(buddy) && page_order(buddy) == order) { + BUG_ON(page_count(buddy) != 0); return 1; } return 0; @@ -352,7 +357,7 @@ static inline void __free_one_page(struc struct page *buddy; buddy = __page_find_buddy(page, page_idx, order); - if (!page_is_buddy(buddy, order)) + if (!page_is_buddy(page, buddy, order)) break; /* Move the buddy up one level. */ list_del(&buddy->lru); @@ -440,8 +445,8 @@ static void __free_pages_ok(struct page arch_free_page(page, order); if (!PageHighMem(page)) - mutex_debug_check_no_locks_freed(page_address(page), - PAGE_SIZE<zone_pgdat; - pg_data_t *orig = zonelist->zones[0]->zone_pgdat; - struct per_cpu_pageset *p; - - p = zone_pcp(z, cpu); - if (pg == orig) { - p->numa_hit++; - } else { - p->numa_miss++; - zone_pcp(zonelist->zones[0], cpu)->numa_foreign++; - } - if (pg == NODE_DATA(numa_node_id())) - p->local_node++; - else - p->other_node++; -#endif -} - /* * Free a 0-order page */ @@ -744,7 +728,7 @@ static void fastcall free_hot_cold_page( pcp = &zone_pcp(zone, get_cpu())->pcp[cold]; local_irq_save(flags); - __inc_page_state(pgfree); + __count_vm_event(PGFREE); list_add(&page->lru, &pcp->list); pcp->count++; if (pcp->count >= pcp->high) { @@ -820,8 +804,8 @@ again: goto failed; } - __mod_page_state_zone(zone, pgalloc, 1 << order); - zone_statistics(zonelist, zone, cpu); + __count_zone_vm_events(PGALLOC, zone, 1 << order); + zone_statistics(zonelist, zone); local_irq_restore(flags); put_cpu(); @@ -951,8 +935,7 @@ restart: goto got_pg; do { - if (cpuset_zone_allowed(*z, gfp_mask|__GFP_HARDWALL)) - wakeup_kswapd(*z, order); + wakeup_kswapd(*z, order); } while (*(++z)); /* @@ -1226,141 +1209,6 @@ #else #define show_node(zone) do { } while (0) #endif -/* - * Accumulate the page_state information across all CPUs. - * The result is unavoidably approximate - it can change - * during and after execution of this function. - */ -static DEFINE_PER_CPU(struct page_state, page_states) = {0}; - -atomic_t nr_pagecache = ATOMIC_INIT(0); -EXPORT_SYMBOL(nr_pagecache); -#ifdef CONFIG_SMP -DEFINE_PER_CPU(long, nr_pagecache_local) = 0; -#endif - -static void __get_page_state(struct page_state *ret, int nr, cpumask_t *cpumask) -{ - unsigned cpu; - - memset(ret, 0, nr * sizeof(unsigned long)); - cpus_and(*cpumask, *cpumask, cpu_online_map); - - for_each_cpu_mask(cpu, *cpumask) { - unsigned long *in; - unsigned long *out; - unsigned off; - unsigned next_cpu; - - in = (unsigned long *)&per_cpu(page_states, cpu); - - next_cpu = next_cpu(cpu, *cpumask); - if (likely(next_cpu < NR_CPUS)) - prefetch(&per_cpu(page_states, next_cpu)); - - out = (unsigned long *)ret; - for (off = 0; off < nr; off++) - *out++ += *in++; - } -} - -void get_page_state_node(struct page_state *ret, int node) -{ - int nr; - cpumask_t mask = node_to_cpumask(node); - - nr = offsetof(struct page_state, GET_PAGE_STATE_LAST); - nr /= sizeof(unsigned long); - - __get_page_state(ret, nr+1, &mask); -} - -void get_page_state(struct page_state *ret) -{ - int nr; - cpumask_t mask = CPU_MASK_ALL; - - nr = offsetof(struct page_state, GET_PAGE_STATE_LAST); - nr /= sizeof(unsigned long); - - __get_page_state(ret, nr + 1, &mask); -} - -void get_full_page_state(struct page_state *ret) -{ - cpumask_t mask = CPU_MASK_ALL; - - __get_page_state(ret, sizeof(*ret) / sizeof(unsigned long), &mask); -} - -unsigned long read_page_state_offset(unsigned long offset) -{ - unsigned long ret = 0; - int cpu; - - for_each_online_cpu(cpu) { - unsigned long in; - - in = (unsigned long)&per_cpu(page_states, cpu) + offset; - ret += *((unsigned long *)in); - } - return ret; -} - -void __mod_page_state_offset(unsigned long offset, unsigned long delta) -{ - void *ptr; - - ptr = &__get_cpu_var(page_states); - *(unsigned long *)(ptr + offset) += delta; -} -EXPORT_SYMBOL(__mod_page_state_offset); - -void mod_page_state_offset(unsigned long offset, unsigned long delta) -{ - unsigned long flags; - void *ptr; - - local_irq_save(flags); - ptr = &__get_cpu_var(page_states); - *(unsigned long *)(ptr + offset) += delta; - local_irq_restore(flags); -} -EXPORT_SYMBOL(mod_page_state_offset); - -void __get_zone_counts(unsigned long *active, unsigned long *inactive, - unsigned long *free, struct pglist_data *pgdat) -{ - struct zone *zones = pgdat->node_zones; - int i; - - *active = 0; - *inactive = 0; - *free = 0; - for (i = 0; i < MAX_NR_ZONES; i++) { - *active += zones[i].nr_active; - *inactive += zones[i].nr_inactive; - *free += zones[i].free_pages; - } -} - -void get_zone_counts(unsigned long *active, - unsigned long *inactive, unsigned long *free) -{ - struct pglist_data *pgdat; - - *active = 0; - *inactive = 0; - *free = 0; - for_each_online_pgdat(pgdat) { - unsigned long l, m, n; - __get_zone_counts(&l, &m, &n, pgdat); - *active += l; - *inactive += m; - *free += n; - } -} - void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages; @@ -1401,7 +1249,6 @@ #define K(x) ((x) << (PAGE_SHIFT-10)) */ void show_free_areas(void) { - struct page_state ps; int cpu, temperature; unsigned long active; unsigned long inactive; @@ -1433,7 +1280,6 @@ void show_free_areas(void) } } - get_page_state(&ps); get_zone_counts(&active, &inactive, &free); printk("Free pages: %11ukB (%ukB HighMem)\n", @@ -1444,13 +1290,13 @@ void show_free_areas(void) "unstable:%lu free:%u slab:%lu mapped:%lu pagetables:%lu\n", active, inactive, - ps.nr_dirty, - ps.nr_writeback, - ps.nr_unstable, + global_page_state(NR_FILE_DIRTY), + global_page_state(NR_WRITEBACK), + global_page_state(NR_UNSTABLE_NFS), nr_free_pages(), - ps.nr_slab, - ps.nr_mapped, - ps.nr_page_table_pages); + global_page_state(NR_SLAB), + global_page_state(NR_FILE_MAPPED), + global_page_state(NR_PAGETABLE)); for_each_zone(zone) { int i; @@ -1485,7 +1331,7 @@ void show_free_areas(void) } for_each_zone(zone) { - unsigned long nr, flags, order, total = 0; + unsigned long nr[MAX_ORDER], flags, order, total = 0; show_node(zone); printk("%s: ", zone->name); @@ -1496,11 +1342,12 @@ void show_free_areas(void) spin_lock_irqsave(&zone->lock, flags); for (order = 0; order < MAX_ORDER; order++) { - nr = zone->free_area[order].nr_free; - total += nr << order; - printk("%lu*%lukB ", nr, K(1UL) << order); + nr[order] = zone->free_area[order].nr_free; + total += nr[order] << order; } spin_unlock_irqrestore(&zone->lock, flags); + for (order = 0; order < MAX_ORDER; order++) + printk("%lu*%lukB ", nr[order], K(1UL) << order); printk("= %lukB\n", K(total)); } @@ -1512,7 +1359,7 @@ void show_free_areas(void) * * Add all populated zones of a node to the zonelist. */ -static int __init build_zonelists_node(pg_data_t *pgdat, +static int __meminit build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist, int nr_zones, int zone_type) { struct zone *zone; @@ -1548,7 +1395,7 @@ static inline int highest_zone(int zone_ #ifdef CONFIG_NUMA #define MAX_NODE_LOAD (num_online_nodes()) -static int __initdata node_load[MAX_NUMNODES]; +static int __meminitdata node_load[MAX_NUMNODES]; /** * find_next_best_node - find the next node that should appear in a given node's fallback list * @node: node whose fallback list we're appending @@ -1563,7 +1410,7 @@ static int __initdata node_load[MAX_NUMN * on them otherwise. * It returns -1 if no node is found. */ -static int __init find_next_best_node(int node, nodemask_t *used_node_mask) +static int __meminit find_next_best_node(int node, nodemask_t *used_node_mask) { int n, val; int min_val = INT_MAX; @@ -1609,7 +1456,7 @@ static int __init find_next_best_node(in return best_node; } -static void __init build_zonelists(pg_data_t *pgdat) +static void __meminit build_zonelists(pg_data_t *pgdat) { int i, j, k, node, local_node; int prev_node, load; @@ -1661,7 +1508,7 @@ static void __init build_zonelists(pg_da #else /* CONFIG_NUMA */ -static void __init build_zonelists(pg_data_t *pgdat) +static void __meminit build_zonelists(pg_data_t *pgdat) { int i, j, k, node, local_node; @@ -1699,14 +1546,29 @@ static void __init build_zonelists(pg_da #endif /* CONFIG_NUMA */ -void __init build_all_zonelists(void) +/* return values int ....just for stop_machine_run() */ +static int __meminit __build_all_zonelists(void *dummy) { - int i; + int nid; + for_each_online_node(nid) + build_zonelists(NODE_DATA(nid)); + return 0; +} - for_each_online_node(i) - build_zonelists(NODE_DATA(i)); - printk("Built %i zonelists\n", num_online_nodes()); - cpuset_init_current_mems_allowed(); +void __meminit build_all_zonelists(void) +{ + if (system_state == SYSTEM_BOOTING) { + __build_all_zonelists(0); + cpuset_init_current_mems_allowed(); + } else { + /* we have to stop all cpus to guaranntee there is no user + of zonelist */ + stop_machine_run(__build_all_zonelists, NULL, NR_CPUS); + /* cpuset refresh routine should be here */ + } + vm_total_pages = nr_free_pagecache_pages(); + printk("Built %i zonelists. Total pages: %ld\n", + num_online_nodes(), vm_total_pages); } /* @@ -1722,7 +1584,8 @@ void __init build_all_zonelists(void) */ #define PAGES_PER_WAITQUEUE 256 -static inline unsigned long wait_table_size(unsigned long pages) +#ifndef CONFIG_MEMORY_HOTPLUG +static inline unsigned long wait_table_hash_nr_entries(unsigned long pages) { unsigned long size = 1; @@ -1740,6 +1603,29 @@ static inline unsigned long wait_table_s return max(size, 4UL); } +#else +/* + * A zone's size might be changed by hot-add, so it is not possible to determine + * a suitable size for its wait_table. So we use the maximum size now. + * + * The max wait table size = 4096 x sizeof(wait_queue_head_t). ie: + * + * i386 (preemption config) : 4096 x 16 = 64Kbyte. + * ia64, x86-64 (no preemption): 4096 x 20 = 80Kbyte. + * ia64, x86-64 (preemption) : 4096 x 24 = 96Kbyte. + * + * The maximum entries are prepared when a zone's memory is (512K + 256) pages + * or more by the traditional way. (See above). It equals: + * + * i386, x86-64, powerpc(4K page size) : = ( 2G + 1M)byte. + * ia64(16K page size) : = ( 8G + 4M)byte. + * powerpc (64K page size) : = (32G +16M)byte. + */ +static inline unsigned long wait_table_hash_nr_entries(unsigned long pages) +{ + return 4096UL; +} +#endif /* * This is an integer logarithm so that shifts can be used later @@ -1964,7 +1850,7 @@ static inline void free_zone_pagesets(in } } -static int pageset_cpuup_callback(struct notifier_block *nfb, +static int __cpuinit pageset_cpuup_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { @@ -1986,7 +1872,7 @@ static int pageset_cpuup_callback(struct return ret; } -static struct notifier_block pageset_notifier = +static struct notifier_block __cpuinitdata pageset_notifier = { &pageset_cpuup_callback, NULL, 0 }; void __init setup_per_cpu_pageset(void) @@ -2005,23 +1891,46 @@ void __init setup_per_cpu_pageset(void) #endif static __meminit -void zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages) +int zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages) { int i; struct pglist_data *pgdat = zone->zone_pgdat; + size_t alloc_size; /* * The per-page waitqueue mechanism uses hashed waitqueues * per zone. */ - zone->wait_table_size = wait_table_size(zone_size_pages); - zone->wait_table_bits = wait_table_bits(zone->wait_table_size); - zone->wait_table = (wait_queue_head_t *) - alloc_bootmem_node(pgdat, zone->wait_table_size - * sizeof(wait_queue_head_t)); + zone->wait_table_hash_nr_entries = + wait_table_hash_nr_entries(zone_size_pages); + zone->wait_table_bits = + wait_table_bits(zone->wait_table_hash_nr_entries); + alloc_size = zone->wait_table_hash_nr_entries + * sizeof(wait_queue_head_t); + + if (system_state == SYSTEM_BOOTING) { + zone->wait_table = (wait_queue_head_t *) + alloc_bootmem_node(pgdat, alloc_size); + } else { + /* + * This case means that a zone whose size was 0 gets new memory + * via memory hot-add. + * But it may be the case that a new node was hot-added. In + * this case vmalloc() will not be able to use this new node's + * memory - this wait_table must be initialized to use this new + * node itself as well. + * To use this new node's memory, further consideration will be + * necessary. + */ + zone->wait_table = (wait_queue_head_t *)vmalloc(alloc_size); + } + if (!zone->wait_table) + return -ENOMEM; - for(i = 0; i < zone->wait_table_size; ++i) + for(i = 0; i < zone->wait_table_hash_nr_entries; ++i) init_waitqueue_head(zone->wait_table + i); + + return 0; } static __meminit void zone_pcp_init(struct zone *zone) @@ -2043,12 +1952,15 @@ #endif zone->name, zone->present_pages, batch); } -static __meminit void init_currently_empty_zone(struct zone *zone, - unsigned long zone_start_pfn, unsigned long size) +__meminit int init_currently_empty_zone(struct zone *zone, + unsigned long zone_start_pfn, + unsigned long size) { struct pglist_data *pgdat = zone->zone_pgdat; - - zone_wait_table_init(zone, size); + int ret; + ret = zone_wait_table_init(zone, size); + if (ret) + return ret; pgdat->nr_zones = zone_idx(zone) + 1; zone->zone_start_pfn = zone_start_pfn; @@ -2056,6 +1968,8 @@ static __meminit void init_currently_emp memmap_init(size, pgdat->node_id, zone_idx(zone), zone_start_pfn); zone_init_free_lists(pgdat, zone, zone->spanned_pages); + + return 0; } /* @@ -2064,12 +1978,13 @@ static __meminit void init_currently_emp * - mark all memory queues empty * - clear the memory bitmaps */ -static void __init free_area_init_core(struct pglist_data *pgdat, +static void __meminit free_area_init_core(struct pglist_data *pgdat, unsigned long *zones_size, unsigned long *zholes_size) { unsigned long j; int nid = pgdat->node_id; unsigned long zone_start_pfn = pgdat->node_start_pfn; + int ret; pgdat_resize_init(pgdat); pgdat->nr_zones = 0; @@ -2090,6 +2005,10 @@ static void __init free_area_init_core(s zone->spanned_pages = size; zone->present_pages = realsize; +#ifdef CONFIG_NUMA + zone->min_unmapped_ratio = (realsize*sysctl_min_unmapped_ratio) + / 100; +#endif zone->name = zone_names[j]; spin_lock_init(&zone->lock); spin_lock_init(&zone->lru_lock); @@ -2106,12 +2025,14 @@ static void __init free_area_init_core(s zone->nr_scan_inactive = 0; zone->nr_active = 0; zone->nr_inactive = 0; + zap_zone_vm_stats(zone); atomic_set(&zone->reclaim_in_progress, 0); if (!size) continue; zonetable_add(zone, nid, j, zone_start_pfn, size); - init_currently_empty_zone(zone, zone_start_pfn, size); + ret = init_currently_empty_zone(zone, zone_start_pfn, size); + BUG_ON(ret); zone_start_pfn += size; } } @@ -2152,7 +2073,7 @@ #endif #endif /* CONFIG_FLAT_NODE_MEM_MAP */ } -void __init free_area_init_node(int nid, struct pglist_data *pgdat, +void __meminit free_area_init_node(int nid, struct pglist_data *pgdat, unsigned long *zones_size, unsigned long node_start_pfn, unsigned long *zholes_size) { @@ -2178,307 +2099,18 @@ void __init free_area_init(unsigned long __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL); } -#ifdef CONFIG_PROC_FS - -#include - -static void *frag_start(struct seq_file *m, loff_t *pos) -{ - pg_data_t *pgdat; - loff_t node = *pos; - for (pgdat = first_online_pgdat(); - pgdat && node; - pgdat = next_online_pgdat(pgdat)) - --node; - - return pgdat; -} - -static void *frag_next(struct seq_file *m, void *arg, loff_t *pos) -{ - pg_data_t *pgdat = (pg_data_t *)arg; - - (*pos)++; - return next_online_pgdat(pgdat); -} - -static void frag_stop(struct seq_file *m, void *arg) -{ -} - -/* - * This walks the free areas for each zone. - */ -static int frag_show(struct seq_file *m, void *arg) -{ - pg_data_t *pgdat = (pg_data_t *)arg; - struct zone *zone; - struct zone *node_zones = pgdat->node_zones; - unsigned long flags; - int order; - - for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { - if (!populated_zone(zone)) - continue; - - spin_lock_irqsave(&zone->lock, flags); - seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name); - for (order = 0; order < MAX_ORDER; ++order) - seq_printf(m, "%6lu ", zone->free_area[order].nr_free); - spin_unlock_irqrestore(&zone->lock, flags); - seq_putc(m, '\n'); - } - return 0; -} - -struct seq_operations fragmentation_op = { - .start = frag_start, - .next = frag_next, - .stop = frag_stop, - .show = frag_show, -}; - -/* - * Output information about zones in @pgdat. - */ -static int zoneinfo_show(struct seq_file *m, void *arg) -{ - pg_data_t *pgdat = arg; - struct zone *zone; - struct zone *node_zones = pgdat->node_zones; - unsigned long flags; - - for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) { - int i; - - if (!populated_zone(zone)) - continue; - - spin_lock_irqsave(&zone->lock, flags); - seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name); - seq_printf(m, - "\n pages free %lu" - "\n min %lu" - "\n low %lu" - "\n high %lu" - "\n active %lu" - "\n inactive %lu" - "\n scanned %lu (a: %lu i: %lu)" - "\n spanned %lu" - "\n present %lu", - zone->free_pages, - zone->pages_min, - zone->pages_low, - zone->pages_high, - zone->nr_active, - zone->nr_inactive, - zone->pages_scanned, - zone->nr_scan_active, zone->nr_scan_inactive, - zone->spanned_pages, - zone->present_pages); - seq_printf(m, - "\n protection: (%lu", - zone->lowmem_reserve[0]); - for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++) - seq_printf(m, ", %lu", zone->lowmem_reserve[i]); - seq_printf(m, - ")" - "\n pagesets"); - for_each_online_cpu(i) { - struct per_cpu_pageset *pageset; - int j; - - pageset = zone_pcp(zone, i); - for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) { - if (pageset->pcp[j].count) - break; - } - if (j == ARRAY_SIZE(pageset->pcp)) - continue; - for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) { - seq_printf(m, - "\n cpu: %i pcp: %i" - "\n count: %i" - "\n high: %i" - "\n batch: %i", - i, j, - pageset->pcp[j].count, - pageset->pcp[j].high, - pageset->pcp[j].batch); - } -#ifdef CONFIG_NUMA - seq_printf(m, - "\n numa_hit: %lu" - "\n numa_miss: %lu" - "\n numa_foreign: %lu" - "\n interleave_hit: %lu" - "\n local_node: %lu" - "\n other_node: %lu", - pageset->numa_hit, - pageset->numa_miss, - pageset->numa_foreign, - pageset->interleave_hit, - pageset->local_node, - pageset->other_node); -#endif - } - seq_printf(m, - "\n all_unreclaimable: %u" - "\n prev_priority: %i" - "\n temp_priority: %i" - "\n start_pfn: %lu", - zone->all_unreclaimable, - zone->prev_priority, - zone->temp_priority, - zone->zone_start_pfn); - spin_unlock_irqrestore(&zone->lock, flags); - seq_putc(m, '\n'); - } - return 0; -} - -struct seq_operations zoneinfo_op = { - .start = frag_start, /* iterate over all zones. The same as in - * fragmentation. */ - .next = frag_next, - .stop = frag_stop, - .show = zoneinfo_show, -}; - -static char *vmstat_text[] = { - "nr_dirty", - "nr_writeback", - "nr_unstable", - "nr_page_table_pages", - "nr_mapped", - "nr_slab", - - "pgpgin", - "pgpgout", - "pswpin", - "pswpout", - - "pgalloc_high", - "pgalloc_normal", - "pgalloc_dma32", - "pgalloc_dma", - - "pgfree", - "pgactivate", - "pgdeactivate", - - "pgfault", - "pgmajfault", - - "pgrefill_high", - "pgrefill_normal", - "pgrefill_dma32", - "pgrefill_dma", - - "pgsteal_high", - "pgsteal_normal", - "pgsteal_dma32", - "pgsteal_dma", - - "pgscan_kswapd_high", - "pgscan_kswapd_normal", - "pgscan_kswapd_dma32", - "pgscan_kswapd_dma", - - "pgscan_direct_high", - "pgscan_direct_normal", - "pgscan_direct_dma32", - "pgscan_direct_dma", - - "pginodesteal", - "slabs_scanned", - "kswapd_steal", - "kswapd_inodesteal", - "pageoutrun", - "allocstall", - - "pgrotated", - "nr_bounce", -}; - -static void *vmstat_start(struct seq_file *m, loff_t *pos) -{ - struct page_state *ps; - - if (*pos >= ARRAY_SIZE(vmstat_text)) - return NULL; - - ps = kmalloc(sizeof(*ps), GFP_KERNEL); - m->private = ps; - if (!ps) - return ERR_PTR(-ENOMEM); - get_full_page_state(ps); - ps->pgpgin /= 2; /* sectors -> kbytes */ - ps->pgpgout /= 2; - return (unsigned long *)ps + *pos; -} - -static void *vmstat_next(struct seq_file *m, void *arg, loff_t *pos) -{ - (*pos)++; - if (*pos >= ARRAY_SIZE(vmstat_text)) - return NULL; - return (unsigned long *)m->private + *pos; -} - -static int vmstat_show(struct seq_file *m, void *arg) -{ - unsigned long *l = arg; - unsigned long off = l - (unsigned long *)m->private; - - seq_printf(m, "%s %lu\n", vmstat_text[off], *l); - return 0; -} - -static void vmstat_stop(struct seq_file *m, void *arg) -{ - kfree(m->private); - m->private = NULL; -} - -struct seq_operations vmstat_op = { - .start = vmstat_start, - .next = vmstat_next, - .stop = vmstat_stop, - .show = vmstat_show, -}; - -#endif /* CONFIG_PROC_FS */ - #ifdef CONFIG_HOTPLUG_CPU static int page_alloc_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { int cpu = (unsigned long)hcpu; - long *count; - unsigned long *src, *dest; if (action == CPU_DEAD) { - int i; - - /* Drain local pagecache count. */ - count = &per_cpu(nr_pagecache_local, cpu); - atomic_add(*count, &nr_pagecache); - *count = 0; local_irq_disable(); __drain_pages(cpu); - - /* Add dead cpu's page_states to our own. */ - dest = (unsigned long *)&__get_cpu_var(page_states); - src = (unsigned long *)&per_cpu(page_states, cpu); - - for (i = 0; i < sizeof(struct page_state)/sizeof(unsigned long); - i++) { - dest[i] += src[i]; - src[i] = 0; - } - + vm_events_fold_cpu(cpu); local_irq_enable(); + refresh_cpu_vm_stats(cpu); } return NOTIFY_OK; } @@ -2670,6 +2302,24 @@ int min_free_kbytes_sysctl_handler(ctl_t return 0; } +#ifdef CONFIG_NUMA +int sysctl_min_unmapped_ratio_sysctl_handler(ctl_table *table, int write, + struct file *file, void __user *buffer, size_t *length, loff_t *ppos) +{ + struct zone *zone; + int rc; + + rc = proc_dointvec_minmax(table, write, file, buffer, length, ppos); + if (rc) + return rc; + + for_each_zone(zone) + zone->min_unmapped_ratio = (zone->present_pages * + sysctl_min_unmapped_ratio) / 100; + return 0; +} +#endif + /* * lowmem_reserve_ratio_sysctl_handler - just a wrapper around * proc_dointvec() so that we can call setup_per_zone_lowmem_reserve() @@ -2804,42 +2454,14 @@ void *__init alloc_large_system_hash(con } #ifdef CONFIG_OUT_OF_LINE_PFN_TO_PAGE -/* - * pfn <-> page translation. out-of-line version. - * (see asm-generic/memory_model.h) - */ -#if defined(CONFIG_FLATMEM) struct page *pfn_to_page(unsigned long pfn) { - return mem_map + (pfn - ARCH_PFN_OFFSET); + return __pfn_to_page(pfn); } unsigned long page_to_pfn(struct page *page) { - return (page - mem_map) + ARCH_PFN_OFFSET; -} -#elif defined(CONFIG_DISCONTIGMEM) -struct page *pfn_to_page(unsigned long pfn) -{ - int nid = arch_pfn_to_nid(pfn); - return NODE_DATA(nid)->node_mem_map + arch_local_page_offset(pfn,nid); -} -unsigned long page_to_pfn(struct page *page) -{ - struct pglist_data *pgdat = NODE_DATA(page_to_nid(page)); - return (page - pgdat->node_mem_map) + pgdat->node_start_pfn; -} -#elif defined(CONFIG_SPARSEMEM) -struct page *pfn_to_page(unsigned long pfn) -{ - return __section_mem_map_addr(__pfn_to_section(pfn)) + pfn; -} - -unsigned long page_to_pfn(struct page *page) -{ - long section_id = page_to_section(page); - return page - __section_mem_map_addr(__nr_to_section(section_id)); + return __page_to_pfn(page); } -#endif /* CONFIG_FLATMEM/DISCONTIGMME/SPARSEMEM */ EXPORT_SYMBOL(pfn_to_page); EXPORT_SYMBOL(page_to_pfn); #endif /* CONFIG_OUT_OF_LINE_PFN_TO_PAGE */ diff --git a/mm/page_io.c b/mm/page_io.c index bb2b0d5..8802994 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -101,7 +101,7 @@ int swap_writepage(struct page *page, st } if (wbc->sync_mode == WB_SYNC_ALL) rw |= (1 << BIO_RW_SYNC); - inc_page_state(pswpout); + count_vm_event(PSWPOUT); set_page_writeback(page); unlock_page(page); submit_bio(rw, bio); @@ -123,7 +123,7 @@ int swap_readpage(struct file *file, str ret = -ENOMEM; goto out; } - inc_page_state(pswpin); + count_vm_event(PSWPIN); submit_bio(READ, bio); out: return ret; diff --git a/mm/pdflush.c b/mm/pdflush.c index c4b6d0a..b02102f 100644 --- a/mm/pdflush.c +++ b/mm/pdflush.c @@ -104,21 +104,20 @@ static int __pdflush(struct pdflush_work list_move(&my_work->list, &pdflush_list); my_work->when_i_went_to_sleep = jiffies; spin_unlock_irq(&pdflush_lock); - schedule(); - if (try_to_freeze()) { - spin_lock_irq(&pdflush_lock); - continue; - } - + try_to_freeze(); spin_lock_irq(&pdflush_lock); if (!list_empty(&my_work->list)) { - printk("pdflush: bogus wakeup!\n"); + /* + * Someone woke us up, but without removing our control + * structure from the global list. swsusp will do this + * in try_to_freeze()->refrigerator(). Handle it. + */ my_work->fn = NULL; continue; } if (my_work->fn == NULL) { - printk("pdflush: NULL work function\n"); + printk("pdflush: bogus wakeup\n"); continue; } spin_unlock_irq(&pdflush_lock); @@ -202,8 +201,7 @@ int pdflush_operation(void (*fn)(unsigne unsigned long flags; int ret = 0; - if (fn == NULL) - BUG(); /* Hard to diagnose if it's deferred */ + BUG_ON(fn == NULL); /* Hard to diagnose if it's deferred */ spin_lock_irqsave(&pdflush_lock, flags); if (list_empty(&pdflush_list)) { diff --git a/mm/readahead.c b/mm/readahead.c index 0f142a4..aa7ec42 100644 --- a/mm/readahead.c +++ b/mm/readahead.c @@ -118,8 +118,7 @@ static inline unsigned long get_next_ra_ #define list_to_page(head) (list_entry((head)->prev, struct page, lru)) /** - * read_cache_pages - populate an address space with some pages, and - * start reads against them. + * read_cache_pages - populate an address space with some pages & start reads against them * @mapping: the address_space * @pages: The address of a list_head which contains the target pages. These * pages have their ->index populated and are otherwise uninitialised. @@ -182,14 +181,11 @@ static int read_pages(struct address_spa list_del(&page->lru); if (!add_to_page_cache(page, mapping, page->index, GFP_KERNEL)) { - ret = mapping->a_ops->readpage(filp, page); - if (ret != AOP_TRUNCATED_PAGE) { - if (!pagevec_add(&lru_pvec, page)) - __pagevec_lru_add(&lru_pvec); - continue; - } /* else fall through to release */ - } - page_cache_release(page); + mapping->a_ops->readpage(filp, page); + if (!pagevec_add(&lru_pvec, page)) + __pagevec_lru_add(&lru_pvec); + } else + page_cache_release(page); } pagevec_lru_add(&lru_pvec); ret = 0; @@ -394,8 +390,8 @@ int do_page_cache_readahead(struct addre * Read 'nr_to_read' pages starting at page 'offset'. If the flag 'block' * is set wait till the read completes. Otherwise attempt to read without * blocking. - * Returns 1 meaning 'success' if read is succesfull without switching off - * readhaead mode. Otherwise return failure. + * Returns 1 meaning 'success' if read is successful without switching off + * readahead mode. Otherwise return failure. */ static int blockable_page_cache_readahead(struct address_space *mapping, struct file *filp, diff --git a/mm/rmap.c b/mm/rmap.c index 1963e26..40158b5 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -103,7 +103,7 @@ int anon_vma_prepare(struct vm_area_stru spin_lock(&mm->page_table_lock); if (likely(!vma->anon_vma)) { vma->anon_vma = anon_vma; - list_add(&vma->anon_vma_node, &anon_vma->head); + list_add_tail(&vma->anon_vma_node, &anon_vma->head); allocated = NULL; } spin_unlock(&mm->page_table_lock); @@ -127,7 +127,7 @@ void __anon_vma_link(struct vm_area_stru struct anon_vma *anon_vma = vma->anon_vma; if (anon_vma) { - list_add(&vma->anon_vma_node, &anon_vma->head); + list_add_tail(&vma->anon_vma_node, &anon_vma->head); validate_anon_vma(vma); } } @@ -138,7 +138,7 @@ void anon_vma_link(struct vm_area_struct if (anon_vma) { spin_lock(&anon_vma->lock); - list_add(&vma->anon_vma_node, &anon_vma->head); + list_add_tail(&vma->anon_vma_node, &anon_vma->head); validate_anon_vma(vma); spin_unlock(&anon_vma->lock); } @@ -205,44 +205,6 @@ out: return anon_vma; } -#ifdef CONFIG_MIGRATION -/* - * Remove an anonymous page from swap replacing the swap pte's - * through real pte's pointing to valid pages and then releasing - * the page from the swap cache. - * - * Must hold page lock on page and mmap_sem of one vma that contains - * the page. - */ -void remove_from_swap(struct page *page) -{ - struct anon_vma *anon_vma; - struct vm_area_struct *vma; - unsigned long mapping; - - if (!PageSwapCache(page)) - return; - - mapping = (unsigned long)page->mapping; - - if (!mapping || (mapping & PAGE_MAPPING_ANON) == 0) - return; - - /* - * We hold the mmap_sem lock. So no need to call page_lock_anon_vma. - */ - anon_vma = (struct anon_vma *) (mapping - PAGE_MAPPING_ANON); - spin_lock(&anon_vma->lock); - - list_for_each_entry(vma, &anon_vma->head, anon_vma_node) - remove_vma_swap(vma, page); - - spin_unlock(&anon_vma->lock); - delete_from_swap_cache(page); -} -EXPORT_SYMBOL(remove_from_swap); -#endif - /* * At what user virtual address is page expected in vma? */ @@ -493,7 +455,7 @@ static void __page_set_anon_rmap(struct * nr_mapped state can be updated without turning off * interrupts because it is not modified via interrupt. */ - __inc_page_state(nr_mapped); + __inc_zone_page_state(page, NR_ANON_PAGES); } /** @@ -537,7 +499,7 @@ void page_add_new_anon_rmap(struct page void page_add_file_rmap(struct page *page) { if (atomic_inc_and_test(&page->_mapcount)) - __inc_page_state(nr_mapped); + __inc_zone_page_state(page, NR_FILE_MAPPED); } /** @@ -569,7 +531,8 @@ #endif */ if (page_test_and_clear_dirty(page)) set_page_dirty(page); - __dec_page_state(nr_mapped); + __dec_zone_page_state(page, + PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED); } } @@ -578,7 +541,7 @@ #endif * repeatedly from either try_to_unmap_anon or try_to_unmap_file. */ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma, - int ignore_refs) + int migration) { struct mm_struct *mm = vma->vm_mm; unsigned long address; @@ -600,9 +563,8 @@ static int try_to_unmap_one(struct page * If it's recently referenced (perhaps page_referenced * skipped over this mm) then we should reactivate it. */ - if ((vma->vm_flags & VM_LOCKED) || - (ptep_clear_flush_young(vma, address, pte) - && !ignore_refs)) { + if (!migration && ((vma->vm_flags & VM_LOCKED) || + (ptep_clear_flush_young(vma, address, pte)))) { ret = SWAP_FAIL; goto out_unmap; } @@ -620,24 +582,45 @@ static int try_to_unmap_one(struct page if (PageAnon(page)) { swp_entry_t entry = { .val = page_private(page) }; - /* - * Store the swap location in the pte. - * See handle_pte_fault() ... - */ - BUG_ON(!PageSwapCache(page)); - swap_duplicate(entry); - if (list_empty(&mm->mmlist)) { - spin_lock(&mmlist_lock); - if (list_empty(&mm->mmlist)) - list_add(&mm->mmlist, &init_mm.mmlist); - spin_unlock(&mmlist_lock); + + if (PageSwapCache(page)) { + /* + * Store the swap location in the pte. + * See handle_pte_fault() ... + */ + swap_duplicate(entry); + if (list_empty(&mm->mmlist)) { + spin_lock(&mmlist_lock); + if (list_empty(&mm->mmlist)) + list_add(&mm->mmlist, &init_mm.mmlist); + spin_unlock(&mmlist_lock); + } + dec_mm_counter(mm, anon_rss); +#ifdef CONFIG_MIGRATION + } else { + /* + * Store the pfn of the page in a special migration + * pte. do_swap_page() will wait until the migration + * pte is removed and then restart fault handling. + */ + BUG_ON(!migration); + entry = make_migration_entry(page, pte_write(pteval)); +#endif } set_pte_at(mm, address, pte, swp_entry_to_pte(entry)); BUG_ON(pte_file(*pte)); - dec_mm_counter(mm, anon_rss); } else +#ifdef CONFIG_MIGRATION + if (migration) { + /* Establish migration entry for a file page */ + swp_entry_t entry; + entry = make_migration_entry(page, pte_write(pteval)); + set_pte_at(mm, address, pte, swp_entry_to_pte(entry)); + } else +#endif dec_mm_counter(mm, file_rss); + page_remove_rmap(page); page_cache_release(page); @@ -736,7 +719,7 @@ static void try_to_unmap_cluster(unsigne pte_unmap_unlock(pte - 1, ptl); } -static int try_to_unmap_anon(struct page *page, int ignore_refs) +static int try_to_unmap_anon(struct page *page, int migration) { struct anon_vma *anon_vma; struct vm_area_struct *vma; @@ -747,7 +730,7 @@ static int try_to_unmap_anon(struct page return ret; list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { - ret = try_to_unmap_one(page, vma, ignore_refs); + ret = try_to_unmap_one(page, vma, migration); if (ret == SWAP_FAIL || !page_mapped(page)) break; } @@ -764,7 +747,7 @@ static int try_to_unmap_anon(struct page * * This function is only called from try_to_unmap for object-based pages. */ -static int try_to_unmap_file(struct page *page, int ignore_refs) +static int try_to_unmap_file(struct page *page, int migration) { struct address_space *mapping = page->mapping; pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); @@ -778,7 +761,7 @@ static int try_to_unmap_file(struct page spin_lock(&mapping->i_mmap_lock); vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { - ret = try_to_unmap_one(page, vma, ignore_refs); + ret = try_to_unmap_one(page, vma, migration); if (ret == SWAP_FAIL || !page_mapped(page)) goto out; } @@ -788,7 +771,7 @@ static int try_to_unmap_file(struct page list_for_each_entry(vma, &mapping->i_mmap_nonlinear, shared.vm_set.list) { - if (vma->vm_flags & VM_LOCKED) + if ((vma->vm_flags & VM_LOCKED) && !migration) continue; cursor = (unsigned long) vma->vm_private_data; if (cursor > max_nl_cursor) @@ -822,7 +805,7 @@ static int try_to_unmap_file(struct page do { list_for_each_entry(vma, &mapping->i_mmap_nonlinear, shared.vm_set.list) { - if (vma->vm_flags & VM_LOCKED) + if ((vma->vm_flags & VM_LOCKED) && !migration) continue; cursor = (unsigned long) vma->vm_private_data; while ( cursor < max_nl_cursor && @@ -863,16 +846,16 @@ out: * SWAP_AGAIN - we missed a mapping, try again later * SWAP_FAIL - the page is unswappable */ -int try_to_unmap(struct page *page, int ignore_refs) +int try_to_unmap(struct page *page, int migration) { int ret; BUG_ON(!PageLocked(page)); if (PageAnon(page)) - ret = try_to_unmap_anon(page, ignore_refs); + ret = try_to_unmap_anon(page, migration); else - ret = try_to_unmap_file(page, ignore_refs); + ret = try_to_unmap_file(page, migration); if (!page_mapped(page)) ret = SWAP_SUCCESS; diff --git a/mm/shmem.c b/mm/shmem.c index 1e43c8a..db21c51 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -23,10 +23,8 @@ * which makes it a completely usable filesystem. */ -#include #include #include -#include #include #include #include @@ -174,7 +172,7 @@ static inline void shmem_unacct_blocks(u } static struct super_operations shmem_ops; -static struct address_space_operations shmem_aops; +static const struct address_space_operations shmem_aops; static struct file_operations shmem_file_operations; static struct inode_operations shmem_inode_operations; static struct inode_operations shmem_dir_inode_operations; @@ -1046,12 +1044,12 @@ repeat: swappage = lookup_swap_cache(swap); if (!swappage) { shmem_swp_unmap(entry); - spin_unlock(&info->lock); /* here we actually do the io */ if (type && *type == VM_FAULT_MINOR) { - inc_page_state(pgmajfault); + __count_vm_event(PGMAJFAULT); *type = VM_FAULT_MAJOR; } + spin_unlock(&info->lock); swappage = shmem_swapin(info, swap, idx); if (!swappage) { spin_lock(&info->lock); @@ -1081,14 +1079,6 @@ repeat: page_cache_release(swappage); goto repeat; } - if (!PageSwapCache(swappage)) { - /* Page migration has occured */ - shmem_swp_unmap(entry); - spin_unlock(&info->lock); - unlock_page(swappage); - page_cache_release(swappage); - goto repeat; - } if (PageWriteback(swappage)) { shmem_swp_unmap(entry); spin_unlock(&info->lock); @@ -1654,9 +1644,9 @@ static ssize_t shmem_file_sendfile(struc return desc.error; } -static int shmem_statfs(struct super_block *sb, struct kstatfs *buf) +static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf) { - struct shmem_sb_info *sbinfo = SHMEM_SB(sb); + struct shmem_sb_info *sbinfo = SHMEM_SB(dentry->d_sb); buf->f_type = TMPFS_MAGIC; buf->f_bsize = PAGE_CACHE_SIZE; @@ -2170,7 +2160,7 @@ static void destroy_inodecache(void) printk(KERN_INFO "shmem_inode_cache: not all structures were freed\n"); } -static struct address_space_operations shmem_aops = { +static const struct address_space_operations shmem_aops = { .writepage = shmem_writepage, .set_page_dirty = __set_page_dirty_nobuffers, #ifdef CONFIG_TMPFS @@ -2233,10 +2223,10 @@ #endif }; -static struct super_block *shmem_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int shmem_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_nodev(fs_type, flags, data, shmem_fill_super); + return get_sb_nodev(fs_type, flags, data, shmem_fill_super, mnt); } static struct file_system_type tmpfs_fs_type = { @@ -2260,10 +2250,8 @@ static int __init init_tmpfs(void) printk(KERN_ERR "Could not register tmpfs\n"); goto out2; } -#ifdef CONFIG_TMPFS - devfs_mk_dir("shm"); -#endif - shm_mnt = do_kern_mount(tmpfs_fs_type.name, MS_NOUSER, + + shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER, tmpfs_fs_type.name, NULL); if (IS_ERR(shm_mnt)) { error = PTR_ERR(shm_mnt); diff --git a/mm/slab.c b/mm/slab.c index f1b644e..85c2e03 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -89,6 +89,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +107,7 @@ #include #include #include #include +#include #include #include @@ -307,6 +309,13 @@ #define CACHE_CACHE 0 #define SIZE_AC 1 #define SIZE_L3 (1 + MAX_NUMNODES) +static int drain_freelist(struct kmem_cache *cache, + struct kmem_list3 *l3, int tofree); +static void free_block(struct kmem_cache *cachep, void **objpp, int len, + int node); +static void enable_cpucache(struct kmem_cache *cachep); +static void cache_reap(void *unused); + /* * This function must be completely optimized away if a constant is passed to * it. Mostly the same as what is in linux/slab.h except it returns an index. @@ -331,6 +340,8 @@ #undef CACHE return 0; } +static int slab_early_init = 1; + #define INDEX_AC index_of(sizeof(struct arraycache_init)) #define INDEX_L3 index_of(sizeof(struct kmem_list3)) @@ -452,7 +463,7 @@ #define STATS_INC_ACTIVE(x) ((x)->num_ac #define STATS_DEC_ACTIVE(x) ((x)->num_active--) #define STATS_INC_ALLOCED(x) ((x)->num_allocations++) #define STATS_INC_GROWN(x) ((x)->grown++) -#define STATS_INC_REAPED(x) ((x)->reaped++) +#define STATS_ADD_REAPED(x,y) ((x)->reaped += (y)) #define STATS_SET_HIGH(x) \ do { \ if ((x)->num_active > (x)->high_mark) \ @@ -476,7 +487,7 @@ #define STATS_INC_ACTIVE(x) do { } while #define STATS_DEC_ACTIVE(x) do { } while (0) #define STATS_INC_ALLOCED(x) do { } while (0) #define STATS_INC_GROWN(x) do { } while (0) -#define STATS_INC_REAPED(x) do { } while (0) +#define STATS_ADD_REAPED(x,y) do { } while (0) #define STATS_SET_HIGH(x) do { } while (0) #define STATS_INC_ERR(x) do { } while (0) #define STATS_INC_NODEALLOCS(x) do { } while (0) @@ -490,17 +501,6 @@ #define STATS_INC_FREEMISS(x) do { } whi #endif #if DEBUG -/* - * Magic nums for obj red zoning. - * Placed in the first word before and the first word after an obj. - */ -#define RED_INACTIVE 0x5A2CF071UL /* when obj is inactive */ -#define RED_ACTIVE 0x170FC2A5UL /* when obj is active */ - -/* ...and for poisoning */ -#define POISON_INUSE 0x5a /* for use-uninitialised poisoning */ -#define POISON_FREE 0x6b /* for use-after-free poisoning */ -#define POISON_END 0xa5 /* end-byte of poisoning */ /* * memory layout of objects: @@ -592,6 +592,7 @@ static inline struct kmem_cache *page_ge { if (unlikely(PageCompound(page))) page = (struct page *)page_private(page); + BUG_ON(!PageSlab(page)); return (struct kmem_cache *)page->lru.next; } @@ -604,6 +605,7 @@ static inline struct slab *page_get_slab { if (unlikely(PageCompound(page))) page = (struct page *)page_private(page); + BUG_ON(!PageSlab(page)); return (struct slab *)page->lru.prev; } @@ -705,12 +707,6 @@ int slab_is_available(void) static DEFINE_PER_CPU(struct work_struct, reap_work); -static void free_block(struct kmem_cache *cachep, void **objpp, int len, - int node); -static void enable_cpucache(struct kmem_cache *cachep); -static void cache_reap(void *unused); -static int __node_shrink(struct kmem_cache *cachep, int node); - static inline struct array_cache *cpu_cache_get(struct kmem_cache *cachep) { return cachep->array[smp_processor_id()]; @@ -1024,6 +1020,41 @@ static void drain_alien_cache(struct kme } } } + +static inline int cache_free_alien(struct kmem_cache *cachep, void *objp, + int nesting) +{ + struct slab *slabp = virt_to_slab(objp); + int nodeid = slabp->nodeid; + struct kmem_list3 *l3; + struct array_cache *alien = NULL; + + /* + * Make sure we are not freeing a object from another node to the array + * cache on this cpu. + */ + if (likely(slabp->nodeid == numa_node_id())) + return 0; + + l3 = cachep->nodelists[numa_node_id()]; + STATS_INC_NODEFREES(cachep); + if (l3->alien && l3->alien[nodeid]) { + alien = l3->alien[nodeid]; + spin_lock_nested(&alien->lock, nesting); + if (unlikely(alien->avail == alien->limit)) { + STATS_INC_ACOVERFLOW(cachep); + __drain_alien_cache(cachep, alien, nodeid); + } + alien->entry[alien->avail++] = objp; + spin_unlock(&alien->lock); + } else { + spin_lock(&(cachep->nodelists[nodeid])->list_lock); + free_block(cachep, &objp, 1, nodeid); + spin_unlock(&(cachep->nodelists[nodeid])->list_lock); + } + return 1; +} + #else #define drain_alien_cache(cachep, alien) do { } while (0) @@ -1038,9 +1069,15 @@ static inline void free_alien_cache(stru { } +static inline int cache_free_alien(struct kmem_cache *cachep, void *objp, + int nesting) +{ + return 0; +} + #endif -static int cpuup_callback(struct notifier_block *nfb, +static int __devinit cpuup_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { long cpu = (long)hcpu; @@ -1207,10 +1244,7 @@ free_array_cache: l3 = cachep->nodelists[node]; if (!l3) continue; - spin_lock_irq(&l3->list_lock); - /* free slabs belonging to this node */ - __node_shrink(cachep, node); - spin_unlock_irq(&l3->list_lock); + drain_freelist(cachep, l3, l3->free_objects); } mutex_unlock(&cache_chain_mutex); break; @@ -1222,7 +1256,9 @@ bad: return NOTIFY_BAD; } -static struct notifier_block cpucache_notifier = { &cpuup_callback, NULL, 0 }; +static struct notifier_block __cpuinitdata cpucache_notifier = { + &cpuup_callback, NULL, 0 +}; /* * swap the static kmem_list3 with kmalloced memory @@ -1238,6 +1274,11 @@ static void init_list(struct kmem_cache local_irq_disable(); memcpy(ptr, list, sizeof(struct kmem_list3)); + /* + * Do not assume that spinlocks can be initialized via memcpy: + */ + spin_lock_init(&ptr->list_lock); + MAKE_ALL_LISTS(cachep, ptr, nodeid); cachep->nodelists[nodeid] = ptr; local_irq_enable(); @@ -1335,6 +1376,8 @@ void __init kmem_cache_init(void) NULL, NULL); } + slab_early_init = 0; + while (sizes->cs_size != ULONG_MAX) { /* * For performance, all the general caches are L1 aligned. @@ -1362,7 +1405,7 @@ void __init kmem_cache_init(void) } /* 4) Replace the bootstrap head arrays */ { - void *ptr; + struct array_cache *ptr; ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL); @@ -1370,6 +1413,11 @@ void __init kmem_cache_init(void) BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache); memcpy(ptr, cpu_cache_get(&cache_cache), sizeof(struct arraycache_init)); + /* + * Do not assume that spinlocks can be initialized via memcpy: + */ + spin_lock_init(&ptr->lock); + cache_cache.array[smp_processor_id()] = ptr; local_irq_enable(); @@ -1380,6 +1428,11 @@ void __init kmem_cache_init(void) != &initarray_generic.cache); memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep), sizeof(struct arraycache_init)); + /* + * Do not assume that spinlocks can be initialized via memcpy: + */ + spin_lock_init(&ptr->lock); + malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] = ptr; local_irq_enable(); @@ -1450,31 +1503,29 @@ __initcall(cpucache_init); static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) { struct page *page; - void *addr; + int nr_pages; int i; - flags |= cachep->gfpflags; #ifndef CONFIG_MMU - /* nommu uses slab's for process anonymous memory allocations, so - * requires __GFP_COMP to properly refcount higher order allocations" + /* + * Nommu uses slab's for process anonymous memory allocations, and thus + * requires __GFP_COMP to properly refcount higher order allocations */ - page = alloc_pages_node(nodeid, (flags | __GFP_COMP), cachep->gfporder); -#else - page = alloc_pages_node(nodeid, flags, cachep->gfporder); + flags |= __GFP_COMP; #endif + flags |= cachep->gfpflags; + + page = alloc_pages_node(nodeid, flags, cachep->gfporder); if (!page) return NULL; - addr = page_address(page); - i = (1 << cachep->gfporder); + nr_pages = (1 << cachep->gfporder); if (cachep->flags & SLAB_RECLAIM_ACCOUNT) - atomic_add(i, &slab_reclaim_pages); - add_page_state(nr_slab, i); - while (i--) { - __SetPageSlab(page); - page++; - } - return addr; + atomic_add(nr_pages, &slab_reclaim_pages); + add_zone_page_state(page_zone(page), NR_SLAB, nr_pages); + for (i = 0; i < nr_pages; i++) + __SetPageSlab(page + i); + return page_address(page); } /* @@ -1486,12 +1537,12 @@ static void kmem_freepages(struct kmem_c struct page *page = virt_to_page(addr); const unsigned long nr_freed = i; + sub_zone_page_state(page_zone(page), NR_SLAB, nr_freed); while (i--) { BUG_ON(!PageSlab(page)); __ClearPageSlab(page); page++; } - sub_page_state(nr_slab, nr_freed); if (current->reclaim_state) current->reclaim_state->reclaimed_slab += nr_freed; free_pages((unsigned long)addr, cachep->gfporder); @@ -1709,6 +1760,8 @@ static void slab_destroy_objs(struct kme } #endif +static void __cache_free(struct kmem_cache *cachep, void *objp, int nesting); + /** * slab_destroy - destroy and release all objects in a slab * @cachep: cache pointer being destroyed @@ -1732,8 +1785,17 @@ static void slab_destroy(struct kmem_cac call_rcu(&slab_rcu->head, kmem_rcu_free); } else { kmem_freepages(cachep, addr); - if (OFF_SLAB(cachep)) - kmem_cache_free(cachep->slabp_cache, slabp); + if (OFF_SLAB(cachep)) { + unsigned long flags; + + /* + * lockdep: we may nest inside an already held + * ac->lock, so pass in a nesting flag: + */ + local_irq_save(flags); + __cache_free(cachep->slabp_cache, slabp, 1); + local_irq_restore(flags); + } } } @@ -1913,8 +1975,7 @@ kmem_cache_create (const char *name, siz void (*dtor)(void*, struct kmem_cache *, unsigned long)) { size_t left_over, slab_size, ralign; - struct kmem_cache *cachep = NULL; - struct list_head *p; + struct kmem_cache *cachep = NULL, *pc; /* * Sanity checks... these are all serious usage bugs. @@ -1934,8 +1995,7 @@ kmem_cache_create (const char *name, siz mutex_lock(&cache_chain_mutex); - list_for_each(p, &cache_chain) { - struct kmem_cache *pc = list_entry(p, struct kmem_cache, next); + list_for_each_entry(pc, &cache_chain, next) { mm_segment_t old_fs = get_fs(); char tmp; int res; @@ -2069,8 +2129,12 @@ #if FORCED_DEBUG && defined(CONFIG_DEBUG #endif #endif - /* Determine if the slab management is 'on' or 'off' slab. */ - if (size >= (PAGE_SIZE >> 3)) + /* + * Determine if the slab management is 'on' or 'off' slab. + * (bootstrapping cannot cope with offslab caches so don't do + * it too early on.) + */ + if ((size >= (PAGE_SIZE >> 3)) && !slab_early_init) /* * Size is large, assume best to place the slab management obj * off-slab (should allow better packing of objs). @@ -2210,32 +2274,45 @@ static void drain_cpu_caches(struct kmem } } -static int __node_shrink(struct kmem_cache *cachep, int node) +/* + * Remove slabs from the list of free slabs. + * Specify the number of slabs to drain in tofree. + * + * Returns the actual number of slabs released. + */ +static int drain_freelist(struct kmem_cache *cache, + struct kmem_list3 *l3, int tofree) { + struct list_head *p; + int nr_freed; struct slab *slabp; - struct kmem_list3 *l3 = cachep->nodelists[node]; - int ret; - for (;;) { - struct list_head *p; + nr_freed = 0; + while (nr_freed < tofree && !list_empty(&l3->slabs_free)) { + spin_lock_irq(&l3->list_lock); p = l3->slabs_free.prev; - if (p == &l3->slabs_free) - break; + if (p == &l3->slabs_free) { + spin_unlock_irq(&l3->list_lock); + goto out; + } - slabp = list_entry(l3->slabs_free.prev, struct slab, list); + slabp = list_entry(p, struct slab, list); #if DEBUG BUG_ON(slabp->inuse); #endif list_del(&slabp->list); - - l3->free_objects -= cachep->num; + /* + * Safe to drop the lock. The slab is no longer linked + * to the cache. + */ + l3->free_objects -= cache->num; spin_unlock_irq(&l3->list_lock); - slab_destroy(cachep, slabp); - spin_lock_irq(&l3->list_lock); + slab_destroy(cache, slabp); + nr_freed++; } - ret = !list_empty(&l3->slabs_full) || !list_empty(&l3->slabs_partial); - return ret; +out: + return nr_freed; } static int __cache_shrink(struct kmem_cache *cachep) @@ -2248,11 +2325,13 @@ static int __cache_shrink(struct kmem_ca check_irq_on(); for_each_online_node(i) { l3 = cachep->nodelists[i]; - if (l3) { - spin_lock_irq(&l3->list_lock); - ret += __node_shrink(cachep, i); - spin_unlock_irq(&l3->list_lock); - } + if (!l3) + continue; + + drain_freelist(cachep, l3, l3->free_objects); + + ret += !list_empty(&l3->slabs_full) || + !list_empty(&l3->slabs_partial); } return (ret ? 1 : 0); } @@ -2460,23 +2539,28 @@ #endif slabp->inuse--; } -static void set_slab_attr(struct kmem_cache *cachep, struct slab *slabp, - void *objp) +/* + * Map pages beginning at addr to the given cache and slab. This is required + * for the slab allocator to be able to lookup the cache and slab of a + * virtual address for kfree, ksize, kmem_ptr_validate, and slab debugging. + */ +static void slab_map_pages(struct kmem_cache *cache, struct slab *slab, + void *addr) { - int i; + int nr_pages; struct page *page; - /* Nasty!!!!!! I hope this is OK. */ - page = virt_to_page(objp); + page = virt_to_page(addr); - i = 1; + nr_pages = 1; if (likely(!PageCompound(page))) - i <<= cachep->gfporder; + nr_pages <<= cache->gfporder; + do { - page_set_cache(page, cachep); - page_set_slab(page, slabp); + page_set_cache(page, cache); + page_set_slab(page, slab); page++; - } while (--i); + } while (--nr_pages); } /* @@ -2548,7 +2632,7 @@ static int cache_grow(struct kmem_cache goto opps1; slabp->nodeid = nodeid; - set_slab_attr(cachep, slabp, objp); + slab_map_pages(cachep, slabp, objp); cache_init_objs(cachep, slabp, ctor_flags); @@ -2596,6 +2680,28 @@ static void kfree_debugcheck(const void } } +static inline void verify_redzone_free(struct kmem_cache *cache, void *obj) +{ + unsigned long redzone1, redzone2; + + redzone1 = *dbg_redzone1(cache, obj); + redzone2 = *dbg_redzone2(cache, obj); + + /* + * Redzone is ok. + */ + if (redzone1 == RED_ACTIVE && redzone2 == RED_ACTIVE) + return; + + if (redzone1 == RED_INACTIVE && redzone2 == RED_INACTIVE) + slab_error(cache, "double free detected"); + else + slab_error(cache, "memory outside object was overwritten"); + + printk(KERN_ERR "%p: redzone 1:0x%lx, redzone 2:0x%lx.\n", + obj, redzone1, redzone2); +} + static void *cache_free_debugcheck(struct kmem_cache *cachep, void *objp, void *caller) { @@ -2607,27 +2713,10 @@ static void *cache_free_debugcheck(struc kfree_debugcheck(objp); page = virt_to_page(objp); - if (page_get_cache(page) != cachep) { - printk(KERN_ERR "mismatch in kmem_cache_free: expected " - "cache %p, got %p\n", - page_get_cache(page), cachep); - printk(KERN_ERR "%p is %s.\n", cachep, cachep->name); - printk(KERN_ERR "%p is %s.\n", page_get_cache(page), - page_get_cache(page)->name); - WARN_ON(1); - } slabp = page_get_slab(page); if (cachep->flags & SLAB_RED_ZONE) { - if (*dbg_redzone1(cachep, objp) != RED_ACTIVE || - *dbg_redzone2(cachep, objp) != RED_ACTIVE) { - slab_error(cachep, "double free, or memory outside" - " object was overwritten"); - printk(KERN_ERR "%p: redzone 1:0x%lx, " - "redzone 2:0x%lx.\n", - objp, *dbg_redzone1(cachep, objp), - *dbg_redzone2(cachep, objp)); - } + verify_redzone_free(cachep, objp); *dbg_redzone1(cachep, objp) = RED_INACTIVE; *dbg_redzone2(cachep, objp) = RED_INACTIVE; } @@ -3011,7 +3100,16 @@ static void free_block(struct kmem_cache if (slabp->inuse == 0) { if (l3->free_objects > l3->free_limit) { l3->free_objects -= cachep->num; + /* + * It is safe to drop the lock. The slab is + * no longer linked to the cache. cachep + * cannot disappear - we are using it and + * all destruction of caches must be + * serialized properly by the user. + */ + spin_unlock(&l3->list_lock); slab_destroy(cachep, slabp); + spin_lock(&l3->list_lock); } else { list_add(&slabp->list, &l3->slabs_free); } @@ -3037,7 +3135,7 @@ #if DEBUG #endif check_irq_off(); l3 = cachep->nodelists[node]; - spin_lock(&l3->list_lock); + spin_lock_nested(&l3->list_lock, SINGLE_DEPTH_NESTING); if (l3->shared) { struct array_cache *shared_array = l3->shared; int max = shared_array->limit - shared_array->avail; @@ -3080,48 +3178,16 @@ #endif * Release an obj back to its cache. If the obj has a constructed state, it must * be in this state _before_ it is released. Called with disabled ints. */ -static inline void __cache_free(struct kmem_cache *cachep, void *objp) +static void __cache_free(struct kmem_cache *cachep, void *objp, int nesting) { struct array_cache *ac = cpu_cache_get(cachep); check_irq_off(); objp = cache_free_debugcheck(cachep, objp, __builtin_return_address(0)); - /* Make sure we are not freeing a object from another - * node to the array cache on this cpu. - */ -#ifdef CONFIG_NUMA - { - struct slab *slabp; - slabp = virt_to_slab(objp); - if (unlikely(slabp->nodeid != numa_node_id())) { - struct array_cache *alien = NULL; - int nodeid = slabp->nodeid; - struct kmem_list3 *l3; - - l3 = cachep->nodelists[numa_node_id()]; - STATS_INC_NODEFREES(cachep); - if (l3->alien && l3->alien[nodeid]) { - alien = l3->alien[nodeid]; - spin_lock(&alien->lock); - if (unlikely(alien->avail == alien->limit)) { - STATS_INC_ACOVERFLOW(cachep); - __drain_alien_cache(cachep, - alien, nodeid); - } - alien->entry[alien->avail++] = objp; - spin_unlock(&alien->lock); - } else { - spin_lock(&(cachep->nodelists[nodeid])-> - list_lock); - free_block(cachep, &objp, 1, nodeid); - spin_unlock(&(cachep->nodelists[nodeid])-> - list_lock); - } - return; - } - } -#endif + if (cache_free_alien(cachep, objp, nesting)) + return; + if (likely(ac->avail < ac->limit)) { STATS_INC_FREEHIT(cachep); ac->entry[ac->avail++] = objp; @@ -3254,26 +3320,10 @@ EXPORT_SYMBOL(kmalloc_node); #endif /** - * kmalloc - allocate memory + * __do_kmalloc - allocate memory * @size: how many bytes of memory are required. - * @flags: the type of memory to allocate. + * @flags: the type of memory to allocate (see kmalloc). * @caller: function caller for debug tracking of the caller - * - * kmalloc is the normal method of allocating memory - * in the kernel. - * - * The @flags argument may be one of: - * - * %GFP_USER - Allocate memory on behalf of user. May sleep. - * - * %GFP_KERNEL - Allocate normal kernel ram. May sleep. - * - * %GFP_ATOMIC - Allocation will not sleep. Use inside interrupt handlers. - * - * Additionally, the %GFP_DMA flag may be set to indicate the memory - * must be suitable for DMA. This can mean different things on different - * platforms. For example, on i386, it means that the memory must come - * from the first 16MB. */ static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, void *caller) @@ -3371,8 +3421,10 @@ void kmem_cache_free(struct kmem_cache * { unsigned long flags; + BUG_ON(virt_to_cache(objp) != cachep); + local_irq_save(flags); - __cache_free(cachep, objp); + __cache_free(cachep, objp, 0); local_irq_restore(flags); } EXPORT_SYMBOL(kmem_cache_free); @@ -3396,8 +3448,8 @@ void kfree(const void *objp) local_irq_save(flags); kfree_debugcheck(objp); c = virt_to_cache(objp); - mutex_debug_check_no_locks_freed(objp, obj_size(c)); - __cache_free(c, (void *)objp); + debug_check_no_locks_freed(objp, obj_size(c)); + __cache_free(c, (void *)objp, 0); local_irq_restore(flags); } EXPORT_SYMBOL(kfree); @@ -3680,7 +3732,7 @@ void drain_array(struct kmem_cache *cach */ static void cache_reap(void *unused) { - struct list_head *walk; + struct kmem_cache *searchp; struct kmem_list3 *l3; int node = numa_node_id(); @@ -3691,13 +3743,7 @@ static void cache_reap(void *unused) return; } - list_for_each(walk, &cache_chain) { - struct kmem_cache *searchp; - struct list_head *p; - int tofree; - struct slab *slabp; - - searchp = list_entry(walk, struct kmem_cache, next); + list_for_each_entry(searchp, &cache_chain, next) { check_irq_on(); /* @@ -3722,47 +3768,22 @@ static void cache_reap(void *unused) drain_array(searchp, l3, l3->shared, 0, node); - if (l3->free_touched) { + if (l3->free_touched) l3->free_touched = 0; - goto next; - } + else { + int freed; - tofree = (l3->free_limit + 5 * searchp->num - 1) / - (5 * searchp->num); - do { - /* - * Do not lock if there are no free blocks. - */ - if (list_empty(&l3->slabs_free)) - break; - - spin_lock_irq(&l3->list_lock); - p = l3->slabs_free.next; - if (p == &(l3->slabs_free)) { - spin_unlock_irq(&l3->list_lock); - break; - } - - slabp = list_entry(p, struct slab, list); - BUG_ON(slabp->inuse); - list_del(&slabp->list); - STATS_INC_REAPED(searchp); - - /* - * Safe to drop the lock. The slab is no longer linked - * to the cache. searchp cannot disappear, we hold - * cache_chain_lock - */ - l3->free_objects -= searchp->num; - spin_unlock_irq(&l3->list_lock); - slab_destroy(searchp, slabp); - } while (--tofree > 0); + freed = drain_freelist(searchp, l3, (l3->free_limit + + 5 * searchp->num - 1) / (5 * searchp->num)); + STATS_ADD_REAPED(searchp, freed); + } next: cond_resched(); } check_irq_on(); mutex_unlock(&cache_chain_mutex); next_reap_node(); + refresh_cpu_vm_stats(smp_processor_id()); /* Set up the next iteration */ schedule_delayed_work(&__get_cpu_var(reap_work), REAPTIMEOUT_CPUC); } @@ -3825,7 +3846,6 @@ static void s_stop(struct seq_file *m, v static int s_show(struct seq_file *m, void *p) { struct kmem_cache *cachep = p; - struct list_head *q; struct slab *slabp; unsigned long active_objs; unsigned long num_objs; @@ -3846,15 +3866,13 @@ static int s_show(struct seq_file *m, vo check_irq_on(); spin_lock_irq(&l3->list_lock); - list_for_each(q, &l3->slabs_full) { - slabp = list_entry(q, struct slab, list); + list_for_each_entry(slabp, &l3->slabs_full, list) { if (slabp->inuse != cachep->num && !error) error = "slabs_full accounting error"; active_objs += cachep->num; active_slabs++; } - list_for_each(q, &l3->slabs_partial) { - slabp = list_entry(q, struct slab, list); + list_for_each_entry(slabp, &l3->slabs_partial, list) { if (slabp->inuse == cachep->num && !error) error = "slabs_partial inuse accounting error"; if (!slabp->inuse && !error) @@ -3862,8 +3880,7 @@ static int s_show(struct seq_file *m, vo active_objs += slabp->inuse; active_slabs++; } - list_for_each(q, &l3->slabs_free) { - slabp = list_entry(q, struct slab, list); + list_for_each_entry(slabp, &l3->slabs_free, list) { if (slabp->inuse && !error) error = "slabs_free/inuse accounting error"; num_slabs++; @@ -3956,7 +3973,7 @@ ssize_t slabinfo_write(struct file *file { char kbuf[MAX_SLABINFO_WRITE + 1], *tmp; int limit, batchcount, shared, res; - struct list_head *p; + struct kmem_cache *cachep; if (count > MAX_SLABINFO_WRITE) return -EINVAL; @@ -3975,10 +3992,7 @@ ssize_t slabinfo_write(struct file *file /* Find the cache in the chain of caches. */ mutex_lock(&cache_chain_mutex); res = -EINVAL; - list_for_each(p, &cache_chain) { - struct kmem_cache *cachep; - - cachep = list_entry(p, struct kmem_cache, next); + list_for_each_entry(cachep, &cache_chain, next) { if (!strcmp(cachep->name, kbuf)) { if (limit < 1 || batchcount < 1 || batchcount > limit || shared < 0) { @@ -4080,7 +4094,6 @@ #endif static int leaks_show(struct seq_file *m, void *p) { struct kmem_cache *cachep = p; - struct list_head *q; struct slab *slabp; struct kmem_list3 *l3; const char *name; @@ -4105,14 +4118,10 @@ static int leaks_show(struct seq_file *m check_irq_on(); spin_lock_irq(&l3->list_lock); - list_for_each(q, &l3->slabs_full) { - slabp = list_entry(q, struct slab, list); + list_for_each_entry(slabp, &l3->slabs_full, list) handle_slab(n, cachep, slabp); - } - list_for_each(q, &l3->slabs_partial) { - slabp = list_entry(q, struct slab, list); + list_for_each_entry(slabp, &l3->slabs_partial, list) handle_slab(n, cachep, slabp); - } spin_unlock_irq(&l3->list_lock); } name = cachep->name; diff --git a/mm/slob.c b/mm/slob.c index a68255b..7b52b20 100644 --- a/mm/slob.c +++ b/mm/slob.c @@ -29,7 +29,6 @@ * essentially no allocation space overhead. */ -#include #include #include #include diff --git a/mm/sparse.c b/mm/sparse.c index 100040c..86c52ab 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -1,7 +1,6 @@ /* * sparse memory mappings. */ -#include #include #include #include @@ -45,7 +44,7 @@ static struct mem_section *sparse_index_ static int sparse_index_init(unsigned long section_nr, int nid) { - static spinlock_t index_init_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(index_init_lock); unsigned long root = SECTION_NR_TO_ROOT(section_nr); struct mem_section *section; int ret = 0; @@ -99,6 +98,22 @@ int __section_nr(struct mem_section* ms) return (root_nr * SECTIONS_PER_ROOT) + (ms - root); } +/* + * During early boot, before section_mem_map is used for an actual + * mem_map, we use section_mem_map to store the section's NUMA + * node. This keeps us from having to use another data structure. The + * node information is cleared just before we store the real mem_map. + */ +static inline unsigned long sparse_encode_early_nid(int nid) +{ + return (nid << SECTION_NID_SHIFT); +} + +static inline int sparse_early_nid(struct mem_section *section) +{ + return (section->section_mem_map >> SECTION_NID_SHIFT); +} + /* Record a memory area against a node. */ void memory_present(int nid, unsigned long start, unsigned long end) { @@ -113,7 +128,8 @@ void memory_present(int nid, unsigned lo ms = __nr_to_section(section); if (!ms->section_mem_map) - ms->section_mem_map = SECTION_MARKED_PRESENT; + ms->section_mem_map = sparse_encode_early_nid(nid) | + SECTION_MARKED_PRESENT; } } @@ -164,6 +180,7 @@ static int sparse_init_one_section(struc if (!valid_section(ms)) return -EINVAL; + ms->section_mem_map &= ~SECTION_MAP_MASK; ms->section_mem_map |= sparse_encode_mem_map(mem_map, pnum); return 1; @@ -172,8 +189,8 @@ static int sparse_init_one_section(struc static struct page *sparse_early_mem_map_alloc(unsigned long pnum) { struct page *map; - int nid = early_pfn_to_nid(section_nr_to_pfn(pnum)); struct mem_section *ms = __nr_to_section(pnum); + int nid = sparse_early_nid(ms); map = alloc_remap(nid, sizeof(struct page) * PAGES_PER_SECTION); if (map) diff --git a/mm/swap.c b/mm/swap.c index 88895c2..8fd095c 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -86,9 +86,8 @@ int rotate_reclaimable_page(struct page zone = page_zone(page); spin_lock_irqsave(&zone->lru_lock, flags); if (PageLRU(page) && !PageActive(page)) { - list_del(&page->lru); - list_add_tail(&page->lru, &zone->inactive_list); - inc_page_state(pgrotated); + list_move_tail(&page->lru, &zone->inactive_list); + __count_vm_event(PGROTATED); } if (!test_clear_page_writeback(page)) BUG(); @@ -108,7 +107,7 @@ void fastcall activate_page(struct page del_page_from_inactive_list(zone, page); SetPageActive(page); add_page_to_active_list(zone, page); - inc_page_state(pgactivate); + __count_vm_event(PGACTIVATE); } spin_unlock_irq(&zone->lru_lock); } @@ -480,48 +479,6 @@ static int cpu_swap_callback(struct noti #endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_SMP */ -#ifdef CONFIG_SMP -void percpu_counter_mod(struct percpu_counter *fbc, long amount) -{ - long count; - long *pcount; - int cpu = get_cpu(); - - pcount = per_cpu_ptr(fbc->counters, cpu); - count = *pcount + amount; - if (count >= FBC_BATCH || count <= -FBC_BATCH) { - spin_lock(&fbc->lock); - fbc->count += count; - *pcount = 0; - spin_unlock(&fbc->lock); - } else { - *pcount = count; - } - put_cpu(); -} -EXPORT_SYMBOL(percpu_counter_mod); - -/* - * Add up all the per-cpu counts, return the result. This is a more accurate - * but much slower version of percpu_counter_read_positive() - */ -long percpu_counter_sum(struct percpu_counter *fbc) -{ - long ret; - int cpu; - - spin_lock(&fbc->lock); - ret = fbc->count; - for_each_possible_cpu(cpu) { - long *pcount = per_cpu_ptr(fbc->counters, cpu); - ret += *pcount; - } - spin_unlock(&fbc->lock); - return ret < 0 ? 0 : ret; -} -EXPORT_SYMBOL(percpu_counter_sum); -#endif - /* * Perform any setup for the swap system */ diff --git a/mm/swap_state.c b/mm/swap_state.c index e0e1583..5f7cf2a 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c @@ -24,7 +24,7 @@ #include * vmscan's shrink_list, to make sync_page look nicer, and to allow * future use of radix_tree tags in the swap cache. */ -static struct address_space_operations swap_aops = { +static const struct address_space_operations swap_aops = { .writepage = swap_writepage, .sync_page = block_sync_page, .set_page_dirty = __set_page_dirty_nobuffers, @@ -38,7 +38,7 @@ static struct backing_dev_info swap_back struct address_space swapper_space = { .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), - .tree_lock = RW_LOCK_UNLOCKED, + .tree_lock = __RW_LOCK_UNLOCKED(swapper_space.tree_lock), .a_ops = &swap_aops, .i_mmap_nonlinear = LIST_HEAD_INIT(swapper_space.i_mmap_nonlinear), .backing_dev_info = &swap_backing_dev_info, @@ -87,7 +87,7 @@ static int __add_to_swap_cache(struct pa SetPageSwapCache(page); set_page_private(page, entry.val); total_swapcache_pages++; - pagecache_acct(1); + __inc_zone_page_state(page, NR_FILE_PAGES); } write_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); @@ -132,7 +132,7 @@ void __delete_from_swap_cache(struct pag set_page_private(page, 0); ClearPageSwapCache(page); total_swapcache_pages--; - pagecache_acct(-1); + __dec_zone_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(del_total); } diff --git a/mm/swapfile.c b/mm/swapfile.c index e5fd538..e70d6c6 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -5,7 +5,6 @@ * Swap reorganised 29.12.95, Stephen Tweedie */ -#include #include #include #include @@ -395,6 +394,9 @@ void free_swap_and_cache(swp_entry_t ent struct swap_info_struct * p; struct page *page = NULL; + if (is_migration_entry(entry)) + return; + p = swap_info_get(entry); if (p) { if (swap_entry_free(p, swp_offset(entry)) == 1) { @@ -615,15 +617,6 @@ static int unuse_mm(struct mm_struct *mm return 0; } -#ifdef CONFIG_MIGRATION -int remove_vma_swap(struct vm_area_struct *vma, struct page *page) -{ - swp_entry_t entry = { .val = page_private(page) }; - - return unuse_vma(vma, entry, page); -} -#endif - /* * Scan swap_map from current position to next entry still in use. * Recycle to start on reaching the end, returning 0 when empty. @@ -716,7 +709,6 @@ static int try_to_unuse(unsigned int typ */ swap_map = &si->swap_map[i]; entry = swp_entry(type, i); -again: page = read_swap_cache_async(entry, NULL, 0); if (!page) { /* @@ -751,12 +743,6 @@ again: wait_on_page_locked(page); wait_on_page_writeback(page); lock_page(page); - if (!PageSwapCache(page)) { - /* Page migration has occured */ - unlock_page(page); - page_cache_release(page); - goto again; - } wait_on_page_writeback(page); /* @@ -785,10 +771,8 @@ again: while (*swap_map > 1 && !retval && (p = p->next) != &start_mm->mmlist) { mm = list_entry(p, struct mm_struct, mmlist); - if (atomic_inc_return(&mm->mm_users) == 1) { - atomic_dec(&mm->mm_users); + if (!atomic_inc_not_zero(&mm->mm_users)) continue; - } spin_unlock(&mmlist_lock); mmput(prev_mm); prev_mm = mm; @@ -1407,19 +1391,7 @@ asmlinkage long sys_swapon(const char __ if (!(p->flags & SWP_USED)) break; error = -EPERM; - /* - * Test if adding another swap device is possible. There are - * two limiting factors: 1) the number of bits for the swap - * type swp_entry_t definition and 2) the number of bits for - * the swap type in the swap ptes as defined by the different - * architectures. To honor both limitations a swap entry - * with swap offset 0 and swap type ~0UL is created, encoded - * to a swap pte, decoded to a swp_entry_t again and finally - * the swap type part is extracted. This will mask all bits - * from the initial ~0UL that can't be encoded in either the - * swp_entry_t or the architecture definition of a swap pte. - */ - if (type > swp_type(pte_to_swp_entry(swp_entry_to_pte(swp_entry(~0UL,0))))) { + if (type >= MAX_SWAPFILES) { spin_unlock(&swap_lock); goto out; } @@ -1504,8 +1476,7 @@ asmlinkage long sys_swapon(const char __ error = -EINVAL; goto bad_swap; } - page = read_cache_page(mapping, 0, - (filler_t *)mapping->a_ops->readpage, swap_file); + page = read_mapping_page(mapping, 0, swap_file); if (IS_ERR(page)) { error = PTR_ERR(page); goto bad_swap; @@ -1709,6 +1680,9 @@ int swap_duplicate(swp_entry_t entry) unsigned long offset, type; int result = 0; + if (is_migration_entry(entry)) + return 1; + type = swp_type(entry); if (type >= nr_swapfiles) goto bad_file; diff --git a/mm/tiny-shmem.c b/mm/tiny-shmem.c index f9d6a9c..5f2cbf0 100644 --- a/mm/tiny-shmem.c +++ b/mm/tiny-shmem.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include @@ -33,9 +32,6 @@ static int __init init_tmpfs(void) { BUG_ON(register_filesystem(&tmpfs_fs_type) != 0); -#ifdef CONFIG_TMPFS - devfs_mk_dir("shm"); -#endif shm_mnt = kern_mount(&tmpfs_fs_type); BUG_ON(IS_ERR(shm_mnt)); diff --git a/mm/truncate.c b/mm/truncate.c index 6cb3fff..cf1b015 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -230,14 +230,24 @@ unsigned long invalidate_mapping_pages(s pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; + pgoff_t index; + int lock_failed; - if (TestSetPageLocked(page)) { - next++; - continue; - } - if (page->index > next) - next = page->index; + lock_failed = TestSetPageLocked(page); + + /* + * We really shouldn't be looking at the ->index of an + * unlocked page. But we're not allowed to lock these + * pages. So we rely upon nobody altering the ->index + * of this (pinned-by-us) page. + */ + index = page->index; + if (index > next) + next = index; next++; + if (lock_failed) + continue; + if (PageDirty(page) || PageWriteback(page)) goto unlock; if (page_mapped(page)) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index c0504f1..7b45079 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -257,6 +257,19 @@ struct vm_struct *get_vm_area_node(unsig } /* Caller must hold vmlist_lock */ +static struct vm_struct *__find_vm_area(void *addr) +{ + struct vm_struct *tmp; + + for (tmp = vmlist; tmp != NULL; tmp = tmp->next) { + if (tmp->addr == addr) + break; + } + + return tmp; +} + +/* Caller must hold vmlist_lock */ struct vm_struct *__remove_vm_area(void *addr) { struct vm_struct **p, *tmp; @@ -317,6 +330,8 @@ void __vunmap(void *addr, int deallocate return; } + debug_check_no_locks_freed(addr, area->size); + if (deallocate_pages) { int i; @@ -498,11 +513,33 @@ EXPORT_SYMBOL(__vmalloc); */ void *vmalloc(unsigned long size) { - return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL); + return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL); } EXPORT_SYMBOL(vmalloc); /** + * vmalloc_user - allocate virtually contiguous memory which has + * been zeroed so it can be mapped to userspace without + * leaking data. + * + * @size: allocation size + */ +void *vmalloc_user(unsigned long size) +{ + struct vm_struct *area; + void *ret; + + ret = __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL); + write_lock(&vmlist_lock); + area = __find_vm_area(ret); + area->flags |= VM_USERMAP; + write_unlock(&vmlist_lock); + + return ret; +} +EXPORT_SYMBOL(vmalloc_user); + +/** * vmalloc_node - allocate memory on a specific node * * @size: allocation size @@ -516,7 +553,7 @@ EXPORT_SYMBOL(vmalloc); */ void *vmalloc_node(unsigned long size, int node) { - return __vmalloc_node(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL, node); + return __vmalloc_node(size, GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL, node); } EXPORT_SYMBOL(vmalloc_node); @@ -556,6 +593,28 @@ void *vmalloc_32(unsigned long size) } EXPORT_SYMBOL(vmalloc_32); +/** + * vmalloc_32_user - allocate virtually contiguous memory (32bit + * addressable) which is zeroed so it can be + * mapped to userspace without leaking data. + * + * @size: allocation size + */ +void *vmalloc_32_user(unsigned long size) +{ + struct vm_struct *area; + void *ret; + + ret = __vmalloc(size, GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL); + write_lock(&vmlist_lock); + area = __find_vm_area(ret); + area->flags |= VM_USERMAP; + write_unlock(&vmlist_lock); + + return ret; +} +EXPORT_SYMBOL(vmalloc_32_user); + long vread(char *buf, char *addr, unsigned long count) { struct vm_struct *tmp; @@ -630,3 +689,64 @@ finished: read_unlock(&vmlist_lock); return buf - buf_start; } + +/** + * remap_vmalloc_range - map vmalloc pages to userspace + * + * @vma: vma to cover (map full range of vma) + * @addr: vmalloc memory + * @pgoff: number of pages into addr before first page to map + * @returns: 0 for success, -Exxx on failure + * + * This function checks that addr is a valid vmalloc'ed area, and + * that it is big enough to cover the vma. Will return failure if + * that criteria isn't met. + * + * Similar to remap_pfn_range (see mm/memory.c) + */ +int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, + unsigned long pgoff) +{ + struct vm_struct *area; + unsigned long uaddr = vma->vm_start; + unsigned long usize = vma->vm_end - vma->vm_start; + int ret; + + if ((PAGE_SIZE-1) & (unsigned long)addr) + return -EINVAL; + + read_lock(&vmlist_lock); + area = __find_vm_area(addr); + if (!area) + goto out_einval_locked; + + if (!(area->flags & VM_USERMAP)) + goto out_einval_locked; + + if (usize + (pgoff << PAGE_SHIFT) > area->size - PAGE_SIZE) + goto out_einval_locked; + read_unlock(&vmlist_lock); + + addr += pgoff << PAGE_SHIFT; + do { + struct page *page = vmalloc_to_page(addr); + ret = vm_insert_page(vma, uaddr, page); + if (ret) + return ret; + + uaddr += PAGE_SIZE; + addr += PAGE_SIZE; + usize -= PAGE_SIZE; + } while (usize > 0); + + /* Prevent "things" like memory migration? VM_flags need a cleanup... */ + vma->vm_flags |= VM_RESERVED; + + return ret; + +out_einval_locked: + read_unlock(&vmlist_lock); + return -EINVAL; +} +EXPORT_SYMBOL(remap_vmalloc_range); + diff --git a/mm/vmscan.c b/mm/vmscan.c index 440a733..5d4c4d0 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -34,6 +34,7 @@ #include #include #include #include +#include #include #include @@ -46,8 +47,6 @@ struct scan_control { /* Incremented by the number of inactive pages that were scanned */ unsigned long nr_scanned; - unsigned long nr_mapped; /* From page_state */ - /* This context's GFP mask */ gfp_t gfp_mask; @@ -61,6 +60,8 @@ struct scan_control { * In this context, it doesn't matter that we scan the * whole list at once. */ int swap_cluster_max; + + int swappiness; }; /* @@ -108,7 +109,7 @@ #endif * From 0 .. 100. Higher means more swappy. */ int vm_swappiness = 60; -static long total_memory; +long vm_total_pages; /* The total number of pages which the VM controls */ static LIST_HEAD(shrinker_list); static DECLARE_RWSEM(shrinker_rwsem); @@ -214,7 +215,7 @@ unsigned long shrink_slab(unsigned long break; if (shrink_ret < nr_before) ret += nr_before - shrink_ret; - mod_page_state(slabs_scanned, this_scan); + count_vm_events(SLABS_SCANNED, this_scan); total_scan -= this_scan; cond_resched(); @@ -288,11 +289,23 @@ static void handle_write_error(struct ad unlock_page(page); } +/* possible outcome of pageout() */ +typedef enum { + /* failed to write page out, page is locked */ + PAGE_KEEP, + /* move page to the active list, page is locked */ + PAGE_ACTIVATE, + /* page has been sent to the disk successfully, page is unlocked */ + PAGE_SUCCESS, + /* page is clean and locked */ + PAGE_CLEAN, +} pageout_t; + /* * pageout is called by shrink_page_list() for each dirty page. * Calls ->writepage(). */ -pageout_t pageout(struct page *page, struct address_space *mapping) +static pageout_t pageout(struct page *page, struct address_space *mapping) { /* * If the page is dirty, only perform writeback if that write @@ -337,6 +350,8 @@ pageout_t pageout(struct page *page, str struct writeback_control wbc = { .sync_mode = WB_SYNC_NONE, .nr_to_write = SWAP_CLUSTER_MAX, + .range_start = 0, + .range_end = LLONG_MAX, .nonblocking = 1, .for_reclaim = 1, }; @@ -554,7 +569,7 @@ keep: list_splice(&ret_pages, page_list); if (pagevec_count(&freed_pvec)) __pagevec_release_nonlru(&freed_pvec); - mod_page_state(pgactivate, pgactivate); + count_vm_events(PGACTIVATE, pgactivate); return nr_reclaimed; } @@ -644,11 +659,11 @@ static unsigned long shrink_inactive_lis nr_reclaimed += nr_freed; local_irq_disable(); if (current_is_kswapd()) { - __mod_page_state_zone(zone, pgscan_kswapd, nr_scan); - __mod_page_state(kswapd_steal, nr_freed); + __count_zone_vm_events(PGSCAN_KSWAPD, zone, nr_scan); + __count_vm_events(KSWAPD_STEAL, nr_freed); } else - __mod_page_state_zone(zone, pgscan_direct, nr_scan); - __mod_page_state_zone(zone, pgsteal, nr_freed); + __count_zone_vm_events(PGSCAN_DIRECT, zone, nr_scan); + __count_vm_events(PGACTIVATE, nr_freed); if (nr_taken == 0) goto done; @@ -727,7 +742,9 @@ static void shrink_active_list(unsigned * how much memory * is mapped. */ - mapped_ratio = (sc->nr_mapped * 100) / total_memory; + mapped_ratio = ((global_page_state(NR_FILE_MAPPED) + + global_page_state(NR_ANON_PAGES)) * 100) / + vm_total_pages; /* * Now decide how much we really want to unmap some pages. The @@ -741,7 +758,7 @@ static void shrink_active_list(unsigned * A 100% value of vm_swappiness overrides this algorithm * altogether. */ - swap_tendency = mapped_ratio / 2 + distress + vm_swappiness; + swap_tendency = mapped_ratio / 2 + distress + sc->swappiness; /* * Now use this metric to decide whether to start moving mapped @@ -824,11 +841,10 @@ static void shrink_active_list(unsigned } } zone->nr_active += pgmoved; - spin_unlock(&zone->lru_lock); - __mod_page_state_zone(zone, pgrefill, pgscanned); - __mod_page_state(pgdeactivate, pgdeactivate); - local_irq_enable(); + __count_zone_vm_events(PGREFILL, zone, pgscanned); + __count_vm_events(PGDEACTIVATE, pgdeactivate); + spin_unlock_irq(&zone->lru_lock); pagevec_release(&pvec); } @@ -957,9 +973,10 @@ unsigned long try_to_free_pages(struct z .may_writepage = !laptop_mode, .swap_cluster_max = SWAP_CLUSTER_MAX, .may_swap = 1, + .swappiness = vm_swappiness, }; - inc_page_state(allocstall); + count_vm_event(ALLOCSTALL); for (i = 0; zones[i] != NULL; i++) { struct zone *zone = zones[i]; @@ -972,7 +989,6 @@ unsigned long try_to_free_pages(struct z } for (priority = DEF_PRIORITY; priority >= 0; priority--) { - sc.nr_mapped = read_page_state(nr_mapped); sc.nr_scanned = 0; if (!priority) disable_swap_token(); @@ -1021,10 +1037,6 @@ out: * For kswapd, balance_pgdat() will work across all this node's zones until * they are all at pages_high. * - * If `nr_pages' is non-zero then it is the number of pages which are to be - * reclaimed, regardless of the zone occupancies. This is a software suspend - * special. - * * Returns the number of pages which were actually freed. * * There is special handling here for zones which are full of pinned pages. @@ -1042,10 +1054,8 @@ out: * the page allocator fallback scheme to ensure that aging of pages is balanced * across the zones. */ -static unsigned long balance_pgdat(pg_data_t *pgdat, unsigned long nr_pages, - int order) +static unsigned long balance_pgdat(pg_data_t *pgdat, int order) { - unsigned long to_free = nr_pages; int all_zones_ok; int priority; int i; @@ -1055,16 +1065,15 @@ static unsigned long balance_pgdat(pg_da struct scan_control sc = { .gfp_mask = GFP_KERNEL, .may_swap = 1, - .swap_cluster_max = nr_pages ? nr_pages : SWAP_CLUSTER_MAX, + .swap_cluster_max = SWAP_CLUSTER_MAX, + .swappiness = vm_swappiness, }; loop_again: total_scanned = 0; nr_reclaimed = 0; sc.may_writepage = !laptop_mode; - sc.nr_mapped = read_page_state(nr_mapped); - - inc_page_state(pageoutrun); + count_vm_event(PAGEOUTRUN); for (i = 0; i < pgdat->nr_zones; i++) { struct zone *zone = pgdat->node_zones + i; @@ -1082,31 +1091,26 @@ loop_again: all_zones_ok = 1; - if (nr_pages == 0) { - /* - * Scan in the highmem->dma direction for the highest - * zone which needs scanning - */ - for (i = pgdat->nr_zones - 1; i >= 0; i--) { - struct zone *zone = pgdat->node_zones + i; + /* + * Scan in the highmem->dma direction for the highest + * zone which needs scanning + */ + for (i = pgdat->nr_zones - 1; i >= 0; i--) { + struct zone *zone = pgdat->node_zones + i; - if (!populated_zone(zone)) - continue; + if (!populated_zone(zone)) + continue; - if (zone->all_unreclaimable && - priority != DEF_PRIORITY) - continue; + if (zone->all_unreclaimable && priority != DEF_PRIORITY) + continue; - if (!zone_watermark_ok(zone, order, - zone->pages_high, 0, 0)) { - end_zone = i; - goto scan; - } + if (!zone_watermark_ok(zone, order, zone->pages_high, + 0, 0)) { + end_zone = i; + goto scan; } - goto out; - } else { - end_zone = pgdat->nr_zones - 1; } + goto out; scan: for (i = 0; i <= end_zone; i++) { struct zone *zone = pgdat->node_zones + i; @@ -1133,11 +1137,9 @@ scan: if (zone->all_unreclaimable && priority != DEF_PRIORITY) continue; - if (nr_pages == 0) { /* Not software suspend */ - if (!zone_watermark_ok(zone, order, - zone->pages_high, end_zone, 0)) - all_zones_ok = 0; - } + if (!zone_watermark_ok(zone, order, zone->pages_high, + end_zone, 0)) + all_zones_ok = 0; zone->temp_priority = priority; if (zone->prev_priority > priority) zone->prev_priority = priority; @@ -1162,8 +1164,6 @@ scan: total_scanned > nr_reclaimed + nr_reclaimed / 2) sc.may_writepage = 1; } - if (nr_pages && to_free > nr_reclaimed) - continue; /* swsusp: need to do more work */ if (all_zones_ok) break; /* kswapd: all done */ /* @@ -1179,7 +1179,7 @@ scan: * matches the direct reclaim path behaviour in terms of impact * on zone->*_priority. */ - if ((nr_reclaimed >= SWAP_CLUSTER_MAX) && !nr_pages) + if (nr_reclaimed >= SWAP_CLUSTER_MAX) break; } out: @@ -1220,7 +1220,6 @@ static int kswapd(void *p) }; cpumask_t cpumask; - daemonize("kswapd%d", pgdat->node_id); cpumask = node_to_cpumask(pgdat->node_id); if (!cpus_empty(cpumask)) set_cpus_allowed(tsk, cpumask); @@ -1261,7 +1260,7 @@ static int kswapd(void *p) } finish_wait(&pgdat->kswapd_wait, &wait); - balance_pgdat(pgdat, 0, order); + balance_pgdat(pgdat, order); } return 0; } @@ -1290,35 +1289,152 @@ void wakeup_kswapd(struct zone *zone, in #ifdef CONFIG_PM /* - * Try to free `nr_pages' of memory, system-wide. Returns the number of freed - * pages. + * Helper function for shrink_all_memory(). Tries to reclaim 'nr_pages' pages + * from LRU lists system-wide, for given pass and priority, and returns the + * number of reclaimed pages + * + * For pass > 3 we also try to shrink the LRU lists that contain a few pages + */ +static unsigned long shrink_all_zones(unsigned long nr_pages, int pass, + int prio, struct scan_control *sc) +{ + struct zone *zone; + unsigned long nr_to_scan, ret = 0; + + for_each_zone(zone) { + + if (!populated_zone(zone)) + continue; + + if (zone->all_unreclaimable && prio != DEF_PRIORITY) + continue; + + /* For pass = 0 we don't shrink the active list */ + if (pass > 0) { + zone->nr_scan_active += (zone->nr_active >> prio) + 1; + if (zone->nr_scan_active >= nr_pages || pass > 3) { + zone->nr_scan_active = 0; + nr_to_scan = min(nr_pages, zone->nr_active); + shrink_active_list(nr_to_scan, zone, sc); + } + } + + zone->nr_scan_inactive += (zone->nr_inactive >> prio) + 1; + if (zone->nr_scan_inactive >= nr_pages || pass > 3) { + zone->nr_scan_inactive = 0; + nr_to_scan = min(nr_pages, zone->nr_inactive); + ret += shrink_inactive_list(nr_to_scan, zone, sc); + if (ret >= nr_pages) + return ret; + } + } + + return ret; +} + +/* + * Try to free `nr_pages' of memory, system-wide, and return the number of + * freed pages. + * + * Rather than trying to age LRUs the aim is to preserve the overall + * LRU order by reclaiming preferentially + * inactive > active > active referenced > active mapped */ unsigned long shrink_all_memory(unsigned long nr_pages) { - pg_data_t *pgdat; - unsigned long nr_to_free = nr_pages; + unsigned long lru_pages, nr_slab; unsigned long ret = 0; - unsigned retry = 2; - struct reclaim_state reclaim_state = { - .reclaimed_slab = 0, + int pass; + struct reclaim_state reclaim_state; + struct zone *zone; + struct scan_control sc = { + .gfp_mask = GFP_KERNEL, + .may_swap = 0, + .swap_cluster_max = nr_pages, + .may_writepage = 1, + .swappiness = vm_swappiness, }; current->reclaim_state = &reclaim_state; -repeat: - for_each_online_pgdat(pgdat) { - unsigned long freed; - - freed = balance_pgdat(pgdat, nr_to_free, 0); - ret += freed; - nr_to_free -= freed; - if ((long)nr_to_free <= 0) + + lru_pages = 0; + for_each_zone(zone) + lru_pages += zone->nr_active + zone->nr_inactive; + + nr_slab = global_page_state(NR_SLAB); + /* If slab caches are huge, it's better to hit them first */ + while (nr_slab >= lru_pages) { + reclaim_state.reclaimed_slab = 0; + shrink_slab(nr_pages, sc.gfp_mask, lru_pages); + if (!reclaim_state.reclaimed_slab) break; + + ret += reclaim_state.reclaimed_slab; + if (ret >= nr_pages) + goto out; + + nr_slab -= reclaim_state.reclaimed_slab; } - if (retry-- && ret < nr_pages) { - blk_congestion_wait(WRITE, HZ/5); - goto repeat; + + /* + * We try to shrink LRUs in 5 passes: + * 0 = Reclaim from inactive_list only + * 1 = Reclaim from active list but don't reclaim mapped + * 2 = 2nd pass of type 1 + * 3 = Reclaim mapped (normal reclaim) + * 4 = 2nd pass of type 3 + */ + for (pass = 0; pass < 5; pass++) { + int prio; + + /* Needed for shrinking slab caches later on */ + if (!lru_pages) + for_each_zone(zone) { + lru_pages += zone->nr_active; + lru_pages += zone->nr_inactive; + } + + /* Force reclaiming mapped pages in the passes #3 and #4 */ + if (pass > 2) { + sc.may_swap = 1; + sc.swappiness = 100; + } + + for (prio = DEF_PRIORITY; prio >= 0; prio--) { + unsigned long nr_to_scan = nr_pages - ret; + + sc.nr_scanned = 0; + ret += shrink_all_zones(nr_to_scan, prio, pass, &sc); + if (ret >= nr_pages) + goto out; + + reclaim_state.reclaimed_slab = 0; + shrink_slab(sc.nr_scanned, sc.gfp_mask, lru_pages); + ret += reclaim_state.reclaimed_slab; + if (ret >= nr_pages) + goto out; + + if (sc.nr_scanned && prio < DEF_PRIORITY - 2) + blk_congestion_wait(WRITE, HZ / 10); + } + + lru_pages = 0; } + + /* + * If ret = 0, we could not shrink LRUs, but there may be something + * in slab caches + */ + if (!ret) + do { + reclaim_state.reclaimed_slab = 0; + shrink_slab(nr_pages, sc.gfp_mask, lru_pages); + ret += reclaim_state.reclaimed_slab; + } while (ret < nr_pages && reclaim_state.reclaimed_slab > 0); + +out: current->reclaim_state = NULL; + return ret; } #endif @@ -1328,7 +1444,7 @@ #ifdef CONFIG_HOTPLUG_CPU not required for correctness. So if the last cpu in a node goes away, we get changed to run anywhere: as the first one comes back, restore their cpu bindings. */ -static int cpu_callback(struct notifier_block *nfb, +static int __devinit cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { pg_data_t *pgdat; @@ -1346,21 +1462,35 @@ static int cpu_callback(struct notifier_ } #endif /* CONFIG_HOTPLUG_CPU */ +/* + * This kswapd start function will be called by init and node-hot-add. + * On node-hot-add, kswapd will moved to proper cpus if cpus are hot-added. + */ +int kswapd_run(int nid) +{ + pg_data_t *pgdat = NODE_DATA(nid); + int ret = 0; + + if (pgdat->kswapd) + return 0; + + pgdat->kswapd = kthread_run(kswapd, pgdat, "kswapd%d", nid); + if (IS_ERR(pgdat->kswapd)) { + /* failure at boot is fatal */ + BUG_ON(system_state == SYSTEM_BOOTING); + printk("Failed to start kswapd on node %d\n",nid); + ret = -1; + } + return ret; +} + static int __init kswapd_init(void) { - pg_data_t *pgdat; + int nid; swap_setup(); - for_each_online_pgdat(pgdat) { - pid_t pid; - - pid = kernel_thread(kswapd, pgdat, CLONE_KERNEL); - BUG_ON(pid < 0); - read_lock(&tasklist_lock); - pgdat->kswapd = find_task_by_pid(pid); - read_unlock(&tasklist_lock); - } - total_memory = nr_free_pagecache_pages(); + for_each_online_node(nid) + kswapd_run(nid); hotcpu_notifier(cpu_callback, 0); return 0; } @@ -1373,10 +1503,6 @@ #ifdef CONFIG_NUMA * * If non-zero call zone_reclaim when the number of free pages falls below * the watermarks. - * - * In the future we may add flags to the mode. However, the page allocator - * should only have to check that zone_reclaim_mode != 0 before calling - * zone_reclaim(). */ int zone_reclaim_mode __read_mostly; @@ -1387,11 +1513,6 @@ #define RECLAIM_SWAP (1<<2) /* Swap page #define RECLAIM_SLAB (1<<3) /* Do a global slab shrink if the zone is out of memory */ /* - * Mininum time between zone reclaim scans - */ -int zone_reclaim_interval __read_mostly = 30*HZ; - -/* * Priority for ZONE_RECLAIM. This determines the fraction of pages * of a node considered for each zone_reclaim. 4 scans 1/16th of * a zone. @@ -1399,6 +1520,12 @@ int zone_reclaim_interval __read_mostly #define ZONE_RECLAIM_PRIORITY 4 /* + * Percentage of pages in a zone that must be unmapped for zone_reclaim to + * occur. + */ +int sysctl_min_unmapped_ratio = 1; + +/* * Try to free up some pages from this zone through reclaim. */ static int __zone_reclaim(struct zone *zone, gfp_t gfp_mask, unsigned int order) @@ -1412,10 +1539,10 @@ static int __zone_reclaim(struct zone *z struct scan_control sc = { .may_writepage = !!(zone_reclaim_mode & RECLAIM_WRITE), .may_swap = !!(zone_reclaim_mode & RECLAIM_SWAP), - .nr_mapped = read_page_state(nr_mapped), .swap_cluster_max = max_t(unsigned long, nr_pages, SWAP_CLUSTER_MAX), .gfp_mask = gfp_mask, + .swappiness = vm_swappiness, }; disable_swap_token(); @@ -1456,16 +1583,6 @@ static int __zone_reclaim(struct zone *z p->reclaim_state = NULL; current->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE); - - if (nr_reclaimed == 0) { - /* - * We were unable to reclaim enough pages to stay on node. We - * now allow off node accesses for a certain time period before - * trying again to reclaim pages from the local zone. - */ - zone->last_unsuccessful_zone_reclaim = jiffies; - } - return nr_reclaimed >= nr_pages; } @@ -1475,14 +1592,17 @@ int zone_reclaim(struct zone *zone, gfp_ int node_id; /* - * Do not reclaim if there was a recent unsuccessful attempt at zone - * reclaim. In that case we let allocations go off node for the - * zone_reclaim_interval. Otherwise we would scan for each off-node - * page allocation. + * Zone reclaim reclaims unmapped file backed pages. + * + * A small portion of unmapped file backed pages is needed for + * file I/O otherwise pages read by file I/O will be immediately + * thrown out if the zone is overallocated. So we do not reclaim + * if less than a specified percentage of the zone is used by + * unmapped file backed pages. */ - if (time_before(jiffies, - zone->last_unsuccessful_zone_reclaim + zone_reclaim_interval)) - return 0; + if (zone_page_state(zone, NR_FILE_PAGES) - + zone_page_state(zone, NR_FILE_MAPPED) <= zone->min_unmapped_ratio) + return 0; /* * Avoid concurrent zone reclaims, do not reclaim in a zone that does diff --git a/mm/vmstat.c b/mm/vmstat.c new file mode 100644 index 0000000..73b83d6 --- /dev/null +++ b/mm/vmstat.c @@ -0,0 +1,614 @@ +/* + * linux/mm/vmstat.c + * + * Manages VM statistics + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * zoned VM statistics + * Copyright (C) 2006 Silicon Graphics, Inc., + * Christoph Lameter + */ + +#include +#include +#include + +void __get_zone_counts(unsigned long *active, unsigned long *inactive, + unsigned long *free, struct pglist_data *pgdat) +{ + struct zone *zones = pgdat->node_zones; + int i; + + *active = 0; + *inactive = 0; + *free = 0; + for (i = 0; i < MAX_NR_ZONES; i++) { + *active += zones[i].nr_active; + *inactive += zones[i].nr_inactive; + *free += zones[i].free_pages; + } +} + +void get_zone_counts(unsigned long *active, + unsigned long *inactive, unsigned long *free) +{ + struct pglist_data *pgdat; + + *active = 0; + *inactive = 0; + *free = 0; + for_each_online_pgdat(pgdat) { + unsigned long l, m, n; + __get_zone_counts(&l, &m, &n, pgdat); + *active += l; + *inactive += m; + *free += n; + } +} + +#ifdef CONFIG_VM_EVENT_COUNTERS +DEFINE_PER_CPU(struct vm_event_state, vm_event_states) = {{0}}; +EXPORT_PER_CPU_SYMBOL(vm_event_states); + +static void sum_vm_events(unsigned long *ret, cpumask_t *cpumask) +{ + int cpu = 0; + int i; + + memset(ret, 0, NR_VM_EVENT_ITEMS * sizeof(unsigned long)); + + cpu = first_cpu(*cpumask); + while (cpu < NR_CPUS) { + struct vm_event_state *this = &per_cpu(vm_event_states, cpu); + + cpu = next_cpu(cpu, *cpumask); + + if (cpu < NR_CPUS) + prefetch(&per_cpu(vm_event_states, cpu)); + + + for (i = 0; i < NR_VM_EVENT_ITEMS; i++) + ret[i] += this->event[i]; + } +} + +/* + * Accumulate the vm event counters across all CPUs. + * The result is unavoidably approximate - it can change + * during and after execution of this function. +*/ +void all_vm_events(unsigned long *ret) +{ + sum_vm_events(ret, &cpu_online_map); +} + +#ifdef CONFIG_HOTPLUG +/* + * Fold the foreign cpu events into our own. + * + * This is adding to the events on one processor + * but keeps the global counts constant. + */ +void vm_events_fold_cpu(int cpu) +{ + struct vm_event_state *fold_state = &per_cpu(vm_event_states, cpu); + int i; + + for (i = 0; i < NR_VM_EVENT_ITEMS; i++) { + count_vm_events(i, fold_state->event[i]); + fold_state->event[i] = 0; + } +} +#endif /* CONFIG_HOTPLUG */ + +#endif /* CONFIG_VM_EVENT_COUNTERS */ + +/* + * Manage combined zone based / global counters + * + * vm_stat contains the global counters + */ +atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS]; +EXPORT_SYMBOL(vm_stat); + +#ifdef CONFIG_SMP + +#define STAT_THRESHOLD 32 + +/* + * Determine pointer to currently valid differential byte given a zone and + * the item number. + * + * Preemption must be off + */ +static inline s8 *diff_pointer(struct zone *zone, enum zone_stat_item item) +{ + return &zone_pcp(zone, smp_processor_id())->vm_stat_diff[item]; +} + +/* + * For use when we know that interrupts are disabled. + */ +void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item, + int delta) +{ + s8 *p; + long x; + + p = diff_pointer(zone, item); + x = delta + *p; + + if (unlikely(x > STAT_THRESHOLD || x < -STAT_THRESHOLD)) { + zone_page_state_add(x, zone, item); + x = 0; + } + + *p = x; +} +EXPORT_SYMBOL(__mod_zone_page_state); + +/* + * For an unknown interrupt state + */ +void mod_zone_page_state(struct zone *zone, enum zone_stat_item item, + int delta) +{ + unsigned long flags; + + local_irq_save(flags); + __mod_zone_page_state(zone, item, delta); + local_irq_restore(flags); +} +EXPORT_SYMBOL(mod_zone_page_state); + +/* + * Optimized increment and decrement functions. + * + * These are only for a single page and therefore can take a struct page * + * argument instead of struct zone *. This allows the inclusion of the code + * generated for page_zone(page) into the optimized functions. + * + * No overflow check is necessary and therefore the differential can be + * incremented or decremented in place which may allow the compilers to + * generate better code. + * + * The increment or decrement is known and therefore one boundary check can + * be omitted. + * + * Some processors have inc/dec instructions that are atomic vs an interrupt. + * However, the code must first determine the differential location in a zone + * based on the processor number and then inc/dec the counter. There is no + * guarantee without disabling preemption that the processor will not change + * in between and therefore the atomicity vs. interrupt cannot be exploited + * in a useful way here. + */ +static void __inc_zone_state(struct zone *zone, enum zone_stat_item item) +{ + s8 *p = diff_pointer(zone, item); + + (*p)++; + + if (unlikely(*p > STAT_THRESHOLD)) { + zone_page_state_add(*p, zone, item); + *p = 0; + } +} + +void __inc_zone_page_state(struct page *page, enum zone_stat_item item) +{ + __inc_zone_state(page_zone(page), item); +} +EXPORT_SYMBOL(__inc_zone_page_state); + +void __dec_zone_page_state(struct page *page, enum zone_stat_item item) +{ + struct zone *zone = page_zone(page); + s8 *p = diff_pointer(zone, item); + + (*p)--; + + if (unlikely(*p < -STAT_THRESHOLD)) { + zone_page_state_add(*p, zone, item); + *p = 0; + } +} +EXPORT_SYMBOL(__dec_zone_page_state); + +void inc_zone_state(struct zone *zone, enum zone_stat_item item) +{ + unsigned long flags; + + local_irq_save(flags); + __inc_zone_state(zone, item); + local_irq_restore(flags); +} + +void inc_zone_page_state(struct page *page, enum zone_stat_item item) +{ + unsigned long flags; + struct zone *zone; + + zone = page_zone(page); + local_irq_save(flags); + __inc_zone_state(zone, item); + local_irq_restore(flags); +} +EXPORT_SYMBOL(inc_zone_page_state); + +void dec_zone_page_state(struct page *page, enum zone_stat_item item) +{ + unsigned long flags; + struct zone *zone; + s8 *p; + + zone = page_zone(page); + local_irq_save(flags); + p = diff_pointer(zone, item); + + (*p)--; + + if (unlikely(*p < -STAT_THRESHOLD)) { + zone_page_state_add(*p, zone, item); + *p = 0; + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(dec_zone_page_state); + +/* + * Update the zone counters for one cpu. + */ +void refresh_cpu_vm_stats(int cpu) +{ + struct zone *zone; + int i; + unsigned long flags; + + for_each_zone(zone) { + struct per_cpu_pageset *pcp; + + pcp = zone_pcp(zone, cpu); + + for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) + if (pcp->vm_stat_diff[i]) { + local_irq_save(flags); + zone_page_state_add(pcp->vm_stat_diff[i], + zone, i); + pcp->vm_stat_diff[i] = 0; + local_irq_restore(flags); + } + } +} + +static void __refresh_cpu_vm_stats(void *dummy) +{ + refresh_cpu_vm_stats(smp_processor_id()); +} + +/* + * Consolidate all counters. + * + * Note that the result is less inaccurate but still inaccurate + * if concurrent processes are allowed to run. + */ +void refresh_vm_stats(void) +{ + on_each_cpu(__refresh_cpu_vm_stats, NULL, 0, 1); +} +EXPORT_SYMBOL(refresh_vm_stats); + +#endif + +#ifdef CONFIG_NUMA +/* + * zonelist = the list of zones passed to the allocator + * z = the zone from which the allocation occurred. + * + * Must be called with interrupts disabled. + */ +void zone_statistics(struct zonelist *zonelist, struct zone *z) +{ + if (z->zone_pgdat == zonelist->zones[0]->zone_pgdat) { + __inc_zone_state(z, NUMA_HIT); + } else { + __inc_zone_state(z, NUMA_MISS); + __inc_zone_state(zonelist->zones[0], NUMA_FOREIGN); + } + if (z->zone_pgdat == NODE_DATA(numa_node_id())) + __inc_zone_state(z, NUMA_LOCAL); + else + __inc_zone_state(z, NUMA_OTHER); +} +#endif + +#ifdef CONFIG_PROC_FS + +#include + +static void *frag_start(struct seq_file *m, loff_t *pos) +{ + pg_data_t *pgdat; + loff_t node = *pos; + for (pgdat = first_online_pgdat(); + pgdat && node; + pgdat = next_online_pgdat(pgdat)) + --node; + + return pgdat; +} + +static void *frag_next(struct seq_file *m, void *arg, loff_t *pos) +{ + pg_data_t *pgdat = (pg_data_t *)arg; + + (*pos)++; + return next_online_pgdat(pgdat); +} + +static void frag_stop(struct seq_file *m, void *arg) +{ +} + +/* + * This walks the free areas for each zone. + */ +static int frag_show(struct seq_file *m, void *arg) +{ + pg_data_t *pgdat = (pg_data_t *)arg; + struct zone *zone; + struct zone *node_zones = pgdat->node_zones; + unsigned long flags; + int order; + + for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) { + if (!populated_zone(zone)) + continue; + + spin_lock_irqsave(&zone->lock, flags); + seq_printf(m, "Node %d, zone %8s ", pgdat->node_id, zone->name); + for (order = 0; order < MAX_ORDER; ++order) + seq_printf(m, "%6lu ", zone->free_area[order].nr_free); + spin_unlock_irqrestore(&zone->lock, flags); + seq_putc(m, '\n'); + } + return 0; +} + +struct seq_operations fragmentation_op = { + .start = frag_start, + .next = frag_next, + .stop = frag_stop, + .show = frag_show, +}; + +static char *vmstat_text[] = { + /* Zoned VM counters */ + "nr_anon_pages", + "nr_mapped", + "nr_file_pages", + "nr_slab", + "nr_page_table_pages", + "nr_dirty", + "nr_writeback", + "nr_unstable", + "nr_bounce", + +#ifdef CONFIG_NUMA + "numa_hit", + "numa_miss", + "numa_foreign", + "numa_interleave", + "numa_local", + "numa_other", +#endif + +#ifdef CONFIG_VM_EVENT_COUNTERS + "pgpgin", + "pgpgout", + "pswpin", + "pswpout", + + "pgalloc_dma", + "pgalloc_dma32", + "pgalloc_normal", + "pgalloc_high", + + "pgfree", + "pgactivate", + "pgdeactivate", + + "pgfault", + "pgmajfault", + + "pgrefill_dma", + "pgrefill_dma32", + "pgrefill_normal", + "pgrefill_high", + + "pgsteal_dma", + "pgsteal_dma32", + "pgsteal_normal", + "pgsteal_high", + + "pgscan_kswapd_dma", + "pgscan_kswapd_dma32", + "pgscan_kswapd_normal", + "pgscan_kswapd_high", + + "pgscan_direct_dma", + "pgscan_direct_dma32", + "pgscan_direct_normal", + "pgscan_direct_high", + + "pginodesteal", + "slabs_scanned", + "kswapd_steal", + "kswapd_inodesteal", + "pageoutrun", + "allocstall", + + "pgrotated", +#endif +}; + +/* + * Output information about zones in @pgdat. + */ +static int zoneinfo_show(struct seq_file *m, void *arg) +{ + pg_data_t *pgdat = arg; + struct zone *zone; + struct zone *node_zones = pgdat->node_zones; + unsigned long flags; + + for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; zone++) { + int i; + + if (!populated_zone(zone)) + continue; + + spin_lock_irqsave(&zone->lock, flags); + seq_printf(m, "Node %d, zone %8s", pgdat->node_id, zone->name); + seq_printf(m, + "\n pages free %lu" + "\n min %lu" + "\n low %lu" + "\n high %lu" + "\n active %lu" + "\n inactive %lu" + "\n scanned %lu (a: %lu i: %lu)" + "\n spanned %lu" + "\n present %lu", + zone->free_pages, + zone->pages_min, + zone->pages_low, + zone->pages_high, + zone->nr_active, + zone->nr_inactive, + zone->pages_scanned, + zone->nr_scan_active, zone->nr_scan_inactive, + zone->spanned_pages, + zone->present_pages); + + for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) + seq_printf(m, "\n %-12s %lu", vmstat_text[i], + zone_page_state(zone, i)); + + seq_printf(m, + "\n protection: (%lu", + zone->lowmem_reserve[0]); + for (i = 1; i < ARRAY_SIZE(zone->lowmem_reserve); i++) + seq_printf(m, ", %lu", zone->lowmem_reserve[i]); + seq_printf(m, + ")" + "\n pagesets"); + for_each_online_cpu(i) { + struct per_cpu_pageset *pageset; + int j; + + pageset = zone_pcp(zone, i); + for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) { + if (pageset->pcp[j].count) + break; + } + if (j == ARRAY_SIZE(pageset->pcp)) + continue; + for (j = 0; j < ARRAY_SIZE(pageset->pcp); j++) { + seq_printf(m, + "\n cpu: %i pcp: %i" + "\n count: %i" + "\n high: %i" + "\n batch: %i", + i, j, + pageset->pcp[j].count, + pageset->pcp[j].high, + pageset->pcp[j].batch); + } + } + seq_printf(m, + "\n all_unreclaimable: %u" + "\n prev_priority: %i" + "\n temp_priority: %i" + "\n start_pfn: %lu", + zone->all_unreclaimable, + zone->prev_priority, + zone->temp_priority, + zone->zone_start_pfn); + spin_unlock_irqrestore(&zone->lock, flags); + seq_putc(m, '\n'); + } + return 0; +} + +struct seq_operations zoneinfo_op = { + .start = frag_start, /* iterate over all zones. The same as in + * fragmentation. */ + .next = frag_next, + .stop = frag_stop, + .show = zoneinfo_show, +}; + +static void *vmstat_start(struct seq_file *m, loff_t *pos) +{ + unsigned long *v; +#ifdef CONFIG_VM_EVENT_COUNTERS + unsigned long *e; +#endif + int i; + + if (*pos >= ARRAY_SIZE(vmstat_text)) + return NULL; + +#ifdef CONFIG_VM_EVENT_COUNTERS + v = kmalloc(NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long) + + sizeof(struct vm_event_state), GFP_KERNEL); +#else + v = kmalloc(NR_VM_ZONE_STAT_ITEMS * sizeof(unsigned long), + GFP_KERNEL); +#endif + m->private = v; + if (!v) + return ERR_PTR(-ENOMEM); + for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) + v[i] = global_page_state(i); +#ifdef CONFIG_VM_EVENT_COUNTERS + e = v + NR_VM_ZONE_STAT_ITEMS; + all_vm_events(e); + e[PGPGIN] /= 2; /* sectors -> kbytes */ + e[PGPGOUT] /= 2; +#endif + return v + *pos; +} + +static void *vmstat_next(struct seq_file *m, void *arg, loff_t *pos) +{ + (*pos)++; + if (*pos >= ARRAY_SIZE(vmstat_text)) + return NULL; + return (unsigned long *)m->private + *pos; +} + +static int vmstat_show(struct seq_file *m, void *arg) +{ + unsigned long *l = arg; + unsigned long off = l - (unsigned long *)m->private; + + seq_printf(m, "%s %lu\n", vmstat_text[off], *l); + return 0; +} + +static void vmstat_stop(struct seq_file *m, void *arg) +{ + kfree(m->private); + m->private = NULL; +} + +struct seq_operations vmstat_op = { + .start = vmstat_start, + .next = vmstat_next, + .stop = vmstat_stop, + .show = vmstat_show, +}; + +#endif /* CONFIG_PROC_FS */ + diff --git a/net/802/fc.c b/net/802/fc.c index 282c4ab..2a27e37 100644 --- a/net/802/fc.c +++ b/net/802/fc.c @@ -10,7 +10,6 @@ * v 1.0 03/22/99 */ -#include #include #include #include diff --git a/net/802/fddi.c b/net/802/fddi.c index ac242a4..797c6d9 100644 --- a/net/802/fddi.c +++ b/net/802/fddi.c @@ -26,7 +26,6 @@ * Maciej W. Rozycki : IPv6 support */ -#include #include #include #include diff --git a/net/802/sysctl_net_802.c b/net/802/sysctl_net_802.c index 7001295..ead5603 100644 --- a/net/802/sysctl_net_802.c +++ b/net/802/sysctl_net_802.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/802/tr.c b/net/802/tr.c index e9dc803..d7d8f40 100644 --- a/net/802/tr.c +++ b/net/802/tr.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #include diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 3948949..458031b 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -364,6 +364,14 @@ static void vlan_transfer_operstate(cons } } +/* + * vlan network devices have devices nesting below it, and are a special + * "super class" of normal network devices; split their locks off into a + * separate class since they always nest. + */ +static struct lock_class_key vlan_netdev_xmit_lock_key; + + /* Attach a VLAN device to a mac address (ie Ethernet Card). * Returns the device that was created, or NULL if there was * an error of some kind. @@ -460,6 +468,7 @@ #endif new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name, vlan_setup); + if (new_dev == NULL) goto out_unlock; @@ -518,6 +527,8 @@ #endif if (register_netdevice(new_dev)) goto out_free_newdev; + lockdep_set_class(&new_dev->_xmit_lock, &vlan_netdev_xmit_lock_key); + new_dev->iflink = real_dev->ifindex; vlan_transfer_operstate(real_dev, new_dev); linkwatch_fire_event(new_dev); /* _MUST_ call rfc2863_policy() */ diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index 7b214cf..a8fc0de 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c @@ -17,7 +17,6 @@ * Jan 20, 1998 Ben Greear Initial Version *****************************************************************************/ -#include #include #include /* offsetof(), etc. */ #include /* return codes */ diff --git a/net/Kconfig b/net/Kconfig index 4193cdc..c6cec5a 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -66,6 +66,13 @@ source "net/ipv6/Kconfig" endif # if INET +config NETWORK_SECMARK + bool "Security Marking" + help + This enables security marking of network packets, similar + to nfmark, but designated for security purposes. + If you are unsure how to answer this question, answer N. + menuconfig NETFILTER bool "Network packet filtering (replaces ipchains)" ---help--- @@ -215,6 +222,21 @@ config NET_PKTGEN To compile this code as a module, choose M here: the module will be called pktgen. +config NET_TCPPROBE + tristate "TCP connection probing" + depends on INET && EXPERIMENTAL && PROC_FS && KPROBES + ---help--- + This module allows for capturing the changes to TCP connection + state in response to incoming packets. It is used for debugging + TCP congestion avoidance modules. If you don't understand + what was just said, you don't need it: say N. + + Documentation on how to use the packet generator can be found + at http://linux-net.osdl.org/index.php/TcpProbe + + To compile this code as a module, choose M here: the + module will be called tcp_probe. + endmenu endmenu diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c index 7076097..f3777ec 100644 --- a/net/appletalk/aarp.c +++ b/net/appletalk/aarp.c @@ -29,7 +29,6 @@ * */ -#include #include #include #include diff --git a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c index dc4048d..7ae4916 100644 --- a/net/appletalk/atalk_proc.c +++ b/net/appletalk/atalk_proc.c @@ -8,7 +8,6 @@ * Free Software Foundation, version 2. */ -#include #include #include #include diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c index 7b1eb9a..5ee96d4 100644 --- a/net/appletalk/ddp.c +++ b/net/appletalk/ddp.c @@ -51,7 +51,6 @@ * */ -#include #include #include #include diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c index af7f060..40b0af7 100644 --- a/net/appletalk/sysctl_net_atalk.c +++ b/net/appletalk/sysctl_net_atalk.c @@ -6,7 +6,6 @@ * Dynamic registration, added aarp entries. (5/30/97 Chris Horn) */ -#include #include #include #include diff --git a/net/atm/Makefile b/net/atm/Makefile index d581875..89656d6 100644 --- a/net/atm/Makefile +++ b/net/atm/Makefile @@ -2,7 +2,7 @@ # # Makefile for the ATM Protocol Families. # -atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o +atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o mpoa-objs := mpc.o mpoa_caches.o mpoa_proc.o obj-$(CONFIG_ATM) += atm.o diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c new file mode 100644 index 0000000..5df4b9a --- /dev/null +++ b/net/atm/atm_sysfs.c @@ -0,0 +1,176 @@ +/* ATM driver model support. */ + +#include +#include +#include +#include +#include +#include "common.h" +#include "resources.h" + +#define to_atm_dev(cldev) container_of(cldev, struct atm_dev, class_dev) + +static ssize_t show_type(struct class_device *cdev, char *buf) +{ + struct atm_dev *adev = to_atm_dev(cdev); + return sprintf(buf, "%s\n", adev->type); +} + +static ssize_t show_address(struct class_device *cdev, char *buf) +{ + char *pos = buf; + struct atm_dev *adev = to_atm_dev(cdev); + int i; + + for (i = 0; i < (ESI_LEN - 1); i++) + pos += sprintf(pos, "%02x:", adev->esi[i]); + pos += sprintf(pos, "%02x\n", adev->esi[i]); + + return pos - buf; +} + +static ssize_t show_atmaddress(struct class_device *cdev, char *buf) +{ + unsigned long flags; + char *pos = buf; + struct atm_dev *adev = to_atm_dev(cdev); + struct atm_dev_addr *aaddr; + int bin[] = { 1, 2, 10, 6, 1 }, *fmt = bin; + int i, j; + + spin_lock_irqsave(&adev->lock, flags); + list_for_each_entry(aaddr, &adev->local, entry) { + for(i = 0, j = 0; i < ATM_ESA_LEN; ++i, ++j) { + if (j == *fmt) { + pos += sprintf(pos, "."); + ++fmt; + j = 0; + } + pos += sprintf(pos, "%02x", aaddr->addr.sas_addr.prv[i]); + } + pos += sprintf(pos, "\n"); + } + spin_unlock_irqrestore(&adev->lock, flags); + + return pos - buf; +} + +static ssize_t show_carrier(struct class_device *cdev, char *buf) +{ + char *pos = buf; + struct atm_dev *adev = to_atm_dev(cdev); + + pos += sprintf(pos, "%d\n", + adev->signal == ATM_PHY_SIG_LOST ? 0 : 1); + + return pos - buf; +} + +static ssize_t show_link_rate(struct class_device *cdev, char *buf) +{ + char *pos = buf; + struct atm_dev *adev = to_atm_dev(cdev); + int link_rate; + + /* show the link rate, not the data rate */ + switch (adev->link_rate) { + case ATM_OC3_PCR: + link_rate = 155520000; + break; + case ATM_OC12_PCR: + link_rate = 622080000; + break; + case ATM_25_PCR: + link_rate = 25600000; + break; + default: + link_rate = adev->link_rate * 8 * 53; + } + pos += sprintf(pos, "%d\n", link_rate); + + return pos - buf; +} + +static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL); +static CLASS_DEVICE_ATTR(atmaddress, S_IRUGO, show_atmaddress, NULL); +static CLASS_DEVICE_ATTR(carrier, S_IRUGO, show_carrier, NULL); +static CLASS_DEVICE_ATTR(type, S_IRUGO, show_type, NULL); +static CLASS_DEVICE_ATTR(link_rate, S_IRUGO, show_link_rate, NULL); + +static struct class_device_attribute *atm_attrs[] = { + &class_device_attr_atmaddress, + &class_device_attr_address, + &class_device_attr_carrier, + &class_device_attr_type, + &class_device_attr_link_rate, + NULL +}; + +static int atm_uevent(struct class_device *cdev, char **envp, int num_envp, char *buf, int size) +{ + struct atm_dev *adev; + int i = 0, len = 0; + + if (!cdev) + return -ENODEV; + + adev = to_atm_dev(cdev); + if (!adev) + return -ENODEV; + + if (add_uevent_var(envp, num_envp, &i, buf, size, &len, + "NAME=%s%d", adev->type, adev->number)) + return -ENOMEM; + + envp[i] = NULL; + return 0; +} + +static void atm_release(struct class_device *cdev) +{ + struct atm_dev *adev = to_atm_dev(cdev); + + kfree(adev); +} + +static struct class atm_class = { + .name = "atm", + .release = atm_release, + .uevent = atm_uevent, +}; + +int atm_register_sysfs(struct atm_dev *adev) +{ + struct class_device *cdev = &adev->class_dev; + int i, err; + + cdev->class = &atm_class; + class_set_devdata(cdev, adev); + + snprintf(cdev->class_id, BUS_ID_SIZE, "%s%d", adev->type, adev->number); + err = class_device_register(cdev); + if (err < 0) + return err; + + for (i = 0; atm_attrs[i]; i++) + class_device_create_file(cdev, atm_attrs[i]); + + return 0; +} + +void atm_unregister_sysfs(struct atm_dev *adev) +{ + struct class_device *cdev = &adev->class_dev; + + class_device_del(cdev); +} + +int __init atm_sysfs_init(void) +{ + return class_register(&atm_class); +} + +void __exit atm_sysfs_exit(void) +{ + class_unregister(&atm_class); +} diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 680ccb1..a487233 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -5,7 +5,6 @@ Author: Marcell GAL, 2000, XDSL Ltd, Hun */ #include -#include #include #include #include diff --git a/net/atm/clip.c b/net/atm/clip.c index 72d8529..121bf6f 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -2,7 +2,6 @@ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ -#include #include #include #include /* for UINT_MAX */ @@ -24,6 +23,7 @@ #include /* for struct sock #include /* for IFF_UP */ #include #include +#include #include #include #include @@ -98,7 +98,7 @@ static void unlink_clip_vcc(struct clip_ printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n", clip_vcc); return; } - spin_lock_bh(&entry->neigh->dev->xmit_lock); /* block clip_start_xmit() */ + netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */ entry->neigh->used = jiffies; for (walk = &entry->vccs; *walk; walk = &(*walk)->next) if (*walk == clip_vcc) { @@ -122,7 +122,7 @@ static void unlink_clip_vcc(struct clip_ printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc " "0x%p)\n", entry, clip_vcc); out: - spin_unlock_bh(&entry->neigh->dev->xmit_lock); + netif_tx_unlock_bh(entry->neigh->dev); } /* The neighbour entry n->lock is held. */ @@ -267,7 +267,7 @@ static void clip_neigh_destroy(struct ne DPRINTK("clip_neigh_destroy (neigh %p)\n", neigh); if (NEIGH2ENTRY(neigh)->vccs) printk(KERN_CRIT "clip_neigh_destroy: vccs != NULL !!!\n"); - NEIGH2ENTRY(neigh)->vccs = (void *) 0xdeadbeef; + NEIGH2ENTRY(neigh)->vccs = (void *) NEIGHBOR_DEAD; } static void clip_neigh_solicit(struct neighbour *neigh, struct sk_buff *skb) diff --git a/net/atm/common.c b/net/atm/common.c index ae00222..fbabff4 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -3,7 +3,6 @@ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ -#include #include #include #include /* struct socket, struct proto_ops */ @@ -791,8 +790,14 @@ static int __init atm_init(void) printk(KERN_ERR "atm_proc_init() failed with %d\n",error); goto out_atmsvc_exit; } + if ((error = atm_sysfs_init()) < 0) { + printk(KERN_ERR "atm_sysfs_init() failed with %d\n",error); + goto out_atmproc_exit; + } out: return error; +out_atmproc_exit: + atm_proc_exit(); out_atmsvc_exit: atmsvc_exit(); out_atmpvc_exit: @@ -805,6 +810,7 @@ out_unregister_vcc_proto: static void __exit atm_exit(void) { atm_proc_exit(); + atm_sysfs_exit(); atmsvc_exit(); atmpvc_exit(); proto_unregister(&vcc_proto); diff --git a/net/atm/common.h b/net/atm/common.h index 4887c31..a422da7 100644 --- a/net/atm/common.h +++ b/net/atm/common.h @@ -28,6 +28,8 @@ int atmpvc_init(void); void atmpvc_exit(void); int atmsvc_init(void); void atmsvc_exit(void); +int atm_sysfs_init(void); +void atm_sysfs_exit(void); #ifdef CONFIG_PROC_FS int atm_proc_init(void); diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c index 851cfa6..8c2022c 100644 --- a/net/atm/ioctl.c +++ b/net/atm/ioctl.c @@ -4,7 +4,6 @@ /* 2003 John Levon */ -#include #include #include #include /* struct socket, struct proto_ops */ diff --git a/net/atm/lec.c b/net/atm/lec.c index c4fc722..4b68a18 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -4,7 +4,6 @@ * */ -#include #include #include #include diff --git a/net/atm/lec.h b/net/atm/lec.h index 6606082..c22a8bf 100644 --- a/net/atm/lec.h +++ b/net/atm/lec.h @@ -9,7 +9,6 @@ #ifndef _LEC_H_ #define _LEC_H_ -#include #include #include #include diff --git a/net/atm/mpc.c b/net/atm/mpc.c index c304ef1..9aafe1e 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -25,7 +25,6 @@ #include #include #include /* Modular too */ -#include #include #include "lec.h" @@ -229,20 +228,15 @@ int atm_mpoa_delete_qos(struct atm_mpoa_ /* this is buggered - we need locking for qos_head */ void atm_mpoa_disp_qos(struct seq_file *m) { - unsigned char *ip; - char ipaddr[16]; struct atm_mpoa_qos *qos; qos = qos_head; seq_printf(m, "QoS entries for shortcuts:\n"); seq_printf(m, "IP address\n TX:max_pcr pcr min_pcr max_cdv max_sdu\n RX:max_pcr pcr min_pcr max_cdv max_sdu\n"); - ipaddr[sizeof(ipaddr)-1] = '\0'; while (qos != NULL) { - ip = (unsigned char *)&qos->ipaddr; - sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(ip)); seq_printf(m, "%u.%u.%u.%u\n %-7d %-7d %-7d %-7d %-7d\n %-7d %-7d %-7d %-7d %-7d\n", - NIPQUAD(ipaddr), + NIPQUAD(qos->ipaddr), qos->qos.txtp.max_pcr, qos->qos.txtp.pcr, qos->qos.txtp.min_pcr, qos->qos.txtp.max_cdv, qos->qos.txtp.max_sdu, qos->qos.rxtp.max_pcr, qos->qos.rxtp.pcr, qos->qos.rxtp.min_pcr, qos->qos.rxtp.max_cdv, qos->qos.rxtp.max_sdu); qos = qos->next; @@ -1083,7 +1077,6 @@ static void MPOA_trigger_rcvd(struct k_m static void check_qos_and_open_shortcut(struct k_message *msg, struct mpoa_client *client, in_cache_entry *entry) { uint32_t dst_ip = msg->content.in_info.in_dst_ip; - unsigned char *ip __attribute__ ((unused)) = (unsigned char *)&dst_ip; struct atm_mpoa_qos *qos = atm_mpoa_search_qos(dst_ip); eg_cache_entry *eg_entry = client->eg_ops->get_by_src_ip(dst_ip, client); @@ -1097,7 +1090,7 @@ static void check_qos_and_open_shortcut( entry->shortcut = eg_entry->shortcut; } if(entry->shortcut){ - dprintk("mpoa: (%s) using egress SVC to reach %u.%u.%u.%u\n",client->dev->name, NIPQUAD(ip)); + dprintk("mpoa: (%s) using egress SVC to reach %u.%u.%u.%u\n",client->dev->name, NIPQUAD(dst_ip)); client->eg_ops->put(eg_entry); return; } @@ -1119,12 +1112,10 @@ static void check_qos_and_open_shortcut( static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *mpc) { - unsigned char *ip; - uint32_t dst_ip = msg->content.in_info.in_dst_ip; in_cache_entry *entry = mpc->in_ops->get(dst_ip, mpc); - ip = (unsigned char *)&dst_ip; - dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %u.%u.%u.%u\n", mpc->dev->name, NIPQUAD(ip)); + + dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %u.%u.%u.%u\n", mpc->dev->name, NIPQUAD(dst_ip)); ddprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", mpc->dev->name, entry); if(entry == NULL){ printk("\nmpoa: (%s) ARGH, received res. reply for an entry that doesn't exist.\n", mpc->dev->name); diff --git a/net/atm/mpoa_caches.c b/net/atm/mpoa_caches.c index 64ddebb..781ed1b 100644 --- a/net/atm/mpoa_caches.c +++ b/net/atm/mpoa_caches.c @@ -223,7 +223,6 @@ static void in_cache_remove_entry(in_cac but an easy one... */ static void clear_count_and_expired(struct mpoa_client *client) { - unsigned char *ip; in_cache_entry *entry, *next_entry; struct timeval now; @@ -236,8 +235,7 @@ static void clear_count_and_expired(stru next_entry = entry->next; if((now.tv_sec - entry->tv.tv_sec) > entry->ctrl_info.holding_time){ - ip = (unsigned char*)&entry->ctrl_info.in_dst_ip; - dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(ip)); + dprintk("mpoa: mpoa_caches.c: holding time expired, ip = %u.%u.%u.%u\n", NIPQUAD(entry->ctrl_info.in_dst_ip)); client->in_ops->remove_entry(entry, client); } entry = next_entry; @@ -455,7 +453,6 @@ static void eg_cache_remove_entry(eg_cac static eg_cache_entry *eg_cache_add_entry(struct k_message *msg, struct mpoa_client *client) { - unsigned char *ip; eg_cache_entry *entry = kmalloc(sizeof(eg_cache_entry), GFP_KERNEL); if (entry == NULL) { @@ -463,8 +460,7 @@ static eg_cache_entry *eg_cache_add_entr return NULL; } - ip = (unsigned char *)&msg->content.eg_info.eg_dst_ip; - dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(ip)); + dprintk("mpoa: mpoa_caches.c: adding an egress entry, ip = %u.%u.%u.%u, this should be our IP\n", NIPQUAD(msg->content.eg_info.eg_dst_ip)); memset(entry, 0, sizeof(eg_cache_entry)); atomic_set(&entry->use, 1); @@ -481,8 +477,8 @@ static eg_cache_entry *eg_cache_add_entr do_gettimeofday(&(entry->tv)); entry->entry_state = EGRESS_RESOLVED; dprintk("mpoa: mpoa_caches.c: new_eg_cache_entry cache_id %lu\n", ntohl(entry->ctrl_info.cache_id)); - ip = (unsigned char *)&entry->ctrl_info.mps_ip; - dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n", NIPQUAD(ip)); + dprintk("mpoa: mpoa_caches.c: mps_ip = %u.%u.%u.%u\n", + NIPQUAD(entry->ctrl_info.mps_ip)); atomic_inc(&entry->use); write_unlock_irq(&client->egress_lock); diff --git a/net/atm/mpoa_proc.c b/net/atm/mpoa_proc.c index 60834b5..d37b891 100644 --- a/net/atm/mpoa_proc.c +++ b/net/atm/mpoa_proc.c @@ -1,4 +1,3 @@ -#include #ifdef CONFIG_PROC_FS #include diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c index 1489067..76a7d8f 100644 --- a/net/atm/pppoatm.c +++ b/net/atm/pppoatm.c @@ -34,7 +34,6 @@ */ #include -#include #include #include #include diff --git a/net/atm/proc.c b/net/atm/proc.c index 4041054..3f95b08 100644 --- a/net/atm/proc.c +++ b/net/atm/proc.c @@ -8,7 +8,6 @@ * the reader. */ -#include #include /* for EXPORT_SYMBOL */ #include #include diff --git a/net/atm/pvc.c b/net/atm/pvc.c index f2c5417..b2148b4 100644 --- a/net/atm/pvc.c +++ b/net/atm/pvc.c @@ -3,7 +3,6 @@ /* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */ -#include #include /* struct socket, struct proto_ops */ #include /* ATM stuff */ #include /* ATM devices */ diff --git a/net/atm/resources.c b/net/atm/resources.c index 18ac806..de25c64 100644 --- a/net/atm/resources.c +++ b/net/atm/resources.c @@ -8,7 +8,6 @@ * use the default destruct function initialized by sock_init_data */ -#include #include #include #include @@ -114,14 +113,27 @@ struct atm_dev *atm_dev_register(const c printk(KERN_ERR "atm_dev_register: " "atm_proc_dev_register failed for dev %s\n", type); - mutex_unlock(&atm_dev_mutex); - kfree(dev); - return NULL; + goto out_fail; + } + + if (atm_register_sysfs(dev) < 0) { + printk(KERN_ERR "atm_dev_register: " + "atm_register_sysfs failed for dev %s\n", + type); + atm_proc_dev_deregister(dev); + goto out_fail; } + list_add_tail(&dev->dev_list, &atm_devs); - mutex_unlock(&atm_dev_mutex); +out: + mutex_unlock(&atm_dev_mutex); return dev; + +out_fail: + kfree(dev); + dev = NULL; + goto out; } @@ -140,6 +152,7 @@ void atm_dev_deregister(struct atm_dev * mutex_unlock(&atm_dev_mutex); atm_dev_release_vccs(dev); + atm_unregister_sysfs(dev); atm_proc_dev_deregister(dev); atm_dev_put(dev); diff --git a/net/atm/resources.h b/net/atm/resources.h index ac7222f..1d004aa 100644 --- a/net/atm/resources.h +++ b/net/atm/resources.h @@ -6,7 +6,6 @@ #ifndef NET_ATM_RESOURCES_H #define NET_ATM_RESOURCES_H -#include #include #include @@ -43,4 +42,6 @@ static inline void atm_proc_dev_deregist #endif /* CONFIG_PROC_FS */ +int atm_register_sysfs(struct atm_dev *adev); +void atm_unregister_sysfs(struct atm_dev *adev); #endif diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index a2e0dd0..10a3c0a 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -13,7 +13,6 @@ * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl) * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) */ -#include #include #include #include diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index dab77ef..47e6e79 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -6,7 +6,6 @@ * * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ -#include #include #include #include diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c index 3bb1527..77ba07c 100644 --- a/net/ax25/ax25_iface.c +++ b/net/ax25/ax25_iface.c @@ -6,7 +6,6 @@ * * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ -#include #include #include #include diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c index a0b534f..136c3ae 100644 --- a/net/ax25/ax25_ip.c +++ b/net/ax25/ax25_ip.c @@ -6,7 +6,6 @@ * * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ -#include #include #include #include @@ -104,11 +103,13 @@ int ax25_rebuild_header(struct sk_buff * { struct sk_buff *ourskb; unsigned char *bp = skb->data; - struct net_device *dev; + ax25_route *route; + struct net_device *dev = NULL; ax25_address *src, *dst; + ax25_digi *digipeat = NULL; ax25_dev *ax25_dev; - ax25_route _route, *route = &_route; ax25_cb *ax25; + char ip_mode = ' '; dst = (ax25_address *)(bp + 1); src = (ax25_address *)(bp + 8); @@ -116,8 +117,12 @@ int ax25_rebuild_header(struct sk_buff * if (arp_find(bp + 1, skb)) return 1; - route = ax25_rt_find_route(route, dst, NULL); - dev = route->dev; + route = ax25_get_route(dst, NULL); + if (route) { + digipeat = route->digipeat; + dev = route->dev; + ip_mode = route->ip_mode; + }; if (dev == NULL) dev = skb->dev; @@ -127,7 +132,7 @@ int ax25_rebuild_header(struct sk_buff * } if (bp[16] == AX25_P_IP) { - if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { + if (ip_mode == 'V' || (ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { /* * We copy the buffer and release the original thereby * keeping it straight @@ -173,7 +178,7 @@ int ax25_rebuild_header(struct sk_buff * ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, - &dst_c, route->digipeat, dev); + &dst_c, digipeat, dev); if (ax25) { ax25_cb_put(ax25); } @@ -191,7 +196,7 @@ int ax25_rebuild_header(struct sk_buff * skb_pull(skb, AX25_KISS_HEADER_LEN); - if (route->digipeat != NULL) { + if (digipeat != NULL) { if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { kfree_skb(skb); goto put; @@ -203,7 +208,8 @@ int ax25_rebuild_header(struct sk_buff * ax25_queue_xmit(skb, dev); put: - ax25_put_route(route); + if (route) + ax25_put_route(route); return 1; } diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 5d99852..d7736e5 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -8,7 +8,6 @@ * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) */ -#include #include #include #include diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 5ac9825..51b7bda 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -41,8 +41,6 @@ #include static ax25_route *ax25_route_list; static DEFINE_RWLOCK(ax25_route_lock); -static ax25_route *ax25_get_route(ax25_address *, struct net_device *); - void ax25_rt_device_down(struct net_device *dev) { ax25_route *s, *t, *ax25_rt; @@ -115,7 +113,7 @@ static int ax25_rt_add(struct ax25_route return -ENOMEM; } - atomic_set(&ax25_rt->ref, 0); + atomic_set(&ax25_rt->refcount, 1); ax25_rt->callsign = route->dest_addr; ax25_rt->dev = ax25_dev->dev; ax25_rt->digipeat = NULL; @@ -140,23 +138,10 @@ static int ax25_rt_add(struct ax25_route return 0; } -static void ax25_rt_destroy(ax25_route *ax25_rt) +void __ax25_put_route(ax25_route *ax25_rt) { - if (atomic_read(&ax25_rt->ref) == 0) { - kfree(ax25_rt->digipeat); - kfree(ax25_rt); - return; - } - - /* - * Uh... Route is still in use; we can't yet destroy it. Retry later. - */ - init_timer(&ax25_rt->timer); - ax25_rt->timer.data = (unsigned long) ax25_rt; - ax25_rt->timer.function = (void *) ax25_rt_destroy; - ax25_rt->timer.expires = jiffies + 5 * HZ; - - add_timer(&ax25_rt->timer); + kfree(ax25_rt->digipeat); + kfree(ax25_rt); } static int ax25_rt_del(struct ax25_routes_struct *route) @@ -177,12 +162,12 @@ static int ax25_rt_del(struct ax25_route ax25cmp(&route->dest_addr, &s->callsign) == 0) { if (ax25_route_list == s) { ax25_route_list = s->next; - ax25_rt_destroy(s); + ax25_put_route(s); } else { for (t = ax25_route_list; t != NULL; t = t->next) { if (t->next == s) { t->next = s->next; - ax25_rt_destroy(s); + ax25_put_route(s); break; } } @@ -362,7 +347,7 @@ #endif * * Only routes with a reference count of zero can be destroyed. */ -static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) +ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) { ax25_route *ax25_spe_rt = NULL; ax25_route *ax25_def_rt = NULL; @@ -392,7 +377,7 @@ static ax25_route *ax25_get_route(ax25_a ax25_rt = ax25_spe_rt; if (ax25_rt != NULL) - atomic_inc(&ax25_rt->ref); + ax25_hold_route(ax25_rt); read_unlock(&ax25_route_lock); @@ -467,24 +452,6 @@ put: return 0; } -ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr, - struct net_device *dev) -{ - ax25_route *ax25_rt; - - if ((ax25_rt = ax25_get_route(addr, dev))) - return ax25_rt; - - route->next = NULL; - atomic_set(&route->ref, 1); - route->callsign = *addr; - route->dev = dev; - route->digipeat = NULL; - route->ip_mode = ' '; - - return route; -} - struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) { diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index ec25405..7259486 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -12,7 +12,6 @@ * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) * Copyright (C) 2002 Ralf Baechle DO1GRB (ralf@gnu.org) */ -#include #include #include #include diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index bdb64c3..369a75b 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -6,7 +6,6 @@ * * Copyright (C) 1996 Mike Shaver (shaver@zeroknowledge.com) */ -#include #include #include #include diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 469eda0..788ea7a 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -24,7 +24,6 @@ /* Bluetooth address family and sockets. */ -#include #include #include @@ -49,7 +48,7 @@ #undef BT_DBG #define BT_DBG(D...) #endif -#define VERSION "2.8" +#define VERSION "2.10" /* Bluetooth sockets */ #define BT_MAX_PROTO 8 @@ -308,13 +307,21 @@ static struct net_proto_family bt_sock_f static int __init bt_init(void) { + int err; + BT_INFO("Core ver %s", VERSION); - sock_register(&bt_sock_family_ops); + err = bt_sysfs_init(); + if (err < 0) + return err; - BT_INFO("HCI device and connection manager initialized"); + err = sock_register(&bt_sock_family_ops); + if (err < 0) { + bt_sysfs_cleanup(); + return err; + } - bt_sysfs_init(); + BT_INFO("HCI device and connection manager initialized"); hci_sock_init(); @@ -325,9 +332,9 @@ static void __exit bt_exit(void) { hci_sock_cleanup(); - bt_sysfs_cleanup(); - sock_unregister(PF_BLUETOOTH); + + bt_sysfs_cleanup(); } subsys_initcall(bt_init); diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index d908d49..e620061 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -29,7 +29,6 @@ * $Id: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $ */ -#include #include #include diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 921204f..7f7b27d 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -29,7 +29,6 @@ * $Id: netdev.c,v 1.8 2002/08/04 21:23:58 maxk Exp $ */ -#include #include #include diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index 2bfe796..28c5583 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -28,7 +28,6 @@ * $Id: sock.c,v 1.4 2002/08/04 21:23:58 maxk Exp $ */ -#include #include #include diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c index b2e7e38..6fb47e0 100644 --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -20,7 +20,6 @@ SOFTWARE IS DISCLAIMED. */ -#include #include #include diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 901eff7..182254a 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -20,7 +20,6 @@ SOFTWARE IS DISCLAIMED. */ -#include #include #include diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c index 8f8fad2..10ad7fd 100644 --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -20,7 +20,6 @@ SOFTWARE IS DISCLAIMED. */ -#include #include #include diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index f812ed1..420ed4d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -24,7 +24,6 @@ /* Bluetooth HCI connection handling. */ -#include #include #include @@ -116,8 +115,8 @@ void hci_add_sco(struct hci_conn *conn, static void hci_conn_timeout(unsigned long arg) { - struct hci_conn *conn = (void *)arg; - struct hci_dev *hdev = conn->hdev; + struct hci_conn *conn = (void *) arg; + struct hci_dev *hdev = conn->hdev; BT_DBG("conn %p state %d", conn, conn->state); @@ -133,11 +132,13 @@ static void hci_conn_timeout(unsigned lo return; } -static void hci_conn_init_timer(struct hci_conn *conn) +static void hci_conn_idle(unsigned long arg) { - init_timer(&conn->timer); - conn->timer.function = hci_conn_timeout; - conn->timer.data = (unsigned long)conn; + struct hci_conn *conn = (void *) arg; + + BT_DBG("conn %p mode %d", conn, conn->mode); + + hci_conn_enter_sniff_mode(conn); } struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) @@ -146,17 +147,27 @@ struct hci_conn *hci_conn_add(struct hci BT_DBG("%s dst %s", hdev->name, batostr(dst)); - if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC))) + conn = kzalloc(sizeof(struct hci_conn), GFP_ATOMIC); + if (!conn) return NULL; - memset(conn, 0, sizeof(struct hci_conn)); bacpy(&conn->dst, dst); - conn->type = type; conn->hdev = hdev; + conn->type = type; + conn->mode = HCI_CM_ACTIVE; conn->state = BT_OPEN; + conn->power_save = 1; + skb_queue_head_init(&conn->data_q); - hci_conn_init_timer(conn); + + init_timer(&conn->disc_timer); + conn->disc_timer.function = hci_conn_timeout; + conn->disc_timer.data = (unsigned long) conn; + + init_timer(&conn->idle_timer); + conn->idle_timer.function = hci_conn_idle; + conn->idle_timer.data = (unsigned long) conn; atomic_set(&conn->refcnt, 0); @@ -179,7 +190,9 @@ int hci_conn_del(struct hci_conn *conn) BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle); - hci_conn_del_timer(conn); + del_timer(&conn->idle_timer); + + del_timer(&conn->disc_timer); if (conn->type == SCO_LINK) { struct hci_conn *acl = conn->link; @@ -365,6 +378,70 @@ int hci_conn_switch_role(struct hci_conn } EXPORT_SYMBOL(hci_conn_switch_role); +/* Enter active mode */ +void hci_conn_enter_active_mode(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p mode %d", conn, conn->mode); + + if (test_bit(HCI_RAW, &hdev->flags)) + return; + + if (conn->mode != HCI_CM_SNIFF || !conn->power_save) + goto timer; + + if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + struct hci_cp_exit_sniff_mode cp; + cp.handle = __cpu_to_le16(conn->handle); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_EXIT_SNIFF_MODE, sizeof(cp), &cp); + } + +timer: + if (hdev->idle_timeout > 0) + mod_timer(&conn->idle_timer, + jiffies + msecs_to_jiffies(hdev->idle_timeout)); +} + +/* Enter sniff mode */ +void hci_conn_enter_sniff_mode(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p mode %d", conn, conn->mode); + + if (test_bit(HCI_RAW, &hdev->flags)) + return; + + if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) + return; + + if (conn->mode != HCI_CM_ACTIVE || !(conn->link_policy & HCI_LP_SNIFF)) + return; + + if (lmp_sniffsubr_capable(hdev) && lmp_sniffsubr_capable(conn)) { + struct hci_cp_sniff_subrate cp; + cp.handle = __cpu_to_le16(conn->handle); + cp.max_latency = __constant_cpu_to_le16(0); + cp.min_remote_timeout = __constant_cpu_to_le16(0); + cp.min_local_timeout = __constant_cpu_to_le16(0); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_SNIFF_SUBRATE, sizeof(cp), &cp); + } + + if (!test_and_set_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + struct hci_cp_sniff_mode cp; + cp.handle = __cpu_to_le16(conn->handle); + cp.max_interval = __cpu_to_le16(hdev->sniff_max_interval); + cp.min_interval = __cpu_to_le16(hdev->sniff_min_interval); + cp.attempt = __constant_cpu_to_le16(4); + cp.timeout = __constant_cpu_to_le16(1); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_SNIFF_MODE, sizeof(cp), &cp); + } +} + /* Drop all connection on the device */ void hci_conn_hash_flush(struct hci_dev *hdev) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a49a697..54e8e5e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -24,7 +24,6 @@ /* Bluetooth HCI core. */ -#include #include #include @@ -412,7 +411,7 @@ int hci_inquiry(void __user *arg) } hci_dev_unlock_bh(hdev); - timeo = ir.length * 2 * HZ; + timeo = ir.length * msecs_to_jiffies(2000); if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0) goto done; @@ -480,7 +479,8 @@ int hci_dev_open(__u16 dev) set_bit(HCI_INIT, &hdev->flags); //__hci_request(hdev, hci_reset_req, 0, HZ); - ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); + ret = __hci_request(hdev, hci_init_req, 0, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); clear_bit(HCI_INIT, &hdev->flags); } @@ -547,7 +547,8 @@ static int hci_dev_do_close(struct hci_d atomic_set(&hdev->cmd_cnt, 1); if (!test_bit(HCI_RAW, &hdev->flags)) { set_bit(HCI_INIT, &hdev->flags); - __hci_request(hdev, hci_reset_req, 0, HZ/4); + __hci_request(hdev, hci_reset_req, 0, + msecs_to_jiffies(250)); clear_bit(HCI_INIT, &hdev->flags); } @@ -620,7 +621,8 @@ int hci_dev_reset(__u16 dev) hdev->acl_cnt = 0; hdev->sco_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) - ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); + ret = __hci_request(hdev, hci_reset_req, 0, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); done: tasklet_enable(&hdev->tx_task); @@ -658,7 +660,8 @@ int hci_dev_cmd(unsigned int cmd, void _ switch (cmd) { case HCISETAUTH: - err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT); + err = hci_request(hdev, hci_auth_req, dr.dev_opt, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); break; case HCISETENCRYPT: @@ -669,18 +672,19 @@ int hci_dev_cmd(unsigned int cmd, void _ if (!test_bit(HCI_AUTH, &hdev->flags)) { /* Auth must be enabled first */ - err = hci_request(hdev, hci_auth_req, - dr.dev_opt, HCI_INIT_TIMEOUT); + err = hci_request(hdev, hci_auth_req, dr.dev_opt, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); if (err) break; } - err = hci_request(hdev, hci_encrypt_req, - dr.dev_opt, HCI_INIT_TIMEOUT); + err = hci_request(hdev, hci_encrypt_req, dr.dev_opt, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); break; case HCISETSCAN: - err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); + err = hci_request(hdev, hci_scan_req, dr.dev_opt, + msecs_to_jiffies(HCI_INIT_TIMEOUT)); break; case HCISETPTYPE: @@ -813,8 +817,8 @@ void hci_free_dev(struct hci_dev *hdev) { skb_queue_purge(&hdev->driver_init); - /* will free via class release */ - class_device_put(&hdev->class_dev); + /* will free via device release */ + put_device(&hdev->dev); } EXPORT_SYMBOL(hci_free_dev); @@ -849,6 +853,10 @@ int hci_register_dev(struct hci_dev *hde hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->link_mode = (HCI_LM_ACCEPT); + hdev->idle_timeout = 0; + hdev->sniff_max_interval = 800; + hdev->sniff_min_interval = 80; + tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev); tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev); tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev); @@ -1221,6 +1229,9 @@ static inline void hci_sched_acl(struct while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); + + hci_conn_enter_active_mode(conn); + hci_send_frame(skb); hdev->acl_last_tx = jiffies; @@ -1299,6 +1310,8 @@ static inline void hci_acldata_packet(st if (conn) { register struct hci_proto *hp; + hci_conn_enter_active_mode(conn); + /* Send to upper protocol */ if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) { hp->recv_acldata(conn, skb, flags); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index eb64555..3896dab 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -24,7 +24,6 @@ /* Bluetooth HCI event handling. */ -#include #include #include @@ -84,6 +83,8 @@ static void hci_cc_link_policy(struct hc { struct hci_conn *conn; struct hci_rp_role_discovery *rd; + struct hci_rp_write_link_policy *lp; + void *sent; BT_DBG("%s ocf 0x%x", hdev->name, ocf); @@ -107,6 +108,27 @@ static void hci_cc_link_policy(struct hc hci_dev_unlock(hdev); break; + case OCF_WRITE_LINK_POLICY: + sent = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY); + if (!sent) + break; + + lp = (struct hci_rp_write_link_policy *) skb->data; + + if (lp->status) + break; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(lp->handle)); + if (conn) { + __le16 policy = get_unaligned((__le16 *) (sent + 2)); + conn->link_policy = __le16_to_cpu(policy); + } + + hci_dev_unlock(hdev); + break; + default: BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf); @@ -275,7 +297,7 @@ static void hci_cc_host_ctl(struct hci_d /* Command Complete OGF INFO_PARAM */ static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb) { - struct hci_rp_read_loc_features *lf; + struct hci_rp_read_local_features *lf; struct hci_rp_read_buffer_size *bs; struct hci_rp_read_bd_addr *ba; @@ -283,7 +305,7 @@ static void hci_cc_info_param(struct hci switch (ocf) { case OCF_READ_LOCAL_FEATURES: - lf = (struct hci_rp_read_loc_features *) skb->data; + lf = (struct hci_rp_read_local_features *) skb->data; if (lf->status) { BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status); @@ -320,9 +342,17 @@ static void hci_cc_info_param(struct hci } hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu); - hdev->sco_mtu = bs->sco_mtu ? bs->sco_mtu : 64; - hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt); - hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt); + hdev->sco_mtu = bs->sco_mtu; + hdev->acl_pkts = __le16_to_cpu(bs->acl_max_pkt); + hdev->sco_pkts = __le16_to_cpu(bs->sco_max_pkt); + + if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) { + hdev->sco_mtu = 64; + hdev->sco_pkts = 8; + } + + hdev->acl_cnt = hdev->acl_pkts; + hdev->sco_cnt = hdev->sco_pkts; BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name, hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts); @@ -440,8 +470,46 @@ static void hci_cs_link_policy(struct hc BT_DBG("%s ocf 0x%x", hdev->name, ocf); switch (ocf) { + case OCF_SNIFF_MODE: + if (status) { + struct hci_conn *conn; + struct hci_cp_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_SNIFF_MODE); + + if (!cp) + break; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) { + clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + } + + hci_dev_unlock(hdev); + } + break; + + case OCF_EXIT_SNIFF_MODE: + if (status) { + struct hci_conn *conn; + struct hci_cp_exit_sniff_mode *cp = hci_sent_cmd_data(hdev, OGF_LINK_POLICY, OCF_EXIT_SNIFF_MODE); + + if (!cp) + break; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle)); + if (conn) { + clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); + } + + hci_dev_unlock(hdev); + } + break; + default: - BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf); + BT_DBG("%s Command status: ogf LINK_POLICY ocf %x", hdev->name, ocf); break; } } @@ -623,14 +691,16 @@ static inline void hci_conn_request_evt( else cp.role = 0x01; /* Remain slave */ - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp); + hci_send_cmd(hdev, OGF_LINK_CTL, + OCF_ACCEPT_CONN_REQ, sizeof(cp), &cp); } else { /* Connection rejected */ struct hci_cp_reject_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); cp.reason = 0x0f; - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ, sizeof(cp), &cp); + hci_send_cmd(hdev, OGF_LINK_CTL, + OCF_REJECT_CONN_REQ, sizeof(cp), &cp); } } @@ -638,7 +708,7 @@ static inline void hci_conn_request_evt( static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_complete *ev = (struct hci_ev_conn_complete *) skb->data; - struct hci_conn *conn = NULL; + struct hci_conn *conn; BT_DBG("%s", hdev->name); @@ -660,12 +730,21 @@ static inline void hci_conn_complete_evt if (test_bit(HCI_ENCRYPT, &hdev->flags)) conn->link_mode |= HCI_LM_ENCRYPT; + /* Get remote features */ + if (conn->type == ACL_LINK) { + struct hci_cp_read_remote_features cp; + cp.handle = ev->handle; + hci_send_cmd(hdev, OGF_LINK_CTL, + OCF_READ_REMOTE_FEATURES, sizeof(cp), &cp); + } + /* Set link policy */ if (conn->type == ACL_LINK && hdev->link_policy) { struct hci_cp_write_link_policy cp; cp.handle = ev->handle; cp.policy = __cpu_to_le16(hdev->link_policy); - hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, sizeof(cp), &cp); + hci_send_cmd(hdev, OGF_LINK_POLICY, + OCF_WRITE_LINK_POLICY, sizeof(cp), &cp); } /* Set packet type for incoming connection */ @@ -676,7 +755,8 @@ static inline void hci_conn_complete_evt __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK): __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK); - hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp); + hci_send_cmd(hdev, OGF_LINK_CTL, + OCF_CHANGE_CONN_PTYPE, sizeof(cp), &cp); } } else conn->state = BT_CLOSED; @@ -704,8 +784,7 @@ static inline void hci_conn_complete_evt static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_disconn_complete *ev = (struct hci_ev_disconn_complete *) skb->data; - struct hci_conn *conn = NULL; - __u16 handle = __le16_to_cpu(ev->handle); + struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); @@ -714,7 +793,7 @@ static inline void hci_disconn_complete_ hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { conn->state = BT_CLOSED; hci_proto_disconn_ind(conn, ev->reason); @@ -771,7 +850,7 @@ static inline void hci_num_comp_pkts_evt static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_role_change *ev = (struct hci_ev_role_change *) skb->data; - struct hci_conn *conn = NULL; + struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); @@ -794,18 +873,43 @@ static inline void hci_role_change_evt(s hci_dev_unlock(hdev); } +/* Mode Change */ +static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_mode_change *ev = (struct hci_ev_mode_change *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + conn->mode = ev->mode; + conn->interval = __le16_to_cpu(ev->interval); + + if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) { + if (conn->mode == HCI_CM_ACTIVE) + conn->power_save = 1; + else + conn->power_save = 0; + } + } + + hci_dev_unlock(hdev); +} + /* Authentication Complete */ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_auth_complete *ev = (struct hci_ev_auth_complete *) skb->data; - struct hci_conn *conn = NULL; - __u16 handle = __le16_to_cpu(ev->handle); + struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_AUTH; @@ -820,8 +924,7 @@ static inline void hci_auth_complete_evt cp.handle = __cpu_to_le16(conn->handle); cp.encrypt = 1; hci_send_cmd(conn->hdev, OGF_LINK_CTL, - OCF_SET_CONN_ENCRYPT, - sizeof(cp), &cp); + OCF_SET_CONN_ENCRYPT, sizeof(cp), &cp); } else { clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend); hci_encrypt_cfm(conn, ev->status, 0x00); @@ -836,14 +939,13 @@ static inline void hci_auth_complete_evt static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_encrypt_change *ev = (struct hci_ev_encrypt_change *) skb->data; - struct hci_conn *conn = NULL; - __u16 handle = __le16_to_cpu(ev->handle); + struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) { if (ev->encrypt) @@ -864,14 +966,13 @@ static inline void hci_encrypt_change_ev static inline void hci_change_conn_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_change_conn_link_key_complete *ev = (struct hci_ev_change_conn_link_key_complete *) skb->data; - struct hci_conn *conn = NULL; - __u16 handle = __le16_to_cpu(ev->handle); + struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) conn->link_mode |= HCI_LM_SECURE; @@ -899,18 +1000,35 @@ static inline void hci_link_key_notify_e { } +/* Remote Features */ +static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_remote_features *ev = (struct hci_ev_remote_features *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn && !ev->status) { + memcpy(conn->features, ev->features, sizeof(conn->features)); + } + + hci_dev_unlock(hdev); +} + /* Clock Offset */ static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_clock_offset *ev = (struct hci_ev_clock_offset *) skb->data; - struct hci_conn *conn = NULL; - __u16 handle = __le16_to_cpu(ev->handle); + struct hci_conn *conn; BT_DBG("%s status %d", hdev->name, ev->status); hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_handle(hdev, handle); + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn && !ev->status) { struct inquiry_entry *ie; @@ -941,6 +1059,23 @@ static inline void hci_pscan_rep_mode_ev hci_dev_unlock(hdev); } +/* Sniff Subrate */ +static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_sniff_subrate *ev = (struct hci_ev_sniff_subrate *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status %d", hdev->name, ev->status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + } + + hci_dev_unlock(hdev); +} + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_event_hdr *hdr = (struct hci_event_hdr *) skb->data; @@ -989,6 +1124,10 @@ void hci_event_packet(struct hci_dev *hd hci_role_change_evt(hdev, skb); break; + case HCI_EV_MODE_CHANGE: + hci_mode_change_evt(hdev, skb); + break; + case HCI_EV_AUTH_COMPLETE: hci_auth_complete_evt(hdev, skb); break; @@ -1013,6 +1152,10 @@ void hci_event_packet(struct hci_dev *hd hci_link_key_notify_evt(hdev, skb); break; + case HCI_EV_REMOTE_FEATURES: + hci_remote_features_evt(hdev, skb); + break; + case HCI_EV_CLOCK_OFFSET: hci_clock_offset_evt(hdev, skb); break; @@ -1021,6 +1164,10 @@ void hci_event_packet(struct hci_dev *hd hci_pscan_rep_mode_evt(hdev, skb); break; + case HCI_EV_SNIFF_SUBRATE: + hci_sniff_subrate_evt(hdev, skb); + break; + case HCI_EV_CMD_STATUS: cs = (struct hci_ev_cmd_status *) skb->data; skb_pull(skb, sizeof(cs)); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 97bdec7..1a35d34 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -24,7 +24,6 @@ /* Bluetooth HCI sockets. */ -#include #include #include diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 0ed3874..3987d16 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -1,9 +1,10 @@ /* Bluetooth HCI driver model support. */ -#include #include #include +#include + #include #include @@ -12,35 +13,35 @@ #undef BT_DBG #define BT_DBG(D...) #endif -static ssize_t show_name(struct class_device *cdev, char *buf) +static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = class_get_devdata(cdev); + struct hci_dev *hdev = dev_get_drvdata(dev); return sprintf(buf, "%s\n", hdev->name); } -static ssize_t show_type(struct class_device *cdev, char *buf) +static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = class_get_devdata(cdev); + struct hci_dev *hdev = dev_get_drvdata(dev); return sprintf(buf, "%d\n", hdev->type); } -static ssize_t show_address(struct class_device *cdev, char *buf) +static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = class_get_devdata(cdev); + struct hci_dev *hdev = dev_get_drvdata(dev); bdaddr_t bdaddr; baswap(&bdaddr, &hdev->bdaddr); return sprintf(buf, "%s\n", batostr(&bdaddr)); } -static ssize_t show_flags(struct class_device *cdev, char *buf) +static ssize_t show_flags(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = class_get_devdata(cdev); + struct hci_dev *hdev = dev_get_drvdata(dev); return sprintf(buf, "0x%lx\n", hdev->flags); } -static ssize_t show_inquiry_cache(struct class_device *cdev, char *buf) +static ssize_t show_inquiry_cache(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = class_get_devdata(cdev); + struct hci_dev *hdev = dev_get_drvdata(dev); struct inquiry_cache *cache = &hdev->inq_cache; struct inquiry_entry *e; int n = 0; @@ -62,94 +63,193 @@ static ssize_t show_inquiry_cache(struct return n; } -static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static CLASS_DEVICE_ATTR(type, S_IRUGO, show_type, NULL); -static CLASS_DEVICE_ATTR(address, S_IRUGO, show_address, NULL); -static CLASS_DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL); -static CLASS_DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL); +static ssize_t show_idle_timeout(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", hdev->idle_timeout); +} -static struct class_device_attribute *bt_attrs[] = { - &class_device_attr_name, - &class_device_attr_type, - &class_device_attr_address, - &class_device_attr_flags, - &class_device_attr_inquiry_cache, - NULL -}; +static ssize_t store_idle_timeout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + char *ptr; + __u32 val; + + val = simple_strtoul(buf, &ptr, 10); + if (ptr == buf) + return -EINVAL; -#ifdef CONFIG_HOTPLUG -static int bt_uevent(struct class_device *cdev, char **envp, int num_envp, char *buf, int size) + if (val != 0 && (val < 500 || val > 3600000)) + return -EINVAL; + + hdev->idle_timeout = val; + + return count; +} + +static ssize_t show_sniff_max_interval(struct device *dev, struct device_attribute *attr, char *buf) { - struct hci_dev *hdev = class_get_devdata(cdev); - int n, i = 0; + struct hci_dev *hdev = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", hdev->sniff_max_interval); +} - envp[i++] = buf; - n = snprintf(buf, size, "INTERFACE=%s", hdev->name) + 1; - buf += n; - size -= n; +static ssize_t store_sniff_max_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + char *ptr; + __u16 val; - if ((size <= 0) || (i >= num_envp)) - return -ENOMEM; + val = simple_strtoul(buf, &ptr, 10); + if (ptr == buf) + return -EINVAL; - envp[i] = NULL; - return 0; + if (val < 0x0002 || val > 0xFFFE || val % 2) + return -EINVAL; + + if (val < hdev->sniff_min_interval) + return -EINVAL; + + hdev->sniff_max_interval = val; + + return count; +} + +static ssize_t show_sniff_min_interval(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", hdev->sniff_min_interval); } -#endif -static void bt_release(struct class_device *cdev) +static ssize_t store_sniff_min_interval(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct hci_dev *hdev = class_get_devdata(cdev); + struct hci_dev *hdev = dev_get_drvdata(dev); + char *ptr; + __u16 val; - kfree(hdev); + val = simple_strtoul(buf, &ptr, 10); + if (ptr == buf) + return -EINVAL; + + if (val < 0x0002 || val > 0xFFFE || val % 2) + return -EINVAL; + + if (val > hdev->sniff_max_interval) + return -EINVAL; + + hdev->sniff_min_interval = val; + + return count; } -struct class bt_class = { - .name = "bluetooth", - .release = bt_release, -#ifdef CONFIG_HOTPLUG - .uevent = bt_uevent, -#endif +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); +static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); +static DEVICE_ATTR(flags, S_IRUGO, show_flags, NULL); +static DEVICE_ATTR(inquiry_cache, S_IRUGO, show_inquiry_cache, NULL); + +static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, + show_idle_timeout, store_idle_timeout); +static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, + show_sniff_max_interval, store_sniff_max_interval); +static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, + show_sniff_min_interval, store_sniff_min_interval); + +static struct device_attribute *bt_attrs[] = { + &dev_attr_name, + &dev_attr_type, + &dev_attr_address, + &dev_attr_flags, + &dev_attr_inquiry_cache, + &dev_attr_idle_timeout, + &dev_attr_sniff_max_interval, + &dev_attr_sniff_min_interval, + NULL }; +struct class *bt_class = NULL; EXPORT_SYMBOL_GPL(bt_class); +static struct bus_type bt_bus = { + .name = "bluetooth", +}; + +static struct platform_device *bt_platform; + +static void bt_release(struct device *dev) +{ + struct hci_dev *hdev = dev_get_drvdata(dev); + kfree(hdev); +} + int hci_register_sysfs(struct hci_dev *hdev) { - struct class_device *cdev = &hdev->class_dev; + struct device *dev = &hdev->dev; unsigned int i; int err; BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); - cdev->class = &bt_class; - class_set_devdata(cdev, hdev); + dev->class = bt_class; + + if (hdev->parent) + dev->parent = hdev->parent; + else + dev->parent = &bt_platform->dev; + + strlcpy(dev->bus_id, hdev->name, BUS_ID_SIZE); + + dev->release = bt_release; - strlcpy(cdev->class_id, hdev->name, BUS_ID_SIZE); - err = class_device_register(cdev); + dev_set_drvdata(dev, hdev); + + err = device_register(dev); if (err < 0) return err; for (i = 0; bt_attrs[i]; i++) - class_device_create_file(cdev, bt_attrs[i]); + device_create_file(dev, bt_attrs[i]); return 0; } void hci_unregister_sysfs(struct hci_dev *hdev) { - struct class_device * cdev = &hdev->class_dev; + struct device *dev = &hdev->dev; BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type); - class_device_del(cdev); + device_del(dev); } int __init bt_sysfs_init(void) { - return class_register(&bt_class); + int err; + + bt_platform = platform_device_register_simple("bluetooth", -1, NULL, 0); + if (IS_ERR(bt_platform)) + return PTR_ERR(bt_platform); + + err = bus_register(&bt_bus); + if (err < 0) { + platform_device_unregister(bt_platform); + return err; + } + + bt_class = class_create(THIS_MODULE, "bluetooth"); + if (IS_ERR(bt_class)) { + bus_unregister(&bt_bus); + platform_device_unregister(bt_platform); + return PTR_ERR(bt_class); + } + + return 0; } void __exit bt_sysfs_cleanup(void) { - class_unregister(&bt_class); + class_destroy(bt_class); + + bus_unregister(&bt_bus); + + platform_device_unregister(bt_platform); } diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index cdb9cfa..b9c24a5 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -20,7 +20,6 @@ SOFTWARE IS DISCLAIMED. */ -#include #include #include diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index b8f6776..099646e 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -20,7 +20,6 @@ SOFTWARE IS DISCLAIMED. */ -#include #include #include diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c index f6b4a80..eaaad65 100644 --- a/net/bluetooth/l2cap.c +++ b/net/bluetooth/l2cap.c @@ -24,7 +24,6 @@ /* Bluetooth L2CAP core and sockets. */ -#include #include #include @@ -64,11 +63,6 @@ static struct bt_sock_list l2cap_sk_list .lock = RW_LOCK_UNLOCKED }; -static int l2cap_conn_del(struct hci_conn *conn, int err); - -static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent); -static void l2cap_chan_del(struct sock *sk, int err); - static void __l2cap_sock_close(struct sock *sk, int reason); static void l2cap_sock_close(struct sock *sk); static void l2cap_sock_kill(struct sock *sk); @@ -110,24 +104,177 @@ static void l2cap_sock_init_timer(struct sk->sk_timer.data = (unsigned long)sk; } +/* ---- L2CAP channels ---- */ +static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) +{ + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->dcid == cid) + break; + } + return s; +} + +static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) +{ + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->scid == cid) + break; + } + return s; +} + +/* Find channel with given SCID. + * Returns locked socket */ +static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) +{ + struct sock *s; + read_lock(&l->lock); + s = __l2cap_get_chan_by_scid(l, cid); + if (s) bh_lock_sock(s); + read_unlock(&l->lock); + return s; +} + +static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) +{ + struct sock *s; + for (s = l->head; s; s = l2cap_pi(s)->next_c) { + if (l2cap_pi(s)->ident == ident) + break; + } + return s; +} + +static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) +{ + struct sock *s; + read_lock(&l->lock); + s = __l2cap_get_chan_by_ident(l, ident); + if (s) bh_lock_sock(s); + read_unlock(&l->lock); + return s; +} + +static u16 l2cap_alloc_cid(struct l2cap_chan_list *l) +{ + u16 cid = 0x0040; + + for (; cid < 0xffff; cid++) { + if(!__l2cap_get_chan_by_scid(l, cid)) + return cid; + } + + return 0; +} + +static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) +{ + sock_hold(sk); + + if (l->head) + l2cap_pi(l->head)->prev_c = sk; + + l2cap_pi(sk)->next_c = l->head; + l2cap_pi(sk)->prev_c = NULL; + l->head = sk; +} + +static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) +{ + struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; + + write_lock(&l->lock); + if (sk == l->head) + l->head = next; + + if (next) + l2cap_pi(next)->prev_c = prev; + if (prev) + l2cap_pi(prev)->next_c = next; + write_unlock(&l->lock); + + __sock_put(sk); +} + +static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) +{ + struct l2cap_chan_list *l = &conn->chan_list; + + BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); + + l2cap_pi(sk)->conn = conn; + + if (sk->sk_type == SOCK_SEQPACKET) { + /* Alloc CID for connection-oriented socket */ + l2cap_pi(sk)->scid = l2cap_alloc_cid(l); + } else if (sk->sk_type == SOCK_DGRAM) { + /* Connectionless socket */ + l2cap_pi(sk)->scid = 0x0002; + l2cap_pi(sk)->dcid = 0x0002; + l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + } else { + /* Raw socket can send/recv signalling messages only */ + l2cap_pi(sk)->scid = 0x0001; + l2cap_pi(sk)->dcid = 0x0001; + l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; + } + + __l2cap_chan_link(l, sk); + + if (parent) + bt_accept_enqueue(parent, sk); +} + +/* Delete channel. + * Must be called on the locked socket. */ +static void l2cap_chan_del(struct sock *sk, int err) +{ + struct l2cap_conn *conn = l2cap_pi(sk)->conn; + struct sock *parent = bt_sk(sk)->parent; + + l2cap_sock_clear_timer(sk); + + BT_DBG("sk %p, conn %p, err %d", sk, conn, err); + + if (conn) { + /* Unlink from channel list */ + l2cap_chan_unlink(&conn->chan_list, sk); + l2cap_pi(sk)->conn = NULL; + hci_conn_put(conn->hcon); + } + + sk->sk_state = BT_CLOSED; + sock_set_flag(sk, SOCK_ZAPPED); + + if (err) + sk->sk_err = err; + + if (parent) { + bt_accept_unlink(sk); + parent->sk_data_ready(parent, 0); + } else + sk->sk_state_change(sk); +} + /* ---- L2CAP connections ---- */ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { - struct l2cap_conn *conn; - - if ((conn = hcon->l2cap_data)) - return conn; + struct l2cap_conn *conn = hcon->l2cap_data; - if (status) + if (conn || status) return conn; - if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC))) + conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); + if (!conn) return NULL; - memset(conn, 0, sizeof(struct l2cap_conn)); hcon->l2cap_data = conn; conn->hcon = hcon; + BT_DBG("hcon %p conn %p", hcon, conn); + conn->mtu = hcon->hdev->acl_mtu; conn->src = &hcon->hdev->bdaddr; conn->dst = &hcon->dst; @@ -135,17 +282,16 @@ static struct l2cap_conn *l2cap_conn_add spin_lock_init(&conn->lock); rwlock_init(&conn->chan_list.lock); - BT_DBG("hcon %p conn %p", hcon, conn); return conn; } -static int l2cap_conn_del(struct hci_conn *hcon, int err) +static void l2cap_conn_del(struct hci_conn *hcon, int err) { - struct l2cap_conn *conn; + struct l2cap_conn *conn = hcon->l2cap_data; struct sock *sk; - if (!(conn = hcon->l2cap_data)) - return 0; + if (!conn) + return; BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); @@ -162,7 +308,6 @@ static int l2cap_conn_del(struct hci_con hcon->l2cap_data = NULL; kfree(conn); - return 0; } static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) @@ -926,160 +1071,6 @@ static int l2cap_sock_release(struct soc return err; } -/* ---- L2CAP channels ---- */ -static struct sock *__l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, u16 cid) -{ - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->dcid == cid) - break; - } - return s; -} - -static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) -{ - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->scid == cid) - break; - } - return s; -} - -/* Find channel with given SCID. - * Returns locked socket */ -static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, u16 cid) -{ - struct sock *s; - read_lock(&l->lock); - s = __l2cap_get_chan_by_scid(l, cid); - if (s) bh_lock_sock(s); - read_unlock(&l->lock); - return s; -} - -static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) -{ - struct sock *s; - for (s = l->head; s; s = l2cap_pi(s)->next_c) { - if (l2cap_pi(s)->ident == ident) - break; - } - return s; -} - -static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, u8 ident) -{ - struct sock *s; - read_lock(&l->lock); - s = __l2cap_get_chan_by_ident(l, ident); - if (s) bh_lock_sock(s); - read_unlock(&l->lock); - return s; -} - -static u16 l2cap_alloc_cid(struct l2cap_chan_list *l) -{ - u16 cid = 0x0040; - - for (; cid < 0xffff; cid++) { - if(!__l2cap_get_chan_by_scid(l, cid)) - return cid; - } - - return 0; -} - -static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk) -{ - sock_hold(sk); - - if (l->head) - l2cap_pi(l->head)->prev_c = sk; - - l2cap_pi(sk)->next_c = l->head; - l2cap_pi(sk)->prev_c = NULL; - l->head = sk; -} - -static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk) -{ - struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c; - - write_lock(&l->lock); - if (sk == l->head) - l->head = next; - - if (next) - l2cap_pi(next)->prev_c = prev; - if (prev) - l2cap_pi(prev)->next_c = next; - write_unlock(&l->lock); - - __sock_put(sk); -} - -static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent) -{ - struct l2cap_chan_list *l = &conn->chan_list; - - BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid); - - l2cap_pi(sk)->conn = conn; - - if (sk->sk_type == SOCK_SEQPACKET) { - /* Alloc CID for connection-oriented socket */ - l2cap_pi(sk)->scid = l2cap_alloc_cid(l); - } else if (sk->sk_type == SOCK_DGRAM) { - /* Connectionless socket */ - l2cap_pi(sk)->scid = 0x0002; - l2cap_pi(sk)->dcid = 0x0002; - l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; - } else { - /* Raw socket can send/recv signalling messages only */ - l2cap_pi(sk)->scid = 0x0001; - l2cap_pi(sk)->dcid = 0x0001; - l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU; - } - - __l2cap_chan_link(l, sk); - - if (parent) - bt_accept_enqueue(parent, sk); -} - -/* Delete channel. - * Must be called on the locked socket. */ -static void l2cap_chan_del(struct sock *sk, int err) -{ - struct l2cap_conn *conn = l2cap_pi(sk)->conn; - struct sock *parent = bt_sk(sk)->parent; - - l2cap_sock_clear_timer(sk); - - BT_DBG("sk %p, conn %p, err %d", sk, conn, err); - - if (conn) { - /* Unlink from channel list */ - l2cap_chan_unlink(&conn->chan_list, sk); - l2cap_pi(sk)->conn = NULL; - hci_conn_put(conn->hcon); - } - - sk->sk_state = BT_CLOSED; - sock_set_flag(sk, SOCK_ZAPPED); - - if (err) - sk->sk_err = err; - - if (parent) { - bt_accept_unlink(sk); - parent->sk_data_ready(parent, 0); - } else - sk->sk_state_change(sk); -} - static void l2cap_conn_ready(struct l2cap_conn *conn) { struct l2cap_chan_list *l = &conn->chan_list; @@ -1835,7 +1826,9 @@ drop: kfree_skb(skb); done: - if (sk) bh_unlock_sock(sk); + if (sk) + bh_unlock_sock(sk); + return 0; } @@ -1926,18 +1919,18 @@ static int l2cap_connect_ind(struct hci_ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status) { + struct l2cap_conn *conn; + BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status); if (hcon->type != ACL_LINK) return 0; if (!status) { - struct l2cap_conn *conn; - conn = l2cap_conn_add(hcon, status); if (conn) l2cap_conn_ready(conn); - } else + } else l2cap_conn_del(hcon, bt_err(status)); return 0; @@ -1951,19 +1944,21 @@ static int l2cap_disconn_ind(struct hci_ return 0; l2cap_conn_del(hcon, bt_err(reason)); + return 0; } static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status) { struct l2cap_chan_list *l; - struct l2cap_conn *conn; + struct l2cap_conn *conn = conn = hcon->l2cap_data; struct l2cap_conn_rsp rsp; struct sock *sk; int result; - if (!(conn = hcon->l2cap_data)) + if (!conn) return 0; + l = &conn->chan_list; BT_DBG("conn %p", conn); @@ -2006,13 +2001,14 @@ static int l2cap_auth_cfm(struct hci_con static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status) { struct l2cap_chan_list *l; - struct l2cap_conn *conn; + struct l2cap_conn *conn = hcon->l2cap_data; struct l2cap_conn_rsp rsp; struct sock *sk; int result; - if (!(conn = hcon->l2cap_data)) + if (!conn) return 0; + l = &conn->chan_list; BT_DBG("conn %p", conn); @@ -2220,7 +2216,7 @@ static int __init l2cap_init(void) goto error; } - class_create_file(&bt_class, &class_attr_l2cap); + class_create_file(bt_class, &class_attr_l2cap); BT_INFO("L2CAP ver %s", VERSION); BT_INFO("L2CAP socket layer initialized"); @@ -2234,7 +2230,7 @@ error: static void __exit l2cap_exit(void) { - class_remove_file(&bt_class, &class_attr_l2cap); + class_remove_file(bt_class, &class_attr_l2cap); if (bt_sock_unregister(BTPROTO_L2CAP) < 0) BT_ERR("L2CAP socket unregistration failed"); diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index ee6a669..e5fd0cb 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -24,7 +24,6 @@ /* Bluetooth kernel library. */ -#include #include #include diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index e99010c..155a2b9 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -27,7 +27,6 @@ * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $ */ -#include #include #include #include @@ -53,8 +52,9 @@ #undef BT_DBG #define BT_DBG(D...) #endif -#define VERSION "1.7" +#define VERSION "1.8" +static int disable_cfc = 0; static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; static struct task_struct *rfcomm_thread; @@ -534,7 +534,7 @@ static struct rfcomm_session *rfcomm_ses s->sock = sock; s->mtu = RFCOMM_DEFAULT_MTU; - s->cfc = RFCOMM_CFC_UNKNOWN; + s->cfc = disable_cfc ? RFCOMM_CFC_DISABLED : RFCOMM_CFC_UNKNOWN; /* Do not increment module usage count for listening sessions. * Otherwise we won't be able to unload the module. */ @@ -1150,6 +1150,8 @@ static inline int rfcomm_check_link_mode static void rfcomm_dlc_accept(struct rfcomm_dlc *d) { + struct sock *sk = d->session->sock->sk; + BT_DBG("dlc %p", d); rfcomm_send_ua(d->session, d->dlci); @@ -1159,6 +1161,9 @@ static void rfcomm_dlc_accept(struct rfc d->state_change(d, 0); rfcomm_dlc_unlock(d); + if (d->link_mode & RFCOMM_LM_MASTER) + hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00); + rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig); } @@ -1223,14 +1228,18 @@ static int rfcomm_apply_pn(struct rfcomm BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits); - if (pn->flow_ctrl == 0xf0 || pn->flow_ctrl == 0xe0) { - d->cfc = s->cfc = RFCOMM_CFC_ENABLED; + if ((pn->flow_ctrl == 0xf0 && s->cfc != RFCOMM_CFC_DISABLED) || + pn->flow_ctrl == 0xe0) { + d->cfc = RFCOMM_CFC_ENABLED; d->tx_credits = pn->credits; } else { - d->cfc = s->cfc = RFCOMM_CFC_DISABLED; + d->cfc = RFCOMM_CFC_DISABLED; set_bit(RFCOMM_TX_THROTTLED, &d->flags); } + if (s->cfc == RFCOMM_CFC_UNKNOWN) + s->cfc = d->cfc; + d->priority = pn->priority; d->mtu = s->mtu = btohs(pn->mtu); @@ -2036,7 +2045,7 @@ static int __init rfcomm_init(void) kernel_thread(rfcomm_run, NULL, CLONE_KERNEL); - class_create_file(&bt_class, &class_attr_rfcomm_dlc); + class_create_file(bt_class, &class_attr_rfcomm_dlc); rfcomm_init_sockets(); @@ -2051,7 +2060,7 @@ #endif static void __exit rfcomm_exit(void) { - class_remove_file(&bt_class, &class_attr_rfcomm_dlc); + class_remove_file(bt_class, &class_attr_rfcomm_dlc); hci_unregister_cb(&rfcomm_cb); @@ -2074,6 +2083,9 @@ #endif module_init(rfcomm_init); module_exit(rfcomm_exit); +module_param(disable_cfc, bool, 0644); +MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); + module_param(l2cap_mtu, uint, 0644); MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 757d2dd..220fee0 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -27,7 +27,6 @@ * $Id: sock.c,v 1.24 2002/10/03 01:00:34 maxk Exp $ */ -#include #include #include @@ -945,7 +944,7 @@ int __init rfcomm_init_sockets(void) if (err < 0) goto error; - class_create_file(&bt_class, &class_attr_rfcomm); + class_create_file(bt_class, &class_attr_rfcomm); BT_INFO("RFCOMM socket layer initialized"); @@ -959,7 +958,7 @@ error: void __exit rfcomm_cleanup_sockets(void) { - class_remove_file(&bt_class, &class_attr_rfcomm); + class_remove_file(bt_class, &class_attr_rfcomm); if (bt_sock_unregister(BTPROTO_RFCOMM) < 0) BT_ERR("RFCOMM socket layer unregistration failed"); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 74368f7..2ff2d5b 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -27,7 +27,6 @@ * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $ */ -#include #include #include @@ -480,12 +479,8 @@ static void rfcomm_dev_data_ready(struct BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len); - if (test_bit(TTY_DONT_FLIP, &tty->flags)) { - tty_buffer_request_room(tty, skb->len); - tty_insert_flip_string(tty, skb->data, skb->len); - tty_flip_buffer_push(tty); - } else - tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len); + tty_insert_flip_string(tty, skb->data, skb->len); + tty_flip_buffer_push(tty); kfree_skb(skb); } @@ -1025,13 +1020,12 @@ int rfcomm_init_ttys(void) rfcomm_tty_driver->owner = THIS_MODULE; rfcomm_tty_driver->driver_name = "rfcomm"; - rfcomm_tty_driver->devfs_name = "bluetooth/rfcomm/"; rfcomm_tty_driver->name = "rfcomm"; rfcomm_tty_driver->major = RFCOMM_TTY_MAJOR; rfcomm_tty_driver->minor_start = RFCOMM_TTY_MINOR; rfcomm_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL; - rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; rfcomm_tty_driver->init_termios = tty_std_termios; rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 0c2d13a..85defcc 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -24,7 +24,6 @@ /* Bluetooth SCO sockets. */ -#include #include #include @@ -970,7 +969,7 @@ static int __init sco_init(void) goto error; } - class_create_file(&bt_class, &class_attr_sco); + class_create_file(bt_class, &class_attr_sco); BT_INFO("SCO (Voice Link) ver %s", VERSION); BT_INFO("SCO socket layer initialized"); @@ -984,7 +983,7 @@ error: static void __exit sco_exit(void) { - class_remove_file(&bt_class, &class_attr_sco); + class_remove_file(bt_class, &class_attr_sco); if (bt_sock_unregister(BTPROTO_SCO) < 0) BT_ERR("SCO socket unregistration failed"); diff --git a/net/bridge/Makefile b/net/bridge/Makefile index 59556e4..f444c12 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_BRIDGE) += bridge.o bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \ - br_stp_if.o br_stp_timer.o + br_stp_if.o br_stp_timer.o br_netlink.o bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o diff --git a/net/bridge/br.c b/net/bridge/br.c index 12da21a..2994387 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -13,7 +13,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -30,36 +29,46 @@ static struct llc_sap *br_stp_sap; static int __init br_init(void) { + int err; + br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv); if (!br_stp_sap) { printk(KERN_ERR "bridge: can't register sap for STP\n"); - return -EBUSY; + return -EADDRINUSE; } br_fdb_init(); -#ifdef CONFIG_BRIDGE_NETFILTER - if (br_netfilter_init()) - return 1; -#endif + err = br_netfilter_init(); + if (err) + goto err_out1; + + err = register_netdevice_notifier(&br_device_notifier); + if (err) + goto err_out2; + + br_netlink_init(); brioctl_set(br_ioctl_deviceless_stub); br_handle_frame_hook = br_handle_frame; br_fdb_get_hook = br_fdb_get; br_fdb_put_hook = br_fdb_put; - register_netdevice_notifier(&br_device_notifier); - return 0; + +err_out2: + br_netfilter_fini(); +err_out1: + llc_sap_put(br_stp_sap); + return err; } static void __exit br_deinit(void) { rcu_assign_pointer(br_stp_sap->rcv_func, NULL); -#ifdef CONFIG_BRIDGE_NETFILTER + br_netlink_fini(); br_netfilter_fini(); -#endif unregister_netdevice_notifier(&br_device_notifier); brioctl_set(NULL); diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 0c88a2a..f8dbcee 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -145,9 +145,9 @@ static int br_set_tx_csum(struct net_dev struct net_bridge *br = netdev_priv(dev); if (data) - br->feature_mask |= NETIF_F_IP_CSUM; + br->feature_mask |= NETIF_F_NO_CSUM; else - br->feature_mask &= ~NETIF_F_IP_CSUM; + br->feature_mask &= ~NETIF_F_ALL_CSUM; br_features_recompute(br); return 0; @@ -184,6 +184,6 @@ void br_dev_setup(struct net_device *dev dev->set_mac_address = br_set_mac_address; dev->priv_flags = IFF_EBRIDGE; - dev->features = NETIF_F_SG | NETIF_F_FRAGLIST - | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_IP_CSUM; + dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | + NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST; } diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 56f3aa4..8be9f21 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -20,14 +20,11 @@ #include #include #include "br_private.h" +/* Don't forward packets to originating port or forwarding diasabled */ static inline int should_deliver(const struct net_bridge_port *p, const struct sk_buff *skb) { - if (skb->dev == p->dev || - p->state != BR_STATE_FORWARDING) - return 0; - - return 1; + return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING); } static inline unsigned packet_length(const struct sk_buff *skb) @@ -37,8 +34,8 @@ static inline unsigned packet_length(con int br_dev_queue_push_xmit(struct sk_buff *skb) { - /* drop mtu oversized packets except tso */ - if (packet_length(skb) > skb->dev->mtu && !skb_shinfo(skb)->tso_size) + /* drop mtu oversized packets except gso */ + if (packet_length(skb) > skb->dev->mtu && !skb_shinfo(skb)->gso_size) kfree_skb(skb); else { #ifdef CONFIG_BRIDGE_NETFILTER @@ -55,10 +52,9 @@ #endif int br_forward_finish(struct sk_buff *skb) { - NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev, - br_dev_queue_push_xmit); + return NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev, + br_dev_queue_push_xmit); - return 0; } static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f5d47bf..f55ef68 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -372,17 +372,28 @@ void br_features_recompute(struct net_br struct net_bridge_port *p; unsigned long features, checksum; - features = br->feature_mask &~ NETIF_F_IP_CSUM; - checksum = br->feature_mask & NETIF_F_IP_CSUM; + checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0; + features = br->feature_mask & ~NETIF_F_ALL_CSUM; list_for_each_entry(p, &br->port_list, list) { - if (!(p->dev->features - & (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM))) + unsigned long feature = p->dev->features; + + if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM)) + checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM; + if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM)) + checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM; + if (!(feature & NETIF_F_IP_CSUM)) checksum = 0; - features &= p->dev->features; + + if (feature & NETIF_F_GSO) + feature |= NETIF_F_TSO; + feature |= NETIF_F_GSO; + + features &= feature; } - br->dev->features = features | checksum | NETIF_F_LLTX; + br->dev->features = features | checksum | NETIF_F_LLTX | + NETIF_F_GSO_ROBUST; } /* called with RTNL */ diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 3da9264..8298a51 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -407,12 +407,8 @@ static unsigned int br_nf_pre_routing_ip if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) { if (pkt_len + sizeof(struct ipv6hdr) > skb->len) goto inhdr_error; - if (pkt_len + sizeof(struct ipv6hdr) < skb->len) { - if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr))) - goto inhdr_error; - if (skb->ip_summed == CHECKSUM_HW) - skb->ip_summed = CHECKSUM_NONE; - } + if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) + goto inhdr_error; } if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb)) goto inhdr_error; @@ -495,11 +491,7 @@ #endif if (skb->len < len || len < 4 * iph->ihl) goto inhdr_error; - if (skb->len > len) { - __pskb_trim(skb, len); - if (skb->ip_summed == CHECKSUM_HW) - skb->ip_summed = CHECKSUM_NONE; - } + pskb_trim_rcsum(skb, len); nf_bridge_put(skb->nf_bridge); if (!nf_bridge_alloc(skb)) @@ -769,7 +761,7 @@ static int br_nf_dev_queue_xmit(struct s { if (skb->protocol == htons(ETH_P_IP) && skb->len > skb->dev->mtu && - !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) + !skb_shinfo(skb)->gso_size) return ip_fragment(skb, br_dev_queue_push_xmit); else return br_dev_queue_push_xmit(skb); diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c new file mode 100644 index 0000000..06abb66 --- /dev/null +++ b/net/bridge/br_netlink.c @@ -0,0 +1,200 @@ +/* + * Bridge netlink control interface + * + * Authors: + * Stephen Hemminger + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include "br_private.h" + +/* + * Create one netlink message for one interface + * Contains port and master info as well as carrier and bridge state. + */ +static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *port, + u32 pid, u32 seq, int event, unsigned int flags) +{ + const struct net_bridge *br = port->br; + const struct net_device *dev = port->dev; + struct ifinfomsg *r; + struct nlmsghdr *nlh; + unsigned char *b = skb->tail; + u32 mtu = dev->mtu; + u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; + u8 portstate = port->state; + + pr_debug("br_fill_info event %d port %s master %s\n", + event, dev->name, br->dev->name); + + nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags); + r = NLMSG_DATA(nlh); + r->ifi_family = AF_BRIDGE; + r->__ifi_pad = 0; + r->ifi_type = dev->type; + r->ifi_index = dev->ifindex; + r->ifi_flags = dev_get_flags(dev); + r->ifi_change = 0; + + RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name); + + RTA_PUT(skb, IFLA_MASTER, sizeof(int), &br->dev->ifindex); + + if (dev->addr_len) + RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr); + + RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu); + if (dev->ifindex != dev->iflink) + RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink); + + + RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate); + + if (event == RTM_NEWLINK) + RTA_PUT(skb, IFLA_PROTINFO, sizeof(portstate), &portstate); + + nlh->nlmsg_len = skb->tail - b; + + return skb->len; + +nlmsg_failure: +rtattr_failure: + + skb_trim(skb, b - skb->data); + return -EINVAL; +} + +/* + * Notify listeners of a change in port information + */ +void br_ifinfo_notify(int event, struct net_bridge_port *port) +{ + struct sk_buff *skb; + int err = -ENOMEM; + + pr_debug("bridge notify event=%d\n", event); + skb = alloc_skb(NLMSG_SPACE(sizeof(struct ifinfomsg) + 128), + GFP_ATOMIC); + if (!skb) + goto err_out; + + err = br_fill_ifinfo(skb, port, current->pid, 0, event, 0); + if (err) + goto err_kfree; + + NETLINK_CB(skb).dst_group = RTNLGRP_LINK; + netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC); + return; + +err_kfree: + kfree_skb(skb); +err_out: + netlink_set_err(rtnl, 0, RTNLGRP_LINK, err); +} + +/* + * Dump information about all ports, in response to GETLINK + */ +static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct net_device *dev; + int idx; + int s_idx = cb->args[0]; + int err = 0; + + read_lock(&dev_base_lock); + for (dev = dev_base, idx = 0; dev; dev = dev->next) { + struct net_bridge_port *p = dev->br_port; + + /* not a bridge port */ + if (!p) + continue; + + if (idx < s_idx) + goto cont; + + err = br_fill_ifinfo(skb, p, NETLINK_CB(cb->skb).pid, + cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI); + if (err <= 0) + break; +cont: + ++idx; + } + read_unlock(&dev_base_lock); + + cb->args[0] = idx; + + return skb->len; +} + +/* + * Change state of port (ie from forwarding to blocking etc) + * Used by spanning tree in user space. + */ +static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) +{ + struct rtattr **rta = arg; + struct ifinfomsg *ifm = NLMSG_DATA(nlh); + struct net_device *dev; + struct net_bridge_port *p; + u8 new_state; + + if (ifm->ifi_family != AF_BRIDGE) + return -EPFNOSUPPORT; + + /* Must pass valid state as PROTINFO */ + if (rta[IFLA_PROTINFO-1]) { + u8 *pstate = RTA_DATA(rta[IFLA_PROTINFO-1]); + new_state = *pstate; + } else + return -EINVAL; + + if (new_state > BR_STATE_BLOCKING) + return -EINVAL; + + /* Find bridge port */ + dev = __dev_get_by_index(ifm->ifi_index); + if (!dev) + return -ENODEV; + + p = dev->br_port; + if (!p) + return -EINVAL; + + /* if kernel STP is running, don't allow changes */ + if (p->br->stp_enabled) + return -EBUSY; + + if (!netif_running(dev)) + return -ENETDOWN; + + if (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED) + return -ENETDOWN; + + p->state = new_state; + br_log_state(p); + return 0; +} + + +static struct rtnetlink_link bridge_rtnetlink_table[RTM_NR_MSGTYPES] = { + [RTM_GETLINK - RTM_BASE] = { .dumpit = br_dump_ifinfo, }, + [RTM_SETLINK - RTM_BASE] = { .doit = br_rtm_setlink, }, +}; + +void __init br_netlink_init(void) +{ + rtnetlink_links[PF_BRIDGE] = bridge_rtnetlink_table; +} + +void __exit br_netlink_fini(void) +{ + rtnetlink_links[PF_BRIDGE] = NULL; +} + diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c index a43a9c1..2027849 100644 --- a/net/bridge/br_notify.c +++ b/net/bridge/br_notify.c @@ -14,6 +14,7 @@ */ #include +#include #include "br_private.h" @@ -49,6 +50,7 @@ static int br_device_event(struct notifi case NETDEV_CHANGEADDR: br_fdb_changeaddr(p, dev->dev_addr); + br_ifinfo_notify(RTM_NEWLINK, p); br_stp_recalculate_bridge_id(br); break; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 86ecea7..c491fb2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -29,7 +29,7 @@ #define BR_MAX_PORTS (1< #include #include +#include #include "br_private.h" #include "br_private_stp.h" @@ -86,6 +87,7 @@ void br_stp_disable_bridge(struct net_br void br_stp_enable_port(struct net_bridge_port *p) { br_init_port(p); + br_ifinfo_notify(RTM_NEWLINK, p); br_port_state_selection(p->br); } @@ -99,6 +101,8 @@ void br_stp_disable_port(struct net_brid printk(KERN_INFO "%s: port %i(%s) entering %s state\n", br->dev->name, p->port_no, p->dev->name, "disabled"); + br_ifinfo_notify(RTM_DELLINK, p); + wasroot = br_is_root_bridge(br); br_become_designated_port(p); p->state = BR_STATE_DISABLED; diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c index ee5a517..02693a2 100644 --- a/net/bridge/netfilter/ebt_ulog.c +++ b/net/bridge/netfilter/ebt_ulog.c @@ -29,7 +29,6 @@ */ #include -#include #include #include #include diff --git a/net/core/Makefile b/net/core/Makefile index 79fe12c..e9bd246 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_NET_DIVERT) += dv.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o obj-$(CONFIG_WIRELESS_EXT) += wireless.o obj-$(CONFIG_NETPOLL) += netpoll.o +obj-$(CONFIG_NET_DMA) += user_dma.o diff --git a/net/core/dev.c b/net/core/dev.c index 4fba549..066a60a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -76,7 +76,6 @@ #include #include #include #include -#include #include #include #include @@ -115,6 +114,8 @@ #include #include #include #include +#include +#include /* * The list of packet types we will receive (as opposed to discard) @@ -148,6 +149,12 @@ static DEFINE_SPINLOCK(ptype_lock); static struct list_head ptype_base[16]; /* 16 way hashed list */ static struct list_head ptype_all; /* Taps */ +#ifdef CONFIG_NET_DMA +static struct dma_client *net_dma_client; +static unsigned int net_dma_count; +static spinlock_t net_dma_event_lock; +#endif + /* * The @dev_base list is protected by @dev_base_lock and the rtnl * semaphore. @@ -222,7 +229,7 @@ #endif * For efficiency */ -int netdev_nit; +static int netdev_nit; /* * Add a protocol ID to the list. Now that the input handler is @@ -1041,7 +1048,7 @@ static inline void net_timestamp(struct * taps currently in use. */ -void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) +static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) { struct packet_type *ptype; @@ -1179,6 +1186,45 @@ out: return ret; } +/** + * skb_gso_segment - Perform segmentation on skb. + * @skb: buffer to segment + * @features: features for the output path (see dev->features) + * + * This function segments the given skb and returns a list of segments. + * + * It may return NULL if the skb requires no segmentation. This is + * only possible when GSO is used for verifying header integrity. + */ +struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); + struct packet_type *ptype; + int type = skb->protocol; + + BUG_ON(skb_shinfo(skb)->frag_list); + BUG_ON(skb->ip_summed != CHECKSUM_HW); + + skb->mac.raw = skb->data; + skb->mac_len = skb->nh.raw - skb->data; + __skb_pull(skb, skb->mac_len); + + rcu_read_lock(); + list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { + if (ptype->type == type && !ptype->dev && ptype->gso_segment) { + segs = ptype->gso_segment(skb, features); + break; + } + } + rcu_read_unlock(); + + __skb_push(skb, skb->data - skb->mac.raw); + + return segs; +} + +EXPORT_SYMBOL(skb_gso_segment); + /* Take action when hardware reception checksum errors are detected. */ #ifdef CONFIG_BUG void netdev_rx_csum_fault(struct net_device *dev) @@ -1192,7 +1238,6 @@ void netdev_rx_csum_fault(struct net_dev EXPORT_SYMBOL(netdev_rx_csum_fault); #endif -#ifdef CONFIG_HIGHMEM /* Actually, we should eliminate this check as soon as we know, that: * 1. IOMMU is present and allows to map all the memory. * 2. No high memory really exists on this machine. @@ -1200,6 +1245,7 @@ #ifdef CONFIG_HIGHMEM static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb) { +#ifdef CONFIG_HIGHMEM int i; if (dev->features & NETIF_F_HIGHDMA) @@ -1209,81 +1255,112 @@ static inline int illegal_highdma(struct if (PageHighMem(skb_shinfo(skb)->frags[i].page)) return 1; +#endif return 0; } -#else -#define illegal_highdma(dev, skb) (0) -#endif -/* Keep head the same: replace data */ -int __skb_linearize(struct sk_buff *skb, gfp_t gfp_mask) +struct dev_gso_cb { + void (*destructor)(struct sk_buff *skb); +}; + +#define DEV_GSO_CB(skb) ((struct dev_gso_cb *)(skb)->cb) + +static void dev_gso_skb_destructor(struct sk_buff *skb) { - unsigned int size; - u8 *data; - long offset; - struct skb_shared_info *ninfo; - int headerlen = skb->data - skb->head; - int expand = (skb->tail + skb->data_len) - skb->end; + struct dev_gso_cb *cb; - if (skb_shared(skb)) - BUG(); + do { + struct sk_buff *nskb = skb->next; - if (expand <= 0) - expand = 0; + skb->next = nskb->next; + nskb->next = NULL; + kfree_skb(nskb); + } while (skb->next); - size = skb->end - skb->head + expand; - size = SKB_DATA_ALIGN(size); - data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask); - if (!data) - return -ENOMEM; + cb = DEV_GSO_CB(skb); + if (cb->destructor) + cb->destructor(skb); +} + +/** + * dev_gso_segment - Perform emulated hardware segmentation on skb. + * @skb: buffer to segment + * + * This function segments the given skb and stores the list of segments + * in skb->next. + */ +static int dev_gso_segment(struct sk_buff *skb) +{ + struct net_device *dev = skb->dev; + struct sk_buff *segs; + int features = dev->features & ~(illegal_highdma(dev, skb) ? + NETIF_F_SG : 0); - /* Copy entire thing */ - if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len)) - BUG(); + segs = skb_gso_segment(skb, features); - /* Set up shinfo */ - ninfo = (struct skb_shared_info*)(data + size); - atomic_set(&ninfo->dataref, 1); - ninfo->tso_size = skb_shinfo(skb)->tso_size; - ninfo->tso_segs = skb_shinfo(skb)->tso_segs; - ninfo->nr_frags = 0; - ninfo->frag_list = NULL; + /* Verifying header integrity only. */ + if (!segs) + return 0; + + if (unlikely(IS_ERR(segs))) + return PTR_ERR(segs); - /* Offset between the two in bytes */ - offset = data - skb->head; + skb->next = segs; + DEV_GSO_CB(skb)->destructor = skb->destructor; + skb->destructor = dev_gso_skb_destructor; - /* Free old data. */ - skb_release_data(skb); + return 0; +} + +int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + if (likely(!skb->next)) { + if (netdev_nit) + dev_queue_xmit_nit(skb, dev); + + if (netif_needs_gso(dev, skb)) { + if (unlikely(dev_gso_segment(skb))) + goto out_kfree_skb; + if (skb->next) + goto gso; + } - skb->head = data; - skb->end = data + size; + return dev->hard_start_xmit(skb, dev); + } - /* Set up new pointers */ - skb->h.raw += offset; - skb->nh.raw += offset; - skb->mac.raw += offset; - skb->tail += offset; - skb->data += offset; +gso: + do { + struct sk_buff *nskb = skb->next; + int rc; - /* We are no longer a clone, even if we were. */ - skb->cloned = 0; + skb->next = nskb->next; + nskb->next = NULL; + rc = dev->hard_start_xmit(nskb, dev); + if (unlikely(rc)) { + nskb->next = skb->next; + skb->next = nskb; + return rc; + } + if (unlikely(netif_queue_stopped(dev) && skb->next)) + return NETDEV_TX_BUSY; + } while (skb->next); + + skb->destructor = DEV_GSO_CB(skb)->destructor; - skb->tail += skb->data_len; - skb->data_len = 0; +out_kfree_skb: + kfree_skb(skb); return 0; } #define HARD_TX_LOCK(dev, cpu) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ - spin_lock(&dev->xmit_lock); \ - dev->xmit_lock_owner = cpu; \ + netif_tx_lock(dev); \ } \ } #define HARD_TX_UNLOCK(dev) { \ if ((dev->features & NETIF_F_LLTX) == 0) { \ - dev->xmit_lock_owner = -1; \ - spin_unlock(&dev->xmit_lock); \ + netif_tx_unlock(dev); \ } \ } @@ -1319,9 +1396,13 @@ int dev_queue_xmit(struct sk_buff *skb) struct Qdisc *q; int rc = -ENOMEM; + /* GSO will handle the following emulations directly. */ + if (netif_needs_gso(dev, skb)) + goto gso; + if (skb_shinfo(skb)->frag_list && !(dev->features & NETIF_F_FRAGLIST) && - __skb_linearize(skb, GFP_ATOMIC)) + __skb_linearize(skb)) goto out_kfree_skb; /* Fragmented skb is linearized if device does not support SG, @@ -1330,25 +1411,26 @@ int dev_queue_xmit(struct sk_buff *skb) */ if (skb_shinfo(skb)->nr_frags && (!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) && - __skb_linearize(skb, GFP_ATOMIC)) + __skb_linearize(skb)) goto out_kfree_skb; /* If packet is not checksummed and device does not support * checksumming for this protocol, complete checksumming here. */ if (skb->ip_summed == CHECKSUM_HW && - (!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) && + (!(dev->features & NETIF_F_GEN_CSUM) && (!(dev->features & NETIF_F_IP_CSUM) || skb->protocol != htons(ETH_P_IP)))) if (skb_checksum_help(skb, 0)) goto out_kfree_skb; +gso: spin_lock_prefetch(&dev->queue_lock); /* Disable soft irqs for various locks below. Also * stops preemption for RCU. */ - local_bh_disable(); + rcu_read_lock_bh(); /* Updates of qdisc are serialized by queue_lock. * The struct Qdisc which is pointed to by qdisc is now a @@ -1382,8 +1464,8 @@ #endif /* The device has no queue. Common case for software devices: loopback, all the sorts of tunnels... - Really, it is unlikely that xmit_lock protection is necessary here. - (f.e. loopback and IP tunnels are clean ignoring statistics + Really, it is unlikely that netif_tx_lock protection is necessary + here. (f.e. loopback and IP tunnels are clean ignoring statistics counters.) However, it is possible, that they rely on protection made by us here. @@ -1399,11 +1481,8 @@ #endif HARD_TX_LOCK(dev, cpu); if (!netif_queue_stopped(dev)) { - if (netdev_nit) - dev_queue_xmit_nit(skb, dev); - rc = 0; - if (!dev->hard_start_xmit(skb, dev)) { + if (!dev_hard_start_xmit(skb, dev)) { HARD_TX_UNLOCK(dev); goto out; } @@ -1422,13 +1501,13 @@ #endif } rc = -ENETDOWN; - local_bh_enable(); + rcu_read_unlock_bh(); out_kfree_skb: kfree_skb(skb); return rc; out: - local_bh_enable(); + rcu_read_unlock_bh(); return rc; } @@ -1846,6 +1925,19 @@ static void net_rx_action(struct softirq } } out: +#ifdef CONFIG_NET_DMA + /* + * There may not be any more sk_buffs coming right now, so push + * any pending DMA copies to hardware + */ + if (net_dma_client) { + struct dma_chan *chan; + rcu_read_lock(); + list_for_each_entry_rcu(chan, &net_dma_client->channels, client_node) + dma_async_memcpy_issue_pending(chan); + rcu_read_unlock(); + } +#endif local_irq_enable(); return; @@ -2785,7 +2877,7 @@ int register_netdevice(struct net_device BUG_ON(dev->reg_state != NETREG_UNINITIALIZED); spin_lock_init(&dev->queue_lock); - spin_lock_init(&dev->xmit_lock); + spin_lock_init(&dev->_xmit_lock); dev->xmit_lock_owner = -1; #ifdef CONFIG_NET_CLS_ACT spin_lock_init(&dev->ingress_lock); @@ -2829,9 +2921,7 @@ #endif /* Fix illegal SG+CSUM combinations. */ if ((dev->features & NETIF_F_SG) && - !(dev->features & (NETIF_F_IP_CSUM | - NETIF_F_NO_CSUM | - NETIF_F_HW_CSUM))) { + !(dev->features & NETIF_F_ALL_CSUM)) { printk("%s: Dropping NETIF_F_SG since no checksum feature.\n", dev->name); dev->features &= ~NETIF_F_SG; @@ -3022,7 +3112,7 @@ static void netdev_wait_allrefs(struct n static DEFINE_MUTEX(net_todo_run_mutex); void netdev_run_todo(void) { - struct list_head list = LIST_HEAD_INIT(list); + struct list_head list; /* Need to guard against multiple cpu's getting out of order. */ mutex_lock(&net_todo_run_mutex); @@ -3037,9 +3127,9 @@ void netdev_run_todo(void) /* Snapshot list, allow later requests */ spin_lock(&net_todo_list_lock); - list_splice_init(&net_todo_list, &list); + list_replace_init(&net_todo_list, &list); spin_unlock(&net_todo_list_lock); - + while (!list_empty(&list)) { struct net_device *dev = list_entry(list.next, struct net_device, todo_list); @@ -3300,6 +3390,88 @@ static int dev_cpu_callback(struct notif } #endif /* CONFIG_HOTPLUG_CPU */ +#ifdef CONFIG_NET_DMA +/** + * net_dma_rebalance - + * This is called when the number of channels allocated to the net_dma_client + * changes. The net_dma_client tries to have one DMA channel per CPU. + */ +static void net_dma_rebalance(void) +{ + unsigned int cpu, i, n; + struct dma_chan *chan; + + lock_cpu_hotplug(); + + if (net_dma_count == 0) { + for_each_online_cpu(cpu) + rcu_assign_pointer(per_cpu(softnet_data.net_dma, cpu), NULL); + unlock_cpu_hotplug(); + return; + } + + i = 0; + cpu = first_cpu(cpu_online_map); + + rcu_read_lock(); + list_for_each_entry(chan, &net_dma_client->channels, client_node) { + n = ((num_online_cpus() / net_dma_count) + + (i < (num_online_cpus() % net_dma_count) ? 1 : 0)); + + while(n) { + per_cpu(softnet_data.net_dma, cpu) = chan; + cpu = next_cpu(cpu, cpu_online_map); + n--; + } + i++; + } + rcu_read_unlock(); + + unlock_cpu_hotplug(); +} + +/** + * netdev_dma_event - event callback for the net_dma_client + * @client: should always be net_dma_client + * @chan: DMA channel for the event + * @event: event type + */ +static void netdev_dma_event(struct dma_client *client, struct dma_chan *chan, + enum dma_event event) +{ + spin_lock(&net_dma_event_lock); + switch (event) { + case DMA_RESOURCE_ADDED: + net_dma_count++; + net_dma_rebalance(); + break; + case DMA_RESOURCE_REMOVED: + net_dma_count--; + net_dma_rebalance(); + break; + default: + break; + } + spin_unlock(&net_dma_event_lock); +} + +/** + * netdev_dma_regiser - register the networking subsystem as a DMA client + */ +static int __init netdev_dma_register(void) +{ + spin_lock_init(&net_dma_event_lock); + net_dma_client = dma_async_client_register(netdev_dma_event); + if (net_dma_client == NULL) + return -ENOMEM; + + dma_async_client_chan_request(net_dma_client, num_online_cpus()); + return 0; +} + +#else +static int __init netdev_dma_register(void) { return -ENODEV; } +#endif /* CONFIG_NET_DMA */ /* * Initialize the DEV module. At boot time this walks the device list and @@ -3353,6 +3525,8 @@ static int __init net_dev_init(void) atomic_set(&queue->backlog_dev.refcnt, 1); } + netdev_dma_register(); + dev_boot_phase = 0; open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); @@ -3371,7 +3545,6 @@ subsys_initcall(net_dev_init); EXPORT_SYMBOL(__dev_get_by_index); EXPORT_SYMBOL(__dev_get_by_name); EXPORT_SYMBOL(__dev_remove_pack); -EXPORT_SYMBOL(__skb_linearize); EXPORT_SYMBOL(dev_valid_name); EXPORT_SYMBOL(dev_add_pack); EXPORT_SYMBOL(dev_alloc_name); diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 05d6085..c57d887 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -62,7 +62,7 @@ #include * Device mc lists are changed by bh at least if IPv6 is enabled, * so that it must be bh protected. * - * We block accesses to device mc filters with dev->xmit_lock. + * We block accesses to device mc filters with netif_tx_lock. */ /* @@ -93,9 +93,9 @@ static void __dev_mc_upload(struct net_d void dev_mc_upload(struct net_device *dev) { - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); __dev_mc_upload(dev); - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); } /* @@ -107,7 +107,7 @@ int dev_mc_delete(struct net_device *dev int err = 0; struct dev_mc_list *dmi, **dmip; - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) { /* @@ -139,13 +139,13 @@ int dev_mc_delete(struct net_device *dev */ __dev_mc_upload(dev); - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); return 0; } } err = -ENOENT; done: - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); return err; } @@ -160,7 +160,7 @@ int dev_mc_add(struct net_device *dev, v dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC); - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) { if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 && dmi->dmi_addrlen == alen) { @@ -176,7 +176,7 @@ int dev_mc_add(struct net_device *dev, v } if ((dmi = dmi1) == NULL) { - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); return -ENOMEM; } memcpy(dmi->dmi_addr, addr, alen); @@ -189,11 +189,11 @@ int dev_mc_add(struct net_device *dev, v __dev_mc_upload(dev); - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); return 0; done: - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); kfree(dmi1); return err; } @@ -204,7 +204,7 @@ done: void dev_mc_discard(struct net_device *dev) { - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); while (dev->mc_list != NULL) { struct dev_mc_list *tmp = dev->mc_list; @@ -215,7 +215,7 @@ void dev_mc_discard(struct net_device *d } dev->mc_count = 0; - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); } #ifdef CONFIG_PROC_FS @@ -250,7 +250,7 @@ static int dev_mc_seq_show(struct seq_fi struct dev_mc_list *m; struct net_device *dev = v; - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); for (m = dev->mc_list; m; m = m->next) { int i; @@ -262,7 +262,7 @@ static int dev_mc_seq_show(struct seq_fi seq_putc(seq, '\n'); } - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); return 0; } diff --git a/net/core/ethtool.c b/net/core/ethtool.c index e6f7610..27ce168 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -30,7 +30,7 @@ u32 ethtool_op_get_link(struct net_devic u32 ethtool_op_get_tx_csum(struct net_device *dev) { - return (dev->features & (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM)) != 0; + return (dev->features & NETIF_F_ALL_CSUM) != 0; } int ethtool_op_set_tx_csum(struct net_device *dev, u32 data) @@ -551,9 +551,7 @@ static int ethtool_set_sg(struct net_dev return -EFAULT; if (edata.data && - !(dev->features & (NETIF_F_IP_CSUM | - NETIF_F_NO_CSUM | - NETIF_F_HW_CSUM))) + !(dev->features & NETIF_F_ALL_CSUM)) return -EINVAL; return __ethtool_set_sg(dev, edata.data); @@ -591,7 +589,7 @@ static int ethtool_set_tso(struct net_de static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr) { - struct ethtool_value edata = { ETHTOOL_GTSO }; + struct ethtool_value edata = { ETHTOOL_GUFO }; if (!dev->ethtool_ops->get_ufo) return -EOPNOTSUPP; @@ -600,6 +598,7 @@ static int ethtool_get_ufo(struct net_de return -EFAULT; return 0; } + static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr) { struct ethtool_value edata; @@ -615,6 +614,29 @@ static int ethtool_set_ufo(struct net_de return dev->ethtool_ops->set_ufo(dev, edata.data); } +static int ethtool_get_gso(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_value edata = { ETHTOOL_GGSO }; + + edata.data = dev->features & NETIF_F_GSO; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + return 0; +} + +static int ethtool_set_gso(struct net_device *dev, char __user *useraddr) +{ + struct ethtool_value edata; + + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + if (edata.data) + dev->features |= NETIF_F_GSO; + else + dev->features &= ~NETIF_F_GSO; + return 0; +} + static int ethtool_self_test(struct net_device *dev, char __user *useraddr) { struct ethtool_test test; @@ -906,6 +928,12 @@ int dev_ethtool(struct ifreq *ifr) case ETHTOOL_SUFO: rc = ethtool_set_ufo(dev, useraddr); break; + case ETHTOOL_GGSO: + rc = ethtool_get_gso(dev, useraddr); + break; + case ETHTOOL_SGSO: + rc = ethtool_set_gso(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } diff --git a/net/core/link_watch.c b/net/core/link_watch.c index 646937c..4b36114 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -11,7 +11,6 @@ * */ -#include #include #include #include @@ -91,11 +90,10 @@ static void rfc2863_policy(struct net_de /* Must be called with the rtnl semaphore held */ void linkwatch_run_queue(void) { - LIST_HEAD(head); - struct list_head *n, *next; + struct list_head head, *n, *next; spin_lock_irq(&lweventlist_lock); - list_splice_init(&lweventlist, &head); + list_replace_init(&lweventlist, &head); spin_unlock_irq(&lweventlist_lock); list_for_each_safe(n, next, &head) { diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 50a8c73..7ad681f 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -15,7 +15,6 @@ * Harald Welte Add neighbour cache statistics like rtstat */ -#include #include #include #include diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 47a6fce..1347276 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/net/core/netpoll.c b/net/core/netpoll.c index e8e05ce..471da45 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -54,6 +54,7 @@ #define MAX_SKB_SIZE \ sizeof(struct iphdr) + sizeof(struct ethhdr)) static void zap_completion_queue(void); +static void arp_reply(struct sk_buff *skb); static void queue_process(void *p) { @@ -153,6 +154,22 @@ static void poll_napi(struct netpoll *np } } +static void service_arp_queue(struct netpoll_info *npi) +{ + struct sk_buff *skb; + + if (unlikely(!npi)) + return; + + skb = skb_dequeue(&npi->arp_tx); + + while (skb != NULL) { + arp_reply(skb); + skb = skb_dequeue(&npi->arp_tx); + } + return; +} + void netpoll_poll(struct netpoll *np) { if(!np->dev || !netif_running(np->dev) || !np->dev->poll_controller) @@ -163,6 +180,8 @@ void netpoll_poll(struct netpoll *np) if (np->dev->poll) poll_napi(np); + service_arp_queue(np->dev->npinfo); + zap_completion_queue(); } @@ -273,24 +292,17 @@ static void netpoll_send_skb(struct netp do { npinfo->tries--; - spin_lock(&np->dev->xmit_lock); - np->dev->xmit_lock_owner = smp_processor_id(); + netif_tx_lock(np->dev); /* * network drivers do not expect to be called if the queue is * stopped. */ - if (netif_queue_stopped(np->dev)) { - np->dev->xmit_lock_owner = -1; - spin_unlock(&np->dev->xmit_lock); - netpoll_poll(np); - udelay(50); - continue; - } + status = NETDEV_TX_BUSY; + if (!netif_queue_stopped(np->dev)) + status = np->dev->hard_start_xmit(skb, np->dev); - status = np->dev->hard_start_xmit(skb, np->dev); - np->dev->xmit_lock_owner = -1; - spin_unlock(&np->dev->xmit_lock); + netif_tx_unlock(np->dev); /* success */ if(!status) { @@ -449,7 +461,9 @@ int __netpoll_rx(struct sk_buff *skb) int proto, len, ulen; struct iphdr *iph; struct udphdr *uh; - struct netpoll *np = skb->dev->npinfo->rx_np; + struct netpoll_info *npi = skb->dev->npinfo; + struct netpoll *np = npi->rx_np; + if (!np) goto out; @@ -459,7 +473,7 @@ int __netpoll_rx(struct sk_buff *skb) /* check if netpoll clients need ARP */ if (skb->protocol == __constant_htons(ETH_P_ARP) && atomic_read(&trapped)) { - arp_reply(skb); + skb_queue_tail(&npi->arp_tx, skb); return 1; } @@ -654,6 +668,7 @@ int netpoll_setup(struct netpoll *np) npinfo->poll_owner = -1; npinfo->tries = MAX_RETRIES; spin_lock_init(&npinfo->rx_lock); + skb_queue_head_init(&npinfo->arp_tx); } else npinfo = ndev->npinfo; diff --git a/net/core/pktgen.c b/net/core/pktgen.c index c23e9c0..67ed14d 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -2897,7 +2897,7 @@ static __inline__ void pktgen_xmit(struc } } - spin_lock_bh(&odev->xmit_lock); + netif_tx_lock_bh(odev); if (!netif_queue_stopped(odev)) { atomic_inc(&(pkt_dev->skb->users)); @@ -2942,7 +2942,7 @@ static __inline__ void pktgen_xmit(struc pkt_dev->next_tx_ns = 0; } - spin_unlock_bh(&odev->xmit_lock); + netif_tx_unlock_bh(odev); /* If pkt_dev->count is zero, then run forever */ if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) { diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3fcfa9c..20e5bb7 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -16,7 +16,6 @@ * Vitaly E. Lavrov RTA_OK arithmetics was wrong. */ -#include #include #include #include @@ -663,7 +662,7 @@ rtnetlink_rcv_msg(struct sk_buff *skb, s sz_idx = type>>2; kind = type&3; - if (kind != 2 && security_netlink_recv(skb)) { + if (kind != 2 && security_netlink_recv(skb, CAP_NET_ADMIN)) { *errp = -EPERM; return -1; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index fb3770f..44f6a18 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -38,7 +38,6 @@ * The functions in this file will not compile correctly with gcc 2.4.x */ -#include #include #include #include @@ -72,6 +71,13 @@ static kmem_cache_t *skbuff_head_cache _ static kmem_cache_t *skbuff_fclone_cache __read_mostly; /* + * lockdep: lock class key used by skb_queue_head_init(): + */ +struct lock_class_key skb_queue_lock_key; + +EXPORT_SYMBOL(skb_queue_lock_key); + +/* * Keep out-of-line to prevent kernel bloat. * __builtin_return_address is not used because it is not always * reliable. @@ -172,9 +178,9 @@ struct sk_buff *__alloc_skb(unsigned int shinfo = skb_shinfo(skb); atomic_set(&shinfo->dataref, 1); shinfo->nr_frags = 0; - shinfo->tso_size = 0; - shinfo->tso_segs = 0; - shinfo->ufo_size = 0; + shinfo->gso_size = 0; + shinfo->gso_segs = 0; + shinfo->gso_type = 0; shinfo->ip6_frag_id = 0; shinfo->frag_list = NULL; @@ -238,8 +244,9 @@ struct sk_buff *alloc_skb_from_cache(kme atomic_set(&(skb_shinfo(skb)->dataref), 1); skb_shinfo(skb)->nr_frags = 0; - skb_shinfo(skb)->tso_size = 0; - skb_shinfo(skb)->tso_segs = 0; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_segs = 0; + skb_shinfo(skb)->gso_type = 0; skb_shinfo(skb)->frag_list = NULL; out: return skb; @@ -271,7 +278,7 @@ static void skb_clone_fraglist(struct sk skb_get(list); } -void skb_release_data(struct sk_buff *skb) +static void skb_release_data(struct sk_buff *skb) { if (!skb->cloned || !atomic_sub_return(skb->nohdr ? (1 << SKB_DATAREF_SHIFT) + 1 : 1, @@ -464,7 +471,7 @@ #ifdef CONFIG_NET_CLS_ACT n->tc_verd = CLR_TC_MUNGED(n->tc_verd); C(input_dev); #endif - + skb_copy_secmark(n, skb); #endif C(truesize); atomic_set(&n->users, 1); @@ -526,9 +533,11 @@ #ifdef CONFIG_NET_CLS_ACT #endif new->tc_index = old->tc_index; #endif + skb_copy_secmark(new, old); atomic_set(&new->users, 1); - skb_shinfo(new)->tso_size = skb_shinfo(old)->tso_size; - skb_shinfo(new)->tso_segs = skb_shinfo(old)->tso_segs; + skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size; + skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs; + skb_shinfo(new)->gso_type = skb_shinfo(old)->gso_type; } /** @@ -780,32 +789,46 @@ struct sk_buff *skb_copy_expand(const st * filled. Used by network drivers which may DMA or transfer data * beyond the buffer end onto the wire. * - * May return NULL in out of memory cases. + * May return error in out of memory cases. The skb is freed on error. */ -struct sk_buff *skb_pad(struct sk_buff *skb, int pad) +int skb_pad(struct sk_buff *skb, int pad) { - struct sk_buff *nskb; + int err; + int ntail; /* If the skbuff is non linear tailroom is always zero.. */ - if (skb_tailroom(skb) >= pad) { + if (!skb_cloned(skb) && skb_tailroom(skb) >= pad) { memset(skb->data+skb->len, 0, pad); - return skb; + return 0; } - - nskb = skb_copy_expand(skb, skb_headroom(skb), skb_tailroom(skb) + pad, GFP_ATOMIC); + + ntail = skb->data_len + pad - (skb->end - skb->tail); + if (likely(skb_cloned(skb) || ntail > 0)) { + err = pskb_expand_head(skb, 0, ntail, GFP_ATOMIC); + if (unlikely(err)) + goto free_skb; + } + + /* FIXME: The use of this function with non-linear skb's really needs + * to be audited. + */ + err = skb_linearize(skb); + if (unlikely(err)) + goto free_skb; + + memset(skb->data + skb->len, 0, pad); + return 0; + +free_skb: kfree_skb(skb); - if (nskb) - memset(nskb->data+nskb->len, 0, pad); - return nskb; + return err; } -/* Trims skb to length len. It can change skb pointers, if "realloc" is 1. - * If realloc==0 and trimming is impossible without change of data, - * it is BUG(). +/* Trims skb to length len. It can change skb pointers. */ -int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc) +int ___pskb_trim(struct sk_buff *skb, unsigned int len) { int offset = skb_headlen(skb); int nfrags = skb_shinfo(skb)->nr_frags; @@ -815,7 +838,6 @@ int ___pskb_trim(struct sk_buff *skb, un int end = offset + skb_shinfo(skb)->frags[i].size; if (end > len) { if (skb_cloned(skb)) { - BUG_ON(!realloc); if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) return -ENOMEM; } @@ -1723,12 +1745,15 @@ unsigned int skb_find_text(struct sk_buf unsigned int to, struct ts_config *config, struct ts_state *state) { + unsigned int ret; + config->get_next_block = skb_ts_get_next_block; config->finish = skb_ts_finish; skb_prepare_seq_read(skb, from, to, TS_SKB_CB(state)); - return textsearch_find(config, state); + ret = textsearch_find(config, state); + return (ret <= to - from ? ret : UINT_MAX); } /** @@ -1826,6 +1851,133 @@ unsigned char *skb_pull_rcsum(struct sk_ EXPORT_SYMBOL_GPL(skb_pull_rcsum); +/** + * skb_segment - Perform protocol segmentation on skb. + * @skb: buffer to segment + * @features: features for the output path (see dev->features) + * + * This function performs segmentation on the given skb. It returns + * the segment at the given position. It returns NULL if there are + * no more segments to generate, or when an error is encountered. + */ +struct sk_buff *skb_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = NULL; + struct sk_buff *tail = NULL; + unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int doffset = skb->data - skb->mac.raw; + unsigned int offset = doffset; + unsigned int headroom; + unsigned int len; + int sg = features & NETIF_F_SG; + int nfrags = skb_shinfo(skb)->nr_frags; + int err = -ENOMEM; + int i = 0; + int pos; + + __skb_push(skb, doffset); + headroom = skb_headroom(skb); + pos = skb_headlen(skb); + + do { + struct sk_buff *nskb; + skb_frag_t *frag; + int hsize, nsize; + int k; + int size; + + len = skb->len - offset; + if (len > mss) + len = mss; + + hsize = skb_headlen(skb) - offset; + if (hsize < 0) + hsize = 0; + nsize = hsize + doffset; + if (nsize > len + doffset || !sg) + nsize = len + doffset; + + nskb = alloc_skb(nsize + headroom, GFP_ATOMIC); + if (unlikely(!nskb)) + goto err; + + if (segs) + tail->next = nskb; + else + segs = nskb; + tail = nskb; + + nskb->dev = skb->dev; + nskb->priority = skb->priority; + nskb->protocol = skb->protocol; + nskb->dst = dst_clone(skb->dst); + memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); + nskb->pkt_type = skb->pkt_type; + nskb->mac_len = skb->mac_len; + + skb_reserve(nskb, headroom); + nskb->mac.raw = nskb->data; + nskb->nh.raw = nskb->data + skb->mac_len; + nskb->h.raw = nskb->nh.raw + (skb->h.raw - skb->nh.raw); + memcpy(skb_put(nskb, doffset), skb->data, doffset); + + if (!sg) { + nskb->csum = skb_copy_and_csum_bits(skb, offset, + skb_put(nskb, len), + len, 0); + continue; + } + + frag = skb_shinfo(nskb)->frags; + k = 0; + + nskb->ip_summed = CHECKSUM_HW; + nskb->csum = skb->csum; + memcpy(skb_put(nskb, hsize), skb->data + offset, hsize); + + while (pos < offset + len) { + BUG_ON(i >= nfrags); + + *frag = skb_shinfo(skb)->frags[i]; + get_page(frag->page); + size = frag->size; + + if (pos < offset) { + frag->page_offset += offset - pos; + frag->size -= offset - pos; + } + + k++; + + if (pos + size <= offset + len) { + i++; + pos += size; + } else { + frag->size -= pos + size - (offset + len); + break; + } + + frag++; + } + + skb_shinfo(nskb)->nr_frags = k; + nskb->data_len = len - hsize; + nskb->len += nskb->data_len; + nskb->truesize += nskb->data_len; + } while ((offset += len) < skb->len); + + return segs; + +err: + while ((skb = segs)) { + segs = skb->next; + kfree(skb); + } + return ERR_PTR(err); +} + +EXPORT_SYMBOL_GPL(skb_segment); + void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", diff --git a/net/core/sock.c b/net/core/sock.c index ed2afdb..51fcfbc 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -92,7 +92,6 @@ */ #include -#include #include #include #include @@ -130,6 +129,53 @@ #ifdef CONFIG_INET #include #endif +/* + * Each address family might have different locking rules, so we have + * one slock key per address family: + */ +static struct lock_class_key af_family_keys[AF_MAX]; +static struct lock_class_key af_family_slock_keys[AF_MAX]; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC +/* + * Make lock validator output more readable. (we pre-construct these + * strings build-time, so that runtime initialization of socket + * locks is fast): + */ +static const char *af_family_key_strings[AF_MAX+1] = { + "sk_lock-AF_UNSPEC", "sk_lock-AF_UNIX" , "sk_lock-AF_INET" , + "sk_lock-AF_AX25" , "sk_lock-AF_IPX" , "sk_lock-AF_APPLETALK", + "sk_lock-AF_NETROM", "sk_lock-AF_BRIDGE" , "sk_lock-AF_ATMPVC" , + "sk_lock-AF_X25" , "sk_lock-AF_INET6" , "sk_lock-AF_ROSE" , + "sk_lock-AF_DECnet", "sk_lock-AF_NETBEUI" , "sk_lock-AF_SECURITY" , + "sk_lock-AF_KEY" , "sk_lock-AF_NETLINK" , "sk_lock-AF_PACKET" , + "sk_lock-AF_ASH" , "sk_lock-AF_ECONET" , "sk_lock-AF_ATMSVC" , + "sk_lock-21" , "sk_lock-AF_SNA" , "sk_lock-AF_IRDA" , + "sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE" , "sk_lock-AF_LLC" , + "sk_lock-27" , "sk_lock-28" , "sk_lock-29" , + "sk_lock-AF_TIPC" , "sk_lock-AF_BLUETOOTH", "sk_lock-AF_MAX" +}; +static const char *af_family_slock_key_strings[AF_MAX+1] = { + "slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" , + "slock-AF_AX25" , "slock-AF_IPX" , "slock-AF_APPLETALK", + "slock-AF_NETROM", "slock-AF_BRIDGE" , "slock-AF_ATMPVC" , + "slock-AF_X25" , "slock-AF_INET6" , "slock-AF_ROSE" , + "slock-AF_DECnet", "slock-AF_NETBEUI" , "slock-AF_SECURITY" , + "slock-AF_KEY" , "slock-AF_NETLINK" , "slock-AF_PACKET" , + "slock-AF_ASH" , "slock-AF_ECONET" , "slock-AF_ATMSVC" , + "slock-21" , "slock-AF_SNA" , "slock-AF_IRDA" , + "slock-AF_PPPOX" , "slock-AF_WANPIPE" , "slock-AF_LLC" , + "slock-27" , "slock-28" , "slock-29" , + "slock-AF_TIPC" , "slock-AF_BLUETOOTH", "slock-AF_MAX" +}; +#endif + +/* + * sk_callback_lock locking rules are per-address-family, + * so split the lock classes by using a per-AF key: + */ +static struct lock_class_key af_callback_keys[AF_MAX]; + /* Take into consideration the size of the struct sk_buff overhead in the * determination of these values, since that is non-constant across * platforms. This makes socket queueing behavior and performance @@ -238,9 +284,16 @@ int sk_receive_skb(struct sock *sk, stru skb->dev = NULL; bh_lock_sock(sk); - if (!sock_owned_by_user(sk)) + if (!sock_owned_by_user(sk)) { + /* + * trylock + unlock semantics: + */ + mutex_acquire(&sk->sk_lock.dep_map, 0, 1, _RET_IP_); + rc = sk->sk_backlog_rcv(sk, skb); - else + + mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_); + } else sk_add_backlog(sk, skb); bh_unlock_sock(sk); out: @@ -565,6 +618,13 @@ #endif ret = -ENONET; break; + case SO_PASSSEC: + if (valbool) + set_bit(SOCK_PASSSEC, &sock->flags); + else + clear_bit(SOCK_PASSSEC, &sock->flags); + break; + /* We implement the SO_SNDLOWAT etc to not be settable (1003.1g 5.3) */ default: @@ -723,6 +783,10 @@ int sock_getsockopt(struct socket *sock, v.val = sk->sk_state == TCP_LISTEN; break; + case SO_PASSSEC: + v.val = test_bit(SOCK_PASSSEC, &sock->flags) ? 1 : 0; + break; + case SO_PEERSEC: return security_socket_getpeersec_stream(sock, optval, optlen, len); @@ -739,6 +803,33 @@ lenout: return 0; } +/* + * Initialize an sk_lock. + * + * (We also register the sk_lock with the lock validator.) + */ +static void inline sock_lock_init(struct sock *sk) +{ + spin_lock_init(&sk->sk_lock.slock); + sk->sk_lock.owner = NULL; + init_waitqueue_head(&sk->sk_lock.wq); + /* + * Make sure we are not reinitializing a held lock: + */ + debug_check_no_locks_freed((void *)&sk->sk_lock, sizeof(sk->sk_lock)); + + /* + * Mark both the sk_lock and the sk_lock.slock as a + * per-address-family lock class: + */ + lockdep_set_class_and_name(&sk->sk_lock.slock, + af_family_slock_keys + sk->sk_family, + af_family_slock_key_strings[sk->sk_family]); + lockdep_init_map(&sk->sk_lock.dep_map, + af_family_key_strings[sk->sk_family], + af_family_keys + sk->sk_family); +} + /** * sk_alloc - All socket objects are allocated here * @family: protocol family @@ -832,9 +923,14 @@ struct sock *sk_clone(const struct sock atomic_set(&newsk->sk_omem_alloc, 0); skb_queue_head_init(&newsk->sk_receive_queue); skb_queue_head_init(&newsk->sk_write_queue); +#ifdef CONFIG_NET_DMA + skb_queue_head_init(&newsk->sk_async_wait_queue); +#endif rwlock_init(&newsk->sk_dst_lock); rwlock_init(&newsk->sk_callback_lock); + lockdep_set_class(&newsk->sk_callback_lock, + af_callback_keys + newsk->sk_family); newsk->sk_dst_cache = NULL; newsk->sk_wmem_queued = 0; @@ -1383,6 +1479,9 @@ void sock_init_data(struct socket *sock, skb_queue_head_init(&sk->sk_receive_queue); skb_queue_head_init(&sk->sk_write_queue); skb_queue_head_init(&sk->sk_error_queue); +#ifdef CONFIG_NET_DMA + skb_queue_head_init(&sk->sk_async_wait_queue); +#endif sk->sk_send_head = NULL; @@ -1406,6 +1505,8 @@ void sock_init_data(struct socket *sock, rwlock_init(&sk->sk_dst_lock); rwlock_init(&sk->sk_callback_lock); + lockdep_set_class(&sk->sk_callback_lock, + af_callback_keys + sk->sk_family); sk->sk_state_change = sock_def_wakeup; sk->sk_data_ready = sock_def_readable; @@ -1433,24 +1534,34 @@ void sock_init_data(struct socket *sock, void fastcall lock_sock(struct sock *sk) { might_sleep(); - spin_lock_bh(&(sk->sk_lock.slock)); + spin_lock_bh(&sk->sk_lock.slock); if (sk->sk_lock.owner) __lock_sock(sk); sk->sk_lock.owner = (void *)1; - spin_unlock_bh(&(sk->sk_lock.slock)); + spin_unlock(&sk->sk_lock.slock); + /* + * The sk_lock has mutex_lock() semantics here: + */ + mutex_acquire(&sk->sk_lock.dep_map, 0, 0, _RET_IP_); + local_bh_enable(); } EXPORT_SYMBOL(lock_sock); void fastcall release_sock(struct sock *sk) { - spin_lock_bh(&(sk->sk_lock.slock)); + /* + * The sk_lock has mutex_unlock() semantics: + */ + mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_); + + spin_lock_bh(&sk->sk_lock.slock); if (sk->sk_backlog.tail) __release_sock(sk); sk->sk_lock.owner = NULL; - if (waitqueue_active(&(sk->sk_lock.wq))) - wake_up(&(sk->sk_lock.wq)); - spin_unlock_bh(&(sk->sk_lock.slock)); + if (waitqueue_active(&sk->sk_lock.wq)) + wake_up(&sk->sk_lock.wq); + spin_unlock_bh(&sk->sk_lock.slock); } EXPORT_SYMBOL(release_sock); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 7104536..0253413 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include diff --git a/net/core/user_dma.c b/net/core/user_dma.c new file mode 100644 index 0000000..b7c98db --- /dev/null +++ b/net/core/user_dma.c @@ -0,0 +1,131 @@ +/* + * Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved. + * Portions based on net/core/datagram.c and copyrighted by their authors. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The full GNU General Public License is included in this distribution in the + * file called COPYING. + */ + +/* + * This code allows the net stack to make use of a DMA engine for + * skb to iovec copies. + */ + +#include +#include +#include /* for BUG_TRAP */ +#include + +#define NET_DMA_DEFAULT_COPYBREAK 4096 + +int sysctl_tcp_dma_copybreak = NET_DMA_DEFAULT_COPYBREAK; + +/** + * dma_skb_copy_datagram_iovec - Copy a datagram to an iovec. + * @skb - buffer to copy + * @offset - offset in the buffer to start copying from + * @iovec - io vector to copy to + * @len - amount of data to copy from buffer to iovec + * @pinned_list - locked iovec buffer data + * + * Note: the iovec is modified during the copy. + */ +int dma_skb_copy_datagram_iovec(struct dma_chan *chan, + struct sk_buff *skb, int offset, struct iovec *to, + size_t len, struct dma_pinned_list *pinned_list) +{ + int start = skb_headlen(skb); + int i, copy = start - offset; + dma_cookie_t cookie = 0; + + /* Copy header. */ + if (copy > 0) { + if (copy > len) + copy = len; + cookie = dma_memcpy_to_iovec(chan, to, pinned_list, + skb->data + offset, copy); + if (cookie < 0) + goto fault; + len -= copy; + if (len == 0) + goto end; + offset += copy; + } + + /* Copy paged appendix. Hmm... why does this look so complicated? */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + int end; + + BUG_TRAP(start <= offset + len); + + end = start + skb_shinfo(skb)->frags[i].size; + copy = end - offset; + if ((copy = end - offset) > 0) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + struct page *page = frag->page; + + if (copy > len) + copy = len; + + cookie = dma_memcpy_pg_to_iovec(chan, to, pinned_list, page, + frag->page_offset + offset - start, copy); + if (cookie < 0) + goto fault; + len -= copy; + if (len == 0) + goto end; + offset += copy; + } + start = end; + } + + if (skb_shinfo(skb)->frag_list) { + struct sk_buff *list = skb_shinfo(skb)->frag_list; + + for (; list; list = list->next) { + int end; + + BUG_TRAP(start <= offset + len); + + end = start + list->len; + copy = end - offset; + if (copy > 0) { + if (copy > len) + copy = len; + cookie = dma_skb_copy_datagram_iovec(chan, list, + offset - start, to, copy, + pinned_list); + if (cookie < 0) + goto fault; + len -= copy; + if (len == 0) + goto end; + offset += copy; + } + start = end; + } + } + +end: + if (!len) { + skb->dma_cookie = cookie; + return cookie; + } + +fault: + return -EFAULT; +} diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig index 7e096ba..859e335 100644 --- a/net/dccp/Kconfig +++ b/net/dccp/Kconfig @@ -26,7 +26,7 @@ config INET_DCCP_DIAG config IP_DCCP_ACKVEC depends on IP_DCCP - def_bool N + bool source "net/dccp/ccids/Kconfig" diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h index ec7a89b..0adf4b5 100644 --- a/net/dccp/ackvec.h +++ b/net/dccp/ackvec.h @@ -11,7 +11,6 @@ #define _ACKVEC_H * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c index d4f9e2d..e961562 100644 --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -30,7 +30,6 @@ * - jiffies wrapping */ -#include #include "../ccid.h" #include "../dccp.h" #include "ccid2.h" diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c index b4a51d0..c39bff7 100644 --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -34,7 +34,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include "../ccid.h" #include "../dccp.h" #include "lib/packet_history.h" diff --git a/net/dccp/ccids/ccid3.h b/net/dccp/ccids/ccid3.h index f18b96d..5ade4f6 100644 --- a/net/dccp/ccids/ccid3.h +++ b/net/dccp/ccids/ccid3.h @@ -36,7 +36,6 @@ #ifndef _DCCP_CCID3_H_ #define _DCCP_CCID3_H_ -#include #include #include #include diff --git a/net/dccp/ccids/lib/loss_interval.c b/net/dccp/ccids/lib/loss_interval.c index 4c01a54..5d7b7d8 100644 --- a/net/dccp/ccids/lib/loss_interval.c +++ b/net/dccp/ccids/lib/loss_interval.c @@ -11,7 +11,6 @@ * (at your option) any later version. */ -#include #include #include "loss_interval.h" diff --git a/net/dccp/ccids/lib/loss_interval.h b/net/dccp/ccids/lib/loss_interval.h index 417d9d8..43bf782 100644 --- a/net/dccp/ccids/lib/loss_interval.h +++ b/net/dccp/ccids/lib/loss_interval.h @@ -13,7 +13,6 @@ #define _DCCP_LI_HIST_ * any later version. */ -#include #include #include #include diff --git a/net/dccp/ccids/lib/packet_history.c b/net/dccp/ccids/lib/packet_history.c index d3f9d20..ad98d6a 100644 --- a/net/dccp/ccids/lib/packet_history.c +++ b/net/dccp/ccids/lib/packet_history.c @@ -34,7 +34,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include diff --git a/net/dccp/ccids/lib/packet_history.h b/net/dccp/ccids/lib/packet_history.h index 122e967..673c209 100644 --- a/net/dccp/ccids/lib/packet_history.h +++ b/net/dccp/ccids/lib/packet_history.h @@ -37,7 +37,6 @@ #ifndef _DCCP_PKT_HIST_ #define _DCCP_PKT_HIST_ -#include #include #include #include diff --git a/net/dccp/ccids/lib/tfrc_equation.c b/net/dccp/ccids/lib/tfrc_equation.c index add3cae..4fd2ebe 100644 --- a/net/dccp/ccids/lib/tfrc_equation.c +++ b/net/dccp/ccids/lib/tfrc_equation.c @@ -12,7 +12,6 @@ * (at your option) any later version. */ -#include #include #include diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 1fe5091..d00a2f4 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -12,7 +12,6 @@ #define _DCCP_H * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/net/dccp/diag.c b/net/dccp/diag.c index 0f25dc3..0f37455 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include #include #include diff --git a/net/dccp/feat.c b/net/dccp/feat.c index b39e2a5..a1b0682 100644 --- a/net/dccp/feat.c +++ b/net/dccp/feat.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include "dccp.h" diff --git a/net/dccp/input.c b/net/dccp/input.c index bfc5366..7f9dc6a 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index f2c011f..c3073e7 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 65e2ab0..ff42bc4 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -12,7 +12,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/dccp/ipv6.h b/net/dccp/ipv6.h index e4d4e93..6eef81f 100644 --- a/net/dccp/ipv6.h +++ b/net/dccp/ipv6.h @@ -11,7 +11,6 @@ #define _DCCP_IPV6_H * published by the Free Software Foundation. */ -#include #include #include diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index c0349e5..9045438 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/dccp/options.c b/net/dccp/options.c index e9feb2a..c3cda1e 100644 --- a/net/dccp/options.c +++ b/net/dccp/options.c @@ -11,7 +11,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/dccp/output.c b/net/dccp/output.c index 7409e4a..58669be 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 2e0ee83..f4f0627 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -719,7 +718,7 @@ int dccp_recvmsg(struct kiocb *iocb, str } dccp_pr_debug("packet_type=%s\n", dccp_packet_name(dh->dccph_type)); - sk_eat_skb(sk, skb); + sk_eat_skb(sk, skb, 0); verify_sock_status: if (sock_flag(sk, SOCK_DONE)) { len = 0; @@ -773,7 +772,7 @@ verify_sock_status: } found_fin_ok: if (!(flags & MSG_PEEK)) - sk_eat_skb(sk, skb); + sk_eat_skb(sk, skb, 0); break; } while (1); out: diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c index 64c89e9..c1ba945 100644 --- a/net/dccp/sysctl.c +++ b/net/dccp/sysctl.c @@ -9,7 +9,6 @@ * as published by the Free Software Foundation. */ -#include #include #include diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 5244415..8447742 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 2b289ef..5486247 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -99,7 +99,6 @@ Version 0.0.6 2.1.110 07-aug-98 E dn_bind fixes *******************************************************************************/ -#include #include #include #include diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index a26ff9f..98a2520 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -24,7 +24,6 @@ * devices. All mtu based now. */ -#include #include #include #include diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index bd4ce86..0375077 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -17,7 +17,6 @@ * this code was copied from it. * */ -#include #include #include #include diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 66e230c..5ce9c9e 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -24,7 +24,6 @@ * */ -#include #include #include #include diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c index 547523b..86f7f3b 100644 --- a/net/decnet/dn_nsp_in.c +++ b/net/decnet/dn_nsp_in.c @@ -45,7 +45,6 @@ GNU General Public License for more details. *******************************************************************************/ -#include #include #include #include @@ -801,8 +800,7 @@ got_it: * We linearize everything except data segments here. */ if (cb->nsp_flags & ~0x60) { - if (unlikely(skb_is_nonlinear(skb)) && - skb_linearize(skb, GFP_ATOMIC) != 0) + if (unlikely(skb_linearize(skb))) goto free_out; } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index e172cf9..1355614 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -55,7 +55,6 @@ GNU General Public License for more details. *******************************************************************************/ -#include #include #include #include @@ -629,8 +628,7 @@ int dn_route_rcv(struct sk_buff *skb, st padlen); if (flags & DN_RT_PKT_CNTL) { - if (unlikely(skb_is_nonlinear(skb)) && - skb_linearize(skb, GFP_ATOMIC) != 0) + if (unlikely(skb_linearize(skb))) goto dump_it; switch(flags & DN_RT_CNTL_MSK) { diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 446faaf..06e785f 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -13,7 +13,6 @@ * Changes: * */ -#include #include #include #include diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c index 0ebc46a..37d9d0a 100644 --- a/net/decnet/dn_table.c +++ b/net/decnet/dn_table.c @@ -12,7 +12,6 @@ * Changes: * */ -#include #include #include #include diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index 74133ec..8b99bd3 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -107,7 +107,7 @@ static inline void dnrmg_receive_user_sk if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) return; - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) + if (security_netlink_recv(skb, CAP_NET_ADMIN)) RCV_SKB_FAIL(-EPERM); /* Eventually we might send routing messages too */ diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c index bda5920..e246f05 100644 --- a/net/decnet/sysctl_net_decnet.c +++ b/net/decnet/sysctl_net_decnet.c @@ -13,7 +13,6 @@ * Steve Whitehouse - Memory buffer settings, like the tcp ones * */ -#include #include #include #include diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c index 8682656..309ae4c 100644 --- a/net/econet/af_econet.c +++ b/net/econet/af_econet.c @@ -9,7 +9,6 @@ * */ -#include #include #include diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index c971f14..387c71c 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -51,7 +51,6 @@ #include #include #include #include -#include #include #include #include diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c index 78b2d13..4926473 100644 --- a/net/ieee80211/ieee80211_crypt_ccmp.c +++ b/net/ieee80211/ieee80211_crypt_ccmp.c @@ -9,7 +9,6 @@ * more details. */ -#include #include #include #include diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c index 93def94..34dba0b 100644 --- a/net/ieee80211/ieee80211_crypt_tkip.c +++ b/net/ieee80211/ieee80211_crypt_tkip.c @@ -9,7 +9,6 @@ * more details. */ -#include #include #include #include @@ -501,8 +500,11 @@ static int michael_mic(struct ieee80211_ static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) { struct ieee80211_hdr_4addr *hdr11; + u16 stype; hdr11 = (struct ieee80211_hdr_4addr *)skb->data; + stype = WLAN_FC_GET_STYPE(le16_to_cpu(hdr11->frame_ctl)); + switch (le16_to_cpu(hdr11->frame_ctl) & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { case IEEE80211_FCTL_TODS: @@ -523,7 +525,13 @@ static void michael_mic_hdr(struct sk_bu break; } - hdr[12] = 0; /* priority */ + if (stype & IEEE80211_STYPE_QOS_DATA) { + const struct ieee80211_hdr_3addrqos *qoshdr = + (struct ieee80211_hdr_3addrqos *)skb->data; + hdr[12] = le16_to_cpu(qoshdr->qos_ctl) & IEEE80211_QCTL_TID; + } else + hdr[12] = 0; /* priority */ + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ } diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c index 649e581..c5a8772 100644 --- a/net/ieee80211/ieee80211_crypt_wep.c +++ b/net/ieee80211/ieee80211_crypt_wep.c @@ -9,7 +9,6 @@ * more details. */ -#include #include #include #include diff --git a/net/ieee80211/ieee80211_geo.c b/net/ieee80211/ieee80211_geo.c index 192243a..305a09d 100644 --- a/net/ieee80211/ieee80211_geo.c +++ b/net/ieee80211/ieee80211_geo.c @@ -24,7 +24,6 @@ ******************************************************************************/ #include -#include #include #include #include diff --git a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c index 2cb84d8..13b1e5f 100644 --- a/net/ieee80211/ieee80211_module.c +++ b/net/ieee80211/ieee80211_module.c @@ -31,7 +31,6 @@ *******************************************************************************/ #include -#include #include #include #include diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index 604b7b0..72d4d4e 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include @@ -405,9 +404,9 @@ #ifdef NOT_YET #endif if (ieee->iw_mode == IW_MODE_MONITOR) { - ieee80211_monitor_rx(ieee, skb, rx_stats); stats->rx_packets++; stats->rx_bytes += skb->len; + ieee80211_monitor_rx(ieee, skb, rx_stats); return 1; } @@ -1692,8 +1691,8 @@ void ieee80211_rx_mgt(struct ieee80211_d WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_WARNING("%s: IEEE80211_REASSOC_REQ received\n", - ieee->dev->name); + IEEE80211_DEBUG_MGMT("%s: IEEE80211_REASSOC_REQ received\n", + ieee->dev->name); if (ieee->handle_reassoc_request != NULL) ieee->handle_reassoc_request(ieee->dev, (struct ieee80211_reassoc_request *) @@ -1705,8 +1704,8 @@ void ieee80211_rx_mgt(struct ieee80211_d WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_WARNING("%s: IEEE80211_ASSOC_REQ received\n", - ieee->dev->name); + IEEE80211_DEBUG_MGMT("%s: IEEE80211_ASSOC_REQ received\n", + ieee->dev->name); if (ieee->handle_assoc_request != NULL) ieee->handle_assoc_request(ieee->dev); break; @@ -1722,10 +1721,10 @@ void ieee80211_rx_mgt(struct ieee80211_d IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_WARNING("%s: Unknown management packet: %d\n", - ieee->dev->name, - WLAN_FC_GET_STYPE(le16_to_cpu - (header->frame_ctl))); + IEEE80211_DEBUG_MGMT("%s: Unknown management packet: %d\n", + ieee->dev->name, + WLAN_FC_GET_STYPE(le16_to_cpu + (header->frame_ctl))); break; } } diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c index 8b4332f..bf04213 100644 --- a/net/ieee80211/ieee80211_tx.c +++ b/net/ieee80211/ieee80211_tx.c @@ -24,7 +24,6 @@ ******************************************************************************/ #include -#include #include #include #include @@ -220,13 +219,43 @@ static struct ieee80211_txb *ieee80211_a return txb; } +static int ieee80211_classify(struct sk_buff *skb) +{ + struct ethhdr *eth; + struct iphdr *ip; + + eth = (struct ethhdr *)skb->data; + if (eth->h_proto != __constant_htons(ETH_P_IP)) + return 0; + + ip = skb->nh.iph; + switch (ip->tos & 0xfc) { + case 0x20: + return 2; + case 0x40: + return 1; + case 0x60: + return 3; + case 0x80: + return 4; + case 0xa0: + return 5; + case 0xc0: + return 6; + case 0xe0: + return 7; + default: + return 0; + } +} + /* Incoming skb is converted to a txb which consists of * a block of 802.11 fragment packets (stored as skbs) */ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) { struct ieee80211_device *ieee = netdev_priv(dev); struct ieee80211_txb *txb = NULL; - struct ieee80211_hdr_3addr *frag_hdr; + struct ieee80211_hdr_3addrqos *frag_hdr; int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size, rts_required; unsigned long flags; @@ -234,9 +263,10 @@ int ieee80211_xmit(struct sk_buff *skb, int ether_type, encrypt, host_encrypt, host_encrypt_msdu, host_build_iv; int bytes, fc, hdr_len; struct sk_buff *skb_frag; - struct ieee80211_hdr_3addr header = { /* Ensure zero initialized */ + struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */ .duration_id = 0, - .seq_ctl = 0 + .seq_ctl = 0, + .qos_ctl = 0 }; u8 dest[ETH_ALEN], src[ETH_ALEN]; struct ieee80211_crypt_data *crypt; @@ -282,12 +312,6 @@ int ieee80211_xmit(struct sk_buff *skb, memcpy(dest, skb->data, ETH_ALEN); memcpy(src, skb->data + ETH_ALEN, ETH_ALEN); - /* Advance the SKB to the start of the payload */ - skb_pull(skb, sizeof(struct ethhdr)); - - /* Determine total amount of storage required for TXB packets */ - bytes = skb->len + SNAP_SIZE + sizeof(u16); - if (host_encrypt || host_build_iv) fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | IEEE80211_FCTL_PROTECTED; @@ -306,9 +330,23 @@ int ieee80211_xmit(struct sk_buff *skb, memcpy(header.addr2, src, ETH_ALEN); memcpy(header.addr3, ieee->bssid, ETH_ALEN); } - header.frame_ctl = cpu_to_le16(fc); hdr_len = IEEE80211_3ADDR_LEN; + if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) { + fc |= IEEE80211_STYPE_QOS_DATA; + hdr_len += 2; + + skb->priority = ieee80211_classify(skb); + header.qos_ctl |= skb->priority & IEEE80211_QCTL_TID; + } + header.frame_ctl = cpu_to_le16(fc); + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + /* Encrypt msdu first on the whole data packet. */ if ((host_encrypt || host_encrypt_msdu) && crypt && crypt->ops && crypt->ops->encrypt_msdu) { @@ -402,7 +440,7 @@ int ieee80211_xmit(struct sk_buff *skb, if (rts_required) { skb_frag = txb->fragments[0]; frag_hdr = - (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len); + (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len); /* * Set header frame_ctl to the RTS. @@ -433,7 +471,7 @@ int ieee80211_xmit(struct sk_buff *skb, crypt->ops->extra_mpdu_prefix_len); frag_hdr = - (struct ieee80211_hdr_3addr *)skb_put(skb_frag, hdr_len); + (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len); memcpy(frag_hdr, &header, hdr_len); /* If this is not the last fragment, then add the MOREFRAGS @@ -516,16 +554,23 @@ int ieee80211_xmit(struct sk_buff *skb, /* Incoming 802.11 strucure is converted to a TXB * a block of 802.11 fragment packets (stored as skbs) */ int ieee80211_tx_frame(struct ieee80211_device *ieee, - struct ieee80211_hdr *frame, int len) + struct ieee80211_hdr *frame, int hdr_len, int total_len, + int encrypt_mpdu) { struct ieee80211_txb *txb = NULL; unsigned long flags; struct net_device_stats *stats = &ieee->stats; struct sk_buff *skb_frag; int priority = -1; + int fraglen = total_len; + int headroom = ieee->tx_headroom; + struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx]; spin_lock_irqsave(&ieee->lock, flags); + if (encrypt_mpdu && (!ieee->sec.encrypt || !crypt)) + encrypt_mpdu = 0; + /* If there is no driver handler to take the TXB, dont' bother * creating it... */ if (!ieee->hard_start_xmit) { @@ -533,32 +578,45 @@ int ieee80211_tx_frame(struct ieee80211_ goto success; } - if (unlikely(len < 24)) { + if (unlikely(total_len < 24)) { printk(KERN_WARNING "%s: skb too small (%d).\n", - ieee->dev->name, len); + ieee->dev->name, total_len); goto success; } + if (encrypt_mpdu) { + frame->frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + fraglen += crypt->ops->extra_mpdu_prefix_len + + crypt->ops->extra_mpdu_postfix_len; + headroom += crypt->ops->extra_mpdu_prefix_len; + } + /* When we allocate the TXB we allocate enough space for the reserve * and full fragment bytes (bytes_per_frag doesn't include prefix, * postfix, header, FCS, etc.) */ - txb = ieee80211_alloc_txb(1, len, ieee->tx_headroom, GFP_ATOMIC); + txb = ieee80211_alloc_txb(1, fraglen, headroom, GFP_ATOMIC); if (unlikely(!txb)) { printk(KERN_WARNING "%s: Could not allocate TXB\n", ieee->dev->name); goto failed; } txb->encrypted = 0; - txb->payload_size = len; + txb->payload_size = fraglen; skb_frag = txb->fragments[0]; - memcpy(skb_put(skb_frag, len), frame, len); + memcpy(skb_put(skb_frag, total_len), frame, total_len); if (ieee->config & (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) skb_put(skb_frag, 4); + /* To avoid overcomplicating things, we do the corner-case frame + * encryption in software. The only real situation where encryption is + * needed here is during software-based shared key authentication. */ + if (encrypt_mpdu) + ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); + success: spin_unlock_irqrestore(&ieee->lock, flags); diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c index b885fd1..a78c4f8 100644 --- a/net/ieee80211/ieee80211_wx.c +++ b/net/ieee80211/ieee80211_wx.c @@ -50,7 +50,8 @@ static char *ieee80211_translate_scan(st char *p; struct iw_event iwe; int i, j; - u8 max_rate, rate; + char *current_val; /* For rates */ + u8 rate; /* First entry *MUST* be the AP MAC address */ iwe.cmd = SIOCGIWAP; @@ -107,9 +108,13 @@ static char *ieee80211_translate_scan(st start = iwe_stream_add_point(start, stop, &iwe, network->ssid); /* Add basic and extended rates */ - max_rate = 0; - p = custom; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); + /* Rate : stuffing multiple values in a single event require a bit + * more of magic - Jean II */ + current_val = start + IW_EV_LCP_LEN; + iwe.cmd = SIOCGIWRATE; + /* Those two flags are ignored... */ + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + for (i = 0, j = 0; i < network->rates_len;) { if (j < network->rates_ex_len && ((network->rates_ex[j] & 0x7F) < @@ -117,28 +122,21 @@ static char *ieee80211_translate_scan(st rate = network->rates_ex[j++] & 0x7F; else rate = network->rates[i++] & 0x7F; - if (rate > max_rate) - max_rate = rate; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), - "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((rate & 0x7f) * 500000); + /* Add new value to event */ + current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); } for (; j < network->rates_ex_len; j++) { rate = network->rates_ex[j] & 0x7F; - p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), - "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); - if (rate > max_rate) - max_rate = rate; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((rate & 0x7f) * 500000); + /* Add new value to event */ + current_val = iwe_stream_add_value(start, current_val, stop, &iwe, IW_EV_PARAM_LEN); } - - iwe.cmd = SIOCGIWRATE; - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - iwe.u.bitrate.value = max_rate * 500000; - start = iwe_stream_add_event(start, stop, &iwe, IW_EV_PARAM_LEN); - - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = p - custom; - if (iwe.u.data.length) - start = iwe_stream_add_point(start, stop, &iwe, custom); + /* Check if we added any rate */ + if((current_val - start) > IW_EV_LCP_LEN) + start = current_val; /* Add quality statistics */ iwe.cmd = IWEVQUAL; @@ -505,7 +503,7 @@ int ieee80211_wx_get_encode(struct ieee8 len = sec->key_sizes[key]; memcpy(keybuf, sec->keys[key], len); - erq->length = (len >= 0 ? len : 0); + erq->length = len; erq->flags |= IW_ENCODE_ENABLED; if (ieee->open_wep) diff --git a/net/ieee80211/softmac/Kconfig b/net/ieee80211/softmac/Kconfig index f2a27cc..2811651 100644 --- a/net/ieee80211/softmac/Kconfig +++ b/net/ieee80211/softmac/Kconfig @@ -2,6 +2,7 @@ config IEEE80211_SOFTMAC tristate "Software MAC add-on to the IEEE 802.11 networking stack" depends on IEEE80211 && EXPERIMENTAL select WIRELESS_EXT + select IEEE80211_CRYPT_WEP ---help--- This option enables the hardware independent software MAC addon for the IEEE 802.11 networking stack. diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index 57ea9f6..44215ce 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -47,9 +47,7 @@ ieee80211softmac_assoc(struct ieee80211s dprintk(KERN_INFO PFX "sent association request!\n"); - /* Change the state to associating */ spin_lock_irqsave(&mac->lock, flags); - mac->associnfo.associating = 1; mac->associated = 0; /* just to make sure */ /* Set a timer for timeout */ @@ -63,6 +61,7 @@ void ieee80211softmac_assoc_timeout(void *d) { struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; + struct ieee80211softmac_network *n; unsigned long flags; spin_lock_irqsave(&mac->lock, flags); @@ -75,58 +74,60 @@ ieee80211softmac_assoc_timeout(void *d) mac->associnfo.associating = 0; mac->associnfo.bssvalid = 0; mac->associated = 0; + + n = ieee80211softmac_get_network_by_bssid_locked(mac, mac->associnfo.bssid); spin_unlock_irqrestore(&mac->lock, flags); dprintk(KERN_INFO PFX "assoc request timed out!\n"); - /* FIXME: we need to know the network here. that requires a bit of restructuring */ - ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL); + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, n); } -/* Sends out a disassociation request to the desired AP */ -static void -ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason) +void +ieee80211softmac_disassoc(struct ieee80211softmac_device *mac) { unsigned long flags; + + spin_lock_irqsave(&mac->lock, flags); + if (mac->associnfo.associating) + cancel_delayed_work(&mac->associnfo.timeout); + + netif_carrier_off(mac->dev); + + mac->associated = 0; + mac->associnfo.bssvalid = 0; + mac->associnfo.associating = 0; + ieee80211softmac_init_txrates(mac); + ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_DISASSOCIATED, NULL); + spin_unlock_irqrestore(&mac->lock, flags); +} + +/* Sends out a disassociation request to the desired AP */ +void +ieee80211softmac_send_disassoc_req(struct ieee80211softmac_device *mac, u16 reason) +{ struct ieee80211softmac_network *found; if (mac->associnfo.bssvalid && mac->associated) { found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); if (found) ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason); - } else if (mac->associnfo.associating) { - cancel_delayed_work(&mac->associnfo.timeout); } - /* Change our state */ - spin_lock_irqsave(&mac->lock, flags); - /* Do NOT clear bssvalid as that will break ieee80211softmac_assoc_work! */ - mac->associated = 0; - mac->associnfo.associating = 0; - ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_DISASSOCIATED, NULL); - spin_unlock_irqrestore(&mac->lock, flags); + ieee80211softmac_disassoc(mac); } static inline int we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len) { - int idx, search, found; - u8 rate, search_rate; + int idx; + u8 rate; for (idx = 0; idx < (from_len); idx++) { rate = (from)[idx]; if (!(rate & IEEE80211_BASIC_RATE_MASK)) continue; - found = 0; rate &= ~IEEE80211_BASIC_RATE_MASK; - for (search = 0; search < mac->ratesinfo.count; search++) { - search_rate = mac->ratesinfo.rates[search]; - search_rate &= ~IEEE80211_BASIC_RATE_MASK; - if (rate == search_rate) { - found = 1; - break; - } - } - if (!found) + if (!ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate)) return 0; } return 1; @@ -163,12 +164,28 @@ network_matches_request(struct ieee80211 } static void -ieee80211softmac_assoc_notify(struct net_device *dev, void *context) +ieee80211softmac_assoc_notify_scan(struct net_device *dev, int event_type, void *context) { struct ieee80211softmac_device *mac = ieee80211_priv(dev); ieee80211softmac_assoc_work((void*)mac); } +static void +ieee80211softmac_assoc_notify_auth(struct net_device *dev, int event_type, void *context) +{ + struct ieee80211softmac_device *mac = ieee80211_priv(dev); + + switch (event_type) { + case IEEE80211SOFTMAC_EVENT_AUTHENTICATED: + ieee80211softmac_assoc_work((void*)mac); + break; + case IEEE80211SOFTMAC_EVENT_AUTH_FAILED: + case IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT: + ieee80211softmac_disassoc(mac); + break; + } +} + /* This function is called to handle userspace requests (asynchronously) */ void ieee80211softmac_assoc_work(void *d) @@ -176,14 +193,22 @@ ieee80211softmac_assoc_work(void *d) struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; struct ieee80211softmac_network *found = NULL; struct ieee80211_network *net = NULL, *best = NULL; + int bssvalid; unsigned long flags; - + + /* ieee80211_disassoc might clear this */ + bssvalid = mac->associnfo.bssvalid; + /* meh */ if (mac->associated) - ieee80211softmac_disassoc(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT); + ieee80211softmac_send_disassoc_req(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT); + + spin_lock_irqsave(&mac->lock, flags); + mac->associnfo.associating = 1; + spin_unlock_irqrestore(&mac->lock, flags); /* try to find the requested network in our list, if we found one already */ - if (mac->associnfo.bssvalid || mac->associnfo.bssfixed) + if (bssvalid || mac->associnfo.bssfixed) found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); /* Search the ieee80211 networks for this network if we didn't find it by bssid, @@ -244,7 +269,7 @@ ieee80211softmac_assoc_work(void *d) * Maybe we can hope to have more memory after scanning finishes ;) */ dprintk(KERN_INFO PFX "Associate: Scanning for networks first.\n"); - ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify, NULL); + ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify_scan, NULL); if (ieee80211softmac_start_scan(mac)) dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n"); return; @@ -274,19 +299,32 @@ ieee80211softmac_assoc_work(void *d) memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1); /* we found a network! authenticate (if necessary) and associate to it. */ - if (!found->authenticated) { + if (found->authenticating) { + dprintk(KERN_INFO PFX "Already requested authentication, waiting...\n"); + if(!mac->associnfo.assoc_wait) { + mac->associnfo.assoc_wait = 1; + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL); + } + return; + } + if (!found->authenticated && !found->authenticating) { /* This relies on the fact that _auth_req only queues the work, * otherwise adding the notification would be racy. */ if (!ieee80211softmac_auth_req(mac, found)) { - dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n"); - ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); + if(!mac->associnfo.assoc_wait) { + dprintk(KERN_INFO PFX "Cannot associate without being authenticated, requested authentication\n"); + mac->associnfo.assoc_wait = 1; + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL); + } } else { printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n"); + mac->associnfo.assoc_wait = 0; ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found); } return; } /* finally! now we can start associating */ + mac->associnfo.assoc_wait = 0; ieee80211softmac_assoc(mac, found); } @@ -297,6 +335,9 @@ ieee80211softmac_associated(struct ieee8 struct ieee80211softmac_network *net) { mac->associnfo.associating = 0; + mac->associnfo.supported_rates = net->supported_rates; + ieee80211softmac_recalc_txrates(mac); + mac->associated = 1; if (mac->set_bssid_filter) mac->set_bssid_filter(mac->dev, net->bssid); @@ -380,7 +421,6 @@ ieee80211softmac_handle_disassoc(struct struct ieee80211_disassoc *disassoc) { struct ieee80211softmac_device *mac = ieee80211_priv(dev); - unsigned long flags; if (unlikely(!mac->running)) return -ENODEV; @@ -392,14 +432,11 @@ ieee80211softmac_handle_disassoc(struct return 0; dprintk(KERN_INFO PFX "got disassoc frame\n"); - netif_carrier_off(dev); - spin_lock_irqsave(&mac->lock, flags); - mac->associnfo.bssvalid = 0; - mac->associated = 0; - ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_DISASSOCIATED, NULL); + ieee80211softmac_disassoc(mac); + + /* try to reassociate */ schedule_work(&mac->associnfo.work); - spin_unlock_irqrestore(&mac->lock, flags); - + return 0; } diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c index 06e3326..ebc33ca 100644 --- a/net/ieee80211/softmac/ieee80211softmac_auth.c +++ b/net/ieee80211/softmac/ieee80211softmac_auth.c @@ -36,8 +36,9 @@ ieee80211softmac_auth_req(struct ieee802 struct ieee80211softmac_auth_queue_item *auth; unsigned long flags; - if (net->authenticating) + if (net->authenticating || net->authenticated) return 0; + net->authenticating = 1; /* Add the network if it's not already added */ ieee80211softmac_add_network(mac, net); @@ -92,7 +93,6 @@ ieee80211softmac_auth_queue(void *data) return; } net->authenticated = 0; - net->authenticating = 1; /* add a timeout call so we eventually give up waiting for an auth reply */ schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT); auth->retry--; @@ -107,6 +107,7 @@ ieee80211softmac_auth_queue(void *data) printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid)); /* Remove this item from the queue */ spin_lock_irqsave(&mac->lock, flags); + net->authenticating = 0; ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net); cancel_delayed_work(&auth->work); /* just to make sure... */ list_del(&auth->list); @@ -212,13 +213,13 @@ ieee80211softmac_auth_resp(struct net_de aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE; spin_unlock_irqrestore(&mac->lock, flags); - /* Switch to correct channel for this network */ - mac->set_channel(mac->dev, net->channel); - - /* Send our response (How to encrypt?) */ + /* Send our response */ ieee80211softmac_send_mgt_frame(mac, aq->net, IEEE80211_STYPE_AUTH, aq->state); - break; + return 0; case IEEE80211SOFTMAC_AUTH_SHARED_PASS: + kfree(net->challenge); + net->challenge = NULL; + net->challenge_len = 0; /* Check the status code of the response */ switch(auth->status) { case WLAN_STATUS_SUCCESS: @@ -229,6 +230,7 @@ ieee80211softmac_auth_resp(struct net_de spin_unlock_irqrestore(&mac->lock, flags); printkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid)); + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net); break; default: printkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n", @@ -279,6 +281,9 @@ ieee80211softmac_deauth_from_net(struct struct list_head *list_ptr; unsigned long flags; + /* deauthentication implies disassociation */ + ieee80211softmac_disassoc(mac); + /* Lock and reset status flags */ spin_lock_irqsave(&mac->lock, flags); net->authenticating = 0; diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c index 8cc8f3f..f34fa2e 100644 --- a/net/ieee80211/softmac/ieee80211softmac_event.c +++ b/net/ieee80211/softmac/ieee80211softmac_event.c @@ -38,7 +38,8 @@ #include "ieee80211softmac_priv.h" * The event context is private and can only be used from * within this module. Its meaning varies with the event * type: - * SCAN_FINISHED: no special meaning + * SCAN_FINISHED, + * DISASSOCIATED: NULL * ASSOCIATED, * ASSOCIATE_FAILED, * ASSOCIATE_TIMEOUT, @@ -59,15 +60,15 @@ #include "ieee80211softmac_priv.h" */ static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = { - "scan finished", - "associated", + NULL, /* scan finished */ + NULL, /* associated */ "associating failed", "associating timed out", "authenticated", "authenticating failed", "authenticating timed out", "associating failed because no suitable network was found", - "disassociated", + NULL, /* disassociated */ }; @@ -77,7 +78,7 @@ ieee80211softmac_notify_callback(void *d struct ieee80211softmac_event event = *(struct ieee80211softmac_event*) d; kfree(d); - event.fun(event.mac->dev, event.context); + event.fun(event.mac->dev, event.event_type, event.context); } int @@ -136,30 +137,24 @@ ieee80211softmac_call_events_locked(stru int we_event; char *msg = NULL; + memset(&wrqu, '\0', sizeof (union iwreq_data)); + switch(event) { case IEEE80211SOFTMAC_EVENT_ASSOCIATED: network = (struct ieee80211softmac_network *)event_ctx; - wrqu.data.length = 0; - wrqu.data.flags = 0; memcpy(wrqu.ap_addr.sa_data, &network->bssid[0], ETH_ALEN); - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - we_event = SIOCGIWAP; - break; + /* fall through */ case IEEE80211SOFTMAC_EVENT_DISASSOCIATED: - wrqu.data.length = 0; - wrqu.data.flags = 0; - memset(&wrqu, '\0', sizeof (union iwreq_data)); wrqu.ap_addr.sa_family = ARPHRD_ETHER; we_event = SIOCGIWAP; break; case IEEE80211SOFTMAC_EVENT_SCAN_FINISHED: - wrqu.data.length = 0; - wrqu.data.flags = 0; - memset(&wrqu, '\0', sizeof (union iwreq_data)); we_event = SIOCGIWSCAN; break; default: msg = event_descriptions[event]; + if (!msg) + msg = "SOFTMAC EVENT BUG"; wrqu.data.length = strlen(msg); we_event = IWEVCUSTOM; break; @@ -172,6 +167,9 @@ ieee80211softmac_call_events_locked(stru if ((eventptr->event_type == event || eventptr->event_type == -1) && (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) { list_del(&eventptr->list); + /* User may have subscribed to ANY event, so + * we tell them which event triggered it. */ + eventptr->event_type = event; schedule_work(&eventptr->work); } } diff --git a/net/ieee80211/softmac/ieee80211softmac_io.c b/net/ieee80211/softmac/ieee80211softmac_io.c index cc6cd56..8cc8b20 100644 --- a/net/ieee80211/softmac/ieee80211softmac_io.c +++ b/net/ieee80211/softmac/ieee80211softmac_io.c @@ -149,6 +149,56 @@ ieee80211softmac_hdr_3addr(struct ieee80 * shouldn't the sequence number be in ieee80211? */ } +static u16 +ieee80211softmac_capabilities(struct ieee80211softmac_device *mac, + struct ieee80211softmac_network *net) +{ + u16 capability = 0; + + /* ESS and IBSS bits are set according to the current mode */ + switch (mac->ieee->iw_mode) { + case IW_MODE_INFRA: + capability = cpu_to_le16(WLAN_CAPABILITY_ESS); + break; + case IW_MODE_ADHOC: + capability = cpu_to_le16(WLAN_CAPABILITY_IBSS); + break; + case IW_MODE_AUTO: + capability = net->capabilities & + (WLAN_CAPABILITY_ESS|WLAN_CAPABILITY_IBSS); + break; + default: + /* bleh. we don't ever go to these modes */ + printk(KERN_ERR PFX "invalid iw_mode!\n"); + break; + } + + /* CF Pollable / CF Poll Request */ + /* Needs to be implemented, for now, the 0's == not supported */ + + /* Privacy Bit */ + capability |= mac->ieee->sec.level ? + cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; + + /* Short Preamble */ + /* Always supported: we probably won't ever be powering devices which + * dont support this... */ + capability |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + /* PBCC */ + /* Not widely used */ + + /* Channel Agility */ + /* Not widely used */ + + /* Short Slot */ + /* Will be implemented later */ + + /* DSSS-OFDM */ + /* Not widely used */ + + return capability; +} /***************************************************************************** * Create Management packets @@ -179,27 +229,9 @@ ieee80211softmac_assoc_req(struct ieee80 return 0; ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid); - /* Fill in capability Info */ - switch (mac->ieee->iw_mode) { - case IW_MODE_INFRA: - (*pkt)->capability = cpu_to_le16(WLAN_CAPABILITY_ESS); - break; - case IW_MODE_ADHOC: - (*pkt)->capability = cpu_to_le16(WLAN_CAPABILITY_IBSS); - break; - case IW_MODE_AUTO: - (*pkt)->capability = net->capabilities & (WLAN_CAPABILITY_ESS|WLAN_CAPABILITY_IBSS); - break; - default: - /* bleh. we don't ever go to these modes */ - printk(KERN_ERR PFX "invalid iw_mode!\n"); - break; - } - /* Need to add this - (*pkt)->capability |= mac->ieee->short_slot ? - cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0; - */ - (*pkt)->capability |= mac->ieee->sec.level ? cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; + /* Fill in the capabilities */ + (*pkt)->capability = ieee80211softmac_capabilities(mac, net); + /* Fill in Listen Interval (?) */ (*pkt)->listen_interval = cpu_to_le16(10); @@ -239,17 +271,9 @@ ieee80211softmac_reassoc_req(struct ieee return 0; ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_REASSOC_REQ, net->bssid, net->bssid); - /* Fill in capability Info */ - (*pkt)->capability = mac->ieee->iw_mode == IW_MODE_MASTER ? - cpu_to_le16(WLAN_CAPABILITY_ESS) : - cpu_to_le16(WLAN_CAPABILITY_IBSS); - /* - (*pkt)->capability |= mac->ieee->short_slot ? - cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME) : 0; - */ - (*pkt)->capability |= mac->ieee->sec.level ? - cpu_to_le16(WLAN_CAPABILITY_PRIVACY) : 0; - + /* Fill in the capabilities */ + (*pkt)->capability = ieee80211softmac_capabilities(mac, net); + /* Fill in Listen Interval (?) */ (*pkt)->listen_interval = cpu_to_le16(10); /* Fill in the current AP MAC */ @@ -268,26 +292,27 @@ ieee80211softmac_reassoc_req(struct ieee static u32 ieee80211softmac_auth(struct ieee80211_auth **pkt, struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, - u16 transaction, u16 status) + u16 transaction, u16 status, int *encrypt_mpdu) { u8 *data; + int auth_mode = mac->ieee->sec.auth_mode; + int is_shared_response = (auth_mode == WLAN_AUTH_SHARED_KEY + && transaction == IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE); + /* Allocate Packet */ (*pkt) = (struct ieee80211_auth *)ieee80211softmac_alloc_mgt( 2 + /* Auth Algorithm */ 2 + /* Auth Transaction Seq */ 2 + /* Status Code */ /* Challenge Text IE */ - mac->ieee->open_wep ? 0 : - 1 + 1 + WLAN_AUTH_CHALLENGE_LEN - ); + is_shared_response ? 0 : 1 + 1 + net->challenge_len + ); if (unlikely((*pkt) == NULL)) return 0; ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_AUTH, net->bssid, net->bssid); /* Algorithm */ - (*pkt)->algorithm = mac->ieee->open_wep ? - cpu_to_le16(WLAN_AUTH_OPEN) : - cpu_to_le16(WLAN_AUTH_SHARED_KEY); + (*pkt)->algorithm = cpu_to_le16(auth_mode); /* Transaction */ (*pkt)->transaction = cpu_to_le16(transaction); /* Status */ @@ -295,18 +320,20 @@ ieee80211softmac_auth(struct ieee80211_a data = (u8 *)(*pkt)->info_element; /* Challenge Text */ - if(!mac->ieee->open_wep){ + if (is_shared_response) { *data = MFIE_TYPE_CHALLENGE; data++; /* Copy the challenge in */ - // *data = challenge length - // data += sizeof(u16); - // memcpy(data, challenge, challenge length); - // data += challenge length; - - /* Add the full size to the packet length */ - } + *data = net->challenge_len; + data++; + memcpy(data, net->challenge, net->challenge_len); + data += net->challenge_len; + + /* Make sure this frame gets encrypted with the shared key */ + *encrypt_mpdu = 1; + } else + *encrypt_mpdu = 0; /* Return the packet size */ return (data - (u8 *)(*pkt)); @@ -396,6 +423,7 @@ ieee80211softmac_send_mgt_frame(struct i { void *pkt = NULL; u32 pkt_size = 0; + int encrypt_mpdu = 0; switch(type) { case IEEE80211_STYPE_ASSOC_REQ: @@ -405,7 +433,7 @@ ieee80211softmac_send_mgt_frame(struct i pkt_size = ieee80211softmac_reassoc_req((struct ieee80211_reassoc_request **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg); break; case IEEE80211_STYPE_AUTH: - pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16)); + pkt_size = ieee80211softmac_auth((struct ieee80211_auth **)(&pkt), mac, (struct ieee80211softmac_network *)ptrarg, (u16)(arg & 0xFFFF), (u16) (arg >> 16), &encrypt_mpdu); break; case IEEE80211_STYPE_DISASSOC: case IEEE80211_STYPE_DEAUTH: @@ -434,52 +462,8 @@ ieee80211softmac_send_mgt_frame(struct i * or get rid of it alltogether? * Does this work for you now? */ - ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt, pkt_size); - - kfree(pkt); - return 0; -} - - -/* Create an rts/cts frame */ -static u32 -ieee80211softmac_rts_cts(struct ieee80211_hdr_2addr **pkt, - struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net, - u32 type) -{ - /* Allocate Packet */ - (*pkt) = kmalloc(IEEE80211_2ADDR_LEN, GFP_ATOMIC); - memset(*pkt, 0, IEEE80211_2ADDR_LEN); - if((*pkt) == NULL) - return 0; - ieee80211softmac_hdr_2addr(mac, (*pkt), type, net->bssid); - return IEEE80211_2ADDR_LEN; -} - - -/* Sends a control packet */ -static int -ieee80211softmac_send_ctl_frame(struct ieee80211softmac_device *mac, - struct ieee80211softmac_network *net, u32 type, u32 arg) -{ - void *pkt = NULL; - u32 pkt_size = 0; - - switch(type) { - case IEEE80211_STYPE_RTS: - case IEEE80211_STYPE_CTS: - pkt_size = ieee80211softmac_rts_cts((struct ieee80211_hdr_2addr **)(&pkt), mac, net, type); - break; - default: - printkl(KERN_DEBUG PFX "Unsupported Control Frame type: %i\n", type); - return -EINVAL; - } - - if(pkt_size == 0) - return -ENOMEM; - - /* Send the packet to the ieee80211 layer for tx */ - ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *) pkt, pkt_size); + ieee80211_tx_frame(mac->ieee, (struct ieee80211_hdr *)pkt, + IEEE80211_3ADDR_LEN, pkt_size, encrypt_mpdu); kfree(pkt); return 0; diff --git a/net/ieee80211/softmac/ieee80211softmac_module.c b/net/ieee80211/softmac/ieee80211softmac_module.c index 6252be2..4b2e57d 100644 --- a/net/ieee80211/softmac/ieee80211softmac_module.c +++ b/net/ieee80211/softmac/ieee80211softmac_module.c @@ -26,6 +26,7 @@ #include "ieee80211softmac_priv.h" #include +#include struct net_device *alloc_ieee80211softmac(int sizeof_priv) { @@ -61,14 +62,6 @@ struct net_device *alloc_ieee80211softma softmac->wait_for_scan = ieee80211softmac_wait_for_scan_implementation; softmac->stop_scan = ieee80211softmac_stop_scan_implementation; - //TODO: The mcast rate has to be assigned dynamically somewhere (in scanning, association. Not sure...) - // It has to be set to the highest rate all stations in the current network can handle. - softmac->txrates.mcast_rate = IEEE80211_CCK_RATE_1MB; - softmac->txrates.mcast_fallback = IEEE80211_CCK_RATE_1MB; - /* This is reassigned in ieee80211softmac_start to sane values. */ - softmac->txrates.default_rate = IEEE80211_CCK_RATE_1MB; - softmac->txrates.default_fallback = IEEE80211_CCK_RATE_1MB; - /* to start with, we can't send anything ... */ netif_carrier_off(dev); @@ -170,15 +163,82 @@ static void ieee80211softmac_start_check } } -void ieee80211softmac_start(struct net_device *dev) +int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate) +{ + int search; + u8 search_rate; + + for (search = 0; search < ri->count; search++) { + search_rate = ri->rates[search]; + search_rate &= ~IEEE80211_BASIC_RATE_MASK; + if (rate == search_rate) + return 1; + } + + return 0; +} + +/* Finds the highest rate which is: + * 1. Present in ri (optionally a basic rate) + * 2. Supported by the device + * 3. Less than or equal to the user-defined rate + */ +static u8 highest_supported_rate(struct ieee80211softmac_device *mac, + struct ieee80211softmac_ratesinfo *ri, int basic_only) +{ + u8 user_rate = mac->txrates.user_rate; + int i; + + if (ri->count == 0) { + dprintk(KERN_ERR PFX "empty ratesinfo?\n"); + return IEEE80211_CCK_RATE_1MB; + } + + for (i = ri->count - 1; i >= 0; i--) { + u8 rate = ri->rates[i]; + if (basic_only && !(rate & IEEE80211_BASIC_RATE_MASK)) + continue; + rate &= ~IEEE80211_BASIC_RATE_MASK; + if (rate > user_rate) + continue; + if (ieee80211softmac_ratesinfo_rate_supported(&mac->ratesinfo, rate)) + return rate; + } + + /* If we haven't found a suitable rate by now, just trust the user */ + return user_rate; +} + +void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac) +{ + struct ieee80211softmac_txrates *txrates = &mac->txrates; + struct ieee80211softmac_txrates oldrates; + u32 change = 0; + + if (mac->txrates_change) + oldrates = mac->txrates; + + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; + txrates->default_rate = highest_supported_rate(mac, &mac->associnfo.supported_rates, 0); + + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; + txrates->default_fallback = lower_rate(mac, txrates->default_rate); + + change |= IEEE80211SOFTMAC_TXRATECHG_MCAST; + txrates->mcast_rate = highest_supported_rate(mac, &mac->associnfo.supported_rates, 1); + + if (mac->txrates_change) + mac->txrates_change(mac->dev, change, &oldrates); + +} + +void ieee80211softmac_init_txrates(struct ieee80211softmac_device *mac) { - struct ieee80211softmac_device *mac = ieee80211_priv(dev); struct ieee80211_device *ieee = mac->ieee; u32 change = 0; + struct ieee80211softmac_txrates *txrates = &mac->txrates; struct ieee80211softmac_txrates oldrates; - ieee80211softmac_start_check_rates(mac); - /* TODO: We need some kind of state machine to lower the default rates * if we loose too many packets. */ @@ -193,22 +253,37 @@ void ieee80211softmac_start(struct net_d more reliable. Note similar logic in ieee80211softmac_wx_set_rate() */ if (ieee->modulation & IEEE80211_CCK_MODULATION) { - mac->txrates.default_rate = IEEE80211_CCK_RATE_11MB; - change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; - mac->txrates.default_fallback = IEEE80211_CCK_RATE_5MB; - change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; + txrates->user_rate = IEEE80211_CCK_RATE_11MB; } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) { - mac->txrates.default_rate = IEEE80211_OFDM_RATE_54MB; - change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; - mac->txrates.default_fallback = IEEE80211_OFDM_RATE_24MB; - change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; + txrates->user_rate = IEEE80211_OFDM_RATE_54MB; } else assert(0); + + txrates->default_rate = IEEE80211_CCK_RATE_1MB; + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT; + + txrates->default_fallback = IEEE80211_CCK_RATE_1MB; + change |= IEEE80211SOFTMAC_TXRATECHG_DEFAULT_FBACK; + + txrates->mcast_rate = IEEE80211_CCK_RATE_1MB; + change |= IEEE80211SOFTMAC_TXRATECHG_MCAST; + + txrates->mgt_mcast_rate = IEEE80211_CCK_RATE_1MB; + change |= IEEE80211SOFTMAC_TXRATECHG_MGT_MCAST; + if (mac->txrates_change) - mac->txrates_change(dev, change, &oldrates); + mac->txrates_change(mac->dev, change, &oldrates); mac->running = 1; } + +void ieee80211softmac_start(struct net_device *dev) +{ + struct ieee80211softmac_device *mac = ieee80211_priv(dev); + + ieee80211softmac_start_check_rates(mac); + ieee80211softmac_init_txrates(mac); +} EXPORT_SYMBOL_GPL(ieee80211softmac_start); void ieee80211softmac_stop(struct net_device *dev) diff --git a/net/ieee80211/softmac/ieee80211softmac_priv.h b/net/ieee80211/softmac/ieee80211softmac_priv.h index 65d9816..fa1f8e3 100644 --- a/net/ieee80211/softmac/ieee80211softmac_priv.h +++ b/net/ieee80211/softmac/ieee80211softmac_priv.h @@ -116,7 +116,10 @@ ieee80211softmac_get_network_by_essid(st struct ieee80211softmac_essid *essid); /* Rates related */ +int ieee80211softmac_ratesinfo_rate_supported(struct ieee80211softmac_ratesinfo *ri, u8 rate); u8 ieee80211softmac_lower_rate_delta(struct ieee80211softmac_device *mac, u8 rate, int delta); +void ieee80211softmac_init_txrates(struct ieee80211softmac_device *mac); +void ieee80211softmac_recalc_txrates(struct ieee80211softmac_device *mac); static inline u8 lower_rate(struct ieee80211softmac_device *mac, u8 rate) { return ieee80211softmac_lower_rate_delta(mac, rate, 1); } @@ -150,6 +153,8 @@ int ieee80211softmac_handle_disassoc(str int ieee80211softmac_handle_reassoc_req(struct net_device * dev, struct ieee80211_reassoc_request * reassoc); void ieee80211softmac_assoc_timeout(void *d); +void ieee80211softmac_send_disassoc_req(struct ieee80211softmac_device *mac, u16 reason); +void ieee80211softmac_disassoc(struct ieee80211softmac_device *mac); /* some helper functions */ static inline int ieee80211softmac_scan_handlers_check_self(struct ieee80211softmac_device *sm) diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c index 27edb2b..75320b6 100644 --- a/net/ieee80211/softmac/ieee80211softmac_wx.c +++ b/net/ieee80211/softmac/ieee80211softmac_wx.c @@ -70,12 +70,44 @@ ieee80211softmac_wx_set_essid(struct net char *extra) { struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); + struct ieee80211softmac_network *n; + struct ieee80211softmac_auth_queue_item *authptr; int length = 0; unsigned long flags; - + + /* Check if we're already associating to this or another network + * If it's another network, cancel and start over with our new network + * If it's our network, ignore the change, we're already doing it! + */ + if((sm->associnfo.associating || sm->associated) && + (data->essid.flags && data->essid.length && extra)) { + /* Get the associating network */ + n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid); + if(n && n->essid.len == (data->essid.length - 1) && + !memcmp(n->essid.data, extra, n->essid.len)) { + dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n", + MAC_ARG(sm->associnfo.bssid)); + return 0; + } else { + dprintk(KERN_INFO PFX "Canceling existing associate request!\n"); + spin_lock_irqsave(&sm->lock,flags); + /* Cancel assoc work */ + cancel_delayed_work(&sm->associnfo.work); + /* We don't have to do this, but it's a little cleaner */ + list_for_each_entry(authptr, &sm->auth_queue, list) + cancel_delayed_work(&authptr->work); + sm->associnfo.bssvalid = 0; + sm->associnfo.bssfixed = 0; + spin_unlock_irqrestore(&sm->lock,flags); + flush_scheduled_work(); + } + } + + spin_lock_irqsave(&sm->lock, flags); - + sm->associnfo.static_essid = 0; + sm->associnfo.assoc_wait = 0; if (data->essid.flags && data->essid.length && extra /*required?*/) { length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE); @@ -211,8 +243,8 @@ ieee80211softmac_wx_set_rate(struct net_ if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION)) goto out_unlock; - mac->txrates.default_rate = rate; - mac->txrates.default_fallback = lower_rate(mac, rate); + mac->txrates.user_rate = rate; + ieee80211softmac_recalc_txrates(mac); err = 0; out_unlock: @@ -388,7 +420,7 @@ ieee80211softmac_wx_set_genie(struct net memcpy(mac->wpa.IE, extra, wrqu->data.length); dprintk(KERN_INFO PFX "generic IE set to "); for (i=0;idata.length;i++) - dprintk("%.2x", mac->wpa.IE[i]); + dprintk("%.2x", (u8)mac->wpa.IE[i]); dprintk("\n"); mac->wpa.IElen = wrqu->data.length; } else { @@ -431,3 +463,35 @@ ieee80211softmac_wx_get_genie(struct net } EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie); +int +ieee80211softmac_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct ieee80211softmac_device *mac = ieee80211_priv(dev); + struct iw_mlme *mlme = (struct iw_mlme *)extra; + u16 reason = cpu_to_le16(mlme->reason_code); + struct ieee80211softmac_network *net; + + if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) { + printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n"); + return -EINVAL; + } + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data); + if (!net) { + printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n"); + return -EINVAL; + } + return ieee80211softmac_deauth_req(mac, net, reason); + case IW_MLME_DISASSOC: + ieee80211softmac_send_disassoc_req(mac, reason); + return 0; + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme); diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index e40f753..da33393 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -414,6 +414,24 @@ config INET_TUNNEL tristate default n +config INET_XFRM_MODE_TRANSPORT + tristate "IP: IPsec transport mode" + default y + select XFRM + ---help--- + Support for IPsec transport mode. + + If unsure, say Y. + +config INET_XFRM_MODE_TUNNEL + tristate "IP: IPsec tunnel mode" + default y + select XFRM + ---help--- + Support for IPsec tunnel mode. + + If unsure, say Y. + config INET_DIAG tristate "INET: socket monitoring interface" default y @@ -532,6 +550,38 @@ config TCP_CONG_SCALABLE properties, though is known to have fairness issues. See http://www-lce.eng.cam.ac.uk/~ctk21/scalable/ +config TCP_CONG_LP + tristate "TCP Low Priority" + depends on EXPERIMENTAL + default n + ---help--- + TCP Low Priority (TCP-LP), a distributed algorithm whose goal is + to utiliza only the excess network bandwidth as compared to the + ``fair share`` of bandwidth as targeted by TCP. + See http://www-ece.rice.edu/networks/TCP-LP/ + +config TCP_CONG_VENO + tristate "TCP Veno" + depends on EXPERIMENTAL + default n + ---help--- + TCP Veno is a sender-side only enhancement of TCP to obtain better + throughput over wireless networks. TCP Veno makes use of state + distinguishing to circumvent the difficult judgment of the packet loss + type. TCP Veno cuts down less congestion window in response to random + loss packets. + See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf + +config TCP_CONG_COMPOUND + tristate "TCP Compound" + depends on EXPERIMENTAL + default n + ---help--- + TCP Compound is a sender-side only change to TCP that uses + a mixed Reno/Vegas approach to calculate the cwnd. + For further details look here: + ftp://ftp.research.microsoft.com/pub/tr/TR-2005-86.pdf + endmenu config TCP_CONG_BIC diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 9ef50a0..38b8039 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -24,6 +24,8 @@ obj-$(CONFIG_INET_ESP) += esp4.o obj-$(CONFIG_INET_IPCOMP) += ipcomp.o obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o obj-$(CONFIG_INET_TUNNEL) += tunnel4.o +obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o +obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o obj-$(CONFIG_IP_PNP) += ipconfig.o obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o obj-$(CONFIG_IP_ROUTE_MULTIPATH_RANDOM) += multipath_random.o @@ -34,6 +36,7 @@ obj-$(CONFIG_IP_VS) += ipvs/ obj-$(CONFIG_INET_DIAG) += inet_diag.o obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o +obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o @@ -41,7 +44,10 @@ obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_high obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o obj-$(CONFIG_TCP_CONG_HTCP) += tcp_htcp.o obj-$(CONFIG_TCP_CONG_VEGAS) += tcp_vegas.o +obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o +obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o +obj-$(CONFIG_TCP_CONG_COMPOUND) += tcp_compound.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ xfrm4_output.o diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 0a27745..318d467 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -68,6 +68,7 @@ */ #include +#include #include #include #include @@ -1096,6 +1097,62 @@ int inet_sk_rebuild_header(struct sock * EXPORT_SYMBOL(inet_sk_rebuild_header); +static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct iphdr *iph; + struct net_protocol *ops; + int proto; + int ihl; + int id; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_TCPV4 | + SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + 0))) + goto out; + + if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) + goto out; + + iph = skb->nh.iph; + ihl = iph->ihl * 4; + if (ihl < sizeof(*iph)) + goto out; + + if (unlikely(!pskb_may_pull(skb, ihl))) + goto out; + + skb->h.raw = __skb_pull(skb, ihl); + iph = skb->nh.iph; + id = ntohs(iph->id); + proto = iph->protocol & (MAX_INET_PROTOS - 1); + segs = ERR_PTR(-EPROTONOSUPPORT); + + rcu_read_lock(); + ops = rcu_dereference(inet_protos[proto]); + if (likely(ops && ops->gso_segment)) + segs = ops->gso_segment(skb, features); + rcu_read_unlock(); + + if (!segs || unlikely(IS_ERR(segs))) + goto out; + + skb = segs; + do { + iph = skb->nh.iph; + iph->id = htons(id++); + iph->tot_len = htons(skb->len - skb->mac_len); + iph->check = 0; + iph->check = ip_fast_csum(skb->nh.raw, iph->ihl); + } while ((skb = skb->next)); + +out: + return segs; +} + #ifdef CONFIG_IP_MULTICAST static struct net_protocol igmp_protocol = { .handler = igmp_rcv, @@ -1105,6 +1162,7 @@ #endif static struct net_protocol tcp_protocol = { .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, + .gso_segment = tcp_tso_segment, .no_policy = 1, }; @@ -1150,6 +1208,7 @@ static int ipv4_proc_init(void); static struct packet_type ip_packet_type = { .type = __constant_htons(ETH_P_IP), .func = ip_rcv, + .gso_segment = inet_gso_segment, }; static int __init inet_init(void) diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index e2e4771..8e748be 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -119,6 +118,7 @@ error: static int ah_input(struct xfrm_state *x, struct sk_buff *skb) { int ah_hlen; + int ihl; struct iphdr *iph; struct ip_auth_hdr *ah; struct ah_data *ahp; @@ -149,13 +149,14 @@ static int ah_input(struct xfrm_state *x ah = (struct ip_auth_hdr*)skb->data; iph = skb->nh.iph; - memcpy(work_buf, iph, iph->ihl*4); + ihl = skb->data - skb->nh.raw; + memcpy(work_buf, iph, ihl); iph->ttl = 0; iph->tos = 0; iph->frag_off = 0; iph->check = 0; - if (iph->ihl != 5) { + if (ihl > sizeof(*iph)) { u32 dummy; if (ip_clear_mutable_options(iph, &dummy)) goto out; @@ -164,7 +165,7 @@ static int ah_input(struct xfrm_state *x u8 auth_data[MAX_AH_AUTH_LEN]; memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, ihl); ahp->icv(ahp, skb, ah->auth_data); if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) { x->stats.integrity_failed++; @@ -172,11 +173,8 @@ static int ah_input(struct xfrm_state *x } } ((struct iphdr*)work_buf)->protocol = ah->nexthdr; - skb->nh.raw = skb_pull(skb, ah_hlen); - memcpy(skb->nh.raw, work_buf, iph->ihl*4); - skb->nh.iph->tot_len = htons(skb->len); - skb_pull(skb, skb->nh.iph->ihl*4); - skb->h.raw = skb->data; + skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl); + __skb_pull(skb, ah_hlen + ihl); return 0; diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 4749d50..7b51b3b 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -80,7 +80,6 @@ #include #include #include #include -#include #include #include #include diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index c1b42b5..ec5da4f 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -11,7 +11,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 54419b2..a7c65e9 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -27,7 +27,6 @@ * if no match found. */ -#include #include #include diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 9d1881c..4e11273 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -143,10 +142,9 @@ static int esp_input(struct xfrm_state * int alen = esp->auth.icv_trunc_len; int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen; int nfrags; - int encap_len = 0; + int ihl; u8 nexthdr[2]; struct scatterlist *sg; - u8 workbuf[60]; int padlen; if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr))) @@ -177,7 +175,6 @@ static int esp_input(struct xfrm_state * skb->ip_summed = CHECKSUM_NONE; esph = (struct ip_esp_hdr*)skb->data; - iph = skb->nh.iph; /* Get ivec. This can be wrong, check against another impls. */ if (esp->conf.ivlen) @@ -204,12 +201,12 @@ static int esp_input(struct xfrm_state * /* ... check padding bits here. Silly. :-) */ + iph = skb->nh.iph; + ihl = iph->ihl * 4; + if (x->encap) { struct xfrm_encap_tmpl *encap = x->encap; - struct udphdr *uh; - - uh = (struct udphdr *)(iph + 1); - encap_len = (void*)esph - (void*)uh; + struct udphdr *uh = (void *)(skb->nh.raw + ihl); /* * 1) if the NAT-T peer's IP or port changed then @@ -246,11 +243,7 @@ static int esp_input(struct xfrm_state * iph->protocol = nexthdr[1]; pskb_trim(skb, skb->len - alen - padlen - 2); - memcpy(workbuf, skb->nh.raw, iph->ihl*4); - skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen); - skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen; - memcpy(skb->nh.raw, workbuf, iph->ihl*4); - skb->nh.iph->tot_len = htons(skb->len); + skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl; return 0; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index cdde963..ba2a707 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -15,7 +15,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -666,3 +665,4 @@ #endif } EXPORT_SYMBOL(inet_addr_type); +EXPORT_SYMBOL(ip_dev_find); diff --git a/net/ipv4/fib_hash.c b/net/ipv4/fib_hash.c index e2890ec..3c1d32a 100644 --- a/net/ipv4/fib_hash.c +++ b/net/ipv4/fib_hash.c @@ -15,7 +15,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index ec566f3..6c642d1 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -19,7 +19,6 @@ * Marc Boucher : routing by fwmark */ -#include #include #include #include diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 0f4145b..5f87533 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -15,7 +15,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 95a639f..1cb6530 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -52,7 +52,6 @@ #define VERSION "0.407" -#include #include #include #include diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 2a04559..4c86ac3 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -64,7 +64,6 @@ * */ -#include #include #include #include @@ -730,7 +729,6 @@ out_err: static void icmp_redirect(struct sk_buff *skb) { struct iphdr *iph; - unsigned long ip; if (skb->len < sizeof(struct iphdr)) goto out_err; @@ -742,7 +740,6 @@ static void icmp_redirect(struct sk_buff goto out; iph = (struct iphdr *)skb->data; - ip = iph->daddr; switch (skb->h.icmph->code & 7) { case ICMP_REDIR_NET: @@ -752,7 +749,8 @@ static void icmp_redirect(struct sk_buff */ case ICMP_REDIR_HOST: case ICMP_REDIR_HOSTTOS: - ip_rt_redirect(skb->nh.iph->saddr, ip, skb->h.icmph->un.gateway, + ip_rt_redirect(skb->nh.iph->saddr, iph->daddr, + skb->h.icmph->un.gateway, iph->saddr, skb->dev); break; } diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index d512239..d299c8e 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -72,7 +72,6 @@ * Vinay Kulkarni */ -#include #include #include #include @@ -2361,7 +2360,7 @@ #endif } seq_printf(seq, - "\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n", + "\t\t\t\t%08X %5d %d:%08lX\t\t%d\n", im->multiaddr, im->users, im->tm_running, im->tm_running ? jiffies_to_clock_t(im->timer.expires-jiffies) : 0, diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 9a01bb8..e50a1bf 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -13,7 +13,6 @@ * 2 of the License, or(at your option) any later version. */ -#include #include #include diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 457db99..8e7e41b 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -11,7 +11,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index ee9b551..95fac55 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -13,7 +13,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 417f126..cdd8053 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -8,7 +8,6 @@ * From code orinally in TCP */ -#include #include #include diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 9f0bb52..a22d11d 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -21,7 +21,6 @@ * Mike McLagan : Routing by source */ -#include #include #include #include diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index da734c4..b84b53a 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -23,7 +23,6 @@ */ #include -#include #include #include #include diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index ab99beb..6ff9b10 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index c9026db..e1a7dba 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -121,7 +121,6 @@ #include #include #include #include -#include #include #include diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index cff9c3a..ca0e714 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -53,7 +53,6 @@ #include #include #include #include -#include #include #include @@ -210,8 +209,7 @@ #if defined(CONFIG_NETFILTER) && defined return dst_output(skb); } #endif - if (skb->len > dst_mtu(skb->dst) && - !(skb_shinfo(skb)->ufo_size || skb_shinfo(skb)->tso_size)) + if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) return ip_fragment(skb, ip_finish_output2); else return ip_finish_output2(skb); @@ -362,7 +360,7 @@ packet_routed: } ip_select_ident_more(iph, &rt->u.dst, sk, - (skb_shinfo(skb)->tso_segs ?: 1) - 1); + (skb_shinfo(skb)->gso_segs ?: 1) - 1); /* Add an IP checksum. */ ip_send_check(iph); @@ -410,6 +408,7 @@ #ifdef CONFIG_BRIDGE_NETFILTER nf_bridge_get(to->nf_bridge); #endif #endif + skb_copy_secmark(to, from); } /* @@ -743,7 +742,8 @@ static inline int ip_ufo_append_data(str (length - transhdrlen)); if (!err) { /* specify the length of each IP datagram fragment*/ - skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); + skb_shinfo(skb)->gso_size = mtu - fragheaderlen; + skb_shinfo(skb)->gso_type = SKB_GSO_UDP; __skb_queue_tail(&sk->sk_write_queue, skb); return 0; @@ -839,7 +839,7 @@ int ip_append_data(struct sock *sk, */ if (transhdrlen && length + fragheaderlen <= mtu && - rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) && + rt->u.dst.dev->features & NETIF_F_ALL_CSUM && !exthdrlen) csummode = CHECKSUM_HW; @@ -1086,14 +1086,16 @@ ssize_t ip_append_page(struct sock *sk, inet->cork.length += size; if ((sk->sk_protocol == IPPROTO_UDP) && - (rt->u.dst.dev->features & NETIF_F_UFO)) - skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen); + (rt->u.dst.dev->features & NETIF_F_UFO)) { + skb_shinfo(skb)->gso_size = mtu - fragheaderlen; + skb_shinfo(skb)->gso_type = SKB_GSO_UDP; + } while (size > 0) { int i; - if (skb_shinfo(skb)->ufo_size) + if (skb_shinfo(skb)->gso_size) len = size; else { diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 12e0bf1..84f43a3 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -17,7 +17,6 @@ * Mike McLagan : Routing by source */ -#include #include #include #include diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index 95278b2..8e03748 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -13,7 +13,6 @@ * - Compression stats. * - Adaptive compression. */ -#include #include #include #include @@ -45,7 +44,6 @@ static LIST_HEAD(ipcomp_tfms_list); static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb) { int err, plen, dlen; - struct iphdr *iph; struct ipcomp_data *ipcd = x->data; u8 *start, *scratch; struct crypto_tfm *tfm; @@ -74,8 +72,6 @@ static int ipcomp_decompress(struct xfrm skb_put(skb, dlen - plen); memcpy(skb->data, scratch, dlen); - iph = skb->nh.iph; - iph->tot_len = htons(dlen + iph->ihl * 4); out: put_cpu(); return err; @@ -83,34 +79,21 @@ out: static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb) { - u8 nexthdr; - int err = 0; + int err = -ENOMEM; struct iphdr *iph; - union { - struct iphdr iph; - char buf[60]; - } tmp_iph; - + struct ip_comp_hdr *ipch; - if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && - skb_linearize(skb, GFP_ATOMIC) != 0) { - err = -ENOMEM; + if (skb_linearize_cow(skb)) goto out; - } skb->ip_summed = CHECKSUM_NONE; /* Remove ipcomp header and decompress original payload */ iph = skb->nh.iph; - memcpy(&tmp_iph, iph, iph->ihl * 4); - nexthdr = *(u8 *)skb->data; - skb_pull(skb, sizeof(struct ip_comp_hdr)); - skb->nh.raw += sizeof(struct ip_comp_hdr); - memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4); - iph = skb->nh.iph; - iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr)); - iph->protocol = nexthdr; - skb->h.raw = skb->data; + ipch = (void *)skb->data; + iph->protocol = ipch->nexthdr; + skb->h.raw = skb->nh.raw + sizeof(*ipch); + __skb_pull(skb, sizeof(*ipch)); err = ipcomp_decompress(x, skb); out: @@ -171,10 +154,8 @@ static int ipcomp_output(struct xfrm_sta goto out_ok; } - if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && - skb_linearize(skb, GFP_ATOMIC) != 0) { + if (skb_linearize_cow(skb)) goto out_ok; - } err = ipcomp_compress(x, skb); iph = skb->nh.iph; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index ea398ee..3291d51 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -94,7 +94,6 @@ #include -#include #include #include #include diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 717ab7d..ba33f86 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -28,7 +28,6 @@ * */ -#include #include #include #include diff --git a/net/ipv4/ipvs/ip_vs_est.c b/net/ipv4/ipvs/ip_vs_est.c index c453e1e..4c19403 100644 --- a/net/ipv4/ipvs/ip_vs_est.c +++ b/net/ipv4/ipvs/ip_vs_est.c @@ -13,7 +13,6 @@ * Changes: * */ -#include #include #include #include diff --git a/net/ipv4/multipath_drr.c b/net/ipv4/multipath_drr.c index db67373..252e837 100644 --- a/net/ipv4/multipath_drr.c +++ b/net/ipv4/multipath_drr.c @@ -12,7 +12,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/multipath_random.c b/net/ipv4/multipath_random.c index 5249dbe..b8c289f 100644 --- a/net/ipv4/multipath_random.c +++ b/net/ipv4/multipath_random.c @@ -12,7 +12,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/multipath_rr.c b/net/ipv4/multipath_rr.c index b6cd287..bba5abe 100644 --- a/net/ipv4/multipath_rr.c +++ b/net/ipv4/multipath_rr.c @@ -12,7 +12,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/multipath_wrandom.c b/net/ipv4/multipath_wrandom.c index 342d0b9..d25ec4a 100644 --- a/net/ipv4/multipath_wrandom.c +++ b/net/ipv4/multipath_wrandom.c @@ -12,7 +12,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index d407253..ef0b5aa 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -55,6 +55,18 @@ config IP_NF_CONNTRACK_MARK of packets, but this mark value is kept in the conntrack session instead of the individual packets. +config IP_NF_CONNTRACK_SECMARK + bool 'Connection tracking security mark support' + depends on IP_NF_CONNTRACK && NETWORK_SECMARK + help + This option enables security markings to be applied to + connections. Typically they are copied to connections from + packets using the CONNSECMARK target and copied back from + connections to packets with the same target, with the packets + being originally labeled via SECMARK. + + If unsure, say 'N'. + config IP_NF_CONNTRACK_EVENTS bool "Connection tracking events (EXPERIMENTAL)" depends on EXPERIMENTAL && IP_NF_CONNTRACK @@ -142,6 +154,8 @@ config IP_NF_TFTP config IP_NF_AMANDA tristate "Amanda backup protocol support" depends on IP_NF_CONNTRACK + select TEXTSEARCH + select TEXTSEARCH_KMP help If you are running the Amanda backup package on this machine or machines that will be MASQUERADED through this @@ -181,14 +195,26 @@ config IP_NF_H323 With this module you can support H.323 on a connection tracking/NAT firewall. - This module supports RAS, Fast-start, H.245 tunnelling, RTP/RTCP - and T.120 based data and applications including audio, video, FAX, - chat, whiteboard, file transfer, etc. For more information, please - see http://nath323.sourceforge.net/. + This module supports RAS, Fast Start, H.245 Tunnelling, Call + Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat, + whiteboard, file transfer, etc. For more information, please + visit http://nath323.sourceforge.net/. If you want to compile it as a module, say 'M' here and read Documentation/modules.txt. If unsure, say 'N'. +config IP_NF_SIP + tristate "SIP protocol support (EXPERIMENTAL)" + depends on IP_NF_CONNTRACK && EXPERIMENTAL + help + SIP is an application-layer control protocol that can establish, + modify, and terminate multimedia sessions (conferences) such as + Internet telephony calls. With the ip_conntrack_sip and + the ip_nat_sip modules you can support the protocol on a connection + tracking/NATing firewall. + + To compile it as a module, choose M here. If unsure, say Y. + config IP_NF_QUEUE tristate "IP Userspace queueing via NETLINK (OBSOLETE)" help @@ -306,7 +332,7 @@ config IP_NF_MATCH_HASHLIMIT help This option adds a new iptables `hashlimit' match. - As opposed to `limit', this match dynamically crates a hash table + As opposed to `limit', this match dynamically creates a hash table of limit buckets, based on your selection of source/destination ip addresses and/or ports. @@ -501,6 +527,12 @@ config IP_NF_NAT_H323 default IP_NF_NAT if IP_NF_H323=y default m if IP_NF_H323=m +config IP_NF_NAT_SIP + tristate + depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n + default IP_NF_NAT if IP_NF_SIP=y + default m if IP_NF_SIP=m + # mangle + specific targets config IP_NF_MANGLE tristate "Packet mangling" diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 461cb1e..3ded4a3 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_IP_NF_AMANDA) += ip_conntra obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o +obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o # NAT helpers @@ -40,6 +41,7 @@ obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o +obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o # generic IP tables obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index d0d1919..80c73ca 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -9,7 +9,6 @@ * */ -#include #include #include #include @@ -1120,7 +1119,8 @@ int arpt_register_table(struct arpt_tabl return ret; } - if (xt_register_table(table, &bootstrap, newinfo) != 0) { + ret = xt_register_table(table, &bootstrap, newinfo); + if (ret != 0) { xt_free_table_info(newinfo); return ret; } diff --git a/net/ipv4/netfilter/ip_conntrack_amanda.c b/net/ipv4/netfilter/ip_conntrack_amanda.c index a604b1c..0a7bd7f 100644 --- a/net/ipv4/netfilter/ip_conntrack_amanda.c +++ b/net/ipv4/netfilter/ip_conntrack_amanda.c @@ -17,33 +17,29 @@ * this value. * */ - -#include #include #include -#include -#include #include +#include +#include +#include +#include #include -#include -#include +#include #include #include static unsigned int master_timeout = 300; +static char *ts_algo = "kmp"; MODULE_AUTHOR("Brian J. Murrell "); MODULE_DESCRIPTION("Amanda connection tracking module"); MODULE_LICENSE("GPL"); module_param(master_timeout, uint, 0600); MODULE_PARM_DESC(master_timeout, "timeout for the master connection"); - -static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; - -/* This is slow, but it's simple. --RR */ -static char *amanda_buffer; -static DEFINE_SPINLOCK(amanda_buffer_lock); +module_param(ts_algo, charp, 0400); +MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)"); unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb, enum ip_conntrack_info ctinfo, @@ -52,12 +48,48 @@ unsigned int (*ip_nat_amanda_hook)(struc struct ip_conntrack_expect *exp); EXPORT_SYMBOL_GPL(ip_nat_amanda_hook); +enum amanda_strings { + SEARCH_CONNECT, + SEARCH_NEWLINE, + SEARCH_DATA, + SEARCH_MESG, + SEARCH_INDEX, +}; + +static struct { + char *string; + size_t len; + struct ts_config *ts; +} search[] = { + [SEARCH_CONNECT] = { + .string = "CONNECT ", + .len = 8, + }, + [SEARCH_NEWLINE] = { + .string = "\n", + .len = 1, + }, + [SEARCH_DATA] = { + .string = "DATA ", + .len = 5, + }, + [SEARCH_MESG] = { + .string = "MESG ", + .len = 5, + }, + [SEARCH_INDEX] = { + .string = "INDEX ", + .len = 6, + }, +}; + static int help(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo) { + struct ts_state ts; struct ip_conntrack_expect *exp; - char *data, *data_limit, *tmp; - unsigned int dataoff, i; + unsigned int dataoff, start, stop, off, i; + char pbuf[sizeof("65535")], *tmp; u_int16_t port, len; int ret = NF_ACCEPT; @@ -77,29 +109,34 @@ static int help(struct sk_buff **pskb, return NF_ACCEPT; } - spin_lock_bh(&amanda_buffer_lock); - skb_copy_bits(*pskb, dataoff, amanda_buffer, (*pskb)->len - dataoff); - data = amanda_buffer; - data_limit = amanda_buffer + (*pskb)->len - dataoff; - *data_limit = '\0'; - - /* Search for the CONNECT string */ - data = strstr(data, "CONNECT "); - if (!data) + memset(&ts, 0, sizeof(ts)); + start = skb_find_text(*pskb, dataoff, (*pskb)->len, + search[SEARCH_CONNECT].ts, &ts); + if (start == UINT_MAX) goto out; - data += strlen("CONNECT "); + start += dataoff + search[SEARCH_CONNECT].len; - /* Only search first line. */ - if ((tmp = strchr(data, '\n'))) - *tmp = '\0'; + memset(&ts, 0, sizeof(ts)); + stop = skb_find_text(*pskb, start, (*pskb)->len, + search[SEARCH_NEWLINE].ts, &ts); + if (stop == UINT_MAX) + goto out; + stop += start; - for (i = 0; i < ARRAY_SIZE(conns); i++) { - char *match = strstr(data, conns[i]); - if (!match) + for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) { + memset(&ts, 0, sizeof(ts)); + off = skb_find_text(*pskb, start, stop, search[i].ts, &ts); + if (off == UINT_MAX) continue; - tmp = data = match + strlen(conns[i]); - port = simple_strtoul(data, &data, 10); - len = data - tmp; + off += start + search[i].len; + + len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off); + if (skb_copy_bits(*pskb, off, pbuf, len)) + break; + pbuf[len] = '\0'; + + port = simple_strtoul(pbuf, &tmp, 10); + len = tmp - pbuf; if (port == 0 || len > 5) break; @@ -125,8 +162,7 @@ static int help(struct sk_buff **pskb, exp->mask.dst.u.tcp.port = 0xFFFF; if (ip_nat_amanda_hook) - ret = ip_nat_amanda_hook(pskb, ctinfo, - tmp - amanda_buffer, + ret = ip_nat_amanda_hook(pskb, ctinfo, off - dataoff, len, exp); else if (ip_conntrack_expect_related(exp) != 0) ret = NF_DROP; @@ -134,12 +170,11 @@ static int help(struct sk_buff **pskb, } out: - spin_unlock_bh(&amanda_buffer_lock); return ret; } static struct ip_conntrack_helper amanda_helper = { - .max_expected = ARRAY_SIZE(conns), + .max_expected = 3, .timeout = 180, .me = THIS_MODULE, .help = help, @@ -155,26 +190,36 @@ static struct ip_conntrack_helper amanda static void __exit ip_conntrack_amanda_fini(void) { + int i; + ip_conntrack_helper_unregister(&amanda_helper); - kfree(amanda_buffer); + for (i = 0; i < ARRAY_SIZE(search); i++) + textsearch_destroy(search[i].ts); } static int __init ip_conntrack_amanda_init(void) { - int ret; - - amanda_buffer = kmalloc(65536, GFP_KERNEL); - if (!amanda_buffer) - return -ENOMEM; - - ret = ip_conntrack_helper_register(&amanda_helper); - if (ret < 0) { - kfree(amanda_buffer); - return ret; + int ret, i; + + ret = -ENOMEM; + for (i = 0; i < ARRAY_SIZE(search); i++) { + search[i].ts = textsearch_prepare(ts_algo, search[i].string, + search[i].len, + GFP_KERNEL, TS_AUTOLOAD); + if (search[i].ts == NULL) + goto err; } + ret = ip_conntrack_helper_register(&amanda_helper); + if (ret < 0) + goto err; return 0; - +err: + for (; i >= 0; i--) { + if (search[i].ts) + textsearch_destroy(search[i].ts); + } + return ret; } module_init(ip_conntrack_amanda_init); diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index a297da7..aa45917 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c @@ -17,7 +17,6 @@ * - export ip_conntrack[_expect]_{find_get,put} functions * */ -#include #include #include #include @@ -724,6 +723,9 @@ #if defined(CONFIG_IP_NF_TARGET_MASQUERA /* this is ugly, but there is no other place where to put it */ conntrack->nat.masq_index = exp->master->nat.masq_index; #endif +#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK + conntrack->secmark = exp->master->secmark; +#endif nf_conntrack_get(&conntrack->master->ct_general); CONNTRACK_STAT_INC(expect_new); } else { @@ -1130,6 +1132,12 @@ void __ip_ct_refresh_acct(struct ip_conn write_lock_bh(&ip_conntrack_lock); + /* Only update if this is not a fixed timeout */ + if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) { + write_unlock_bh(&ip_conntrack_lock); + return; + } + /* If not in hash table, timer will not be active yet */ if (!is_confirmed(ct)) { ct->timeout.expires = extra_jiffies; diff --git a/net/ipv4/netfilter/ip_conntrack_ftp.c b/net/ipv4/netfilter/ip_conntrack_ftp.c index 3e542bf..1d18c86 100644 --- a/net/ipv4/netfilter/ip_conntrack_ftp.c +++ b/net/ipv4/netfilter/ip_conntrack_ftp.c @@ -8,7 +8,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -56,37 +55,48 @@ static int try_eprt(const char *, size_t static int try_epsv_response(const char *, size_t, u_int32_t [], char); static const struct ftp_search { - enum ip_conntrack_dir dir; const char *pattern; size_t plen; char skip; char term; enum ip_ct_ftp_type ftptype; int (*getnum)(const char *, size_t, u_int32_t[], char); -} search[] = { - { - IP_CT_DIR_ORIGINAL, - "PORT", sizeof("PORT") - 1, ' ', '\r', - IP_CT_FTP_PORT, - try_rfc959, +} search[IP_CT_DIR_MAX][2] = { + [IP_CT_DIR_ORIGINAL] = { + { + .pattern = "PORT", + .plen = sizeof("PORT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = IP_CT_FTP_PORT, + .getnum = try_rfc959, + }, + { + .pattern = "EPRT", + .plen = sizeof("EPRT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = IP_CT_FTP_EPRT, + .getnum = try_eprt, + }, }, - { - IP_CT_DIR_REPLY, - "227 ", sizeof("227 ") - 1, '(', ')', - IP_CT_FTP_PASV, - try_rfc959, - }, - { - IP_CT_DIR_ORIGINAL, - "EPRT", sizeof("EPRT") - 1, ' ', '\r', - IP_CT_FTP_EPRT, - try_eprt, - }, - { - IP_CT_DIR_REPLY, - "229 ", sizeof("229 ") - 1, '(', ')', - IP_CT_FTP_EPSV, - try_epsv_response, + [IP_CT_DIR_REPLY] = { + { + .pattern = "227 ", + .plen = sizeof("227 ") - 1, + .skip = '(', + .term = ')', + .ftptype = IP_CT_FTP_PASV, + .getnum = try_rfc959, + }, + { + .pattern = "229 ", + .plen = sizeof("229 ") - 1, + .skip = '(', + .term = ')', + .ftptype = IP_CT_FTP_EPSV, + .getnum = try_epsv_response, + }, }, }; @@ -346,17 +356,15 @@ static int help(struct sk_buff **pskb, array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF; array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF; - for (i = 0; i < ARRAY_SIZE(search); i++) { - if (search[i].dir != dir) continue; - + for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { found = find_pattern(fb_ptr, (*pskb)->len - dataoff, - search[i].pattern, - search[i].plen, - search[i].skip, - search[i].term, + search[dir][i].pattern, + search[dir][i].plen, + search[dir][i].skip, + search[dir][i].term, &matchoff, &matchlen, array, - search[i].getnum); + search[dir][i].getnum); if (found) break; } if (found == -1) { @@ -366,7 +374,7 @@ static int help(struct sk_buff **pskb, this case. */ if (net_ratelimit()) printk("conntrack_ftp: partial %s %u+%u\n", - search[i].pattern, + search[dir][i].pattern, ntohl(th->seq), datalen); ret = NF_DROP; goto out; @@ -426,7 +434,7 @@ static int help(struct sk_buff **pskb, /* Now, NAT might want to mangle the packet, and register the * (possibly changed) expectation itself. */ if (ip_nat_ftp_hook) - ret = ip_nat_ftp_hook(pskb, ctinfo, search[i].ftptype, + ret = ip_nat_ftp_hook(pskb, ctinfo, search[dir][i].ftptype, matchoff, matchlen, exp, &seq); else { /* Can't expect this? Best to drop packet now. */ diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323.c b/net/ipv4/netfilter/ip_conntrack_helper_h323.c index 518f581..af35235 100644 --- a/net/ipv4/netfilter/ip_conntrack_helper_h323.c +++ b/net/ipv4/netfilter/ip_conntrack_helper_h323.c @@ -11,7 +11,6 @@ * For more information, please see http://nath323.sourceforge.net/ */ -#include #include #include #include @@ -22,6 +21,8 @@ #include #include #include +#include +#include #if 0 #define DEBUGP printk @@ -38,6 +39,12 @@ static int gkrouted_only = 1; module_param(gkrouted_only, int, 0600); MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper"); +static int callforward_filter = 1; +module_param(callforward_filter, bool, 0600); +MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations " + "if both endpoints are on different sides " + "(determined by routing information)"); + /* Hooks for NAT */ int (*set_h245_addr_hook) (struct sk_buff ** pskb, unsigned char **data, int dataoff, @@ -77,6 +84,12 @@ int (*nat_h245_hook) (struct sk_buff ** unsigned char **data, int dataoff, TransportAddress * addr, u_int16_t port, struct ip_conntrack_expect * exp); +int (*nat_callforwarding_hook) (struct sk_buff ** pskb, + struct ip_conntrack * ct, + enum ip_conntrack_info ctinfo, + unsigned char **data, int dataoff, + TransportAddress * addr, u_int16_t port, + struct ip_conntrack_expect * exp); int (*nat_q931_hook) (struct sk_buff ** pskb, struct ip_conntrack * ct, enum ip_conntrack_info ctinfo, @@ -683,6 +696,92 @@ static int expect_h245(struct sk_buff ** return ret; } +/* Forwarding declaration */ +void ip_conntrack_q931_expect(struct ip_conntrack *new, + struct ip_conntrack_expect *this); + +/****************************************************************************/ +static int expect_callforwarding(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + unsigned char **data, int dataoff, + TransportAddress * addr) +{ + int dir = CTINFO2DIR(ctinfo); + int ret = 0; + u_int32_t ip; + u_int16_t port; + struct ip_conntrack_expect *exp = NULL; + + /* Read alternativeAddress */ + if (!get_h225_addr(*data, addr, &ip, &port) || port == 0) + return 0; + + /* If the calling party is on the same side of the forward-to party, + * we don't need to track the second call */ + if (callforward_filter) { + struct rtable *rt1, *rt2; + struct flowi fl1 = { + .fl4_dst = ip, + }; + struct flowi fl2 = { + .fl4_dst = ct->tuplehash[!dir].tuple.src.ip, + }; + + if (ip_route_output_key(&rt1, &fl1) == 0) { + if (ip_route_output_key(&rt2, &fl2) == 0) { + if (rt1->rt_gateway == rt2->rt_gateway && + rt1->u.dst.dev == rt2->u.dst.dev) + ret = 1; + dst_release(&rt2->u.dst); + } + dst_release(&rt1->u.dst); + } + if (ret) { + DEBUGP("ip_ct_q931: Call Forwarding not tracked\n"); + return 0; + } + } + + /* Create expect for the second call leg */ + if ((exp = ip_conntrack_expect_alloc(ct)) == NULL) + return -1; + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.src.u.tcp.port = 0; + exp->tuple.dst.ip = ip; + exp->tuple.dst.u.tcp.port = htons(port); + exp->tuple.dst.protonum = IPPROTO_TCP; + exp->mask.src.ip = 0xFFFFFFFF; + exp->mask.src.u.tcp.port = 0; + exp->mask.dst.ip = 0xFFFFFFFF; + exp->mask.dst.u.tcp.port = 0xFFFF; + exp->mask.dst.protonum = 0xFF; + exp->flags = 0; + + if (ct->tuplehash[dir].tuple.src.ip != + ct->tuplehash[!dir].tuple.dst.ip && nat_callforwarding_hook) { + /* Need NAT */ + ret = nat_callforwarding_hook(pskb, ct, ctinfo, data, dataoff, + addr, port, exp); + } else { /* Conntrack only */ + exp->expectfn = ip_conntrack_q931_expect; + + if (ip_conntrack_expect_related(exp) == 0) { + DEBUGP("ip_ct_q931: expect Call Forwarding " + "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", + NIPQUAD(exp->tuple.src.ip), + ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), + ntohs(exp->tuple.dst.u.tcp.port)); + } else + ret = -1; + } + + ip_conntrack_expect_put(exp); + + return ret; +} + /****************************************************************************/ static int process_setup(struct sk_buff **pskb, struct ip_conntrack *ct, enum ip_conntrack_info ctinfo, @@ -878,6 +977,15 @@ static int process_facility(struct sk_bu DEBUGP("ip_ct_q931: Facility\n"); + if (facility->reason.choice == eFacilityReason_callForwarded) { + if (facility->options & eFacility_UUIE_alternativeAddress) + return expect_callforwarding(pskb, ct, ctinfo, data, + dataoff, + &facility-> + alternativeAddress); + return 0; + } + if (facility->options & eFacility_UUIE_h245Address) { ret = expect_h245(pskb, ct, ctinfo, data, dataoff, &facility->h245Address); @@ -1677,7 +1785,6 @@ static int __init init(void) fini(); return ret; } - DEBUGP("ip_ct_h323: init success\n"); return 0; } @@ -1696,6 +1803,7 @@ EXPORT_SYMBOL_GPL(set_ras_addr_hook); EXPORT_SYMBOL_GPL(nat_rtp_rtcp_hook); EXPORT_SYMBOL_GPL(nat_t120_hook); EXPORT_SYMBOL_GPL(nat_h245_hook); +EXPORT_SYMBOL_GPL(nat_callforwarding_hook); EXPORT_SYMBOL_GPL(nat_q931_hook); MODULE_AUTHOR("Jing Min Zhao "); diff --git a/net/ipv4/netfilter/ip_conntrack_helper_h323_types.c b/net/ipv4/netfilter/ip_conntrack_helper_h323_types.c index 022c47b..4b35961 100644 --- a/net/ipv4/netfilter/ip_conntrack_helper_h323_types.c +++ b/net/ipv4/netfilter/ip_conntrack_helper_h323_types.c @@ -1,4 +1,4 @@ -/* Generated by Jing Min Zhao's ASN.1 parser, Mar 15 2006 +/* Generated by Jing Min Zhao's ASN.1 parser, Apr 20 2006 * * Copyright (c) 2006 Jing Min Zhao * @@ -1069,8 +1069,8 @@ static field_t _Facility_UUIE_fastStart[ static field_t _Facility_UUIE[] = { /* SEQUENCE */ {FNAME("protocolIdentifier") OID, BYTE, 0, 0, SKIP, 0, NULL}, - {FNAME("alternativeAddress") CHOICE, 3, 7, 7, SKIP | EXT | OPT, 0, - _TransportAddress}, + {FNAME("alternativeAddress") CHOICE, 3, 7, 7, DECODE | EXT | OPT, + offsetof(Facility_UUIE, alternativeAddress), _TransportAddress}, {FNAME("alternativeAliasAddress") SEQOF, SEMI, 0, 0, SKIP | OPT, 0, _Facility_UUIE_alternativeAliasAddress}, {FNAME("conferenceID") OCTSTR, FIXD, 16, 0, SKIP | OPT, 0, NULL}, diff --git a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c index 8ccfe17..b020a33 100644 --- a/net/ipv4/netfilter/ip_conntrack_helper_pptp.c +++ b/net/ipv4/netfilter/ip_conntrack_helper_pptp.c @@ -46,7 +46,6 @@ * */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ip_conntrack_irc.c b/net/ipv4/netfilter/ip_conntrack_irc.c index a2ac5ce..4488907 100644 --- a/net/ipv4/netfilter/ip_conntrack_irc.c +++ b/net/ipv4/netfilter/ip_conntrack_irc.c @@ -22,7 +22,6 @@ * */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ip_conntrack_netlink.c b/net/ipv4/netfilter/ip_conntrack_netlink.c index 01bd7ca..33891bb 100644 --- a/net/ipv4/netfilter/ip_conntrack_netlink.c +++ b/net/ipv4/netfilter/ip_conntrack_netlink.c @@ -399,38 +399,54 @@ #endif /* CONFIG_IP_NF_CONNTRACK_EVENTS static int ctnetlink_done(struct netlink_callback *cb) { DEBUGP("entered %s\n", __FUNCTION__); + if (cb->args[1]) + ip_conntrack_put((struct ip_conntrack *)cb->args[1]); return 0; } static int ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { - struct ip_conntrack *ct = NULL; + struct ip_conntrack *ct, *last; struct ip_conntrack_tuple_hash *h; struct list_head *i; - u_int32_t *id = (u_int32_t *) &cb->args[1]; DEBUGP("entered %s, last bucket=%lu id=%u\n", __FUNCTION__, cb->args[0], *id); read_lock_bh(&ip_conntrack_lock); - for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++, *id = 0) { + for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) { +restart: + last = (struct ip_conntrack *)cb->args[1]; list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) { h = (struct ip_conntrack_tuple_hash *) i; if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) continue; ct = tuplehash_to_ctrack(h); - if (ct->id <= *id) - continue; + if (last != NULL) { + if (ct == last) { + ip_conntrack_put(last); + cb->args[1] = 0; + last = NULL; + } else + continue; + } if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, - 1, ct) < 0) + 1, ct) < 0) { + nf_conntrack_get(&ct->ct_general); + cb->args[1] = (unsigned long)ct; goto out; - *id = ct->id; + } + } + if (last != NULL) { + ip_conntrack_put(last); + cb->args[1] = 0; + goto restart; } } -out: +out: read_unlock_bh(&ip_conntrack_lock); DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id); @@ -629,7 +645,7 @@ static const size_t cta_min_nat[CTA_NAT_ }; static inline int -ctnetlink_parse_nat(struct nfattr *cda[], +ctnetlink_parse_nat(struct nfattr *nat, const struct ip_conntrack *ct, struct ip_nat_range *range) { struct nfattr *tb[CTA_NAT_MAX]; @@ -639,7 +655,7 @@ ctnetlink_parse_nat(struct nfattr *cda[] memset(range, 0, sizeof(*range)); - nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]); + nfattr_parse_nested(tb, CTA_NAT_MAX, nat); if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat)) return -EINVAL; @@ -854,39 +870,30 @@ ctnetlink_change_status(struct ip_conntr /* ASSURED bit can only be set */ return -EINVAL; - if (cda[CTA_NAT-1]) { + if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { #ifndef CONFIG_IP_NF_NAT_NEEDED return -EINVAL; #else - unsigned int hooknum; struct ip_nat_range range; - if (ctnetlink_parse_nat(cda, ct, &range) < 0) - return -EINVAL; - - DEBUGP("NAT: %u.%u.%u.%u-%u.%u.%u.%u:%u-%u\n", - NIPQUAD(range.min_ip), NIPQUAD(range.max_ip), - htons(range.min.all), htons(range.max.all)); - - /* This is tricky but it works. ip_nat_setup_info needs the - * hook number as parameter, so let's do the correct - * conversion and run away */ - if (status & IPS_SRC_NAT_DONE) - hooknum = NF_IP_POST_ROUTING; /* IP_NAT_MANIP_SRC */ - else if (status & IPS_DST_NAT_DONE) - hooknum = NF_IP_PRE_ROUTING; /* IP_NAT_MANIP_DST */ - else - return -EINVAL; /* Missing NAT flags */ - - DEBUGP("NAT status: %lu\n", - status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK)); - - if (ip_nat_initialized(ct, HOOK2MANIP(hooknum))) - return -EEXIST; - ip_nat_setup_info(ct, &range, hooknum); - - DEBUGP("NAT status after setup_info: %lu\n", - ct->status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK)); + if (cda[CTA_NAT_DST-1]) { + if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct, + &range) < 0) + return -EINVAL; + if (ip_nat_initialized(ct, + HOOK2MANIP(NF_IP_PRE_ROUTING))) + return -EEXIST; + ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING); + } + if (cda[CTA_NAT_SRC-1]) { + if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct, + &range) < 0) + return -EINVAL; + if (ip_nat_initialized(ct, + HOOK2MANIP(NF_IP_POST_ROUTING))) + return -EEXIST; + ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING); + } #endif } @@ -1106,7 +1113,7 @@ ctnetlink_new_conntrack(struct sock *ctn /* implicit 'else' */ /* we only allow nat config for new conntracks */ - if (cda[CTA_NAT-1]) { + if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { err = -EINVAL; goto out_unlock; } diff --git a/net/ipv4/netfilter/ip_conntrack_proto_gre.c b/net/ipv4/netfilter/ip_conntrack_proto_gre.c index 5679479..4ee016c 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_gre.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_gre.c @@ -23,7 +23,6 @@ * */ -#include #include #include #include @@ -77,10 +76,10 @@ static inline int gre_key_cmpfn(const st } /* look up the source key for a given tuple */ -static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t) +static __be16 gre_keymap_lookup(struct ip_conntrack_tuple *t) { struct ip_ct_gre_keymap *km; - u_int32_t key = 0; + __be16 key = 0; read_lock_bh(&ip_ct_gre_lock); km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn, @@ -190,7 +189,7 @@ static int gre_pkt_to_tuple(const struct struct ip_conntrack_tuple *tuple) { struct gre_hdr_pptp _pgrehdr, *pgrehdr; - u_int32_t srckey; + __be16 srckey; struct gre_hdr _grehdr, *grehdr; /* first only delinearize old RFC1701 GRE header */ diff --git a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c index d8b14a9..23f1c50 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_icmp.c @@ -224,7 +224,7 @@ icmp_error(struct sk_buff *skb, enum ip_ } /* See ip_conntrack_proto_tcp.c */ - if (hooknum == NF_IP_PRE_ROUTING && + if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && nf_ip_checksum(skb, hooknum, skb->nh.iph->ihl * 4, 0)) { if (LOG_INVALID(IPPROTO_ICMP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, diff --git a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c index 0416073..2d3612c 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_sctp.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_sctp.c @@ -254,7 +254,7 @@ static int do_basic_checks(struct ip_con } DEBUGP("Basic checks passed\n"); - return 0; + return count == 0; } static int new_state(enum ip_conntrack_dir dir, diff --git a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c index 062b252..fb920e7 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_tcp.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_tcp.c @@ -19,7 +19,6 @@ * version 2.2 */ -#include #include #include #include @@ -870,7 +869,7 @@ static int tcp_error(struct sk_buff *skb * and moreover root might send raw packets. */ /* FIXME: Source route IP option packets --RR */ - if (hooknum == NF_IP_PRE_ROUTING && + if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_TCP)) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, diff --git a/net/ipv4/netfilter/ip_conntrack_proto_udp.c b/net/ipv4/netfilter/ip_conntrack_proto_udp.c index 7089986..9b2c16b 100644 --- a/net/ipv4/netfilter/ip_conntrack_proto_udp.c +++ b/net/ipv4/netfilter/ip_conntrack_proto_udp.c @@ -120,7 +120,7 @@ static int udp_error(struct sk_buff *skb * because the semantic of CHECKSUM_HW is different there * and moreover root might send raw packets. * FIXME: Source route IP option packets --RR */ - if (hooknum == NF_IP_PRE_ROUTING && + if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_UDP)) { if (LOG_INVALID(IPPROTO_UDP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, diff --git a/net/ipv4/netfilter/ip_conntrack_sip.c b/net/ipv4/netfilter/ip_conntrack_sip.c new file mode 100644 index 0000000..fc87ce0 --- /dev/null +++ b/net/ipv4/netfilter/ip_conntrack_sip.c @@ -0,0 +1,471 @@ +/* SIP extension for IP connection tracking. + * + * (C) 2005 by Christian Hentschel + * based on RR's ip_conntrack_ftp.c and other modules. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hentschel "); +MODULE_DESCRIPTION("SIP connection tracking helper"); + +#define MAX_PORTS 8 +static unsigned short ports[MAX_PORTS]; +static int ports_c; +module_param_array(ports, ushort, &ports_c, 0400); +MODULE_PARM_DESC(ports, "port numbers of sip servers"); + +static unsigned int sip_timeout = SIP_TIMEOUT; +module_param(sip_timeout, uint, 0600); +MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session"); + +unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack *ct, + const char **dptr); +EXPORT_SYMBOL_GPL(ip_nat_sip_hook); + +unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *exp, + const char *dptr); +EXPORT_SYMBOL_GPL(ip_nat_sdp_hook); + +int ct_sip_get_info(const char *dptr, size_t dlen, + unsigned int *matchoff, + unsigned int *matchlen, + struct sip_header_nfo *hnfo); +EXPORT_SYMBOL_GPL(ct_sip_get_info); + + +static int digits_len(const char *dptr, const char *limit, int *shift); +static int epaddr_len(const char *dptr, const char *limit, int *shift); +static int skp_digits_len(const char *dptr, const char *limit, int *shift); +static int skp_epaddr_len(const char *dptr, const char *limit, int *shift); + +struct sip_header_nfo ct_sip_hdrs[] = { + { /* Via header */ + .lname = "Via:", + .lnlen = sizeof("Via:") - 1, + .sname = "\r\nv:", + .snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */ + .ln_str = "UDP ", + .ln_strlen = sizeof("UDP ") - 1, + .match_len = epaddr_len, + }, + { /* Contact header */ + .lname = "Contact:", + .lnlen = sizeof("Contact:") - 1, + .sname = "\r\nm:", + .snlen = sizeof("\r\nm:") - 1, + .ln_str = "sip:", + .ln_strlen = sizeof("sip:") - 1, + .match_len = skp_epaddr_len + }, + { /* Content length header */ + .lname = "Content-Length:", + .lnlen = sizeof("Content-Length:") - 1, + .sname = "\r\nl:", + .snlen = sizeof("\r\nl:") - 1, + .ln_str = ":", + .ln_strlen = sizeof(":") - 1, + .match_len = skp_digits_len + }, + { /* SDP media info */ + .lname = "\nm=", + .lnlen = sizeof("\nm=") - 1, + .sname = "\rm=", + .snlen = sizeof("\rm=") - 1, + .ln_str = "audio ", + .ln_strlen = sizeof("audio ") - 1, + .match_len = digits_len + }, + { /* SDP owner address*/ + .lname = "\no=", + .lnlen = sizeof("\no=") - 1, + .sname = "\ro=", + .snlen = sizeof("\ro=") - 1, + .ln_str = "IN IP4 ", + .ln_strlen = sizeof("IN IP4 ") - 1, + .match_len = epaddr_len + }, + { /* SDP connection info */ + .lname = "\nc=", + .lnlen = sizeof("\nc=") - 1, + .sname = "\rc=", + .snlen = sizeof("\rc=") - 1, + .ln_str = "IN IP4 ", + .ln_strlen = sizeof("IN IP4 ") - 1, + .match_len = epaddr_len + }, + { /* Requests headers */ + .lname = "sip:", + .lnlen = sizeof("sip:") - 1, + .sname = "sip:", + .snlen = sizeof("sip:") - 1, /* yes, i know.. ;) */ + .ln_str = "@", + .ln_strlen = sizeof("@") - 1, + .match_len = epaddr_len + }, + { /* SDP version header */ + .lname = "\nv=", + .lnlen = sizeof("\nv=") - 1, + .sname = "\rv=", + .snlen = sizeof("\rv=") - 1, + .ln_str = "=", + .ln_strlen = sizeof("=") - 1, + .match_len = digits_len + } +}; +EXPORT_SYMBOL_GPL(ct_sip_hdrs); + +/* get line lenght until first CR or LF seen. */ +int ct_sip_lnlen(const char *line, const char *limit) +{ + const char *k = line; + + while ((line <= limit) && (*line == '\r' || *line == '\n')) + line++; + + while (line <= limit) { + if (*line == '\r' || *line == '\n') + break; + line++; + } + return line - k; +} +EXPORT_SYMBOL_GPL(ct_sip_lnlen); + +/* Linear string search, case sensitive. */ +const char *ct_sip_search(const char *needle, const char *haystack, + size_t needle_len, size_t haystack_len) +{ + const char *limit = haystack + (haystack_len - needle_len); + + while (haystack <= limit) { + if (memcmp(haystack, needle, needle_len) == 0) + return haystack; + haystack++; + } + return NULL; +} +EXPORT_SYMBOL_GPL(ct_sip_search); + +static int digits_len(const char *dptr, const char *limit, int *shift) +{ + int len = 0; + while (dptr <= limit && isdigit(*dptr)) { + dptr++; + len++; + } + return len; +} + +/* get digits lenght, skiping blank spaces. */ +static int skp_digits_len(const char *dptr, const char *limit, int *shift) +{ + for (; dptr <= limit && *dptr == ' '; dptr++) + (*shift)++; + + return digits_len(dptr, limit, shift); +} + +/* Simple ipaddr parser.. */ +static int parse_ipaddr(const char *cp, const char **endp, + u_int32_t *ipaddr, const char *limit) +{ + unsigned long int val; + int i, digit = 0; + + for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) { + digit = 0; + if (!isdigit(*cp)) + break; + + val = simple_strtoul(cp, (char **)&cp, 10); + if (val > 0xFF) + return -1; + + ((u_int8_t *)ipaddr)[i] = val; + digit = 1; + + if (*cp != '.') + break; + cp++; + } + if (!digit) + return -1; + + if (endp) + *endp = cp; + + return 0; +} + +/* skip ip address. returns it lenght. */ +static int epaddr_len(const char *dptr, const char *limit, int *shift) +{ + const char *aux = dptr; + u_int32_t ip; + + if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) { + DEBUGP("ip: %s parse failed.!\n", dptr); + return 0; + } + + /* Port number */ + if (*dptr == ':') { + dptr++; + dptr += digits_len(dptr, limit, shift); + } + return dptr - aux; +} + +/* get address length, skiping user info. */ +static int skp_epaddr_len(const char *dptr, const char *limit, int *shift) +{ + int s = *shift; + + for (; dptr <= limit && *dptr != '@'; dptr++) + (*shift)++; + + if (*dptr == '@') { + dptr++; + (*shift)++; + } else + *shift = s; + + return epaddr_len(dptr, limit, shift); +} + +/* Returns 0 if not found, -1 error parsing. */ +int ct_sip_get_info(const char *dptr, size_t dlen, + unsigned int *matchoff, + unsigned int *matchlen, + struct sip_header_nfo *hnfo) +{ + const char *limit, *aux, *k = dptr; + int shift = 0; + + limit = dptr + (dlen - hnfo->lnlen); + + while (dptr <= limit) { + if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) && + (strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) { + dptr++; + continue; + } + aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen, + ct_sip_lnlen(dptr, limit)); + if (!aux) { + DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str, + hnfo->lname); + return -1; + } + aux += hnfo->ln_strlen; + + *matchlen = hnfo->match_len(aux, limit, &shift); + if (!*matchlen) + return -1; + + *matchoff = (aux - k) + shift; + + DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname, + *matchlen); + return 1; + } + DEBUGP("%s header not found.\n", hnfo->lname); + return 0; +} + +static int set_expected_rtp(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + u_int32_t ipaddr, u_int16_t port, + const char *dptr) +{ + struct ip_conntrack_expect *exp; + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + int ret; + + exp = ip_conntrack_expect_alloc(ct); + if (exp == NULL) + return NF_DROP; + + exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip; + exp->tuple.src.u.udp.port = 0; + exp->tuple.dst.ip = ipaddr; + exp->tuple.dst.u.udp.port = htons(port); + exp->tuple.dst.protonum = IPPROTO_UDP; + + exp->mask.src.ip = 0xFFFFFFFF; + exp->mask.src.u.udp.port = 0; + exp->mask.dst.ip = 0xFFFFFFFF; + exp->mask.dst.u.udp.port = 0xFFFF; + exp->mask.dst.protonum = 0xFF; + + exp->expectfn = NULL; + exp->flags = 0; + + if (ip_nat_sdp_hook) + ret = ip_nat_sdp_hook(pskb, ctinfo, exp, dptr); + else { + if (ip_conntrack_expect_related(exp) != 0) + ret = NF_DROP; + else + ret = NF_ACCEPT; + } + ip_conntrack_expect_put(exp); + + return ret; +} + +static int sip_help(struct sk_buff **pskb, + struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo) +{ + unsigned int dataoff, datalen; + const char *dptr; + int ret = NF_ACCEPT; + int matchoff, matchlen; + u_int32_t ipaddr; + u_int16_t port; + + /* No Data ? */ + dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + if (dataoff >= (*pskb)->len) { + DEBUGP("skb->len = %u\n", (*pskb)->len); + return NF_ACCEPT; + } + + ip_ct_refresh(ct, *pskb, sip_timeout * HZ); + + if (!skb_is_nonlinear(*pskb)) + dptr = (*pskb)->data + dataoff; + else { + DEBUGP("Copy of skbuff not supported yet.\n"); + goto out; + } + + if (ip_nat_sip_hook) { + if (!ip_nat_sip_hook(pskb, ctinfo, ct, &dptr)) { + ret = NF_DROP; + goto out; + } + } + + /* After this point NAT, could have mangled skb, so + we need to recalculate payload lenght. */ + datalen = (*pskb)->len - dataoff; + + if (datalen < (sizeof("SIP/2.0 200") - 1)) + goto out; + + /* RTP info only in some SDP pkts */ + if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 && + memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) { + goto out; + } + /* Get ip and port address from SDP packet. */ + if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, + &ct_sip_hdrs[POS_CONNECTION]) > 0) { + + /* We'll drop only if there are parse problems. */ + if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr, + dptr + datalen) < 0) { + ret = NF_DROP; + goto out; + } + if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen, + &ct_sip_hdrs[POS_MEDIA]) > 0) { + + port = simple_strtoul(dptr + matchoff, NULL, 10); + if (port < 1024) { + ret = NF_DROP; + goto out; + } + ret = set_expected_rtp(pskb, ct, ctinfo, + ipaddr, port, dptr); + } + } +out: + return ret; +} + +static struct ip_conntrack_helper sip[MAX_PORTS]; +static char sip_names[MAX_PORTS][10]; + +static void fini(void) +{ + int i; + for (i = 0; i < ports_c; i++) { + DEBUGP("unregistering helper for port %d\n", ports[i]); + ip_conntrack_helper_unregister(&sip[i]); + } +} + +static int __init init(void) +{ + int i, ret; + char *tmpname; + + if (ports_c == 0) + ports[ports_c++] = SIP_PORT; + + for (i = 0; i < ports_c; i++) { + /* Create helper structure */ + memset(&sip[i], 0, sizeof(struct ip_conntrack_helper)); + + sip[i].tuple.dst.protonum = IPPROTO_UDP; + sip[i].tuple.src.u.udp.port = htons(ports[i]); + sip[i].mask.src.u.udp.port = 0xFFFF; + sip[i].mask.dst.protonum = 0xFF; + sip[i].max_expected = 1; + sip[i].timeout = 3 * 60; /* 3 minutes */ + sip[i].me = THIS_MODULE; + sip[i].help = sip_help; + + tmpname = &sip_names[i][0]; + if (ports[i] == SIP_PORT) + sprintf(tmpname, "sip"); + else + sprintf(tmpname, "sip-%d", i); + sip[i].name = tmpname; + + DEBUGP("port #%d: %d\n", i, ports[i]); + + ret = ip_conntrack_helper_register(&sip[i]); + if (ret) { + printk("ERROR registering helper for port %d\n", + ports[i]); + fini(); + return ret; + } + } + return 0; +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index 929d61f..7bd3c22 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -12,7 +12,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -189,6 +188,11 @@ #if defined(CONFIG_IP_NF_CONNTRACK_MARK) return -ENOSPC; #endif +#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK + if (seq_printf(s, "secmark=%u ", conntrack->secmark)) + return -ENOSPC; +#endif + if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) return -ENOSPC; @@ -417,7 +421,7 @@ static unsigned int ip_conntrack_help(un /* This is where we call the helper: as the packet goes out. */ ct = ip_conntrack_get(*pskb, &ctinfo); - if (ct && ct->helper) { + if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) { unsigned int ret; ret = ct->helper->help(pskb, ct, ctinfo); if (ret != NF_ACCEPT) @@ -564,6 +568,8 @@ extern unsigned int ip_ct_generic_timeou static int log_invalid_proto_min = 0; static int log_invalid_proto_max = 255; +int ip_conntrack_checksum = 1; + static struct ctl_table_header *ip_ct_sysctl_header; static ctl_table ip_ct_sysctl_table[] = { @@ -592,6 +598,14 @@ static ctl_table ip_ct_sysctl_table[] = .proc_handler = &proc_dointvec, }, { + .ctl_name = NET_IPV4_NF_CONNTRACK_CHECKSUM, + .procname = "ip_conntrack_checksum", + .data = &ip_conntrack_checksum, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, .procname = "ip_conntrack_tcp_timeout_syn_sent", .data = &ip_ct_tcp_timeout_syn_sent, @@ -946,6 +960,7 @@ EXPORT_SYMBOL_GPL(__ip_conntrack_helper_ EXPORT_SYMBOL_GPL(ip_conntrack_proto_find_get); EXPORT_SYMBOL_GPL(ip_conntrack_proto_put); EXPORT_SYMBOL_GPL(__ip_conntrack_proto_find); +EXPORT_SYMBOL_GPL(ip_conntrack_checksum); #if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \ defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE) EXPORT_SYMBOL_GPL(ip_ct_port_tuple_to_nfattr); diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c index 5d506e0..cbcaa45 100644 --- a/net/ipv4/netfilter/ip_nat_helper.c +++ b/net/ipv4/netfilter/ip_nat_helper.c @@ -15,7 +15,6 @@ * - make ip_nat_resize_packet more generic (TCP and UDP) * - add ip_nat_mangle_udp_packet */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ip_nat_helper_h323.c b/net/ipv4/netfilter/ip_nat_helper_h323.c index d45663d..419b878 100644 --- a/net/ipv4/netfilter/ip_nat_helper_h323.c +++ b/net/ipv4/netfilter/ip_nat_helper_h323.c @@ -487,6 +487,80 @@ static int nat_q931(struct sk_buff **psk } /****************************************************************************/ +static void ip_nat_callforwarding_expect(struct ip_conntrack *new, + struct ip_conntrack_expect *this) +{ + struct ip_nat_range range; + + /* This must be a fresh one. */ + BUG_ON(new->status & IPS_NAT_DONE_MASK); + + /* Change src to where master sends to */ + range.flags = IP_NAT_RANGE_MAP_IPS; + range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip; + + /* hook doesn't matter, but it has to do source manip */ + ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING); + + /* For DST manip, map port here to where it's expected. */ + range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED); + range.min = range.max = this->saved_proto; + range.min_ip = range.max_ip = this->saved_ip; + + /* hook doesn't matter, but it has to do destination manip */ + ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING); + + ip_conntrack_q931_expect(new, this); +} + +/****************************************************************************/ +static int nat_callforwarding(struct sk_buff **pskb, struct ip_conntrack *ct, + enum ip_conntrack_info ctinfo, + unsigned char **data, int dataoff, + TransportAddress * addr, u_int16_t port, + struct ip_conntrack_expect *exp) +{ + int dir = CTINFO2DIR(ctinfo); + u_int16_t nated_port; + + /* Set expectations for NAT */ + exp->saved_ip = exp->tuple.dst.ip; + exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip; + exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; + exp->expectfn = ip_nat_callforwarding_expect; + exp->dir = !dir; + + /* Try to get same port: if not, try to change it. */ + for (nated_port = port; nated_port != 0; nated_port++) { + exp->tuple.dst.u.tcp.port = htons(nated_port); + if (ip_conntrack_expect_related(exp) == 0) + break; + } + + if (nated_port == 0) { /* No port available */ + if (net_ratelimit()) + printk("ip_nat_q931: out of TCP ports\n"); + return 0; + } + + /* Modify signal */ + if (!set_h225_addr(pskb, data, dataoff, addr, + ct->tuplehash[!dir].tuple.dst.ip, + nated_port) == 0) { + ip_conntrack_unexpect_related(exp); + return -1; + } + + /* Success */ + DEBUGP("ip_nat_q931: expect Call Forwarding " + "%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n", + NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port), + NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port)); + + return 0; +} + +/****************************************************************************/ static int __init init(void) { BUG_ON(set_h245_addr_hook != NULL); @@ -496,6 +570,7 @@ static int __init init(void) BUG_ON(nat_rtp_rtcp_hook != NULL); BUG_ON(nat_t120_hook != NULL); BUG_ON(nat_h245_hook != NULL); + BUG_ON(nat_callforwarding_hook != NULL); BUG_ON(nat_q931_hook != NULL); set_h245_addr_hook = set_h245_addr; @@ -505,6 +580,7 @@ static int __init init(void) nat_rtp_rtcp_hook = nat_rtp_rtcp; nat_t120_hook = nat_t120; nat_h245_hook = nat_h245; + nat_callforwarding_hook = nat_callforwarding; nat_q931_hook = nat_q931; DEBUGP("ip_nat_h323: init success\n"); @@ -521,6 +597,7 @@ static void __exit fini(void) nat_rtp_rtcp_hook = NULL; nat_t120_hook = NULL; nat_h245_hook = NULL; + nat_callforwarding_hook = NULL; nat_q931_hook = NULL; synchronize_net(); } diff --git a/net/ipv4/netfilter/ip_nat_helper_pptp.c b/net/ipv4/netfilter/ip_nat_helper_pptp.c index f397772..1d14996 100644 --- a/net/ipv4/netfilter/ip_nat_helper_pptp.c +++ b/net/ipv4/netfilter/ip_nat_helper_pptp.c @@ -35,7 +35,6 @@ * */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c index 96ceaba..38acfdf 100644 --- a/net/ipv4/netfilter/ip_nat_proto_gre.c +++ b/net/ipv4/netfilter/ip_nat_proto_gre.c @@ -23,7 +23,6 @@ * */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ip_nat_sip.c b/net/ipv4/netfilter/ip_nat_sip.c new file mode 100644 index 0000000..6ffba63 --- /dev/null +++ b/net/ipv4/netfilter/ip_nat_sip.c @@ -0,0 +1,249 @@ +/* SIP extension for UDP NAT alteration. + * + * (C) 2005 by Christian Hentschel + * based on RR's ip_nat_ftp.c and other modules. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Christian Hentschel "); +MODULE_DESCRIPTION("SIP NAT helper"); + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(format, args...) +#endif + +extern struct sip_header_nfo ct_sip_hdrs[]; + +static unsigned int mangle_sip_packet(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack *ct, + const char **dptr, size_t dlen, + char *buffer, int bufflen, + struct sip_header_nfo *hnfo) +{ + unsigned int matchlen, matchoff; + + if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0) + return 0; + + if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + matchoff, matchlen, buffer, bufflen)) + return 0; + + /* We need to reload this. Thanks Patrick. */ + *dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + return 1; +} + +static unsigned int ip_nat_sip(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack *ct, + const char **dptr) +{ + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")]; + unsigned int bufflen, dataoff; + u_int32_t ip; + u_int16_t port; + + dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + + ip = ct->tuplehash[!dir].tuple.dst.ip; + port = ct->tuplehash[!dir].tuple.dst.u.udp.port; + bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port)); + + /* short packet ? */ + if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1)) + return 0; + + /* Basic rules: requests and responses. */ + if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) { + const char *aux; + + if ((ctinfo) < IP_CT_IS_REPLY) { + mangle_sip_packet(pskb, ctinfo, ct, dptr, + (*pskb)->len - dataoff, + buffer, bufflen, + &ct_sip_hdrs[POS_CONTACT]); + return 1; + } + + if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, + (*pskb)->len - dataoff, + buffer, bufflen, &ct_sip_hdrs[POS_VIA])) + return 0; + + /* This search should ignore case, but later.. */ + aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1, + (*pskb)->len - dataoff); + if (!aux) + return 0; + + if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"), + ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff))) + return 1; + + return mangle_sip_packet(pskb, ctinfo, ct, dptr, + (*pskb)->len - dataoff, + buffer, bufflen, + &ct_sip_hdrs[POS_CONTACT]); + } + if ((ctinfo) < IP_CT_IS_REPLY) { + if (!mangle_sip_packet(pskb, ctinfo, ct, dptr, + (*pskb)->len - dataoff, + buffer, bufflen, &ct_sip_hdrs[POS_VIA])) + return 0; + + /* Mangle Contact if exists only. - watch udp_nat_mangle()! */ + mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff, + buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]); + return 1; + } + /* This mangle requests headers. */ + return mangle_sip_packet(pskb, ctinfo, ct, dptr, + ct_sip_lnlen(*dptr, + *dptr + (*pskb)->len - dataoff), + buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]); +} + +static int mangle_content_len(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack *ct, + const char *dptr) +{ + unsigned int dataoff, matchoff, matchlen; + char buffer[sizeof("65536")]; + int bufflen; + + dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + + /* Get actual SDP lenght */ + if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, + &matchlen, &ct_sip_hdrs[POS_SDP_HEADER]) > 0) { + + /* since ct_sip_get_info() give us a pointer passing 'v=' + we need to add 2 bytes in this count. */ + int c_len = (*pskb)->len - dataoff - matchoff + 2; + + /* Now, update SDP lenght */ + if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff, + &matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) { + + bufflen = sprintf(buffer, "%u", c_len); + + return ip_nat_mangle_udp_packet(pskb, ct, ctinfo, + matchoff, matchlen, + buffer, bufflen); + } + } + return 0; +} + +static unsigned int mangle_sdp(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack *ct, + u_int32_t newip, u_int16_t port, + const char *dptr) +{ + char buffer[sizeof("nnn.nnn.nnn.nnn")]; + unsigned int dataoff, bufflen; + + dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr); + + /* Mangle owner and contact info. */ + bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip)); + if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, + buffer, bufflen, &ct_sip_hdrs[POS_OWNER])) + return 0; + + if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, + buffer, bufflen, &ct_sip_hdrs[POS_CONNECTION])) + return 0; + + /* Mangle media port. */ + bufflen = sprintf(buffer, "%u", port); + if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff, + buffer, bufflen, &ct_sip_hdrs[POS_MEDIA])) + return 0; + + return mangle_content_len(pskb, ctinfo, ct, dptr); +} + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int ip_nat_sdp(struct sk_buff **pskb, + enum ip_conntrack_info ctinfo, + struct ip_conntrack_expect *exp, + const char *dptr) +{ + struct ip_conntrack *ct = exp->master; + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + u_int32_t newip; + u_int16_t port; + + DEBUGP("ip_nat_sdp():\n"); + + /* Connection will come from reply */ + newip = ct->tuplehash[!dir].tuple.dst.ip; + + exp->tuple.dst.ip = newip; + exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port; + exp->dir = !dir; + + /* When you see the packet, we need to NAT it the same as the + this one. */ + exp->expectfn = ip_nat_follow_master; + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) { + exp->tuple.dst.u.udp.port = htons(port); + if (ip_conntrack_expect_related(exp) == 0) + break; + } + + if (port == 0) + return NF_DROP; + + if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) { + ip_conntrack_unexpect_related(exp); + return NF_DROP; + } + return NF_ACCEPT; +} + +static void __exit fini(void) +{ + ip_nat_sip_hook = NULL; + ip_nat_sdp_hook = NULL; + /* Make sure noone calls it, meanwhile. */ + synchronize_net(); +} + +static int __init init(void) +{ + BUG_ON(ip_nat_sip_hook); + BUG_ON(ip_nat_sdp_hook); + ip_nat_sip_hook = ip_nat_sip; + ip_nat_sdp_hook = ip_nat_sdp; + return 0; +} + +module_init(init); +module_exit(fini); diff --git a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c index c332442..0b1b416 100644 --- a/net/ipv4/netfilter/ip_nat_snmp_basic.c +++ b/net/ipv4/netfilter/ip_nat_snmp_basic.c @@ -43,7 +43,6 @@ * 2000-08-06: Convert to new helper API (Harald Welte). * */ -#include #include #include #include @@ -1348,4 +1347,4 @@ static void __exit ip_nat_snmp_basic_fin module_init(ip_nat_snmp_basic_init); module_exit(ip_nat_snmp_basic_fini); -module_param(debug, bool, 0600); +module_param(debug, int, 0600); diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c index 67e6767..17de077 100644 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ b/net/ipv4/netfilter/ip_nat_standalone.c @@ -18,7 +18,6 @@ * - now capable of multiple expectations for one master * */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c index b93f049..198ac36 100644 --- a/net/ipv4/netfilter/ip_queue.c +++ b/net/ipv4/netfilter/ip_queue.c @@ -457,11 +457,19 @@ dev_cmp(struct ipq_queue_entry *entry, u if (entry->info->indev) if (entry->info->indev->ifindex == ifindex) return 1; - if (entry->info->outdev) if (entry->info->outdev->ifindex == ifindex) return 1; - +#ifdef CONFIG_BRIDGE_NETFILTER + if (entry->skb->nf_bridge) { + if (entry->skb->nf_bridge->physindev && + entry->skb->nf_bridge->physindev->ifindex == ifindex) + return 1; + if (entry->skb->nf_bridge->physoutdev && + entry->skb->nf_bridge->physoutdev->ifindex == ifindex) + return 1; + } +#endif return 0; } @@ -507,7 +515,7 @@ ipq_rcv_skb(struct sk_buff *skb) if (type <= IPQM_BASE) return; - if (security_netlink_recv(skb)) + if (security_netlink_recv(skb, CAP_NET_ADMIN)) RCV_SKB_FAIL(-EPERM); write_lock_bh(&queue_lock); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index cee3397..fc5bdd5 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -14,7 +14,6 @@ * 08 Oct 2005 Harald Welte * - Generalize into "x_tables" layer and "{ip,ip6,arp}_tables" */ -#include #include #include #include @@ -1761,7 +1760,7 @@ translate_compat_table(const char *name, goto free_newinfo; /* And one copy for every other CPU */ - for_each_cpu(i) + for_each_possible_cpu(i) if (newinfo->entries[i] && newinfo->entries[i] != entry1) memcpy(newinfo->entries[i], entry1, newinfo->size); @@ -2113,7 +2112,8 @@ int ipt_register_table(struct xt_table * return ret; } - if (xt_register_table(table, &bootstrap, newinfo) != 0) { + ret = xt_register_table(table, &bootstrap, newinfo); + if (ret != 0) { xt_free_table_info(newinfo); return ret; } diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index aad9d28..cbffeae 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -10,7 +10,6 @@ * */ #include -#include #include #include #include @@ -241,25 +240,17 @@ clusterip_hashfn(struct sk_buff *skb, st struct iphdr *iph = skb->nh.iph; unsigned long hashval; u_int16_t sport, dport; - struct tcphdr *th; - struct udphdr *uh; - struct icmphdr *ih; + u_int16_t *ports; switch (iph->protocol) { case IPPROTO_TCP: - th = (void *)iph+iph->ihl*4; - sport = ntohs(th->source); - dport = ntohs(th->dest); - break; case IPPROTO_UDP: - uh = (void *)iph+iph->ihl*4; - sport = ntohs(uh->source); - dport = ntohs(uh->dest); - break; + case IPPROTO_SCTP: + case IPPROTO_DCCP: case IPPROTO_ICMP: - ih = (void *)iph+iph->ihl*4; - sport = ntohs(ih->un.echo.id); - dport = (ih->type<<8)|ih->code; + ports = (void *)iph+iph->ihl*4; + sport = ports[0]; + dport = ports[1]; break; default: if (net_ratelimit()) { diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index 8b3e7f9..ebd94f2 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ipt_NETMAP.c b/net/ipv4/netfilter/ipt_NETMAP.c index 2fcf107..736c4b5 100644 --- a/net/ipv4/netfilter/ipt_NETMAP.c +++ b/net/ipv4/netfilter/ipt_NETMAP.c @@ -10,7 +10,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 0bba3c2..269bc20 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c @@ -12,7 +12,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -147,6 +146,7 @@ static void send_reset(struct sk_buff *o /* This packet will not be the same as the other: clear nf fields */ nf_reset(nskb); nskb->nfmark = 0; + skb_init_secmark(nskb); tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl); diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c index c84cc03..d7dd7fe 100644 --- a/net/ipv4/netfilter/ipt_ULOG.c +++ b/net/ipv4/netfilter/ipt_ULOG.c @@ -47,7 +47,6 @@ */ #include -#include #include #include #include diff --git a/net/ipv4/netfilter/ipt_hashlimit.c b/net/ipv4/netfilter/ipt_hashlimit.c index 7c6836c..92980ab 100644 --- a/net/ipv4/netfilter/ipt_hashlimit.c +++ b/net/ipv4/netfilter/ipt_hashlimit.c @@ -28,9 +28,6 @@ #include #include #include #include -#include -#include -#include #include #include #include @@ -83,6 +80,7 @@ struct ipt_hashlimit_htable { /* used internally */ spinlock_t lock; /* lock for list_head */ u_int32_t rnd; /* random seed for hash */ + int rnd_initialized; struct timer_list timer; /* timer for gc */ atomic_t count; /* number entries in table */ @@ -137,8 +135,10 @@ __dsthash_alloc_init(struct ipt_hashlimi /* initialize hash with random val at the time we allocate * the first hashtable entry */ - if (!ht->rnd) + if (!ht->rnd_initialized) { get_random_bytes(&ht->rnd, 4); + ht->rnd_initialized = 1; + } if (ht->cfg.max && atomic_read(&ht->count) >= ht->cfg.max) { @@ -217,7 +217,7 @@ static int htable_create(struct ipt_hash atomic_set(&hinfo->count, 0); atomic_set(&hinfo->use, 1); - hinfo->rnd = 0; + hinfo->rnd_initialized = 0; spin_lock_init(&hinfo->lock); hinfo->pde = create_proc_entry(minfo->name, 0, hashlimit_procdir); if (!hinfo->pde) { @@ -381,49 +381,6 @@ static inline void rateinfo_recalc(struc dh->rateinfo.credit = dh->rateinfo.credit_cap; } -static inline int get_ports(const struct sk_buff *skb, int offset, - u16 ports[2]) -{ - union { - struct tcphdr th; - struct udphdr uh; - sctp_sctphdr_t sctph; - } hdr_u, *ptr_u; - - /* Must not be a fragment. */ - if (offset) - return 1; - - /* Must be big enough to read ports (both UDP and TCP have - them at the start). */ - ptr_u = skb_header_pointer(skb, skb->nh.iph->ihl*4, 8, &hdr_u); - if (!ptr_u) - return 1; - - switch (skb->nh.iph->protocol) { - case IPPROTO_TCP: - ports[0] = ptr_u->th.source; - ports[1] = ptr_u->th.dest; - break; - case IPPROTO_UDP: - ports[0] = ptr_u->uh.source; - ports[1] = ptr_u->uh.dest; - break; - case IPPROTO_SCTP: - ports[0] = ptr_u->sctph.source; - ports[1] = ptr_u->sctph.dest; - break; - default: - /* all other protocols don't supprot per-port hash - * buckets */ - ports[0] = ports[1] = 0; - break; - } - - return 0; -} - - static int hashlimit_match(const struct sk_buff *skb, const struct net_device *in, @@ -449,8 +406,22 @@ hashlimit_match(const struct sk_buff *sk dst.src_ip = skb->nh.iph->saddr; if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT ||hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT) { - u_int16_t ports[2]; - if (get_ports(skb, offset, ports)) { + u_int16_t _ports[2], *ports; + + switch (skb->nh.iph->protocol) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_DCCP: + ports = skb_header_pointer(skb, skb->nh.iph->ihl*4, + sizeof(_ports), &_ports); + break; + default: + _ports[0] = _ports[1] = 0; + ports = _ports; + break; + } + if (!ports) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ *hotdrop = 1; @@ -561,7 +532,7 @@ static void hashlimit_destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize) { - struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *) matchinfo; + struct ipt_hashlimit_info *r = matchinfo; htable_put(r->hinfo); } diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c index b847ee4..61a2139 100644 --- a/net/ipv4/netfilter/ipt_recent.c +++ b/net/ipv4/netfilter/ipt_recent.c @@ -1,1007 +1,499 @@ -/* Kernel module to check if the source address has been seen recently. */ -/* Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org */ -/* Author: Stephen Frost */ -/* Project Page: http://snowman.net/projects/ipt_recent/ */ -/* This software is distributed under the terms of the GPL, Version 2 */ -/* This copyright does not cover user programs that use kernel services - * by normal system calls. */ - -#include -#include +/* + * Copyright (c) 2006 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is a replacement of the old ipt_recent module, which carried the + * following copyright notice: + * + * Author: Stephen Frost + * Copyright 2002-2003, Stephen Frost, 2.5.x port by laforge@netfilter.org + */ +#include +#include #include -#include -#include -#include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include -#undef DEBUG -#define HASH_LOG 9 +MODULE_AUTHOR("Patrick McHardy "); +MODULE_DESCRIPTION("IP tables recently seen matching module"); +MODULE_LICENSE("GPL"); -/* Defaults, these can be overridden on the module command-line. */ static unsigned int ip_list_tot = 100; static unsigned int ip_pkt_list_tot = 20; static unsigned int ip_list_hash_size = 0; static unsigned int ip_list_perms = 0644; -#ifdef DEBUG -static int debug = 1; -#endif - -static char version[] = -KERN_INFO RECENT_NAME " " RECENT_VER ": Stephen Frost . http://snowman.net/projects/ipt_recent/\n"; - -MODULE_AUTHOR("Stephen Frost "); -MODULE_DESCRIPTION("IP tables recently seen matching module " RECENT_VER); -MODULE_LICENSE("GPL"); module_param(ip_list_tot, uint, 0400); module_param(ip_pkt_list_tot, uint, 0400); module_param(ip_list_hash_size, uint, 0400); module_param(ip_list_perms, uint, 0400); -#ifdef DEBUG -module_param(debug, bool, 0600); -MODULE_PARM_DESC(debug,"enable debugging output"); -#endif -MODULE_PARM_DESC(ip_list_tot,"number of IPs to remember per list"); -MODULE_PARM_DESC(ip_pkt_list_tot,"number of packets per IP to remember"); -MODULE_PARM_DESC(ip_list_hash_size,"size of hash table used to look up IPs"); -MODULE_PARM_DESC(ip_list_perms,"permissions on /proc/net/ipt_recent/* files"); - -/* Structure of our list of recently seen addresses. */ -struct recent_ip_list { - u_int32_t addr; - u_int8_t ttl; - unsigned long last_seen; - unsigned long *last_pkts; - u_int32_t oldest_pkt; - u_int32_t hash_entry; - u_int32_t time_pos; -}; - -struct time_info_list { - u_int32_t position; - u_int32_t time; +MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list"); +MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP to remember (max. 255)"); +MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs"); +MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/ipt_recent/* files"); + + +struct recent_entry { + struct list_head list; + struct list_head lru_list; + u_int32_t addr; + u_int8_t ttl; + u_int8_t index; + u_int16_t nstamps; + unsigned long stamps[0]; }; -/* Structure of our linked list of tables of recent lists. */ -struct recent_ip_tables { - char name[IPT_RECENT_NAME_LEN]; - int count; - int time_pos; - struct recent_ip_list *table; - struct recent_ip_tables *next; - spinlock_t list_lock; - int *hash_table; - struct time_info_list *time_info; +struct recent_table { + struct list_head list; + char name[IPT_RECENT_NAME_LEN]; #ifdef CONFIG_PROC_FS - struct proc_dir_entry *status_proc; -#endif /* CONFIG_PROC_FS */ + struct proc_dir_entry *proc; +#endif + unsigned int refcnt; + unsigned int entries; + struct list_head lru_list; + struct list_head iphash[0]; }; -/* Our current list of addresses we have recently seen. - * Only added to on a --set, and only updated on --set || --update - */ -static struct recent_ip_tables *r_tables = NULL; - -/* We protect r_list with this spinlock so two processors are not modifying - * the list at the same time. - */ +static LIST_HEAD(tables); static DEFINE_SPINLOCK(recent_lock); +static DEFINE_MUTEX(recent_mutex); #ifdef CONFIG_PROC_FS -/* Our /proc/net/ipt_recent entry */ -static struct proc_dir_entry *proc_net_ipt_recent = NULL; -#endif - -/* Function declaration for later. */ -static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop); - -/* Function to hash a given address into the hash table of table_size size */ -static int hash_func(unsigned int addr, int table_size) -{ - int result = 0; - unsigned int value = addr; - do { result ^= value; } while((value >>= HASH_LOG)); - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": %d = hash_func(%u,%d)\n", - result & (table_size - 1), - addr, - table_size); +static struct proc_dir_entry *proc_dir; +static struct file_operations recent_fops; #endif - return(result & (table_size - 1)); -} +static u_int32_t hash_rnd; +static int hash_rnd_initted; -#ifdef CONFIG_PROC_FS -/* This is the function which produces the output for our /proc output - * interface which lists each IP address, the last seen time and the - * other recent times the address was seen. - */ - -static int ip_recent_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +static unsigned int recent_entry_hash(u_int32_t addr) { - int len = 0, count, last_len = 0, pkt_count; - off_t pos = 0; - off_t begin = 0; - struct recent_ip_tables *curr_table; - - curr_table = (struct recent_ip_tables*) data; - - spin_lock_bh(&curr_table->list_lock); - for(count = 0; count < ip_list_tot; count++) { - if(!curr_table->table[count].addr) continue; - last_len = len; - len += sprintf(buffer+len,"src=%u.%u.%u.%u ",NIPQUAD(curr_table->table[count].addr)); - len += sprintf(buffer+len,"ttl: %u ",curr_table->table[count].ttl); - len += sprintf(buffer+len,"last_seen: %lu ",curr_table->table[count].last_seen); - len += sprintf(buffer+len,"oldest_pkt: %u ",curr_table->table[count].oldest_pkt); - len += sprintf(buffer+len,"last_pkts: %lu",curr_table->table[count].last_pkts[0]); - for(pkt_count = 1; pkt_count < ip_pkt_list_tot; pkt_count++) { - if(!curr_table->table[count].last_pkts[pkt_count]) break; - len += sprintf(buffer+len,", %lu",curr_table->table[count].last_pkts[pkt_count]); - } - len += sprintf(buffer+len,"\n"); - pos = begin + len; - if(pos < offset) { len = 0; begin = pos; } - if(pos > offset + length) { len = last_len; break; } + if (!hash_rnd_initted) { + get_random_bytes(&hash_rnd, 4); + hash_rnd_initted = 1; } - - *start = buffer + (offset - begin); - len -= (offset - begin); - if(len > length) len = length; - - spin_unlock_bh(&curr_table->list_lock); - return len; + return jhash_1word(addr, hash_rnd) & (ip_list_hash_size - 1); } -/* ip_recent_ctrl provides an interface for users to modify the table - * directly. This allows adding entries, removing entries, and - * flushing the entire table. - * This is done by opening up the appropriate table for writing and - * sending one of: - * xx.xx.xx.xx -- Add entry to table with current time - * +xx.xx.xx.xx -- Add entry to table with current time - * -xx.xx.xx.xx -- Remove entry from table - * clear -- Flush table, remove all entries - */ - -static int ip_recent_ctrl(struct file *file, const char __user *input, unsigned long size, void *data) +static struct recent_entry * +recent_entry_lookup(const struct recent_table *table, u_int32_t addr, u_int8_t ttl) { - static const u_int32_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff }; - u_int32_t val; - int base, used = 0; - char c, *cp; - union iaddr { - uint8_t bytes[4]; - uint32_t word; - } res; - uint8_t *pp = res.bytes; - int digit; - - char buffer[20]; - int len, check_set = 0, count; - u_int32_t addr = 0; - struct sk_buff *skb; - struct ipt_recent_info *info; - struct recent_ip_tables *curr_table; - - curr_table = (struct recent_ip_tables*) data; - - if(size > 20) len = 20; else len = size; - - if(copy_from_user(buffer,input,len)) return -EFAULT; - - if(len < 20) buffer[len] = '\0'; - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl len: %d, input: `%.20s'\n",len,buffer); -#endif + struct recent_entry *e; + unsigned int h; + + h = recent_entry_hash(addr); + list_for_each_entry(e, &table->iphash[h], list) + if (e->addr == addr && (ttl == e->ttl || !ttl || !e->ttl)) + return e; + return NULL; +} - cp = buffer; - while(isspace(*cp)) { cp++; used++; if(used >= len-5) return used; } +static void recent_entry_remove(struct recent_table *t, struct recent_entry *e) +{ + list_del(&e->list); + list_del(&e->lru_list); + kfree(e); + t->entries--; +} - /* Check if we are asked to flush the entire table */ - if(!memcmp(cp,"clear",5)) { - used += 5; - spin_lock_bh(&curr_table->list_lock); - curr_table->time_pos = 0; - for(count = 0; count < ip_list_hash_size; count++) { - curr_table->hash_table[count] = -1; - } - for(count = 0; count < ip_list_tot; count++) { - curr_table->table[count].last_seen = 0; - curr_table->table[count].addr = 0; - curr_table->table[count].ttl = 0; - memset(curr_table->table[count].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long)); - curr_table->table[count].oldest_pkt = 0; - curr_table->table[count].time_pos = 0; - curr_table->time_info[count].position = count; - curr_table->time_info[count].time = 0; - } - spin_unlock_bh(&curr_table->list_lock); - return used; - } +static struct recent_entry * +recent_entry_init(struct recent_table *t, u_int32_t addr, u_int8_t ttl) +{ + struct recent_entry *e; - check_set = IPT_RECENT_SET; - switch(*cp) { - case '+': check_set = IPT_RECENT_SET; cp++; used++; break; - case '-': check_set = IPT_RECENT_REMOVE; cp++; used++; break; - default: if(!isdigit(*cp)) return (used+1); break; + if (t->entries >= ip_list_tot) { + e = list_entry(t->lru_list.next, struct recent_entry, lru_list); + recent_entry_remove(t, e); } + e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot, + GFP_ATOMIC); + if (e == NULL) + return NULL; + e->addr = addr; + e->ttl = ttl; + e->stamps[0] = jiffies; + e->nstamps = 1; + e->index = 1; + list_add_tail(&e->list, &t->iphash[recent_entry_hash(addr)]); + list_add_tail(&e->lru_list, &t->lru_list); + t->entries++; + return e; +} -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl cp: `%c', check_set: %d\n",*cp,check_set); -#endif - /* Get addr (effectively inet_aton()) */ - /* Shamelessly stolen from libc, a function in the kernel for doing - * this would, of course, be greatly preferred, but our options appear - * to be rather limited, so we will just do it ourselves here. - */ - res.word = 0; - - c = *cp; - for(;;) { - if(!isdigit(c)) return used; - val = 0; base = 10; digit = 0; - if(c == '0') { - c = *++cp; - if(c == 'x' || c == 'X') base = 16, c = *++cp; - else { base = 8; digit = 1; } - } - for(;;) { - if(isascii(c) && isdigit(c)) { - if(base == 8 && (c == '8' || c == '0')) return used; - val = (val * base) + (c - '0'); - c = *++cp; - digit = 1; - } else if(base == 16 && isascii(c) && isxdigit(c)) { - val = (val << 4) | (c + 10 - (islower(c) ? 'a' : 'A')); - c = *++cp; - digit = 1; - } else break; - } - if(c == '.') { - if(pp > res.bytes + 2 || val > 0xff) return used; - *pp++ = val; - c = *++cp; - } else break; - } - used = cp - buffer; - if(c != '\0' && (!isascii(c) || !isspace(c))) return used; - if(c == '\n') used++; - if(!digit) return used; +static void recent_entry_update(struct recent_table *t, struct recent_entry *e) +{ + e->stamps[e->index++] = jiffies; + if (e->index > e->nstamps) + e->nstamps = e->index; + e->index %= ip_pkt_list_tot; + list_move_tail(&e->lru_list, &t->lru_list); +} - if(val > max[pp - res.bytes]) return used; - addr = res.word | htonl(val); +static struct recent_table *recent_table_lookup(const char *name) +{ + struct recent_table *t; - if(!addr && check_set == IPT_RECENT_SET) return used; + list_for_each_entry(t, &tables, list) + if (!strcmp(t->name, name)) + return t; + return NULL; +} -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": ip_recent_ctrl c: %c, addr: %u used: %d\n",c,addr,used); -#endif +static void recent_table_flush(struct recent_table *t) +{ + struct recent_entry *e, *next; + unsigned int i; - /* Set up and just call match */ - info = kmalloc(sizeof(struct ipt_recent_info),GFP_KERNEL); - if(!info) { return -ENOMEM; } - info->seconds = 0; - info->hit_count = 0; - info->check_set = check_set; - info->invert = 0; - info->side = IPT_RECENT_SOURCE; - strncpy(info->name,curr_table->name,IPT_RECENT_NAME_LEN); - info->name[IPT_RECENT_NAME_LEN-1] = '\0'; - - skb = kmalloc(sizeof(struct sk_buff),GFP_KERNEL); - if (!skb) { - used = -ENOMEM; - goto out_free_info; - } - skb->nh.iph = kmalloc(sizeof(struct iphdr),GFP_KERNEL); - if (!skb->nh.iph) { - used = -ENOMEM; - goto out_free_skb; + for (i = 0; i < ip_list_hash_size; i++) { + list_for_each_entry_safe(e, next, &t->iphash[i], list) + recent_entry_remove(t, e); } - - skb->nh.iph->saddr = addr; - skb->nh.iph->daddr = 0; - /* Clear ttl since we have no way of knowing it */ - skb->nh.iph->ttl = 0; - match(skb,NULL,NULL,NULL,info,0,0,NULL); - - kfree(skb->nh.iph); -out_free_skb: - kfree(skb); -out_free_info: - kfree(info); - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": Leaving ip_recent_ctrl addr: %u used: %d\n",addr,used); -#endif - return used; } -#endif /* CONFIG_PROC_FS */ - -/* 'match' is our primary function, called by the kernel whenever a rule is - * hit with our module as an option to it. - * What this function does depends on what was specifically asked of it by - * the user: - * --set -- Add or update last seen time of the source address of the packet - * -- matchinfo->check_set == IPT_RECENT_SET - * --rcheck -- Just check if the source address is in the list - * -- matchinfo->check_set == IPT_RECENT_CHECK - * --update -- If the source address is in the list, update last_seen - * -- matchinfo->check_set == IPT_RECENT_UPDATE - * --remove -- If the source address is in the list, remove it - * -- matchinfo->check_set == IPT_RECENT_REMOVE - * --seconds -- Option to --rcheck/--update, only match if last_seen within seconds - * -- matchinfo->seconds - * --hitcount -- Option to --rcheck/--update, only match if seen hitcount times - * -- matchinfo->hit_count - * --seconds and --hitcount can be combined - */ static int -match(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - int *hotdrop) +ipt_recent_match(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + const struct xt_match *match, const void *matchinfo, + int offset, unsigned int protoff, int *hotdrop) { - int pkt_count, hits_found, ans; - unsigned long now; const struct ipt_recent_info *info = matchinfo; - u_int32_t addr = 0, time_temp; - u_int8_t ttl = skb->nh.iph->ttl; - int *hash_table; - int orig_hash_result, hash_result, temp, location = 0, time_loc, end_collision_chain = -1; - struct time_info_list *time_info; - struct recent_ip_tables *curr_table; - struct recent_ip_tables *last_table; - struct recent_ip_list *r_list; - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match() called\n"); -#endif - - /* Default is false ^ info->invert */ - ans = info->invert; + struct recent_table *t; + struct recent_entry *e; + u_int32_t addr; + u_int8_t ttl; + int ret = info->invert; -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): name = '%s'\n",info->name); -#endif + if (info->side == IPT_RECENT_DEST) + addr = skb->nh.iph->daddr; + else + addr = skb->nh.iph->saddr; - /* if out != NULL then routing has been done and TTL changed. - * We change it back here internally for match what came in before routing. */ - if(out) ttl++; + ttl = skb->nh.iph->ttl; + /* use TTL as seen before forwarding */ + if (out && !skb->sk) + ttl++; - /* Find the right table */ spin_lock_bh(&recent_lock); - curr_table = r_tables; - while( (last_table = curr_table) && strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (curr_table = curr_table->next) ); - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): table found('%s')\n",info->name); -#endif - - spin_unlock_bh(&recent_lock); - - /* Table with this name not found, match impossible */ - if(!curr_table) { return ans; } - - /* Make sure no one is changing the list while we work with it */ - spin_lock_bh(&curr_table->list_lock); - - r_list = curr_table->table; - if(info->side == IPT_RECENT_DEST) addr = skb->nh.iph->daddr; else addr = skb->nh.iph->saddr; - - if(!addr) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match() address (%u) invalid, leaving.\n",addr); -#endif - spin_unlock_bh(&curr_table->list_lock); - return ans; - } - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): checking table, addr: %u, ttl: %u, orig_ttl: %u\n",addr,ttl,skb->nh.iph->ttl); -#endif - - /* Get jiffies now in case they changed while we were waiting for a lock */ - now = jiffies; - hash_table = curr_table->hash_table; - time_info = curr_table->time_info; - - orig_hash_result = hash_result = hash_func(addr,ip_list_hash_size); - /* Hash entry at this result used */ - /* Check for TTL match if requested. If TTL is zero then a match would never - * happen, so match regardless of existing TTL in that case. Zero means the - * entry was added via the /proc interface anyway, so we will just use the - * first TTL we get for that IP address. */ - if(info->check_set & IPT_RECENT_TTL) { - while(hash_table[hash_result] != -1 && !(r_list[hash_table[hash_result]].addr == addr && - (!r_list[hash_table[hash_result]].ttl || r_list[hash_table[hash_result]].ttl == ttl))) { - /* Collision in hash table */ - hash_result = (hash_result + 1) % ip_list_hash_size; - } - } else { - while(hash_table[hash_result] != -1 && r_list[hash_table[hash_result]].addr != addr) { - /* Collision in hash table */ - hash_result = (hash_result + 1) % ip_list_hash_size; - } - } - - if(hash_table[hash_result] == -1 && !(info->check_set & IPT_RECENT_SET)) { - /* IP not in list and not asked to SET */ - spin_unlock_bh(&curr_table->list_lock); - return ans; - } - - /* Check if we need to handle the collision, do not need to on REMOVE */ - if(orig_hash_result != hash_result && !(info->check_set & IPT_RECENT_REMOVE)) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision in hash table. (or: %d,hr: %d,oa: %u,ha: %u)\n", - orig_hash_result, - hash_result, - r_list[hash_table[orig_hash_result]].addr, - addr); -#endif - - /* We had a collision. - * orig_hash_result is where we started, hash_result is where we ended up. - * So, swap them because we are likely to see the same guy again sooner */ -#ifdef DEBUG - if(debug) { - printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[orig_hash_result] = %d\n",hash_table[orig_hash_result]); - printk(KERN_INFO RECENT_NAME ": match(): Collision; r_list[hash_table[orig_hash_result]].hash_entry = %d\n", - r_list[hash_table[orig_hash_result]].hash_entry); - } -#endif - - r_list[hash_table[orig_hash_result]].hash_entry = hash_result; - - - temp = hash_table[orig_hash_result]; -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision; hash_table[hash_result] = %d\n",hash_table[hash_result]); -#endif - hash_table[orig_hash_result] = hash_table[hash_result]; - hash_table[hash_result] = temp; - temp = hash_result; - hash_result = orig_hash_result; - orig_hash_result = temp; - time_info[r_list[hash_table[orig_hash_result]].time_pos].position = hash_table[orig_hash_result]; - if(hash_table[hash_result] != -1) { - r_list[hash_table[hash_result]].hash_entry = hash_result; - time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result]; - } - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): Collision handled.\n"); -#endif + t = recent_table_lookup(info->name); + e = recent_entry_lookup(t, addr, + info->check_set & IPT_RECENT_TTL ? ttl : 0); + if (e == NULL) { + if (!(info->check_set & IPT_RECENT_SET)) + goto out; + e = recent_entry_init(t, addr, ttl); + if (e == NULL) + *hotdrop = 1; + ret ^= 1; + goto out; } - if(hash_table[hash_result] == -1) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): New table entry. (hr: %d,ha: %u)\n", - hash_result, addr); -#endif - - /* New item found and IPT_RECENT_SET, so we need to add it */ - location = time_info[curr_table->time_pos].position; - hash_table[r_list[location].hash_entry] = -1; - hash_table[hash_result] = location; - memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long)); - r_list[location].time_pos = curr_table->time_pos; - r_list[location].addr = addr; - r_list[location].ttl = ttl; - r_list[location].last_seen = now; - r_list[location].oldest_pkt = 1; - r_list[location].last_pkts[0] = now; - r_list[location].hash_entry = hash_result; - time_info[curr_table->time_pos].time = r_list[location].last_seen; - curr_table->time_pos = (curr_table->time_pos + 1) % ip_list_tot; - - ans = !info->invert; - } else { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): Existing table entry. (hr: %d,ha: %u)\n", - hash_result, - addr); -#endif - - /* Existing item found */ - location = hash_table[hash_result]; - /* We have a match on address, now to make sure it meets all requirements for a - * full match. */ - if(info->check_set & IPT_RECENT_CHECK || info->check_set & IPT_RECENT_UPDATE) { - if(!info->seconds && !info->hit_count) ans = !info->invert; else ans = info->invert; - if(info->seconds && !info->hit_count) { - if(time_before_eq(now,r_list[location].last_seen+info->seconds*HZ)) ans = !info->invert; else ans = info->invert; - } - if(info->seconds && info->hit_count) { - for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) { - if(r_list[location].last_pkts[pkt_count] == 0) break; - if(time_before_eq(now,r_list[location].last_pkts[pkt_count]+info->seconds*HZ)) hits_found++; - } - if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert; - } - if(info->hit_count && !info->seconds) { - for(pkt_count = 0, hits_found = 0; pkt_count < ip_pkt_list_tot; pkt_count++) { - if(r_list[location].last_pkts[pkt_count] == 0) break; - hits_found++; - } - if(hits_found >= info->hit_count) ans = !info->invert; else ans = info->invert; + if (info->check_set & IPT_RECENT_SET) + ret ^= 1; + else if (info->check_set & IPT_RECENT_REMOVE) { + recent_entry_remove(t, e); + ret ^= 1; + } else if (info->check_set & (IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) { + unsigned long t = jiffies - info->seconds * HZ; + unsigned int i, hits = 0; + + for (i = 0; i < e->nstamps; i++) { + if (info->seconds && time_after(t, e->stamps[i])) + continue; + if (++hits >= info->hit_count) { + ret ^= 1; + break; } } -#ifdef DEBUG - if(debug) { - if(ans) - printk(KERN_INFO RECENT_NAME ": match(): match addr: %u\n",addr); - else - printk(KERN_INFO RECENT_NAME ": match(): no match addr: %u\n",addr); - } -#endif - - /* If and only if we have been asked to SET, or to UPDATE (on match) do we add the - * current timestamp to the last_seen. */ - if((info->check_set & IPT_RECENT_SET && (ans = !info->invert)) || (info->check_set & IPT_RECENT_UPDATE && ans)) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): SET or UPDATE; updating time info.\n"); -#endif - /* Have to update our time info */ - time_loc = r_list[location].time_pos; - time_info[time_loc].time = now; - time_info[time_loc].position = location; - while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) { - time_temp = time_info[time_loc].time; - time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time; - time_info[(time_loc+1)%ip_list_tot].time = time_temp; - time_temp = time_info[time_loc].position; - time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position; - time_info[(time_loc+1)%ip_list_tot].position = time_temp; - r_list[time_info[time_loc].position].time_pos = time_loc; - r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot; - time_loc = (time_loc+1) % ip_list_tot; - } - r_list[location].time_pos = time_loc; - r_list[location].ttl = ttl; - r_list[location].last_pkts[r_list[location].oldest_pkt] = now; - r_list[location].oldest_pkt = ++r_list[location].oldest_pkt % ip_pkt_list_tot; - r_list[location].last_seen = now; - } - /* If we have been asked to remove the entry from the list, just set it to 0 */ - if(info->check_set & IPT_RECENT_REMOVE) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; clearing entry (or: %d, hr: %d).\n",orig_hash_result,hash_result); -#endif - /* Check if this is part of a collision chain */ - while(hash_table[(orig_hash_result+1) % ip_list_hash_size] != -1) { - orig_hash_result++; - if(hash_func(r_list[hash_table[orig_hash_result]].addr,ip_list_hash_size) == hash_result) { - /* Found collision chain, how deep does this rabbit hole go? */ -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; found collision chain.\n"); -#endif - end_collision_chain = orig_hash_result; - } - } - if(end_collision_chain != -1) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match(): REMOVE; part of collision chain, moving to end.\n"); -#endif - /* Part of a collision chain, swap it with the end of the chain - * before removing. */ - r_list[hash_table[end_collision_chain]].hash_entry = hash_result; - temp = hash_table[end_collision_chain]; - hash_table[end_collision_chain] = hash_table[hash_result]; - hash_table[hash_result] = temp; - time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result]; - hash_result = end_collision_chain; - r_list[hash_table[hash_result]].hash_entry = hash_result; - time_info[r_list[hash_table[hash_result]].time_pos].position = hash_table[hash_result]; - } - location = hash_table[hash_result]; - hash_table[r_list[location].hash_entry] = -1; - time_loc = r_list[location].time_pos; - time_info[time_loc].time = 0; - time_info[time_loc].position = location; - while((time_info[(time_loc+1) % ip_list_tot].time < time_info[time_loc].time) && ((time_loc+1) % ip_list_tot) != curr_table->time_pos) { - time_temp = time_info[time_loc].time; - time_info[time_loc].time = time_info[(time_loc+1)%ip_list_tot].time; - time_info[(time_loc+1)%ip_list_tot].time = time_temp; - time_temp = time_info[time_loc].position; - time_info[time_loc].position = time_info[(time_loc+1)%ip_list_tot].position; - time_info[(time_loc+1)%ip_list_tot].position = time_temp; - r_list[time_info[time_loc].position].time_pos = time_loc; - r_list[time_info[(time_loc+1)%ip_list_tot].position].time_pos = (time_loc+1)%ip_list_tot; - time_loc = (time_loc+1) % ip_list_tot; - } - r_list[location].time_pos = time_loc; - r_list[location].last_seen = 0; - r_list[location].addr = 0; - r_list[location].ttl = 0; - memset(r_list[location].last_pkts,0,ip_pkt_list_tot*sizeof(unsigned long)); - r_list[location].oldest_pkt = 0; - ans = !info->invert; - } - spin_unlock_bh(&curr_table->list_lock); - return ans; } - spin_unlock_bh(&curr_table->list_lock); -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": match() left.\n"); -#endif - return ans; + if (info->check_set & IPT_RECENT_SET || + (info->check_set & IPT_RECENT_UPDATE && ret)) { + recent_entry_update(t, e); + e->ttl = ttl; + } +out: + spin_unlock_bh(&recent_lock); + return ret; } -/* This function is to verify that the rule given during the userspace iptables - * command is correct. - * If the command is valid then we check if the table name referred to by the - * rule exists, if not it is created. - */ static int -checkentry(const char *tablename, - const void *ip, - const struct xt_match *match, - void *matchinfo, - unsigned int matchsize, - unsigned int hook_mask) +ipt_recent_checkentry(const char *tablename, const void *ip, + const struct xt_match *match, void *matchinfo, + unsigned int matchsize, unsigned int hook_mask) { - int flag = 0, c; - unsigned long *hold; const struct ipt_recent_info *info = matchinfo; - struct recent_ip_tables *curr_table, *find_table, *last_table; - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() entered.\n"); -#endif - - /* seconds and hit_count only valid for CHECK/UPDATE */ - if(info->check_set & IPT_RECENT_SET) { flag++; if(info->seconds || info->hit_count) return 0; } - if(info->check_set & IPT_RECENT_REMOVE) { flag++; if(info->seconds || info->hit_count) return 0; } - if(info->check_set & IPT_RECENT_CHECK) flag++; - if(info->check_set & IPT_RECENT_UPDATE) flag++; - - /* One and only one of these should ever be set */ - if(flag != 1) return 0; - - /* Name must be set to something */ - if(!info->name || !info->name[0]) return 0; + struct recent_table *t; + unsigned i; + int ret = 0; - /* Things look good, create a list for this if it does not exist */ - /* Lock the linked list while we play with it */ - spin_lock_bh(&recent_lock); - - /* Look for an entry with this name already created */ - /* Finds the end of the list and the entry before the end if current name does not exist */ - find_table = r_tables; - while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) ); + if (hweight8(info->check_set & + (IPT_RECENT_SET | IPT_RECENT_REMOVE | + IPT_RECENT_CHECK | IPT_RECENT_UPDATE)) != 1) + return 0; + if ((info->check_set & (IPT_RECENT_SET | IPT_RECENT_REMOVE)) && + (info->seconds || info->hit_count)) + return 0; + if (info->name[0] == '\0' || + strnlen(info->name, IPT_RECENT_NAME_LEN) == IPT_RECENT_NAME_LEN) + return 0; - /* If a table already exists just increment the count on that table and return */ - if(find_table) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), incrementing count.\n",info->name); -#endif - find_table->count++; - spin_unlock_bh(&recent_lock); - return 1; + mutex_lock(&recent_mutex); + t = recent_table_lookup(info->name); + if (t != NULL) { + t->refcnt++; + ret = 1; + goto out; } - spin_unlock_bh(&recent_lock); - - /* Table with this name not found */ - /* Allocate memory for new linked list item */ - -#ifdef DEBUG - if(debug) { - printk(KERN_INFO RECENT_NAME ": checkentry: no table found (%s)\n",info->name); - printk(KERN_INFO RECENT_NAME ": checkentry: Allocationg %d for link-list entry.\n",sizeof(struct recent_ip_tables)); + t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size, + GFP_KERNEL); + if (t == NULL) + goto out; + t->refcnt = 1; + strcpy(t->name, info->name); + INIT_LIST_HEAD(&t->lru_list); + for (i = 0; i < ip_list_hash_size; i++) + INIT_LIST_HEAD(&t->iphash[i]); +#ifdef CONFIG_PROC_FS + t->proc = create_proc_entry(t->name, ip_list_perms, proc_dir); + if (t->proc == NULL) { + kfree(t); + goto out; } + t->proc->proc_fops = &recent_fops; + t->proc->data = t; #endif + spin_lock_bh(&recent_lock); + list_add_tail(&t->list, &tables); + spin_unlock_bh(&recent_lock); + ret = 1; +out: + mutex_unlock(&recent_mutex); + return ret; +} - curr_table = vmalloc(sizeof(struct recent_ip_tables)); - if(curr_table == NULL) return 0; - - spin_lock_init(&curr_table->list_lock); - curr_table->next = NULL; - curr_table->count = 1; - curr_table->time_pos = 0; - strncpy(curr_table->name,info->name,IPT_RECENT_NAME_LEN); - curr_table->name[IPT_RECENT_NAME_LEN-1] = '\0'; - - /* Allocate memory for this table and the list of packets in each entry. */ -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for table (%s).\n", - sizeof(struct recent_ip_list)*ip_list_tot, - info->name); -#endif - - curr_table->table = vmalloc(sizeof(struct recent_ip_list)*ip_list_tot); - if(curr_table->table == NULL) { vfree(curr_table); return 0; } - memset(curr_table->table,0,sizeof(struct recent_ip_list)*ip_list_tot); -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for pkt_list.\n", - sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot); -#endif - - hold = vmalloc(sizeof(unsigned long)*ip_pkt_list_tot*ip_list_tot); -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: After pkt_list allocation.\n"); -#endif - if(hold == NULL) { - printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for pkt_list.\n"); - vfree(curr_table->table); - vfree(curr_table); - return 0; - } - for(c = 0; c < ip_list_tot; c++) { - curr_table->table[c].last_pkts = hold + c*ip_pkt_list_tot; - } +static void +ipt_recent_destroy(const struct xt_match *match, void *matchinfo, + unsigned int matchsize) +{ + const struct ipt_recent_info *info = matchinfo; + struct recent_table *t; - /* Allocate memory for the hash table */ -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for hash_table.\n", - sizeof(int)*ip_list_hash_size); + mutex_lock(&recent_mutex); + t = recent_table_lookup(info->name); + if (--t->refcnt == 0) { + spin_lock_bh(&recent_lock); + list_del(&t->list); + spin_unlock_bh(&recent_lock); + recent_table_flush(t); +#ifdef CONFIG_PROC_FS + remove_proc_entry(t->name, proc_dir); #endif - - curr_table->hash_table = vmalloc(sizeof(int)*ip_list_hash_size); - if(!curr_table->hash_table) { - printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for hash_table.\n"); - vfree(hold); - vfree(curr_table->table); - vfree(curr_table); - return 0; - } - - for(c = 0; c < ip_list_hash_size; c++) { - curr_table->hash_table[c] = -1; + kfree(t); } + mutex_unlock(&recent_mutex); +} - /* Allocate memory for the time info */ -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: Allocating %d for time_info.\n", - sizeof(struct time_info_list)*ip_list_tot); -#endif +#ifdef CONFIG_PROC_FS +struct recent_iter_state { + struct recent_table *table; + unsigned int bucket; +}; - curr_table->time_info = vmalloc(sizeof(struct time_info_list)*ip_list_tot); - if(!curr_table->time_info) { - printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for time_info.\n"); - vfree(curr_table->hash_table); - vfree(hold); - vfree(curr_table->table); - vfree(curr_table); - return 0; - } - for(c = 0; c < ip_list_tot; c++) { - curr_table->time_info[c].position = c; - curr_table->time_info[c].time = 0; - } +static void *recent_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct recent_iter_state *st = seq->private; + struct recent_table *t = st->table; + struct recent_entry *e; + loff_t p = *pos; - /* Put the new table in place */ spin_lock_bh(&recent_lock); - find_table = r_tables; - while( (last_table = find_table) && strncmp(info->name,find_table->name,IPT_RECENT_NAME_LEN) && (find_table = find_table->next) ); - - /* If a table already exists just increment the count on that table and return */ - if(find_table) { - find_table->count++; - spin_unlock_bh(&recent_lock); -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry: table found (%s), created by other process.\n",info->name); -#endif - vfree(curr_table->time_info); - vfree(curr_table->hash_table); - vfree(hold); - vfree(curr_table->table); - vfree(curr_table); - return 1; - } - if(!last_table) r_tables = curr_table; else last_table->next = curr_table; - - spin_unlock_bh(&recent_lock); -#ifdef CONFIG_PROC_FS - /* Create our proc 'status' entry. */ - curr_table->status_proc = create_proc_entry(curr_table->name, ip_list_perms, proc_net_ipt_recent); - if (!curr_table->status_proc) { - vfree(hold); - printk(KERN_INFO RECENT_NAME ": checkentry: unable to allocate for /proc entry.\n"); - /* Destroy the created table */ - spin_lock_bh(&recent_lock); - last_table = NULL; - curr_table = r_tables; - if(!curr_table) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, no tables.\n"); -#endif - spin_unlock_bh(&recent_lock); - return 0; - } - while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) ); - if(!curr_table) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() create_proc failed, table already destroyed.\n"); -#endif - spin_unlock_bh(&recent_lock); - return 0; + for (st->bucket = 0; st->bucket < ip_list_hash_size; st->bucket++) { + list_for_each_entry(e, &t->iphash[st->bucket], list) { + if (p-- == 0) + return e; } - if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next; - spin_unlock_bh(&recent_lock); - vfree(curr_table->time_info); - vfree(curr_table->hash_table); - vfree(curr_table->table); - vfree(curr_table); - return 0; } - - curr_table->status_proc->owner = THIS_MODULE; - curr_table->status_proc->data = curr_table; - wmb(); - curr_table->status_proc->read_proc = ip_recent_get_info; - curr_table->status_proc->write_proc = ip_recent_ctrl; -#endif /* CONFIG_PROC_FS */ - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": checkentry() left.\n"); -#endif + return NULL; +} - return 1; +static void *recent_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct recent_iter_state *st = seq->private; + struct recent_table *t = st->table; + struct recent_entry *e = v; + struct list_head *head = e->list.next; + + while (head == &t->iphash[st->bucket]) { + if (++st->bucket >= ip_list_hash_size) + return NULL; + head = t->iphash[st->bucket].next; + } + (*pos)++; + return list_entry(head, struct recent_entry, list); } -/* This function is called in the event that a rule matching this module is - * removed. - * When this happens we need to check if there are no other rules matching - * the table given. If that is the case then we remove the table and clean - * up its memory. - */ -static void -destroy(const struct xt_match *match, void *matchinfo, unsigned int matchsize) +static void recent_seq_stop(struct seq_file *s, void *v) { - const struct ipt_recent_info *info = matchinfo; - struct recent_ip_tables *curr_table, *last_table; + spin_unlock_bh(&recent_lock); +} -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": destroy() entered.\n"); -#endif +static int recent_seq_show(struct seq_file *seq, void *v) +{ + struct recent_entry *e = v; + unsigned int i; + + i = (e->index - 1) % ip_pkt_list_tot; + seq_printf(seq, "src=%u.%u.%u.%u ttl: %u last_seen: %lu oldest_pkt: %u", + NIPQUAD(e->addr), e->ttl, e->stamps[i], e->index); + for (i = 0; i < e->nstamps; i++) + seq_printf(seq, "%s %lu", i ? "," : "", e->stamps[i]); + seq_printf(seq, "\n"); + return 0; +} - if(matchsize != IPT_ALIGN(sizeof(struct ipt_recent_info))) return; +static struct seq_operations recent_seq_ops = { + .start = recent_seq_start, + .next = recent_seq_next, + .stop = recent_seq_stop, + .show = recent_seq_show, +}; - /* Lock the linked list while we play with it */ - spin_lock_bh(&recent_lock); +static int recent_seq_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *pde = PDE(inode); + struct seq_file *seq; + struct recent_iter_state *st; + int ret; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (st == NULL) + return -ENOMEM; + ret = seq_open(file, &recent_seq_ops); + if (ret) + kfree(st); + st->table = pde->data; + seq = file->private_data; + seq->private = st; + return ret; +} - /* Look for an entry with this name already created */ - /* Finds the end of the list and the entry before the end if current name does not exist */ - last_table = NULL; - curr_table = r_tables; - if(!curr_table) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": destroy() No tables found, leaving.\n"); -#endif +static ssize_t recent_proc_write(struct file *file, const char __user *input, + size_t size, loff_t *loff) +{ + struct proc_dir_entry *pde = PDE(file->f_dentry->d_inode); + struct recent_table *t = pde->data; + struct recent_entry *e; + char buf[sizeof("+255.255.255.255")], *c = buf; + u_int32_t addr; + int add; + + if (size > sizeof(buf)) + size = sizeof(buf); + if (copy_from_user(buf, input, size)) + return -EFAULT; + while (isspace(*c)) + c++; + + if (size - (c - buf) < 5) + return c - buf; + if (!strncmp(c, "clear", 5)) { + c += 5; + spin_lock_bh(&recent_lock); + recent_table_flush(t); spin_unlock_bh(&recent_lock); - return; + return c - buf; } - while( strncmp(info->name,curr_table->name,IPT_RECENT_NAME_LEN) && (last_table = curr_table) && (curr_table = curr_table->next) ); - /* If a table does not exist then do nothing and return */ - if(!curr_table) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table not found, leaving.\n"); -#endif - spin_unlock_bh(&recent_lock); - return; + switch (*c) { + case '-': + add = 0; + c++; + break; + case '+': + c++; + default: + add = 1; + break; } + addr = in_aton(c); - curr_table->count--; - - /* If count is still non-zero then there are still rules referenceing it so we do nothing */ - if(curr_table->count) { -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, non-zero count, leaving.\n"); -#endif - spin_unlock_bh(&recent_lock); - return; + spin_lock_bh(&recent_lock); + e = recent_entry_lookup(t, addr, 0); + if (e == NULL) { + if (add) + recent_entry_init(t, addr, 0); + } else { + if (add) + recent_entry_update(t, e); + else + recent_entry_remove(t, e); } - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": destroy() table found, zero count, removing.\n"); -#endif - - /* Count must be zero so we remove this table from the list */ - if(last_table) last_table->next = curr_table->next; else r_tables = curr_table->next; - spin_unlock_bh(&recent_lock); + return size; +} - /* lock to make sure any late-runners still using this after we removed it from - * the list finish up then remove everything */ - spin_lock_bh(&curr_table->list_lock); - spin_unlock_bh(&curr_table->list_lock); - -#ifdef CONFIG_PROC_FS - if(curr_table->status_proc) remove_proc_entry(curr_table->name,proc_net_ipt_recent); +static struct file_operations recent_fops = { + .open = recent_seq_open, + .read = seq_read, + .write = recent_proc_write, + .release = seq_release_private, + .owner = THIS_MODULE, +}; #endif /* CONFIG_PROC_FS */ - vfree(curr_table->table[0].last_pkts); - vfree(curr_table->table); - vfree(curr_table->hash_table); - vfree(curr_table->time_info); - vfree(curr_table); - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": destroy() left.\n"); -#endif - return; -} - -/* This is the structure we pass to ipt_register to register our - * module with iptables. - */ static struct ipt_match recent_match = { .name = "recent", - .match = match, + .match = ipt_recent_match, .matchsize = sizeof(struct ipt_recent_info), - .checkentry = checkentry, - .destroy = destroy, - .me = THIS_MODULE + .checkentry = ipt_recent_checkentry, + .destroy = ipt_recent_destroy, + .me = THIS_MODULE, }; -/* Kernel module initialization. */ static int __init ipt_recent_init(void) { - int err, count; + int err; - printk(version); -#ifdef CONFIG_PROC_FS - proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net); - if(!proc_net_ipt_recent) return -ENOMEM; -#endif - - if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) { - printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n"); - ip_list_hash_size = 0; - } - - if(!ip_list_hash_size) { - ip_list_hash_size = ip_list_tot*3; - count = 2*2; - while(ip_list_hash_size > count) count = count*2; - ip_list_hash_size = count; - } - -#ifdef DEBUG - if(debug) printk(KERN_INFO RECENT_NAME ": ip_list_hash_size: %d\n",ip_list_hash_size); -#endif + if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255) + return -EINVAL; + ip_list_hash_size = 1 << fls(ip_list_tot); err = ipt_register_match(&recent_match); +#ifdef CONFIG_PROC_FS if (err) - remove_proc_entry("ipt_recent", proc_net); + return err; + proc_dir = proc_mkdir("ipt_recent", proc_net); + if (proc_dir == NULL) { + ipt_unregister_match(&recent_match); + err = -ENOMEM; + } +#endif return err; } -/* Kernel module destruction. */ -static void __exit ipt_recent_fini(void) +static void __exit ipt_recent_exit(void) { + BUG_ON(!list_empty(&tables)); ipt_unregister_match(&recent_match); - - remove_proc_entry("ipt_recent",proc_net); +#ifdef CONFIG_PROC_FS + remove_proc_entry("ipt_recent", proc_net); +#endif } -/* Register our module with the kernel. */ module_init(ipt_recent_init); -module_exit(ipt_recent_fini); +module_exit(ipt_recent_exit); diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 397b95c..4e7998b 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -10,7 +10,6 @@ * * Extended to all five netfilter hooks by Brad Chapman & Harald Welte */ -#include #include #include #include diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 77d9744..0af803d 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -14,7 +14,6 @@ * Derived from net/ipv4/netfilter/ip_conntrack_standalone.c */ -#include #include #include #include @@ -145,7 +144,7 @@ static unsigned int ipv4_conntrack_help( /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(*pskb, &ctinfo); - if (!ct) + if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) return NF_ACCEPT; help = nfct_help(ct); diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 4b0d361..663a73e 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -235,7 +235,7 @@ icmp_error(struct sk_buff *skb, unsigned } /* See ip_conntrack_proto_tcp.c */ - if (hooknum == NF_IP_PRE_ROUTING && + if (nf_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING && nf_ip_checksum(skb, hooknum, dataoff, 0)) { if (LOG_INVALID(IPPROTO_ICMP)) nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL, diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 291831e..05f5114 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -32,7 +32,6 @@ #include #include #include #include -#include #include #include #include diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index fc25624..bd221ec 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -103,7 +103,7 @@ static void raw_v4_unhash(struct sock *s } struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num, - unsigned long raddr, unsigned long laddr, + __be32 raddr, __be32 laddr, int dif) { struct hlist_node *node; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index cc9423d..2dc6dbb 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -64,7 +64,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -206,21 +205,27 @@ __u8 ip_tos2prio[16] = { struct rt_hash_bucket { struct rtable *chain; }; -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) +#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) || \ + defined(CONFIG_PROVE_LOCKING) /* * Instead of using one spinlock for each rt_hash_bucket, we use a table of spinlocks * The size of this table is a power of two and depends on the number of CPUS. + * (on lockdep we have a quite big spinlock_t, so keep the size down there) */ -#if NR_CPUS >= 32 -#define RT_HASH_LOCK_SZ 4096 -#elif NR_CPUS >= 16 -#define RT_HASH_LOCK_SZ 2048 -#elif NR_CPUS >= 8 -#define RT_HASH_LOCK_SZ 1024 -#elif NR_CPUS >= 4 -#define RT_HASH_LOCK_SZ 512 +#ifdef CONFIG_LOCKDEP +# define RT_HASH_LOCK_SZ 256 #else -#define RT_HASH_LOCK_SZ 256 +# if NR_CPUS >= 32 +# define RT_HASH_LOCK_SZ 4096 +# elif NR_CPUS >= 16 +# define RT_HASH_LOCK_SZ 2048 +# elif NR_CPUS >= 8 +# define RT_HASH_LOCK_SZ 1024 +# elif NR_CPUS >= 4 +# define RT_HASH_LOCK_SZ 512 +# else +# define RT_HASH_LOCK_SZ 256 +# endif #endif static spinlock_t *rt_hash_locks; @@ -244,7 +249,7 @@ static unsigned int rt_hash_rnd; static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat); #define RT_CACHE_STAT_INC(field) \ - (per_cpu(rt_cache_stat, raw_smp_processor_id()).field++) + (__raw_get_cpu_var(rt_cache_stat).field++) static int rt_intern_hash(unsigned hash, struct rtable *rth, struct rtable **res); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 6b6c3ad..70cea9d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -182,14 +181,6 @@ ctl_table ipv4_table[] = { .strategy = &ipv4_doint_and_flush_strategy, }, { - .ctl_name = NET_IPV4_AUTOCONFIG, - .procname = "ip_autoconfig", - .data = &ipv4_config.autoconfig, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec - }, - { .ctl_name = NET_IPV4_NO_PMTU_DISC, .procname = "ip_no_pmtu_disc", .data = &ipv4_config.no_pmtu_disc, @@ -688,6 +679,24 @@ #endif .mode = 0644, .proc_handler = &proc_dointvec }, +#ifdef CONFIG_NET_DMA + { + .ctl_name = NET_TCP_DMA_COPYBREAK, + .procname = "tcp_dma_copybreak", + .data = &sysctl_tcp_dma_copybreak, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, +#endif + { + .ctl_name = NET_TCP_SLOW_START_AFTER_IDLE, + .procname = "tcp_slow_start_after_idle", + .data = &sysctl_tcp_slow_start_after_idle, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec + }, { .ctl_name = 0 } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e2b7b80..f6a2d92 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -247,7 +247,6 @@ * TCP_CLOSE socket is finished */ -#include #include #include #include @@ -258,12 +257,13 @@ #include #include #include #include +#include #include #include #include #include - +#include #include #include @@ -571,7 +571,7 @@ new_segment: skb->ip_summed = CHECKSUM_HW; tp->write_seq += copy; TCP_SKB_CB(skb)->end_seq += copy; - skb_shinfo(skb)->tso_segs = 0; + skb_shinfo(skb)->gso_segs = 0; if (!copied) TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_PSH; @@ -622,14 +622,10 @@ ssize_t tcp_sendpage(struct socket *sock ssize_t res; struct sock *sk = sock->sk; -#define TCP_ZC_CSUM_FLAGS (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) - if (!(sk->sk_route_caps & NETIF_F_SG) || - !(sk->sk_route_caps & TCP_ZC_CSUM_FLAGS)) + !(sk->sk_route_caps & NETIF_F_ALL_CSUM)) return sock_no_sendpage(sock, page, offset, size, flags); -#undef TCP_ZC_CSUM_FLAGS - lock_sock(sk); TCP_CHECK_TIMER(sk); res = do_tcp_sendpages(sk, &page, offset, size, flags); @@ -646,7 +642,7 @@ static inline int select_size(struct soc int tmp = tp->mss_cache; if (sk->sk_route_caps & NETIF_F_SG) { - if (sk->sk_route_caps & NETIF_F_TSO) + if (sk_can_gso(sk)) tmp = 0; else { int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER); @@ -726,9 +722,7 @@ new_segment: /* * Check whether we can use HW checksum. */ - if (sk->sk_route_caps & - (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | - NETIF_F_HW_CSUM)) + if (sk->sk_route_caps & NETIF_F_ALL_CSUM) skb->ip_summed = CHECKSUM_HW; skb_entail(sk, tp, skb); @@ -824,7 +818,7 @@ new_segment: tp->write_seq += copy; TCP_SKB_CB(skb)->end_seq += copy; - skb_shinfo(skb)->tso_segs = 0; + skb_shinfo(skb)->gso_segs = 0; from += copy; copied += copy; @@ -937,7 +931,7 @@ static int tcp_recv_urg(struct sock *sk, * calculation of whether or not we must ACK for the sake of * a window update. */ -static void cleanup_rbuf(struct sock *sk, int copied) +void tcp_cleanup_rbuf(struct sock *sk, int copied) { struct tcp_sock *tp = tcp_sk(sk); int time_to_ack = 0; @@ -1072,11 +1066,11 @@ int tcp_read_sock(struct sock *sk, read_ break; } if (skb->h.th->fin) { - sk_eat_skb(sk, skb); + sk_eat_skb(sk, skb, 0); ++seq; break; } - sk_eat_skb(sk, skb); + sk_eat_skb(sk, skb, 0); if (!desc->count) break; } @@ -1086,7 +1080,7 @@ int tcp_read_sock(struct sock *sk, read_ /* Clean up data we have read: This will do ACK frames. */ if (copied) - cleanup_rbuf(sk, copied); + tcp_cleanup_rbuf(sk, copied); return copied; } @@ -1110,6 +1104,7 @@ int tcp_recvmsg(struct kiocb *iocb, stru int target; /* Read at least this many bytes */ long timeo; struct task_struct *user_recv = NULL; + int copied_early = 0; lock_sock(sk); @@ -1133,6 +1128,17 @@ int tcp_recvmsg(struct kiocb *iocb, stru target = sock_rcvlowat(sk, flags & MSG_WAITALL, len); +#ifdef CONFIG_NET_DMA + tp->ucopy.dma_chan = NULL; + preempt_disable(); + if ((len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) && + !sysctl_tcp_low_latency && __get_cpu_var(softnet_data.net_dma)) { + preempt_enable_no_resched(); + tp->ucopy.pinned_list = dma_pin_iovec_pages(msg->msg_iov, len); + } else + preempt_enable_no_resched(); +#endif + do { struct sk_buff *skb; u32 offset; @@ -1220,7 +1226,7 @@ int tcp_recvmsg(struct kiocb *iocb, stru } } - cleanup_rbuf(sk, copied); + tcp_cleanup_rbuf(sk, copied); if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) { /* Install new reader */ @@ -1274,6 +1280,10 @@ int tcp_recvmsg(struct kiocb *iocb, stru } else sk_wait_data(sk, &timeo); +#ifdef CONFIG_NET_DMA + tp->ucopy.wakeup = 0; +#endif + if (user_recv) { int chunk; @@ -1329,13 +1339,39 @@ do_prequeue: } if (!(flags & MSG_TRUNC)) { - err = skb_copy_datagram_iovec(skb, offset, - msg->msg_iov, used); - if (err) { - /* Exception. Bailout! */ - if (!copied) - copied = -EFAULT; - break; +#ifdef CONFIG_NET_DMA + if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan = get_softnet_dma(); + + if (tp->ucopy.dma_chan) { + tp->ucopy.dma_cookie = dma_skb_copy_datagram_iovec( + tp->ucopy.dma_chan, skb, offset, + msg->msg_iov, used, + tp->ucopy.pinned_list); + + if (tp->ucopy.dma_cookie < 0) { + + printk(KERN_ALERT "dma_cookie < 0\n"); + + /* Exception. Bailout! */ + if (!copied) + copied = -EFAULT; + break; + } + if ((offset + used) == skb->len) + copied_early = 1; + + } else +#endif + { + err = skb_copy_datagram_iovec(skb, offset, + msg->msg_iov, used); + if (err) { + /* Exception. Bailout! */ + if (!copied) + copied = -EFAULT; + break; + } } } @@ -1355,15 +1391,19 @@ skip_copy: if (skb->h.th->fin) goto found_fin_ok; - if (!(flags & MSG_PEEK)) - sk_eat_skb(sk, skb); + if (!(flags & MSG_PEEK)) { + sk_eat_skb(sk, skb, copied_early); + copied_early = 0; + } continue; found_fin_ok: /* Process the FIN. */ ++*seq; - if (!(flags & MSG_PEEK)) - sk_eat_skb(sk, skb); + if (!(flags & MSG_PEEK)) { + sk_eat_skb(sk, skb, copied_early); + copied_early = 0; + } break; } while (len > 0); @@ -1386,12 +1426,42 @@ skip_copy: tp->ucopy.len = 0; } +#ifdef CONFIG_NET_DMA + if (tp->ucopy.dma_chan) { + struct sk_buff *skb; + dma_cookie_t done, used; + + dma_async_memcpy_issue_pending(tp->ucopy.dma_chan); + + while (dma_async_memcpy_complete(tp->ucopy.dma_chan, + tp->ucopy.dma_cookie, &done, + &used) == DMA_IN_PROGRESS) { + /* do partial cleanup of sk_async_wait_queue */ + while ((skb = skb_peek(&sk->sk_async_wait_queue)) && + (dma_async_is_complete(skb->dma_cookie, done, + used) == DMA_SUCCESS)) { + __skb_dequeue(&sk->sk_async_wait_queue); + kfree_skb(skb); + } + } + + /* Safe to free early-copied skbs now */ + __skb_queue_purge(&sk->sk_async_wait_queue); + dma_chan_put(tp->ucopy.dma_chan); + tp->ucopy.dma_chan = NULL; + } + if (tp->ucopy.pinned_list) { + dma_unpin_iovec_pages(tp->ucopy.pinned_list); + tp->ucopy.pinned_list = NULL; + } +#endif + /* According to UNIX98, msg_name/msg_namelen are ignored * on connected socket. I was just happy when found this 8) --ANK */ /* Clean up data we have read: This will do ACK frames. */ - cleanup_rbuf(sk, copied); + tcp_cleanup_rbuf(sk, copied); TCP_CHECK_TIMER(sk); release_sock(sk); @@ -1658,6 +1728,9 @@ int tcp_disconnect(struct sock *sk, int __skb_queue_purge(&sk->sk_receive_queue); sk_stream_writequeue_purge(sk); __skb_queue_purge(&tp->out_of_order_queue); +#ifdef CONFIG_NET_DMA + __skb_queue_purge(&sk->sk_async_wait_queue); +#endif inet->dport = 0; @@ -1858,7 +1931,7 @@ static int do_tcp_setsockopt(struct sock (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) && inet_csk_ack_scheduled(sk)) { icsk->icsk_ack.pending |= ICSK_ACK_PUSHED; - cleanup_rbuf(sk, 1); + tcp_cleanup_rbuf(sk, 1); if (!(val & 1)) icsk->icsk_ack.pingpong = 1; } @@ -2071,6 +2144,89 @@ int compat_tcp_getsockopt(struct sock *s EXPORT_SYMBOL(compat_tcp_getsockopt); #endif +struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct tcphdr *th; + unsigned thlen; + unsigned int seq; + unsigned int delta; + unsigned int oldlen; + unsigned int len; + + if (!pskb_may_pull(skb, sizeof(*th))) + goto out; + + th = skb->h.th; + thlen = th->doff * 4; + if (thlen < sizeof(*th)) + goto out; + + if (!pskb_may_pull(skb, thlen)) + goto out; + + oldlen = (u16)~skb->len; + __skb_pull(skb, thlen); + + if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { + /* Packet is from an untrusted source, reset gso_segs. */ + int type = skb_shinfo(skb)->gso_type; + int mss; + + if (unlikely(type & + ~(SKB_GSO_TCPV4 | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_TCPV6 | + 0) || + !(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)))) + goto out; + + mss = skb_shinfo(skb)->gso_size; + skb_shinfo(skb)->gso_segs = (skb->len + mss - 1) / mss; + + segs = NULL; + goto out; + } + + segs = skb_segment(skb, features); + if (IS_ERR(segs)) + goto out; + + len = skb_shinfo(skb)->gso_size; + delta = htonl(oldlen + (thlen + len)); + + skb = segs; + th = skb->h.th; + seq = ntohl(th->seq); + + do { + th->fin = th->psh = 0; + + th->check = ~csum_fold(th->check + delta); + if (skb->ip_summed != CHECKSUM_HW) + th->check = csum_fold(csum_partial(skb->h.raw, thlen, + skb->csum)); + + seq += len; + skb = skb->next; + th = skb->h.th; + + th->seq = htonl(seq); + th->cwr = 0; + } while (skb->next); + + delta = htonl(oldlen + (skb->tail - skb->h.raw) + skb->data_len); + th->check = ~csum_fold(th->check + delta); + if (skb->ip_summed != CHECKSUM_HW) + th->check = csum_fold(csum_partial(skb->h.raw, thlen, + skb->csum)); + +out: + return segs; +} +EXPORT_SYMBOL(tcp_tso_segment); + extern void __skb_cb_too_small_for_tcp(int, int); extern struct tcp_congestion_ops tcp_reno; diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 035f209..b0134ab 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -12,7 +12,6 @@ * this behaves the same as the original Reno. */ -#include #include #include #include @@ -198,12 +197,6 @@ static u32 bictcp_undo_cwnd(struct sock return max(tp->snd_cwnd, ca->last_max_cwnd); } -static u32 bictcp_min_cwnd(struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - return tp->snd_ssthresh; -} - static void bictcp_state(struct sock *sk, u8 new_state) { if (new_state == TCP_CA_Loss) @@ -231,7 +224,6 @@ static struct tcp_congestion_ops bictcp .cong_avoid = bictcp_cong_avoid, .set_state = bictcp_state, .undo_cwnd = bictcp_undo_cwnd, - .min_cwnd = bictcp_min_cwnd, .pkts_acked = bictcp_acked, .owner = THIS_MODULE, .name = "bic", diff --git a/net/ipv4/tcp_compound.c b/net/ipv4/tcp_compound.c new file mode 100644 index 0000000..bc54f7e --- /dev/null +++ b/net/ipv4/tcp_compound.c @@ -0,0 +1,448 @@ +/* + * TCP Vegas congestion control + * + * This is based on the congestion detection/avoidance scheme described in + * Lawrence S. Brakmo and Larry L. Peterson. + * "TCP Vegas: End to end congestion avoidance on a global internet." + * IEEE Journal on Selected Areas in Communication, 13(8):1465--1480, + * October 1995. Available from: + * ftp://ftp.cs.arizona.edu/xkernel/Papers/jsac.ps + * + * See http://www.cs.arizona.edu/xkernel/ for their implementation. + * The main aspects that distinguish this implementation from the + * Arizona Vegas implementation are: + * o We do not change the loss detection or recovery mechanisms of + * Linux in any way. Linux already recovers from losses quite well, + * using fine-grained timers, NewReno, and FACK. + * o To avoid the performance penalty imposed by increasing cwnd + * only every-other RTT during slow start, we increase during + * every RTT during slow start, just like Reno. + * o Largely to allow continuous cwnd growth during slow start, + * we use the rate at which ACKs come back as the "actual" + * rate, rather than the rate at which data is sent. + * o To speed convergence to the right rate, we set the cwnd + * to achieve the right ("actual") rate when we exit slow start. + * o To filter out the noise caused by delayed ACKs, we use the + * minimum RTT sample observed during the last RTT to calculate + * the actual rate. + * o When the sender re-starts from idle, it waits until it has + * received ACKs for an entire flight of new data before making + * a cwnd adjustment decision. The original Vegas implementation + * assumed senders never went idle. + * + * + * TCP Compound based on TCP Vegas + * + * further details can be found here: + * ftp://ftp.research.microsoft.com/pub/tr/TR-2005-86.pdf + */ + +#include +#include +#include +#include +#include + +#include + +/* Default values of the Vegas variables, in fixed-point representation + * with V_PARAM_SHIFT bits to the right of the binary point. + */ +#define V_PARAM_SHIFT 1 + +#define TCP_COMPOUND_ALPHA 3U +#define TCP_COMPOUND_BETA 1U +#define TCP_COMPOUND_GAMMA 30 +#define TCP_COMPOUND_ZETA 1 + +/* TCP compound variables */ +struct compound { + u32 beg_snd_nxt; /* right edge during last RTT */ + u32 beg_snd_una; /* left edge during last RTT */ + u32 beg_snd_cwnd; /* saves the size of the cwnd */ + u8 doing_vegas_now; /* if true, do vegas for this RTT */ + u16 cntRTT; /* # of RTTs measured within last RTT */ + u32 minRTT; /* min of RTTs measured within last RTT (in usec) */ + u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */ + + u32 cwnd; + u32 dwnd; +}; + +/* There are several situations when we must "re-start" Vegas: + * + * o when a connection is established + * o after an RTO + * o after fast recovery + * o when we send a packet and there is no outstanding + * unacknowledged data (restarting an idle connection) + * + * In these circumstances we cannot do a Vegas calculation at the + * end of the first RTT, because any calculation we do is using + * stale info -- both the saved cwnd and congestion feedback are + * stale. + * + * Instead we must wait until the completion of an RTT during + * which we actually receive ACKs. + */ +static inline void vegas_enable(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct compound *vegas = inet_csk_ca(sk); + + /* Begin taking Vegas samples next time we send something. */ + vegas->doing_vegas_now = 1; + + /* Set the beginning of the next send window. */ + vegas->beg_snd_nxt = tp->snd_nxt; + + vegas->cntRTT = 0; + vegas->minRTT = 0x7fffffff; +} + +/* Stop taking Vegas samples for now. */ +static inline void vegas_disable(struct sock *sk) +{ + struct compound *vegas = inet_csk_ca(sk); + + vegas->doing_vegas_now = 0; +} + +static void tcp_compound_init(struct sock *sk) +{ + struct compound *vegas = inet_csk_ca(sk); + const struct tcp_sock *tp = tcp_sk(sk); + + vegas->baseRTT = 0x7fffffff; + vegas_enable(sk); + + vegas->dwnd = 0; + vegas->cwnd = tp->snd_cwnd; +} + +/* Do RTT sampling needed for Vegas. + * Basically we: + * o min-filter RTT samples from within an RTT to get the current + * propagation delay + queuing delay (we are min-filtering to try to + * avoid the effects of delayed ACKs) + * o min-filter RTT samples from a much longer window (forever for now) + * to find the propagation delay (baseRTT) + */ +static void tcp_compound_rtt_calc(struct sock *sk, u32 usrtt) +{ + struct compound *vegas = inet_csk_ca(sk); + u32 vrtt = usrtt + 1; /* Never allow zero rtt or baseRTT */ + + /* Filter to find propagation delay: */ + if (vrtt < vegas->baseRTT) + vegas->baseRTT = vrtt; + + /* Find the min RTT during the last RTT to find + * the current prop. delay + queuing delay: + */ + + vegas->minRTT = min(vegas->minRTT, vrtt); + vegas->cntRTT++; +} + +static void tcp_compound_state(struct sock *sk, u8 ca_state) +{ + + if (ca_state == TCP_CA_Open) + vegas_enable(sk); + else + vegas_disable(sk); +} + + +/* 64bit divisor, dividend and result. dynamic precision */ +static inline u64 div64_64(u64 dividend, u64 divisor) +{ + u32 d = divisor; + + if (divisor > 0xffffffffULL) { + unsigned int shift = fls(divisor >> 32); + + d = divisor >> shift; + dividend >>= shift; + } + + /* avoid 64 bit division if possible */ + if (dividend >> 32) + do_div(dividend, d); + else + dividend = (u32) dividend / d; + + return dividend; +} + +/* calculate the quartic root of "a" using Newton-Raphson */ +static u32 qroot(u64 a) +{ + u32 x, x1; + + /* Initial estimate is based on: + * qrt(x) = exp(log(x) / 4) + */ + x = 1u << (fls64(a) >> 2); + + /* + * Iteration based on: + * 3 + * x = ( 3 * x + a / x ) / 4 + * k+1 k k + */ + do { + u64 x3 = x; + + x1 = x; + x3 *= x; + x3 *= x; + + x = (3 * x + (u32) div64_64(a, x3)) / 4; + } while (abs(x1 - x) > 1); + + return x; +} + + +/* + * If the connection is idle and we are restarting, + * then we don't want to do any Vegas calculations + * until we get fresh RTT samples. So when we + * restart, we reset our Vegas state to a clean + * slate. After we get acks for this flight of + * packets, _then_ we can make Vegas calculations + * again. + */ +static void tcp_compound_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START) + tcp_compound_init(sk); +} + +static void tcp_compound_cong_avoid(struct sock *sk, u32 ack, + u32 seq_rtt, u32 in_flight, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct compound *vegas = inet_csk_ca(sk); + u8 inc = 0; + + if (vegas->cwnd + vegas->dwnd > tp->snd_cwnd) { + if (vegas->cwnd > tp->snd_cwnd || vegas->dwnd > tp->snd_cwnd) { + vegas->cwnd = tp->snd_cwnd; + vegas->dwnd = 0; + } else + vegas->cwnd = tp->snd_cwnd - vegas->dwnd; + + } + + if (!tcp_is_cwnd_limited(sk, in_flight)) + return; + + if (vegas->cwnd <= tp->snd_ssthresh) + inc = 1; + else if (tp->snd_cwnd_cnt < tp->snd_cwnd) + tp->snd_cwnd_cnt++; + + if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + inc = 1; + tp->snd_cwnd_cnt = 0; + } + + if (inc && tp->snd_cwnd < tp->snd_cwnd_clamp) + vegas->cwnd++; + + /* The key players are v_beg_snd_una and v_beg_snd_nxt. + * + * These are so named because they represent the approximate values + * of snd_una and snd_nxt at the beginning of the current RTT. More + * precisely, they represent the amount of data sent during the RTT. + * At the end of the RTT, when we receive an ACK for v_beg_snd_nxt, + * we will calculate that (v_beg_snd_nxt - v_beg_snd_una) outstanding + * bytes of data have been ACKed during the course of the RTT, giving + * an "actual" rate of: + * + * (v_beg_snd_nxt - v_beg_snd_una) / (rtt duration) + * + * Unfortunately, v_beg_snd_una is not exactly equal to snd_una, + * because delayed ACKs can cover more than one segment, so they + * don't line up nicely with the boundaries of RTTs. + * + * Another unfortunate fact of life is that delayed ACKs delay the + * advance of the left edge of our send window, so that the number + * of bytes we send in an RTT is often less than our cwnd will allow. + * So we keep track of our cwnd separately, in v_beg_snd_cwnd. + */ + + if (after(ack, vegas->beg_snd_nxt)) { + /* Do the Vegas once-per-RTT cwnd adjustment. */ + u32 old_wnd, old_snd_cwnd; + + /* Here old_wnd is essentially the window of data that was + * sent during the previous RTT, and has all + * been acknowledged in the course of the RTT that ended + * with the ACK we just received. Likewise, old_snd_cwnd + * is the cwnd during the previous RTT. + */ + if (!tp->mss_cache) + return; + + old_wnd = (vegas->beg_snd_nxt - vegas->beg_snd_una) / + tp->mss_cache; + old_snd_cwnd = vegas->beg_snd_cwnd; + + /* Save the extent of the current window so we can use this + * at the end of the next RTT. + */ + vegas->beg_snd_una = vegas->beg_snd_nxt; + vegas->beg_snd_nxt = tp->snd_nxt; + vegas->beg_snd_cwnd = tp->snd_cwnd; + + /* We do the Vegas calculations only if we got enough RTT + * samples that we can be reasonably sure that we got + * at least one RTT sample that wasn't from a delayed ACK. + * If we only had 2 samples total, + * then that means we're getting only 1 ACK per RTT, which + * means they're almost certainly delayed ACKs. + * If we have 3 samples, we should be OK. + */ + + if (vegas->cntRTT > 2) { + u32 rtt, target_cwnd, diff; + u32 brtt, dwnd; + + /* We have enough RTT samples, so, using the Vegas + * algorithm, we determine if we should increase or + * decrease cwnd, and by how much. + */ + + /* Pluck out the RTT we are using for the Vegas + * calculations. This is the min RTT seen during the + * last RTT. Taking the min filters out the effects + * of delayed ACKs, at the cost of noticing congestion + * a bit later. + */ + rtt = vegas->minRTT; + + /* Calculate the cwnd we should have, if we weren't + * going too fast. + * + * This is: + * (actual rate in segments) * baseRTT + * We keep it as a fixed point number with + * V_PARAM_SHIFT bits to the right of the binary point. + */ + if (!rtt) + return; + + brtt = vegas->baseRTT; + target_cwnd = ((old_wnd * brtt) + << V_PARAM_SHIFT) / rtt; + + /* Calculate the difference between the window we had, + * and the window we would like to have. This quantity + * is the "Diff" from the Arizona Vegas papers. + * + * Again, this is a fixed point number with + * V_PARAM_SHIFT bits to the right of the binary + * point. + */ + + diff = (old_wnd << V_PARAM_SHIFT) - target_cwnd; + + dwnd = vegas->dwnd; + + if (diff < (TCP_COMPOUND_GAMMA << V_PARAM_SHIFT)) { + u64 v; + u32 x; + + /* + * The TCP Compound paper describes the choice + * of "k" determines the agressiveness, + * ie. slope of the response function. + * + * For same value as HSTCP would be 0.8 + * but for computaional reasons, both the + * original authors and this implementation + * use 0.75. + */ + v = old_wnd; + x = qroot(v * v * v) >> TCP_COMPOUND_ALPHA; + if (x > 1) + dwnd = x - 1; + else + dwnd = 0; + + dwnd += vegas->dwnd; + + } else if ((dwnd << V_PARAM_SHIFT) < + (diff * TCP_COMPOUND_BETA)) + dwnd = 0; + else + dwnd = + ((dwnd << V_PARAM_SHIFT) - + (diff * + TCP_COMPOUND_BETA)) >> V_PARAM_SHIFT; + + vegas->dwnd = dwnd; + + } + + /* Wipe the slate clean for the next RTT. */ + vegas->cntRTT = 0; + vegas->minRTT = 0x7fffffff; + } + + tp->snd_cwnd = vegas->cwnd + vegas->dwnd; +} + +/* Extract info for Tcp socket info provided via netlink. */ +static void tcp_compound_get_info(struct sock *sk, u32 ext, struct sk_buff *skb) +{ + const struct compound *ca = inet_csk_ca(sk); + if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) { + struct tcpvegas_info *info; + + info = RTA_DATA(__RTA_PUT(skb, INET_DIAG_VEGASINFO, + sizeof(*info))); + + info->tcpv_enabled = ca->doing_vegas_now; + info->tcpv_rttcnt = ca->cntRTT; + info->tcpv_rtt = ca->baseRTT; + info->tcpv_minrtt = ca->minRTT; + rtattr_failure:; + } +} + +static struct tcp_congestion_ops tcp_compound = { + .init = tcp_compound_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = tcp_compound_cong_avoid, + .rtt_sample = tcp_compound_rtt_calc, + .set_state = tcp_compound_state, + .cwnd_event = tcp_compound_cwnd_event, + .get_info = tcp_compound_get_info, + + .owner = THIS_MODULE, + .name = "compound", +}; + +static int __init tcp_compound_register(void) +{ + BUG_ON(sizeof(struct compound) > ICSK_CA_PRIV_SIZE); + tcp_register_congestion_control(&tcp_compound); + return 0; +} + +static void __exit tcp_compound_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_compound); +} + +module_init(tcp_compound_register); +module_exit(tcp_compound_unregister); + +MODULE_AUTHOR("Angelo P. Castellani, Stephen Hemminger"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Compound"); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 91c2f41..5765f9d 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -6,7 +6,6 @@ * Copyright (C) 2005 Stephen Hemminger */ -#include #include #include #include @@ -38,7 +37,7 @@ int tcp_register_congestion_control(stru int ret = 0; /* all algorithms must implement ssthresh and cong_avoid ops */ - if (!ca->ssthresh || !ca->cong_avoid || !ca->min_cwnd) { + if (!ca->ssthresh || !ca->cong_avoid) { printk(KERN_ERR "TCP %s does not implement required ops\n", ca->name); return -EINVAL; @@ -251,8 +250,8 @@ u32 tcp_reno_ssthresh(struct sock *sk) } EXPORT_SYMBOL_GPL(tcp_reno_ssthresh); -/* Lower bound on congestion window. */ -u32 tcp_reno_min_cwnd(struct sock *sk) +/* Lower bound on congestion window with halving. */ +u32 tcp_reno_min_cwnd(const struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); return tp->snd_ssthresh/2; diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 31a4986..2be2798 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -12,7 +12,6 @@ * this behaves the same as the original Reno. */ -#include #include #include #include @@ -325,11 +324,6 @@ static u32 bictcp_undo_cwnd(struct sock return max(tcp_sk(sk)->snd_cwnd, ca->last_max_cwnd); } -static u32 bictcp_min_cwnd(struct sock *sk) -{ - return tcp_sk(sk)->snd_ssthresh; -} - static void bictcp_state(struct sock *sk, u8 new_state) { if (new_state == TCP_CA_Loss) @@ -357,7 +351,6 @@ static struct tcp_congestion_ops cubictc .cong_avoid = bictcp_cong_avoid, .set_state = bictcp_state, .undo_cwnd = bictcp_undo_cwnd, - .min_cwnd = bictcp_min_cwnd, .pkts_acked = bictcp_acked, .owner = THIS_MODULE, .name = "cubic", diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index c148c10..57c5f0b 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -11,7 +11,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include @@ -26,7 +25,10 @@ static void tcp_diag_get_info(struct soc const struct tcp_sock *tp = tcp_sk(sk); struct tcp_info *info = _info; - r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq; + if (sk->sk_state == TCP_LISTEN) + r->idiag_rqueue = sk->sk_ack_backlog; + else + r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq; r->idiag_wqueue = tp->write_seq - tp->snd_una; if (info != NULL) tcp_get_info(sk, info); diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index ba7c63c..aaa1538 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -6,7 +6,6 @@ * John Heffner */ -#include #include #include @@ -98,6 +97,10 @@ struct hstcp { u32 ai; }; +static int max_ssthresh = 100; +module_param(max_ssthresh, int, 0644); +MODULE_PARM_DESC(max_ssthresh, "limited slow start threshold (RFC3742)"); + static void hstcp_init(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); @@ -119,9 +122,23 @@ static void hstcp_cong_avoid(struct sock if (!tcp_is_cwnd_limited(sk, in_flight)) return; - if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); - else { + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* RFC3742: limited slow start + * the window is increased by 1/K MSS for each arriving ACK, + * for K = int(cwnd/(0.5 max_ssthresh)) + */ + if (max_ssthresh > 0 && tp->snd_cwnd > max_ssthresh) { + u32 k = max(tp->snd_cwnd / (max_ssthresh >> 1), 1U); + if (++tp->snd_cwnd_cnt >= k) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } + } else { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + } + } else { /* Update AIMD parameters */ if (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd) { while (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd && diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index 1b2ff53..6edfe5e 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -6,7 +6,6 @@ * http://www.hamilton.ie/net/htcp3.pdf */ -#include #include #include #include @@ -246,14 +245,6 @@ static void htcp_cong_avoid(struct sock } } -/* Lower bound on congestion window. */ -static u32 htcp_min_cwnd(struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - return tp->snd_ssthresh; -} - - static void htcp_init(struct sock *sk) { struct htcp *ca = inet_csk_ca(sk); @@ -285,7 +276,6 @@ static void htcp_state(struct sock *sk, static struct tcp_congestion_ops htcp = { .init = htcp_init, .ssthresh = htcp_recalc_ssthresh, - .min_cwnd = htcp_min_cwnd, .cong_avoid = htcp_cong_avoid, .set_state = htcp_state, .undo_cwnd = htcp_cwnd_undo, diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index 40dbb38..7406e0c 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -10,7 +10,6 @@ * root at danielinux.net */ -#include #include #include diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index b5521a9..738dad9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -63,7 +63,6 @@ * Pasi Sarolahti: F-RTO for dealing with spurious RTOs */ -#include #include #include #include @@ -71,6 +70,7 @@ #include #include #include #include +#include int sysctl_tcp_timestamps = 1; int sysctl_tcp_window_scaling = 1; @@ -1072,7 +1072,7 @@ tcp_sacktag_write_queue(struct sock *sk, else pkt_len = (end_seq - TCP_SKB_CB(skb)->seq); - if (tcp_fragment(sk, skb, pkt_len, skb_shinfo(skb)->tso_size)) + if (tcp_fragment(sk, skb, pkt_len, skb_shinfo(skb)->gso_size)) break; pcount = tcp_skb_pcount(skb); } @@ -1688,17 +1688,26 @@ static inline void tcp_moderate_cwnd(str tp->snd_cwnd_stamp = tcp_time_stamp; } +/* Lower bound on congestion window is slow start threshold + * unless congestion avoidance choice decides to overide it. + */ +static inline u32 tcp_cwnd_min(const struct sock *sk) +{ + const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops; + + return ca_ops->min_cwnd ? ca_ops->min_cwnd(sk) : tcp_sk(sk)->snd_ssthresh; +} + /* Decrease cwnd each second ack. */ static void tcp_cwnd_down(struct sock *sk) { - const struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); int decr = tp->snd_cwnd_cnt + 1; tp->snd_cwnd_cnt = decr&1; decr >>= 1; - if (decr && tp->snd_cwnd > icsk->icsk_ca_ops->min_cwnd(sk)) + if (decr && tp->snd_cwnd > tcp_cwnd_min(sk)) tp->snd_cwnd -= decr; tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1); @@ -3785,6 +3794,50 @@ static inline int tcp_checksum_complete_ __tcp_checksum_complete_user(sk, skb); } +#ifdef CONFIG_NET_DMA +static int tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, int hlen) +{ + struct tcp_sock *tp = tcp_sk(sk); + int chunk = skb->len - hlen; + int dma_cookie; + int copied_early = 0; + + if (tp->ucopy.wakeup) + return 0; + + if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan = get_softnet_dma(); + + if (tp->ucopy.dma_chan && skb->ip_summed == CHECKSUM_UNNECESSARY) { + + dma_cookie = dma_skb_copy_datagram_iovec(tp->ucopy.dma_chan, + skb, hlen, tp->ucopy.iov, chunk, tp->ucopy.pinned_list); + + if (dma_cookie < 0) + goto out; + + tp->ucopy.dma_cookie = dma_cookie; + copied_early = 1; + + tp->ucopy.len -= chunk; + tp->copied_seq += chunk; + tcp_rcv_space_adjust(sk); + + if ((tp->ucopy.len == 0) || + (tcp_flag_word(skb->h.th) & TCP_FLAG_PSH) || + (atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1))) { + tp->ucopy.wakeup = 1; + sk->sk_data_ready(sk, 0); + } + } else if (chunk > 0) { + tp->ucopy.wakeup = 1; + sk->sk_data_ready(sk, 0); + } +out: + return copied_early; +} +#endif /* CONFIG_NET_DMA */ + /* * TCP receive function for the ESTABLISHED state. * @@ -3886,8 +3939,6 @@ int tcp_rcv_established(struct sock *sk, tp->rcv_nxt == tp->rcv_wup) tcp_store_ts_recent(tp); - tcp_rcv_rtt_measure_ts(sk, skb); - /* We know that such packets are checksummed * on entry. */ @@ -3901,14 +3952,23 @@ int tcp_rcv_established(struct sock *sk, } } else { int eaten = 0; + int copied_early = 0; - if (tp->ucopy.task == current && - tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len && - sock_owned_by_user(sk)) { - __set_current_state(TASK_RUNNING); + if (tp->copied_seq == tp->rcv_nxt && + len - tcp_header_len <= tp->ucopy.len) { +#ifdef CONFIG_NET_DMA + if (tcp_dma_try_early_copy(sk, skb, tcp_header_len)) { + copied_early = 1; + eaten = 1; + } +#endif + if (tp->ucopy.task == current && sock_owned_by_user(sk) && !copied_early) { + __set_current_state(TASK_RUNNING); - if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) { + if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) + eaten = 1; + } + if (eaten) { /* Predicted packet is in window by definition. * seq == rcv_nxt and rcv_wup <= rcv_nxt. * Hence, check seq<=rcv_wup reduces to: @@ -3924,8 +3984,9 @@ int tcp_rcv_established(struct sock *sk, __skb_pull(skb, tcp_header_len); tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq; NET_INC_STATS_BH(LINUX_MIB_TCPHPHITSTOUSER); - eaten = 1; } + if (copied_early) + tcp_cleanup_rbuf(sk, skb->len); } if (!eaten) { if (tcp_checksum_complete_user(sk, skb)) @@ -3966,6 +4027,11 @@ int tcp_rcv_established(struct sock *sk, __tcp_ack_snd_check(sk, 0); no_ack: +#ifdef CONFIG_NET_DMA + if (copied_early) + __skb_queue_tail(&sk->sk_async_wait_queue, skb); + else +#endif if (eaten) __kfree_skb(skb); else @@ -4111,8 +4177,6 @@ static int tcp_rcv_synsent_state_process */ TCP_ECN_rcv_synack(tp, th); - if (tp->ecn_flags&TCP_ECN_OK) - sock_set_flag(sk, SOCK_NO_LARGESEND); tp->snd_wl1 = TCP_SKB_CB(skb)->seq; tcp_ack(sk, skb, FLAG_SLOWPATH); @@ -4255,8 +4319,6 @@ discard: tp->max_window = tp->snd_wnd; TCP_ECN_rcv_syn(tp, th); - if (tp->ecn_flags&TCP_ECN_OK) - sock_set_flag(sk, SOCK_NO_LARGESEND); tcp_mtup_init(sk); tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 672950e..5a886e6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -52,7 +52,6 @@ * a single port at the same time. */ -#include #include #include @@ -71,6 +70,7 @@ #include #include #include #include +#include #include #include @@ -90,7 +90,7 @@ static struct socket *tcp_socket; void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb); struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { - .lhash_lock = RW_LOCK_UNLOCKED, + .lhash_lock = __RW_LOCK_UNLOCKED(tcp_hashinfo.lhash_lock), .lhash_users = ATOMIC_INIT(0), .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(tcp_hashinfo.lhash_wait), }; @@ -241,6 +241,7 @@ int tcp_v4_connect(struct sock *sk, stru goto failure; /* OK, now commit destination to socket. */ + sk->sk_gso_type = SKB_GSO_TCPV4; sk_setup_caps(sk, &rt->u.dst); if (!tp->write_seq) @@ -883,6 +884,7 @@ struct sock *tcp_v4_syn_recv_sock(struct if (!newsk) goto exit; + newsk->sk_gso_type = SKB_GSO_TCPV4; sk_setup_caps(newsk, dst); newtp = tcp_sk(newsk); @@ -1088,11 +1090,21 @@ process: skb->dev = NULL; - bh_lock_sock(sk); + bh_lock_sock_nested(sk); ret = 0; if (!sock_owned_by_user(sk)) { - if (!tcp_prequeue(sk, skb)) +#ifdef CONFIG_NET_DMA + struct tcp_sock *tp = tcp_sk(sk); + if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list) + tp->ucopy.dma_chan = get_softnet_dma(); + if (tp->ucopy.dma_chan) + ret = tcp_v4_do_rcv(sk, skb); + else +#endif + { + if (!tcp_prequeue(sk, skb)) ret = tcp_v4_do_rcv(sk, skb); + } } else sk_add_backlog(sk, skb); bh_unlock_sock(sk); @@ -1296,6 +1308,11 @@ int tcp_v4_destroy_sock(struct sock *sk) /* Cleans up our, hopefully empty, out_of_order_queue. */ __skb_queue_purge(&tp->out_of_order_queue); +#ifdef CONFIG_NET_DMA + /* Cleans up our sk_async_wait_queue */ + __skb_queue_purge(&sk->sk_async_wait_queue); +#endif + /* Clean prequeue, it must be empty really */ __skb_queue_purge(&tp->ucopy.prequeue); @@ -1710,7 +1727,8 @@ static void get_tcp4_sock(struct sock *s sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %02X %08X:%08X %02X:%08lX " "%08X %5d %8d %lu %d %p %u %u %u %u %d", i, src, srcp, dest, destp, sp->sk_state, - tp->write_seq - tp->snd_una, tp->rcv_nxt - tp->copied_seq, + tp->write_seq - tp->snd_una, + (sp->sk_state == TCP_LISTEN) ? sp->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq), timer_active, jiffies_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c new file mode 100644 index 0000000..1f977b6 --- /dev/null +++ b/net/ipv4/tcp_lp.c @@ -0,0 +1,338 @@ +/* + * TCP Low Priority (TCP-LP) + * + * TCP Low Priority is a distributed algorithm whose goal is to utilize only + * the excess network bandwidth as compared to the ``fair share`` of + * bandwidth as targeted by TCP. Available from: + * http://www.ece.rice.edu/~akuzma/Doc/akuzma/TCP-LP.pdf + * + * Original Author: + * Aleksandar Kuzmanovic + * + * See http://www-ece.rice.edu/networks/TCP-LP/ for their implementation. + * As of 2.6.13, Linux supports pluggable congestion control algorithms. + * Due to the limitation of the API, we take the following changes from + * the original TCP-LP implementation: + * o We use newReno in most core CA handling. Only add some checking + * within cong_avoid. + * o Error correcting in remote HZ, therefore remote HZ will be keeped + * on checking and updating. + * o Handling calculation of One-Way-Delay (OWD) within rtt_sample, sicne + * OWD have a similar meaning as RTT. Also correct the buggy formular. + * o Handle reaction for Early Congestion Indication (ECI) within + * pkts_acked, as mentioned within pseudo code. + * o OWD is handled in relative format, where local time stamp will in + * tcp_time_stamp format. + * + * Port from 2.4.19 to 2.6.16 as module by: + * Wong Hoi Sing Edison + * Hung Hing Lun + * + * Version: $Id: tcp_lp.c,v 1.22 2006-05-02 18:18:19 hswong3i Exp $ + */ + +#include +#include +#include + +/* resolution of owd */ +#define LP_RESOL 1000 + +/** + * enum tcp_lp_state + * @LP_VALID_RHZ: is remote HZ valid? + * @LP_VALID_OWD: is OWD valid? + * @LP_WITHIN_THR: are we within threshold? + * @LP_WITHIN_INF: are we within inference? + * + * TCP-LP's state flags. + * We create this set of state flag mainly for debugging. + */ +enum tcp_lp_state { + LP_VALID_RHZ = (1 << 0), + LP_VALID_OWD = (1 << 1), + LP_WITHIN_THR = (1 << 3), + LP_WITHIN_INF = (1 << 4), +}; + +/** + * struct lp + * @flag: TCP-LP state flag + * @sowd: smoothed OWD << 3 + * @owd_min: min OWD + * @owd_max: max OWD + * @owd_max_rsv: resrved max owd + * @remote_hz: estimated remote HZ + * @remote_ref_time: remote reference time + * @local_ref_time: local reference time + * @last_drop: time for last active drop + * @inference: current inference + * + * TCP-LP's private struct. + * We get the idea from original TCP-LP implementation where only left those we + * found are really useful. + */ +struct lp { + u32 flag; + u32 sowd; + u32 owd_min; + u32 owd_max; + u32 owd_max_rsv; + u32 remote_hz; + u32 remote_ref_time; + u32 local_ref_time; + u32 last_drop; + u32 inference; +}; + +/** + * tcp_lp_init + * + * Init all required variables. + * Clone the handling from Vegas module implementation. + */ +static void tcp_lp_init(struct sock *sk) +{ + struct lp *lp = inet_csk_ca(sk); + + lp->flag = 0; + lp->sowd = 0; + lp->owd_min = 0xffffffff; + lp->owd_max = 0; + lp->owd_max_rsv = 0; + lp->remote_hz = 0; + lp->remote_ref_time = 0; + lp->local_ref_time = 0; + lp->last_drop = 0; + lp->inference = 0; +} + +/** + * tcp_lp_cong_avoid + * + * Implementation of cong_avoid. + * Will only call newReno CA when away from inference. + * From TCP-LP's paper, this will be handled in additive increasement. + */ +static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, + int flag) +{ + struct lp *lp = inet_csk_ca(sk); + + if (!(lp->flag & LP_WITHIN_INF)) + tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag); +} + +/** + * tcp_lp_remote_hz_estimator + * + * Estimate remote HZ. + * We keep on updating the estimated value, where original TCP-LP + * implementation only guest it for once and use forever. + */ +static u32 tcp_lp_remote_hz_estimator(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct lp *lp = inet_csk_ca(sk); + s64 rhz = lp->remote_hz << 6; /* remote HZ << 6 */ + s64 m = 0; + + /* not yet record reference time + * go away!! record it before come back!! */ + if (lp->remote_ref_time == 0 || lp->local_ref_time == 0) + goto out; + + /* we can't calc remote HZ with no different!! */ + if (tp->rx_opt.rcv_tsval == lp->remote_ref_time + || tp->rx_opt.rcv_tsecr == lp->local_ref_time) + goto out; + + m = HZ * (tp->rx_opt.rcv_tsval - + lp->remote_ref_time) / (tp->rx_opt.rcv_tsecr - + lp->local_ref_time); + if (m < 0) + m = -m; + + if (rhz != 0) { + m -= rhz >> 6; /* m is now error in remote HZ est */ + rhz += m; /* 63/64 old + 1/64 new */ + } else + rhz = m << 6; + + /* record time for successful remote HZ calc */ + lp->flag |= LP_VALID_RHZ; + + out: + /* record reference time stamp */ + lp->remote_ref_time = tp->rx_opt.rcv_tsval; + lp->local_ref_time = tp->rx_opt.rcv_tsecr; + + return rhz >> 6; +} + +/** + * tcp_lp_owd_calculator + * + * Calculate one way delay (in relative format). + * Original implement OWD as minus of remote time difference to local time + * difference directly. As this time difference just simply equal to RTT, when + * the network status is stable, remote RTT will equal to local RTT, and result + * OWD into zero. + * It seems to be a bug and so we fixed it. + */ +static u32 tcp_lp_owd_calculator(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct lp *lp = inet_csk_ca(sk); + s64 owd = 0; + + lp->remote_hz = tcp_lp_remote_hz_estimator(sk); + + if (lp->flag & LP_VALID_RHZ) { + owd = + tp->rx_opt.rcv_tsval * (LP_RESOL / lp->remote_hz) - + tp->rx_opt.rcv_tsecr * (LP_RESOL / HZ); + if (owd < 0) + owd = -owd; + } + + if (owd > 0) + lp->flag |= LP_VALID_OWD; + else + lp->flag &= ~LP_VALID_OWD; + + return owd; +} + +/** + * tcp_lp_rtt_sample + * + * Implementation or rtt_sample. + * Will take the following action, + * 1. calc OWD, + * 2. record the min/max OWD, + * 3. calc smoothed OWD (SOWD). + * Most ideas come from the original TCP-LP implementation. + */ +static void tcp_lp_rtt_sample(struct sock *sk, u32 usrtt) +{ + struct lp *lp = inet_csk_ca(sk); + s64 mowd = tcp_lp_owd_calculator(sk); + + /* sorry that we don't have valid data */ + if (!(lp->flag & LP_VALID_RHZ) || !(lp->flag & LP_VALID_OWD)) + return; + + /* record the next min owd */ + if (mowd < lp->owd_min) + lp->owd_min = mowd; + + /* always forget the max of the max + * we just set owd_max as one below it */ + if (mowd > lp->owd_max) { + if (mowd > lp->owd_max_rsv) { + if (lp->owd_max_rsv == 0) + lp->owd_max = mowd; + else + lp->owd_max = lp->owd_max_rsv; + lp->owd_max_rsv = mowd; + } else + lp->owd_max = mowd; + } + + /* calc for smoothed owd */ + if (lp->sowd != 0) { + mowd -= lp->sowd >> 3; /* m is now error in owd est */ + lp->sowd += mowd; /* owd = 7/8 owd + 1/8 new */ + } else + lp->sowd = mowd << 3; /* take the measured time be owd */ +} + +/** + * tcp_lp_pkts_acked + * + * Implementation of pkts_acked. + * Deal with active drop under Early Congestion Indication. + * Only drop to half and 1 will be handle, because we hope to use back + * newReno in increase case. + * We work it out by following the idea from TCP-LP's paper directly + */ +static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct lp *lp = inet_csk_ca(sk); + + /* calc inference */ + if (tcp_time_stamp > tp->rx_opt.rcv_tsecr) + lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr); + + /* test if within inference */ + if (lp->last_drop && (tcp_time_stamp - lp->last_drop < lp->inference)) + lp->flag |= LP_WITHIN_INF; + else + lp->flag &= ~LP_WITHIN_INF; + + /* test if within threshold */ + if (lp->sowd >> 3 < + lp->owd_min + 15 * (lp->owd_max - lp->owd_min) / 100) + lp->flag |= LP_WITHIN_THR; + else + lp->flag &= ~LP_WITHIN_THR; + + pr_debug("TCP-LP: %05o|%5u|%5u|%15u|%15u|%15u\n", lp->flag, + tp->snd_cwnd, lp->remote_hz, lp->owd_min, lp->owd_max, + lp->sowd >> 3); + + if (lp->flag & LP_WITHIN_THR) + return; + + /* FIXME: try to reset owd_min and owd_max here + * so decrease the chance the min/max is no longer suitable + * and will usually within threshold when whithin inference */ + lp->owd_min = lp->sowd >> 3; + lp->owd_max = lp->sowd >> 2; + lp->owd_max_rsv = lp->sowd >> 2; + + /* happened within inference + * drop snd_cwnd into 1 */ + if (lp->flag & LP_WITHIN_INF) + tp->snd_cwnd = 1U; + + /* happened after inference + * cut snd_cwnd into half */ + else + tp->snd_cwnd = max(tp->snd_cwnd >> 1U, 1U); + + /* record this drop time */ + lp->last_drop = tcp_time_stamp; +} + +static struct tcp_congestion_ops tcp_lp = { + .init = tcp_lp_init, + .ssthresh = tcp_reno_ssthresh, + .cong_avoid = tcp_lp_cong_avoid, + .min_cwnd = tcp_reno_min_cwnd, + .rtt_sample = tcp_lp_rtt_sample, + .pkts_acked = tcp_lp_pkts_acked, + + .owner = THIS_MODULE, + .name = "lp" +}; + +static int __init tcp_lp_register(void) +{ + BUG_ON(sizeof(struct lp) > ICSK_CA_PRIV_SIZE); + return tcp_register_congestion_control(&tcp_lp); +} + +static void __exit tcp_lp_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_lp); +} + +module_init(tcp_lp_register); +module_exit(tcp_lp_unregister); + +MODULE_AUTHOR("Wong Hoi Sing Edison, Hung Hing Lun"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Low Priority"); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 2b9b7f6..0ccb7cb 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -20,7 +20,6 @@ * Jorge Cwik, */ -#include #include #include #include @@ -41,7 +40,7 @@ int sysctl_tcp_abort_on_overflow; struct inet_timewait_death_row tcp_death_row = { .sysctl_max_tw_buckets = NR_FILE * 2, .period = TCP_TIMEWAIT_LEN / INET_TWDR_TWKILL_SLOTS, - .death_lock = SPIN_LOCK_UNLOCKED, + .death_lock = __SPIN_LOCK_UNLOCKED(tcp_death_row.death_lock), .hashinfo = &tcp_hashinfo, .tw_timer = TIMER_INITIALIZER(inet_twdr_hangman, 0, (unsigned long)&tcp_death_row), @@ -440,8 +439,6 @@ struct sock *tcp_create_openreq_child(st newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len; newtp->rx_opt.mss_clamp = req->mss; TCP_ECN_openreq_child(newtp, req); - if (newtp->ecn_flags&TCP_ECN_OK) - sock_set_flag(newsk, SOCK_NO_LARGESEND); TCP_INC_STATS_BH(TCP_MIB_PASSIVEOPENS); } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f33c9dd..5c08ea2 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -59,6 +59,9 @@ int sysctl_tcp_tso_win_divisor = 3; int sysctl_tcp_mtu_probing = 0; int sysctl_tcp_base_mss = 512; +/* By default, RFC2861 behavior. */ +int sysctl_tcp_slow_start_after_idle = 1; + static void update_send_head(struct sock *sk, struct tcp_sock *tp, struct sk_buff *skb) { @@ -138,7 +141,8 @@ static void tcp_event_data_sent(struct t struct inet_connection_sock *icsk = inet_csk(sk); const u32 now = tcp_time_stamp; - if (!tp->packets_out && (s32)(now - tp->lsndtime) > icsk->icsk_rto) + if (sysctl_tcp_slow_start_after_idle && + (!tp->packets_out && (s32)(now - tp->lsndtime) > icsk->icsk_rto)) tcp_cwnd_restart(sk, __sk_dst_get(sk)); tp->lsndtime = now; @@ -506,20 +510,21 @@ static void tcp_queue_skb(struct sock *s static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb, unsigned int mss_now) { - if (skb->len <= mss_now || - !(sk->sk_route_caps & NETIF_F_TSO)) { + if (skb->len <= mss_now || !sk_can_gso(sk)) { /* Avoid the costly divide in the normal * non-TSO case. */ - skb_shinfo(skb)->tso_segs = 1; - skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->gso_segs = 1; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; } else { unsigned int factor; factor = skb->len + (mss_now - 1); factor /= mss_now; - skb_shinfo(skb)->tso_segs = factor; - skb_shinfo(skb)->tso_size = mss_now; + skb_shinfo(skb)->gso_segs = factor; + skb_shinfo(skb)->gso_size = mss_now; + skb_shinfo(skb)->gso_type = sk->sk_gso_type; } } @@ -818,9 +823,7 @@ unsigned int tcp_current_mss(struct sock mss_now = tp->mss_cache; - if (large_allowed && - (sk->sk_route_caps & NETIF_F_TSO) && - !tp->urg_mode) + if (large_allowed && sk_can_gso(sk) && !tp->urg_mode) doing_tso = 1; if (dst) { @@ -910,7 +913,7 @@ static int tcp_init_tso_segs(struct sock if (!tso_segs || (tso_segs > 1 && - skb_shinfo(skb)->tso_size != mss_now)) { + tcp_skb_mss(skb) != mss_now)) { tcp_set_skb_tso_segs(sk, skb, mss_now); tso_segs = tcp_skb_pcount(skb); } @@ -1720,8 +1723,9 @@ int tcp_retransmit_skb(struct sock *sk, tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) { if (!pskb_trim(skb, 0)) { TCP_SKB_CB(skb)->seq = TCP_SKB_CB(skb)->end_seq - 1; - skb_shinfo(skb)->tso_segs = 1; - skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->gso_segs = 1; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; skb->ip_summed = CHECKSUM_NONE; skb->csum = 0; } @@ -1926,8 +1930,9 @@ void tcp_send_fin(struct sock *sk) skb->csum = 0; TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_FIN); TCP_SKB_CB(skb)->sacked = 0; - skb_shinfo(skb)->tso_segs = 1; - skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->gso_segs = 1; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ TCP_SKB_CB(skb)->seq = tp->write_seq; @@ -1959,8 +1964,9 @@ void tcp_send_active_reset(struct sock * skb->csum = 0; TCP_SKB_CB(skb)->flags = (TCPCB_FLAG_ACK | TCPCB_FLAG_RST); TCP_SKB_CB(skb)->sacked = 0; - skb_shinfo(skb)->tso_segs = 1; - skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->gso_segs = 1; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; /* Send it off. */ TCP_SKB_CB(skb)->seq = tcp_acceptable_seq(sk, tp); @@ -2035,16 +2041,15 @@ struct sk_buff * tcp_make_synack(struct memset(th, 0, sizeof(struct tcphdr)); th->syn = 1; th->ack = 1; - if (dst->dev->features&NETIF_F_TSO) - ireq->ecn_ok = 0; TCP_ECN_make_synack(req, th); th->source = inet_sk(sk)->sport; th->dest = ireq->rmt_port; TCP_SKB_CB(skb)->seq = tcp_rsk(req)->snt_isn; TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + 1; TCP_SKB_CB(skb)->sacked = 0; - skb_shinfo(skb)->tso_segs = 1; - skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->gso_segs = 1; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; th->seq = htonl(TCP_SKB_CB(skb)->seq); th->ack_seq = htonl(tcp_rsk(req)->rcv_isn + 1); if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */ @@ -2148,8 +2153,9 @@ int tcp_connect(struct sock *sk) TCP_SKB_CB(buff)->flags = TCPCB_FLAG_SYN; TCP_ECN_send_syn(sk, tp, buff); TCP_SKB_CB(buff)->sacked = 0; - skb_shinfo(buff)->tso_segs = 1; - skb_shinfo(buff)->tso_size = 0; + skb_shinfo(buff)->gso_segs = 1; + skb_shinfo(buff)->gso_size = 0; + skb_shinfo(buff)->gso_type = 0; buff->csum = 0; TCP_SKB_CB(buff)->seq = tp->write_seq++; TCP_SKB_CB(buff)->end_seq = tp->write_seq; @@ -2253,8 +2259,9 @@ void tcp_send_ack(struct sock *sk) buff->csum = 0; TCP_SKB_CB(buff)->flags = TCPCB_FLAG_ACK; TCP_SKB_CB(buff)->sacked = 0; - skb_shinfo(buff)->tso_segs = 1; - skb_shinfo(buff)->tso_size = 0; + skb_shinfo(buff)->gso_segs = 1; + skb_shinfo(buff)->gso_size = 0; + skb_shinfo(buff)->gso_type = 0; /* Send it off, this clears delayed acks for us. */ TCP_SKB_CB(buff)->seq = TCP_SKB_CB(buff)->end_seq = tcp_acceptable_seq(sk, tp); @@ -2289,8 +2296,9 @@ static int tcp_xmit_probe_skb(struct soc skb->csum = 0; TCP_SKB_CB(skb)->flags = TCPCB_FLAG_ACK; TCP_SKB_CB(skb)->sacked = urgent; - skb_shinfo(skb)->tso_segs = 1; - skb_shinfo(skb)->tso_size = 0; + skb_shinfo(skb)->gso_segs = 1; + skb_shinfo(skb)->gso_size = 0; + skb_shinfo(skb)->gso_type = 0; /* Use a previous sequence. This should cause the other * end to send an ack. Don't queue or clone SKB, just diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c new file mode 100644 index 0000000..d7d517a --- /dev/null +++ b/net/ipv4/tcp_probe.c @@ -0,0 +1,181 @@ +/* + * tcpprobe - Observe the TCP flow with kprobes. + * + * The idea for this came from Werner Almesberger's umlsim + * Copyright (C) 2004, Stephen Hemminger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Stephen Hemminger "); +MODULE_DESCRIPTION("TCP cwnd snooper"); +MODULE_LICENSE("GPL"); + +static int port = 0; +MODULE_PARM_DESC(port, "Port to match (0=all)"); +module_param(port, int, 0); + +static int bufsize = 64*1024; +MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)"); +module_param(bufsize, int, 0); + +static const char procname[] = "tcpprobe"; + +struct { + struct kfifo *fifo; + spinlock_t lock; + wait_queue_head_t wait; + struct timeval tstart; +} tcpw; + +static void printl(const char *fmt, ...) +{ + va_list args; + int len; + struct timeval now; + char tbuf[256]; + + va_start(args, fmt); + do_gettimeofday(&now); + + now.tv_sec -= tcpw.tstart.tv_sec; + now.tv_usec -= tcpw.tstart.tv_usec; + if (now.tv_usec < 0) { + --now.tv_sec; + now.tv_usec += 1000000; + } + + len = sprintf(tbuf, "%lu.%06lu ", + (unsigned long) now.tv_sec, + (unsigned long) now.tv_usec); + len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args); + va_end(args); + + kfifo_put(tcpw.fifo, tbuf, len); + wake_up(&tcpw.wait); +} + +static int jtcp_sendmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t size) +{ + const struct tcp_sock *tp = tcp_sk(sk); + const struct inet_sock *inet = inet_sk(sk); + + if (port == 0 || ntohs(inet->dport) == port || + ntohs(inet->sport) == port) { + printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n", + NIPQUAD(inet->saddr), ntohs(inet->sport), + NIPQUAD(inet->daddr), ntohs(inet->dport), + size, tp->snd_nxt, tp->snd_una, + tp->snd_cwnd, tcp_current_ssthresh(sk), + tp->snd_wnd); + } + + jprobe_return(); + return 0; +} + +static struct jprobe tcp_send_probe = { + .kp = { .addr = (kprobe_opcode_t *) &tcp_sendmsg, }, + .entry = (kprobe_opcode_t *) &jtcp_sendmsg, +}; + + +static int tcpprobe_open(struct inode * inode, struct file * file) +{ + kfifo_reset(tcpw.fifo); + do_gettimeofday(&tcpw.tstart); + return 0; +} + +static ssize_t tcpprobe_read(struct file *file, char __user *buf, + size_t len, loff_t *ppos) +{ + int error = 0, cnt; + unsigned char *tbuf; + + if (!buf || len < 0) + return -EINVAL; + + if (len == 0) + return 0; + + tbuf = vmalloc(len); + if (!tbuf) + return -ENOMEM; + + error = wait_event_interruptible(tcpw.wait, + __kfifo_len(tcpw.fifo) != 0); + if (error) + return error; + + cnt = kfifo_get(tcpw.fifo, tbuf, len); + error = copy_to_user(buf, tbuf, cnt); + + vfree(tbuf); + + return error ? error : cnt; +} + +static struct file_operations tcpprobe_fops = { + .owner = THIS_MODULE, + .open = tcpprobe_open, + .read = tcpprobe_read, +}; + +static __init int tcpprobe_init(void) +{ + int ret = -ENOMEM; + + init_waitqueue_head(&tcpw.wait); + spin_lock_init(&tcpw.lock); + tcpw.fifo = kfifo_alloc(bufsize, GFP_KERNEL, &tcpw.lock); + + if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops)) + goto err0; + + ret = register_jprobe(&tcp_send_probe); + if (ret) + goto err1; + + pr_info("TCP watch registered (port=%d)\n", port); + return 0; + err1: + proc_net_remove(procname); + err0: + kfifo_free(tcpw.fifo); + return ret; +} +module_init(tcpprobe_init); + +static __exit void tcpprobe_exit(void) +{ + kfifo_free(tcpw.fifo); + proc_net_remove(procname); + unregister_jprobe(&tcp_send_probe); + +} +module_exit(tcpprobe_exit); diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index 26d7486..4624501 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -5,7 +5,6 @@ * John Heffner */ -#include #include #include diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 3b74034..490360b 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -31,7 +31,6 @@ * assumed senders never went idle. */ -#include #include #include #include diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c new file mode 100644 index 0000000..11b42a7 --- /dev/null +++ b/net/ipv4/tcp_veno.c @@ -0,0 +1,231 @@ +/* + * TCP Veno congestion control + * + * This is based on the congestion detection/avoidance scheme described in + * C. P. Fu, S. C. Liew. + * "TCP Veno: TCP Enhancement for Transmission over Wireless Access Networks." + * IEEE Journal on Selected Areas in Communication, + * Feb. 2003. + * See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf + */ + +#include +#include +#include +#include +#include + +#include + +/* Default values of the Veno variables, in fixed-point representation + * with V_PARAM_SHIFT bits to the right of the binary point. + */ +#define V_PARAM_SHIFT 1 +static const int beta = 3 << V_PARAM_SHIFT; + +/* Veno variables */ +struct veno { + u8 doing_veno_now; /* if true, do veno for this rtt */ + u16 cntrtt; /* # of rtts measured within last rtt */ + u32 minrtt; /* min of rtts measured within last rtt (in usec) */ + u32 basertt; /* the min of all Veno rtt measurements seen (in usec) */ + u32 inc; /* decide whether to increase cwnd */ + u32 diff; /* calculate the diff rate */ +}; + +/* There are several situations when we must "re-start" Veno: + * + * o when a connection is established + * o after an RTO + * o after fast recovery + * o when we send a packet and there is no outstanding + * unacknowledged data (restarting an idle connection) + * + */ +static inline void veno_enable(struct sock *sk) +{ + struct veno *veno = inet_csk_ca(sk); + + /* turn on Veno */ + veno->doing_veno_now = 1; + + veno->minrtt = 0x7fffffff; +} + +static inline void veno_disable(struct sock *sk) +{ + struct veno *veno = inet_csk_ca(sk); + + /* turn off Veno */ + veno->doing_veno_now = 0; +} + +static void tcp_veno_init(struct sock *sk) +{ + struct veno *veno = inet_csk_ca(sk); + + veno->basertt = 0x7fffffff; + veno->inc = 1; + veno_enable(sk); +} + +/* Do rtt sampling needed for Veno. */ +static void tcp_veno_rtt_calc(struct sock *sk, u32 usrtt) +{ + struct veno *veno = inet_csk_ca(sk); + u32 vrtt = usrtt + 1; /* Never allow zero rtt or basertt */ + + /* Filter to find propagation delay: */ + if (vrtt < veno->basertt) + veno->basertt = vrtt; + + /* Find the min rtt during the last rtt to find + * the current prop. delay + queuing delay: + */ + veno->minrtt = min(veno->minrtt, vrtt); + veno->cntrtt++; +} + +static void tcp_veno_state(struct sock *sk, u8 ca_state) +{ + if (ca_state == TCP_CA_Open) + veno_enable(sk); + else + veno_disable(sk); +} + +/* + * If the connection is idle and we are restarting, + * then we don't want to do any Veno calculations + * until we get fresh rtt samples. So when we + * restart, we reset our Veno state to a clean + * state. After we get acks for this flight of + * packets, _then_ we can make Veno calculations + * again. + */ +static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event) +{ + if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START) + tcp_veno_init(sk); +} + +static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, + u32 seq_rtt, u32 in_flight, int flag) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct veno *veno = inet_csk_ca(sk); + + if (!veno->doing_veno_now) + return tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag); + + /* limited by applications */ + if (!tcp_is_cwnd_limited(sk, in_flight)) + return; + + /* We do the Veno calculations only if we got enough rtt samples */ + if (veno->cntrtt <= 2) { + /* We don't have enough rtt samples to do the Veno + * calculation, so we'll behave like Reno. + */ + tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag); + } else { + u32 rtt, target_cwnd; + + /* We have enough rtt samples, so, using the Veno + * algorithm, we determine the state of the network. + */ + + rtt = veno->minrtt; + + target_cwnd = ((tp->snd_cwnd * veno->basertt) + << V_PARAM_SHIFT) / rtt; + + veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd; + + if (tp->snd_cwnd <= tp->snd_ssthresh) { + /* Slow start. */ + tcp_slow_start(tp); + } else { + /* Congestion avoidance. */ + if (veno->diff < beta) { + /* In the "non-congestive state", increase cwnd + * every rtt. + */ + if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + if (tp->snd_cwnd < tp->snd_cwnd_clamp) + tp->snd_cwnd++; + tp->snd_cwnd_cnt = 0; + } else + tp->snd_cwnd_cnt++; + } else { + /* In the "congestive state", increase cwnd + * every other rtt. + */ + if (tp->snd_cwnd_cnt >= tp->snd_cwnd) { + if (veno->inc + && tp->snd_cwnd < + tp->snd_cwnd_clamp) { + tp->snd_cwnd++; + veno->inc = 0; + } else + veno->inc = 1; + tp->snd_cwnd_cnt = 0; + } else + tp->snd_cwnd_cnt++; + } + + } + if (tp->snd_cwnd < 2) + tp->snd_cwnd = 2; + else if (tp->snd_cwnd > tp->snd_cwnd_clamp) + tp->snd_cwnd = tp->snd_cwnd_clamp; + } + /* Wipe the slate clean for the next rtt. */ + /* veno->cntrtt = 0; */ + veno->minrtt = 0x7fffffff; +} + +/* Veno MD phase */ +static u32 tcp_veno_ssthresh(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct veno *veno = inet_csk_ca(sk); + + if (veno->diff < beta) + /* in "non-congestive state", cut cwnd by 1/5 */ + return max(tp->snd_cwnd * 4 / 5, 2U); + else + /* in "congestive state", cut cwnd by 1/2 */ + return max(tp->snd_cwnd >> 1U, 2U); +} + +static struct tcp_congestion_ops tcp_veno = { + .init = tcp_veno_init, + .ssthresh = tcp_veno_ssthresh, + .cong_avoid = tcp_veno_cong_avoid, + .rtt_sample = tcp_veno_rtt_calc, + .set_state = tcp_veno_state, + .cwnd_event = tcp_veno_cwnd_event, + + .owner = THIS_MODULE, + .name = "veno", +}; + +static int __init tcp_veno_register(void) +{ + BUG_ON(sizeof(struct veno) > ICSK_CA_PRIV_SIZE); + tcp_register_congestion_control(&tcp_veno); + return 0; +} + +static void __exit tcp_veno_unregister(void) +{ + tcp_unregister_congestion_control(&tcp_veno); +} + +module_init(tcp_veno_register); +module_exit(tcp_veno_unregister); + +MODULE_AUTHOR("Bin Zhou, Cheng Peng Fu"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TCP Veno"); diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index 0c340c3..5446312 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -1,10 +1,26 @@ /* - * TCP Westwood+ + * TCP Westwood+: end-to-end bandwidth estimation for TCP * - * Angelo Dell'Aera: TCP Westwood+ support + * Angelo Dell'Aera: author of the first version of TCP Westwood+ in Linux 2.4 + * + * Support at http://c3lab.poliba.it/index.php/Westwood + * Main references in literature: + * + * - Mascolo S, Casetti, M. Gerla et al. + * "TCP Westwood: bandwidth estimation for TCP" Proc. ACM Mobicom 2001 + * + * - A. Grieco, s. Mascolo + * "Performance evaluation of New Reno, Vegas, Westwood+ TCP" ACM Computer + * Comm. Review, 2004 + * + * - A. Dell'Aera, L. Grieco, S. Mascolo. + * "Linux 2.4 Implementation of Westwood+ TCP with Rate-Halving : + * A Performance Evaluation Over the Internet" (ICC 2004), Paris, June 2004 + * + * Westwood+ employs end-to-end bandwidth measurement to set cwnd and + * ssthresh after packet loss. The probing phase is as the original Reno. */ -#include #include #include #include @@ -22,6 +38,8 @@ struct westwood { u32 accounted; u32 rtt; u32 rtt_min; /* minimum observed RTT */ + u8 first_ack; /* flag which infers that this is the first ack */ + u8 reset_rtt_min; /* Reset RTT min to next RTT sample*/ }; @@ -49,9 +67,11 @@ static void tcp_westwood_init(struct soc w->bw_est = 0; w->accounted = 0; w->cumul_ack = 0; + w->reset_rtt_min = 1; w->rtt_min = w->rtt = TCP_WESTWOOD_INIT_RTT; w->rtt_win_sx = tcp_time_stamp; w->snd_una = tcp_sk(sk)->snd_una; + w->first_ack = 1; } /* @@ -63,10 +83,16 @@ static inline u32 westwood_do_filter(u32 return (((7 * a) + b) >> 3); } -static inline void westwood_filter(struct westwood *w, u32 delta) +static void westwood_filter(struct westwood *w, u32 delta) { - w->bw_ns_est = westwood_do_filter(w->bw_ns_est, w->bk / delta); - w->bw_est = westwood_do_filter(w->bw_est, w->bw_ns_est); + /* If the filter is empty fill it with the first sample of bandwidth */ + if (w->bw_ns_est == 0 && w->bw_est == 0) { + w->bw_ns_est = w->bk / delta; + w->bw_est = w->bw_ns_est; + } else { + w->bw_ns_est = westwood_do_filter(w->bw_ns_est, w->bk / delta); + w->bw_est = westwood_do_filter(w->bw_est, w->bw_ns_est); + } } /* @@ -91,6 +117,15 @@ static void westwood_update_window(struc struct westwood *w = inet_csk_ca(sk); s32 delta = tcp_time_stamp - w->rtt_win_sx; + /* Initialize w->snd_una with the first acked sequence number in order + * to fix mismatch between tp->snd_una and w->snd_una for the first + * bandwidth sample + */ + if (w->first_ack) { + w->snd_una = tcp_sk(sk)->snd_una; + w->first_ack = 0; + } + /* * See if a RTT-window has passed. * Be careful since if RTT is less than @@ -108,6 +143,16 @@ static void westwood_update_window(struc } } +static inline void update_rtt_min(struct westwood *w) +{ + if (w->reset_rtt_min) { + w->rtt_min = w->rtt; + w->reset_rtt_min = 0; + } else + w->rtt_min = min(w->rtt, w->rtt_min); +} + + /* * @westwood_fast_bw * It is called when we are in fast path. In particular it is called when @@ -123,7 +168,7 @@ static inline void westwood_fast_bw(stru w->bk += tp->snd_una - w->snd_una; w->snd_una = tp->snd_una; - w->rtt_min = min(w->rtt, w->rtt_min); + update_rtt_min(w); } /* @@ -162,12 +207,6 @@ static inline u32 westwood_acked_count(s return w->cumul_ack; } -static inline u32 westwood_bw_rttmin(const struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - const struct westwood *w = inet_csk_ca(sk); - return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2); -} /* * TCP Westwood @@ -175,9 +214,11 @@ static inline u32 westwood_bw_rttmin(con * in packets we use mss_cache). Rttmin is guaranteed to be >= 2 * so avoids ever returning 0. */ -static u32 tcp_westwood_cwnd_min(struct sock *sk) +static u32 tcp_westwood_bw_rttmin(const struct sock *sk) { - return westwood_bw_rttmin(sk); + const struct tcp_sock *tp = tcp_sk(sk); + const struct westwood *w = inet_csk_ca(sk); + return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2); } static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event) @@ -191,17 +232,19 @@ static void tcp_westwood_event(struct so break; case CA_EVENT_COMPLETE_CWR: - tp->snd_cwnd = tp->snd_ssthresh = westwood_bw_rttmin(sk); + tp->snd_cwnd = tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk); break; case CA_EVENT_FRTO: - tp->snd_ssthresh = westwood_bw_rttmin(sk); + tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk); + /* Update RTT_min when next ack arrives */ + w->reset_rtt_min = 1; break; case CA_EVENT_SLOW_ACK: westwood_update_window(sk); w->bk += westwood_acked_count(sk); - w->rtt_min = min(w->rtt, w->rtt_min); + update_rtt_min(w); break; default: @@ -235,7 +278,7 @@ static struct tcp_congestion_ops tcp_wes .init = tcp_westwood_init, .ssthresh = tcp_reno_ssthresh, .cong_avoid = tcp_reno_cong_avoid, - .min_cwnd = tcp_westwood_cwnd_min, + .min_cwnd = tcp_westwood_bw_rttmin, .cwnd_event = tcp_westwood_event, .get_info = tcp_westwood_info, .pkts_acked = tcp_westwood_pkts_acked, diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 3f93292..9bfcdda 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -91,7 +91,6 @@ #include #include #include #include -#include #include #include #include diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c index 3e174c8..817ed84 100644 --- a/net/ipv4/xfrm4_input.c +++ b/net/ipv4/xfrm4_input.c @@ -13,7 +13,6 @@ #include #include #include #include -#include #include #include @@ -24,15 +23,6 @@ int xfrm4_rcv(struct sk_buff *skb) EXPORT_SYMBOL(xfrm4_rcv); -static inline void ipip_ecn_decapsulate(struct sk_buff *skb) -{ - struct iphdr *outer_iph = skb->nh.iph; - struct iphdr *inner_iph = skb->h.ipiph; - - if (INET_ECN_is_ce(outer_iph->tos)) - IP_ECN_set_ce(inner_iph); -} - static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq) { switch (nexthdr) { @@ -113,24 +103,10 @@ int xfrm4_rcv_encap(struct sk_buff *skb, xfrm_vec[xfrm_nr++] = x; - iph = skb->nh.iph; + if (x->mode->input(x, skb)) + goto drop; if (x->props.mode) { - if (iph->protocol != IPPROTO_IPIP) - goto drop; - if (!pskb_may_pull(skb, sizeof(struct iphdr))) - goto drop; - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) - goto drop; - if (x->props.flags & XFRM_STATE_DECAP_DSCP) - ipv4_copy_dscp(iph, skb->h.ipiph); - if (!(x->props.flags & XFRM_STATE_NOECN)) - ipip_ecn_decapsulate(skb); - skb->mac.raw = memmove(skb->data - skb->mac_len, - skb->mac.raw, skb->mac_len); - skb->nh.raw = skb->data; - memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); decaps = 1; break; } diff --git a/net/ipv4/xfrm4_mode_transport.c b/net/ipv4/xfrm4_mode_transport.c new file mode 100644 index 0000000..a9e6b3d --- /dev/null +++ b/net/ipv4/xfrm4_mode_transport.c @@ -0,0 +1,83 @@ +/* + * xfrm4_mode_transport.c - Transport mode encapsulation for IPv4. + * + * Copyright (c) 2004-2006 Herbert Xu + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Add encapsulation header. + * + * The IP header will be moved forward to make space for the encapsulation + * header. + * + * On exit, skb->h will be set to the start of the payload to be processed + * by x->type->output and skb->nh will be set to the top IP header. + */ +static int xfrm4_transport_output(struct sk_buff *skb) +{ + struct xfrm_state *x; + struct iphdr *iph; + int ihl; + + iph = skb->nh.iph; + skb->h.ipiph = iph; + + ihl = iph->ihl * 4; + skb->h.raw += ihl; + + x = skb->dst->xfrm; + skb->nh.raw = memmove(skb_push(skb, x->props.header_len), iph, ihl); + return 0; +} + +/* Remove encapsulation header. + * + * The IP header will be moved over the top of the encapsulation header. + * + * On entry, skb->h shall point to where the IP header should be and skb->nh + * shall be set to where the IP header currently is. skb->data shall point + * to the start of the payload. + */ +static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb) +{ + int ihl = skb->data - skb->h.raw; + + if (skb->h.raw != skb->nh.raw) + skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl); + skb->nh.iph->tot_len = htons(skb->len + ihl); + skb->h.raw = skb->data; + return 0; +} + +static struct xfrm_mode xfrm4_transport_mode = { + .input = xfrm4_transport_input, + .output = xfrm4_transport_output, + .owner = THIS_MODULE, + .encap = XFRM_MODE_TRANSPORT, +}; + +static int __init xfrm4_transport_init(void) +{ + return xfrm_register_mode(&xfrm4_transport_mode, AF_INET); +} + +static void __exit xfrm4_transport_exit(void) +{ + int err; + + err = xfrm_unregister_mode(&xfrm4_transport_mode, AF_INET); + BUG_ON(err); +} + +module_init(xfrm4_transport_init); +module_exit(xfrm4_transport_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TRANSPORT); diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c new file mode 100644 index 0000000..f8d880b --- /dev/null +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -0,0 +1,125 @@ +/* + * xfrm4_mode_tunnel.c - Tunnel mode encapsulation for IPv4. + * + * Copyright (c) 2004-2006 Herbert Xu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline void ipip_ecn_decapsulate(struct sk_buff *skb) +{ + struct iphdr *outer_iph = skb->nh.iph; + struct iphdr *inner_iph = skb->h.ipiph; + + if (INET_ECN_is_ce(outer_iph->tos)) + IP_ECN_set_ce(inner_iph); +} + +/* Add encapsulation header. + * + * The top IP header will be constructed per RFC 2401. The following fields + * in it shall be filled in by x->type->output: + * tot_len + * check + * + * On exit, skb->h will be set to the start of the payload to be processed + * by x->type->output and skb->nh will be set to the top IP header. + */ +static int xfrm4_tunnel_output(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct xfrm_state *x = dst->xfrm; + struct iphdr *iph, *top_iph; + int flags; + + iph = skb->nh.iph; + skb->h.ipiph = iph; + + skb->nh.raw = skb_push(skb, x->props.header_len); + top_iph = skb->nh.iph; + + top_iph->ihl = 5; + top_iph->version = 4; + + /* DS disclosed */ + top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos); + + flags = x->props.flags; + if (flags & XFRM_STATE_NOECN) + IP_ECN_clear(top_iph); + + top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? + 0 : (iph->frag_off & htons(IP_DF)); + if (!top_iph->frag_off) + __ip_select_ident(top_iph, dst->child, 0); + + top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT); + + top_iph->saddr = x->props.saddr.a4; + top_iph->daddr = x->id.daddr.a4; + top_iph->protocol = IPPROTO_IPIP; + + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); + return 0; +} + +static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) +{ + struct iphdr *iph = skb->nh.iph; + int err = -EINVAL; + + if (iph->protocol != IPPROTO_IPIP) + goto out; + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + goto out; + + if (skb_cloned(skb) && + (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + goto out; + + if (x->props.flags & XFRM_STATE_DECAP_DSCP) + ipv4_copy_dscp(iph, skb->h.ipiph); + if (!(x->props.flags & XFRM_STATE_NOECN)) + ipip_ecn_decapsulate(skb); + skb->mac.raw = memmove(skb->data - skb->mac_len, + skb->mac.raw, skb->mac_len); + skb->nh.raw = skb->data; + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); + err = 0; + +out: + return err; +} + +static struct xfrm_mode xfrm4_tunnel_mode = { + .input = xfrm4_tunnel_input, + .output = xfrm4_tunnel_output, + .owner = THIS_MODULE, + .encap = XFRM_MODE_TUNNEL, +}; + +static int __init xfrm4_tunnel_init(void) +{ + return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET); +} + +static void __exit xfrm4_tunnel_exit(void) +{ + int err; + + err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET); + BUG_ON(err); +} + +module_init(xfrm4_tunnel_init); +module_exit(xfrm4_tunnel_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL); diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 4ef8efa..193363e 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -9,70 +9,15 @@ */ #include +#include +#include #include #include #include -#include #include #include #include -/* Add encapsulation header. - * - * In transport mode, the IP header will be moved forward to make space - * for the encapsulation header. - * - * In tunnel mode, the top IP header will be constructed per RFC 2401. - * The following fields in it shall be filled in by x->type->output: - * tot_len - * check - * - * On exit, skb->h will be set to the start of the payload to be processed - * by x->type->output and skb->nh will be set to the top IP header. - */ -static void xfrm4_encap(struct sk_buff *skb) -{ - struct dst_entry *dst = skb->dst; - struct xfrm_state *x = dst->xfrm; - struct iphdr *iph, *top_iph; - int flags; - - iph = skb->nh.iph; - skb->h.ipiph = iph; - - skb->nh.raw = skb_push(skb, x->props.header_len); - top_iph = skb->nh.iph; - - if (!x->props.mode) { - skb->h.raw += iph->ihl*4; - memmove(top_iph, iph, iph->ihl*4); - return; - } - - top_iph->ihl = 5; - top_iph->version = 4; - - /* DS disclosed */ - top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos); - - flags = x->props.flags; - if (flags & XFRM_STATE_NOECN) - IP_ECN_clear(top_iph); - - top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ? - 0 : (iph->frag_off & htons(IP_DF)); - if (!top_iph->frag_off) - __ip_select_ident(top_iph, dst->child, 0); - - top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT); - - top_iph->saddr = x->props.saddr.a4; - top_iph->daddr = x->id.daddr.a4; - top_iph->protocol = IPPROTO_IPIP; - - memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options)); -} - static int xfrm4_tunnel_check_size(struct sk_buff *skb) { int mtu, ret = 0; @@ -121,7 +66,9 @@ static int xfrm4_output_one(struct sk_bu if (err) goto error; - xfrm4_encap(skb); + err = x->mode->output(skb); + if (err) + goto error; err = x->type->output(x, skb); if (err) @@ -152,16 +99,10 @@ error_nolock: goto out_exit; } -static int xfrm4_output_finish(struct sk_buff *skb) +static int xfrm4_output_finish2(struct sk_buff *skb) { int err; -#ifdef CONFIG_NETFILTER - if (!skb->dst->xfrm) { - IPCB(skb)->flags |= IPSKB_REROUTED; - return dst_output(skb); - } -#endif while (likely((err = xfrm4_output_one(skb)) == 0)) { nf_reset(skb); @@ -174,7 +115,7 @@ #endif return dst_output(skb); err = nf_hook(PF_INET, NF_IP_POST_ROUTING, &skb, NULL, - skb->dst->dev, xfrm4_output_finish); + skb->dst->dev, xfrm4_output_finish2); if (unlikely(err != 1)) break; } @@ -182,6 +123,48 @@ #endif return err; } +static int xfrm4_output_finish(struct sk_buff *skb) +{ + struct sk_buff *segs; + +#ifdef CONFIG_NETFILTER + if (!skb->dst->xfrm) { + IPCB(skb)->flags |= IPSKB_REROUTED; + return dst_output(skb); + } +#endif + + if (!skb_shinfo(skb)->gso_size) + return xfrm4_output_finish2(skb); + + skb->protocol = htons(ETH_P_IP); + segs = skb_gso_segment(skb, 0); + kfree_skb(skb); + if (unlikely(IS_ERR(segs))) + return PTR_ERR(segs); + + do { + struct sk_buff *nskb = segs->next; + int err; + + segs->next = NULL; + err = xfrm4_output_finish2(segs); + + if (unlikely(err)) { + while ((segs = nskb)) { + nskb = segs->next; + segs->next = NULL; + kfree_skb(segs); + } + return err; + } + + segs = nskb; + } while (segs); + + return 0; +} + int xfrm4_output(struct sk_buff *skb) { return NF_HOOK_COND(PF_INET, NF_IP_POST_ROUTING, skb, NULL, skb->dst->dev, diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 8604c74..8f50eae 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -9,7 +9,6 @@ */ #include -#include #include #include #include @@ -17,8 +16,6 @@ #include static struct dst_ops xfrm4_dst_ops; static struct xfrm_policy_afinfo xfrm4_policy_afinfo; -static struct xfrm_type_map xfrm4_type_map = { .lock = RW_LOCK_UNLOCKED }; - static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) { return __ip_route_output_key((struct rtable**)dst, fl); @@ -237,9 +234,7 @@ _decode_session4(struct sk_buff *skb, st static inline int xfrm4_garbage_collect(void) { - read_lock(&xfrm4_policy_afinfo.lock); xfrm4_policy_afinfo.garbage_collect(); - read_unlock(&xfrm4_policy_afinfo.lock); return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2); } @@ -299,8 +294,6 @@ static struct dst_ops xfrm4_dst_ops = { static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { .family = AF_INET, - .lock = RW_LOCK_UNLOCKED, - .type_map = &xfrm4_type_map, .dst_ops = &xfrm4_dst_ops, .dst_lookup = xfrm4_dst_lookup, .find_bundle = __xfrm4_find_bundle, diff --git a/net/ipv4/xfrm4_state.c b/net/ipv4/xfrm4_state.c index dbabf81..81e1751 100644 --- a/net/ipv4/xfrm4_state.c +++ b/net/ipv4/xfrm4_state.c @@ -131,7 +131,6 @@ __xfrm4_find_acq(u8 mode, u32 reqid, u8 static struct xfrm_state_afinfo xfrm4_state_afinfo = { .family = AF_INET, - .lock = RW_LOCK_UNLOCKED, .init_flags = xfrm4_init_flags, .init_tempsel = __xfrm4_init_tempsel, .state_lookup = __xfrm4_state_lookup, diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index f8a107a..e923d4d 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -106,6 +106,26 @@ config INET6_TUNNEL tristate default n +config INET6_XFRM_MODE_TRANSPORT + tristate "IPv6: IPsec transport mode" + depends on IPV6 + default IPV6 + select XFRM + ---help--- + Support for IPsec transport mode. + + If unsure, say Y. + +config INET6_XFRM_MODE_TUNNEL + tristate "IPv6: IPsec tunnel mode" + depends on IPV6 + default IPV6 + select XFRM + ---help--- + Support for IPsec tunnel mode. + + If unsure, say Y. + config IPV6_TUNNEL tristate "IPv6: IPv6-in-IPv6 tunnel" select INET6_TUNNEL diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index a760b09..386e0a6 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_INET6_ESP) += esp6.o obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o +obj-$(CONFIG_INET6_XFRM_MODE_TRANSPORT) += xfrm6_mode_transport.o +obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o obj-$(CONFIG_NETFILTER) += netfilter/ obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 445006e..c250d0a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -40,7 +40,6 @@ * status etc. */ -#include #include #include #include @@ -862,6 +861,8 @@ static int inline ipv6_saddr_label(const * 2002::/16 2 * ::/96 3 * ::ffff:0:0/96 4 + * fc00::/7 5 + * 2001::/32 6 */ if (type & IPV6_ADDR_LOOPBACK) return 0; @@ -869,8 +870,12 @@ static int inline ipv6_saddr_label(const return 3; else if (type & IPV6_ADDR_MAPPED) return 4; + else if (addr->s6_addr32[0] == htonl(0x20010000)) + return 6; else if (addr->s6_addr16[0] == htons(0x2002)) return 2; + else if ((addr->s6_addr[0] & 0xfe) == 0xfc) + return 5; return 1; } @@ -1069,6 +1074,9 @@ #ifdef CONFIG_IPV6_PRIVACY if (hiscore.attrs & IPV6_SADDR_SCORE_PRIVACY) continue; } +#else + if (hiscore.rule < 7) + hiscore.rule++; #endif /* Rule 8: Use longest matching prefix */ if (hiscore.rule < 8) { @@ -2860,6 +2868,11 @@ inet6_rtm_newaddr(struct sk_buff *skb, s return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen); } +/* Maximum length of ifa_cacheinfo attributes */ +#define INET6_IFADDR_RTA_SPACE \ + RTA_SPACE(16) /* IFA_ADDRESS */ + \ + RTA_SPACE(sizeof(struct ifa_cacheinfo)) /* CACHEINFO */ + static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, u32 pid, u32 seq, int event, unsigned int flags) { @@ -3092,7 +3105,7 @@ static int inet6_dump_ifacaddr(struct sk static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa) { struct sk_buff *skb; - int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128); + int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + INET6_IFADDR_RTA_SPACE); skb = alloc_skb(size, GFP_ATOMIC); if (!skb) { @@ -3142,6 +3155,17 @@ #endif #endif } +/* Maximum length of ifinfomsg attributes */ +#define INET6_IFINFO_RTA_SPACE \ + RTA_SPACE(IFNAMSIZ) /* IFNAME */ + \ + RTA_SPACE(MAX_ADDR_LEN) /* ADDRESS */ + \ + RTA_SPACE(sizeof(u32)) /* MTU */ + \ + RTA_SPACE(sizeof(int)) /* LINK */ + \ + RTA_SPACE(0) /* PROTINFO */ + \ + RTA_SPACE(sizeof(u32)) /* FLAGS */ + \ + RTA_SPACE(sizeof(struct ifla_cacheinfo)) /* CACHEINFO */ + \ + RTA_SPACE(sizeof(__s32[DEVCONF_MAX])) /* CONF */ + static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev, u32 pid, u32 seq, int event, unsigned int flags) { @@ -3235,8 +3259,7 @@ static int inet6_dump_ifinfo(struct sk_b void inet6_ifinfo_notify(int event, struct inet6_dev *idev) { struct sk_buff *skb; - /* 128 bytes ?? */ - int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+128); + int size = NLMSG_SPACE(sizeof(struct ifinfomsg) + INET6_IFINFO_RTA_SPACE); skb = alloc_skb(size, GFP_ATOMIC); if (!skb) { @@ -3252,6 +3275,11 @@ void inet6_ifinfo_notify(int event, stru netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV6_IFINFO, GFP_ATOMIC); } +/* Maximum length of prefix_cacheinfo attributes */ +#define INET6_PREFIX_RTA_SPACE \ + RTA_SPACE(sizeof(((struct prefix_info *)NULL)->prefix)) /* ADDRESS */ + \ + RTA_SPACE(sizeof(struct prefix_cacheinfo)) /* CACHEINFO */ + static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev, struct prefix_info *pinfo, u32 pid, u32 seq, int event, unsigned int flags) @@ -3296,7 +3324,7 @@ static void inet6_prefix_notify(int even struct prefix_info *pinfo) { struct sk_buff *skb; - int size = NLMSG_SPACE(sizeof(struct prefixmsg)+128); + int size = NLMSG_SPACE(sizeof(struct prefixmsg) + INET6_PREFIX_RTA_SPACE); skb = alloc_skb(size, GFP_ATOMIC); if (!skb) { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index e19457f..5a0ba58 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -660,8 +659,6 @@ int inet6_sk_rebuild_header(struct sock } ip6_dst_store(sk, dst, NULL); - sk->sk_route_caps = dst->dev->features & - ~(NETIF_F_IP_CSUM | NETIF_F_TSO); } return 0; diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 6778173..9d4831b 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -24,7 +24,6 @@ * This file is derived from net/ipv4/ah.c. */ -#include #include #include #include @@ -292,7 +291,7 @@ static int ah6_input(struct xfrm_state * memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len); memset(ah->auth_data, 0, ahp->icv_trunc_len); - skb_push(skb, skb->data - skb->nh.raw); + skb_push(skb, hdr_len); ahp->icv(ahp, skb, ah->auth_data); if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) { LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n"); @@ -301,12 +300,8 @@ static int ah6_input(struct xfrm_state * } } - skb->nh.raw = skb_pull(skb, ah_hlen); - memcpy(skb->nh.raw, tmp_hdr, hdr_len); - skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); - skb_pull(skb, hdr_len); - skb->h.raw = skb->data; - + skb->h.raw = memcpy(skb->nh.raw += ah_hlen, tmp_hdr, hdr_len); + __skb_pull(skb, ah_hlen + hdr_len); kfree(tmp_hdr); diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 39ec528..f6881d7 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -14,7 +14,6 @@ */ #include -#include #include #include #include diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 22f0460..a278d5e 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -24,7 +24,6 @@ * This file is derived from net/ipv4/esp.c */ -#include #include #include #include @@ -142,25 +141,17 @@ static int esp6_input(struct xfrm_state int hdr_len = skb->h.raw - skb->nh.raw; int nfrags; - unsigned char *tmp_hdr = NULL; int ret = 0; if (!pskb_may_pull(skb, sizeof(struct ipv6_esp_hdr))) { ret = -EINVAL; - goto out_nofree; + goto out; } if (elen <= 0 || (elen & (blksize-1))) { ret = -EINVAL; - goto out_nofree; - } - - tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); - if (!tmp_hdr) { - ret = -ENOMEM; - goto out_nofree; + goto out; } - memcpy(tmp_hdr, skb->nh.raw, hdr_len); /* If integrity check is required, do this. */ if (esp->auth.icv_full_len) { @@ -222,16 +213,12 @@ static int esp6_input(struct xfrm_state /* ... check padding bits here. Silly. :-) */ pskb_trim(skb, skb->len - alen - padlen - 2); - skb->h.raw = skb_pull(skb, sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen); - skb->nh.raw += sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen; - memcpy(skb->nh.raw, tmp_hdr, hdr_len); - skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); ret = nexthdr[1]; } + skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - hdr_len; + out: - kfree(tmp_hdr); -out_nofree: return ret; } diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index a18d425..9d0ee7f 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -179,7 +179,7 @@ static int ipv6_destopt_rcv(struct sk_bu static struct inet6_protocol destopt_protocol = { .handler = ipv6_destopt_rcv, - .flags = INET6_PROTO_NOPOLICY, + .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR, }; void __init ipv6_destopt_init(void) @@ -340,7 +340,7 @@ looped_back: static struct inet6_protocol rthdr_protocol = { .handler = ipv6_rthdr_rcv, - .flags = INET6_PROTO_NOPOLICY, + .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR, }; void __init ipv6_rthdr_init(void) diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index eb2865d..5c950cc 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -13,7 +13,6 @@ * 2 of the License, or(at your option) any later version. */ -#include #include #include #include @@ -187,8 +186,6 @@ int inet6_csk_xmit(struct sk_buff *skb, } ip6_dst_store(sk, dst, NULL); - sk->sk_route_caps = dst->dev->features & - ~(NETIF_F_IP_CSUM | NETIF_F_TSO); } skb->dst = dst_clone(dst); diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 2ae84c9..d2f3fc9 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -14,7 +14,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 2cb6149..7642212 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -19,7 +19,6 @@ * remove ip6_null_entry from the top of * routing table. */ -#include #include #include #include diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index f9ca639..1d672b0 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index aceee25..df8f051 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -84,14 +84,9 @@ int ipv6_rcv(struct sk_buff *skb, struct */ IP6CB(skb)->iif = skb->dst ? ((struct rt6_info *)skb->dst)->rt6i_idev->dev->ifindex : dev->ifindex; - if (skb->len < sizeof(struct ipv6hdr)) + if (unlikely(!pskb_may_pull(skb, sizeof(*hdr)))) goto err; - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) { - IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); - goto drop; - } - hdr = skb->nh.ipv6h; if (hdr->version != 6) diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index e460489..2c5b445 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -28,7 +28,6 @@ * for datagram xmit */ -#include #include #include #include @@ -39,6 +38,7 @@ #include #include #include #include +#include #include #include @@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *s int ip6_output(struct sk_buff *skb) { - if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->ufo_size) || + if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) || dst_allfrag(skb->dst)) return ip6_fragment(skb, ip6_output2); else @@ -229,7 +229,7 @@ int ip6_xmit(struct sock *sk, struct sk_ skb->priority = sk->sk_priority; mtu = dst_mtu(dst); - if ((skb->len <= mtu) || ipfragok) { + if ((skb->len <= mtu) || ipfragok || skb_shinfo(skb)->gso_size) { IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); @@ -458,6 +458,7 @@ #ifdef CONFIG_BRIDGE_NETFILTER nf_bridge_get(to->nf_bridge); #endif #endif + skb_copy_secmark(to, from); } int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr) @@ -488,6 +489,7 @@ int ip6_find_1stfragopt(struct sk_buff * return offset; } +EXPORT_SYMBOL_GPL(ip6_find_1stfragopt); static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) { @@ -830,8 +832,9 @@ static inline int ip6_ufo_append_data(st struct frag_hdr fhdr; /* specify the length of each IP datagram fragment*/ - skb_shinfo(skb)->ufo_size = (mtu - fragheaderlen) - - sizeof(struct frag_hdr); + skb_shinfo(skb)->gso_size = mtu - fragheaderlen - + sizeof(struct frag_hdr); + skb_shinfo(skb)->gso_type = SKB_GSO_UDP; ipv6_select_ident(skb, &fhdr); skb_shinfo(skb)->ip6_frag_id = fhdr.identification; __skb_queue_tail(&sk->sk_write_queue, skb); diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index a995796..bc77c0e 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -19,7 +19,6 @@ * */ -#include #include #include #include diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 4863643..b285b03 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -30,7 +30,6 @@ * The decompression of IP datagram MUST be done after the reassembly, * AH/ESP processing. */ -#include #include #include #include @@ -65,38 +64,25 @@ static LIST_HEAD(ipcomp6_tfms_list); static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb) { - int err = 0; - u8 nexthdr = 0; - int hdr_len = skb->h.raw - skb->nh.raw; - unsigned char *tmp_hdr = NULL; + int err = -ENOMEM; struct ipv6hdr *iph; + struct ipv6_comp_hdr *ipch; int plen, dlen; struct ipcomp_data *ipcd = x->data; u8 *start, *scratch; struct crypto_tfm *tfm; int cpu; - if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && - skb_linearize(skb, GFP_ATOMIC) != 0) { - err = -ENOMEM; + if (skb_linearize_cow(skb)) goto out; - } skb->ip_summed = CHECKSUM_NONE; /* Remove ipcomp header and decompress original payload */ iph = skb->nh.ipv6h; - tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC); - if (!tmp_hdr) - goto out; - memcpy(tmp_hdr, iph, hdr_len); - nexthdr = *(u8 *)skb->data; - skb_pull(skb, sizeof(struct ipv6_comp_hdr)); - skb->nh.raw += sizeof(struct ipv6_comp_hdr); - memcpy(skb->nh.raw, tmp_hdr, hdr_len); - iph = skb->nh.ipv6h; - iph->payload_len = htons(ntohs(iph->payload_len) - sizeof(struct ipv6_comp_hdr)); - skb->h.raw = skb->data; + ipch = (void *)skb->data; + skb->h.raw = skb->nh.raw + sizeof(*ipch); + __skb_pull(skb, sizeof(*ipch)); /* decompression */ plen = skb->len; @@ -125,18 +111,11 @@ static int ipcomp6_input(struct xfrm_sta skb_put(skb, dlen - plen); memcpy(skb->data, scratch, dlen); + err = ipch->nexthdr; - iph = skb->nh.ipv6h; - iph->payload_len = htons(skb->len); - out_put_cpu: put_cpu(); out: - kfree(tmp_hdr); - if (err) - goto error_out; - return nexthdr; -error_out: return err; } @@ -159,10 +138,8 @@ static int ipcomp6_output(struct xfrm_st goto out_ok; } - if ((skb_is_nonlinear(skb) || skb_cloned(skb)) && - skb_linearize(skb, GFP_ATOMIC) != 0) { + if (skb_linearize_cow(skb)) goto out_ok; - } /* compression */ plen = skb->len - hdr_len; diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 4c20eeb..0c17dec 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -58,9 +57,80 @@ #include DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; +static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct ipv6hdr *ipv6h; + struct inet6_protocol *ops; + int proto; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_TCPV6 | + 0))) + goto out; + + if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) + goto out; + + ipv6h = skb->nh.ipv6h; + proto = ipv6h->nexthdr; + __skb_pull(skb, sizeof(*ipv6h)); + + rcu_read_lock(); + for (;;) { + struct ipv6_opt_hdr *opth; + int len; + + if (proto != NEXTHDR_HOP) { + ops = rcu_dereference(inet6_protos[proto]); + + if (unlikely(!ops)) + goto unlock; + + if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) + break; + } + + if (unlikely(!pskb_may_pull(skb, 8))) + goto unlock; + + opth = (void *)skb->data; + len = opth->hdrlen * 8 + 8; + + if (unlikely(!pskb_may_pull(skb, len))) + goto unlock; + + proto = opth->nexthdr; + __skb_pull(skb, len); + } + + skb->h.raw = skb->data; + if (likely(ops->gso_segment)) + segs = ops->gso_segment(skb, features); + +unlock: + rcu_read_unlock(); + + if (unlikely(IS_ERR(segs))) + goto out; + + for (skb = segs; skb; skb = skb->next) { + ipv6h = skb->nh.ipv6h; + ipv6h->payload_len = htons(skb->len - skb->mac_len - + sizeof(*ipv6h)); + } + +out: + return segs; +} + static struct packet_type ipv6_packet_type = { .type = __constant_htons(ETH_P_IPV6), .func = ipv6_rcv, + .gso_segment = ipv6_gso_segment, }; struct ip6_ra_chain *ip6_ra_chain; diff --git a/net/ipv6/ipv6_syms.c b/net/ipv6/ipv6_syms.c index 1648278..dd4d1ce 100644 --- a/net/ipv6/ipv6_syms.c +++ b/net/ipv6/ipv6_syms.c @@ -1,5 +1,4 @@ -#include #include #include #include diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 6e871af..9d697d4 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -28,7 +28,6 @@ * - MLDv2 support */ -#include #include #include #include diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index dfa20d3..b50055b 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -48,7 +48,6 @@ #define ND_PRINTK3 ND_PRINTK #endif #include -#include #include #include #include diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index b4b7d44..968a14b 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c @@ -505,7 +505,7 @@ ipq_rcv_skb(struct sk_buff *skb) if (type <= IPQM_BASE) return; - if (security_netlink_recv(skb)) + if (security_netlink_recv(skb, CAP_NET_ADMIN)) RCV_SKB_FAIL(-EPERM); write_lock_bh(&queue_lock); diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 2e72f89..f26898b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -19,13 +19,13 @@ */ #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -377,7 +377,7 @@ #endif } while (!hotdrop); #ifdef CONFIG_NETFILTER_DEBUG - ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac; + ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON; #endif read_unlock_bh(&table->lock); @@ -1281,7 +1281,8 @@ int ip6t_register_table(struct xt_table return ret; } - if (xt_register_table(table, &bootstrap, newinfo) != 0) { + ret = xt_register_table(table, &bootstrap, newinfo); + if (ret != 0) { xt_free_table_info(newinfo); return ret; } diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index de1175c..8629ba1 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -15,7 +15,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 93bae36..c2ab38f 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -20,7 +20,6 @@ * structures. */ -#include #include #include #include @@ -189,7 +188,7 @@ static unsigned int ipv6_confirm(unsigne /* This is where we call the helper: as the packet goes out. */ ct = nf_ct_get(*pskb, &ctinfo); - if (!ct) + if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY) goto out; help = nfct_help(ct); diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 86c6703..ef18a7b 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -233,7 +233,7 @@ icmpv6_error(struct sk_buff *skb, unsign return -NF_ACCEPT; } - if (hooknum == NF_IP6_PRE_ROUTING && + if (nf_conntrack_checksum && hooknum == NF_IP6_PRE_ROUTING && nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) { nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL, "nf_ct_icmpv6: ICMPv6 checksum failed\n"); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 3e31903..00d5583 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -14,7 +14,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include @@ -456,13 +455,9 @@ static int nf_ct_frag6_queue(struct nf_c DEBUGP("queue: message is too short.\n"); goto err; } - if (end-offset < skb->len) { - if (pskb_trim(skb, end - offset)) { - DEBUGP("Can't trim\n"); - goto err; - } - if (skb->ip_summed != CHECKSUM_UNNECESSARY) - skb->ip_summed = CHECKSUM_NONE; + if (pskb_trim_rcsum(skb, end - offset)) { + DEBUGP("Can't trim\n"); + goto err; } /* Find out which fragments are in front and at the back of us diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 779ddf7..efee7a6 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -17,7 +17,6 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index eef985e..4e299c6 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -28,7 +28,6 @@ * YOSHIFUJI,H. @USAGI Always remove fragment header to * calculate ICV correctly. */ -#include #include #include #include diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8a77793..87c39c9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -25,7 +25,6 @@ */ #include -#include #include #include #include @@ -349,7 +348,7 @@ static struct rt6_info *rt6_select(struc (strict & RT6_SELECT_F_REACHABLE) && last && last != rt0) { /* no entries matched; do round-robin */ - static spinlock_t lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(lock); spin_lock(&lock); *head = rt0->u.next; rt0->u.next = last->u.next; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 6578c30..c56aeec 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -18,7 +18,6 @@ * Nate Thompson : 6to4 support */ -#include #include #include #include diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 8eff9fa..7a4639d 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 301eee7..5bdcb90 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -26,7 +26,6 @@ */ #include -#include #include #include #include @@ -270,9 +269,8 @@ static int tcp_v6_connect(struct sock *s ipv6_addr_copy(&np->saddr, saddr); inet->rcv_saddr = LOOPBACK4_IPV6; + sk->sk_gso_type = SKB_GSO_TCPV6; ip6_dst_store(sk, dst, NULL); - sk->sk_route_caps = dst->dev->features & - ~(NETIF_F_IP_CSUM | NETIF_F_TSO); icsk->icsk_ext_hdr_len = 0; if (np->opt) @@ -930,9 +928,8 @@ static struct sock * tcp_v6_syn_recv_soc * comment in that function for the gory details. -acme */ + sk->sk_gso_type = SKB_GSO_TCPV6; ip6_dst_store(newsk, dst, NULL); - newsk->sk_route_caps = dst->dev->features & - ~(NETIF_F_IP_CSUM | NETIF_F_TSO); newtcp6sk = (struct tcp6_sock *)newsk; inet_sk(newsk)->pinet6 = &newtcp6sk->inet6; @@ -1218,8 +1215,16 @@ process: bh_lock_sock(sk); ret = 0; if (!sock_owned_by_user(sk)) { - if (!tcp_prequeue(sk, skb)) - ret = tcp_v6_do_rcv(sk, skb); +#ifdef CONFIG_NET_DMA + struct tcp_sock *tp = tcp_sk(sk); + if (tp->ucopy.dma_chan) + ret = tcp_v6_do_rcv(sk, skb); + else +#endif + { + if (!tcp_prequeue(sk, skb)) + ret = tcp_v6_do_rcv(sk, skb); + } } else sk_add_backlog(sk, skb); bh_unlock_sock(sk); @@ -1461,7 +1466,8 @@ static void get_tcp6_sock(struct seq_fil dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], destp, sp->sk_state, - tp->write_seq-tp->snd_una, tp->rcv_nxt-tp->copied_seq, + tp->write_seq-tp->snd_una, + (sp->sk_state == TCP_LISTEN) ? sp->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq), timer_active, jiffies_to_clock_t(timer_expires - jiffies), icsk->icsk_retransmits, @@ -1597,6 +1603,7 @@ #endif static struct inet6_protocol tcpv6_protocol = { .handler = tcp_v6_rcv, .err_handler = tcp_v6_err, + .gso_segment = tcp_tso_segment, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 8d3432a..ccc57f4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -23,7 +23,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 00cfdee..0405d74 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -13,21 +13,9 @@ #include #include #include #include -#include -#include -#include #include #include -static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) -{ - struct ipv6hdr *outer_iph = skb->nh.ipv6h; - struct ipv6hdr *inner_iph = skb->h.ipv6h; - - if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) - IP6_ECN_set_ce(inner_iph); -} - int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi) { int err; @@ -81,21 +69,10 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u xfrm_vec[xfrm_nr++] = x; + if (x->mode->input(x, skb)) + goto drop; + if (x->props.mode) { /* XXX */ - if (nexthdr != IPPROTO_IPV6) - goto drop; - if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) - goto drop; - if (skb_cloned(skb) && - pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) - goto drop; - if (x->props.flags & XFRM_STATE_DECAP_DSCP) - ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h); - if (!(x->props.flags & XFRM_STATE_NOECN)) - ipip6_ecn_decapsulate(skb); - skb->mac.raw = memmove(skb->data - skb->mac_len, - skb->mac.raw, skb->mac_len); - skb->nh.raw = skb->data; decaps = 1; break; } diff --git a/net/ipv6/xfrm6_mode_transport.c b/net/ipv6/xfrm6_mode_transport.c new file mode 100644 index 0000000..711d713 --- /dev/null +++ b/net/ipv6/xfrm6_mode_transport.c @@ -0,0 +1,88 @@ +/* + * xfrm6_mode_transport.c - Transport mode encapsulation for IPv6. + * + * Copyright (C) 2002 USAGI/WIDE Project + * Copyright (c) 2004-2006 Herbert Xu + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Add encapsulation header. + * + * The IP header and mutable extension headers will be moved forward to make + * space for the encapsulation header. + * + * On exit, skb->h will be set to the start of the encapsulation header to be + * filled in by x->type->output and skb->nh will be set to the nextheader field + * of the extension header directly preceding the encapsulation header, or in + * its absence, that of the top IP header. The value of skb->data will always + * point to the top IP header. + */ +static int xfrm6_transport_output(struct sk_buff *skb) +{ + struct xfrm_state *x = skb->dst->xfrm; + struct ipv6hdr *iph; + u8 *prevhdr; + int hdr_len; + + skb_push(skb, x->props.header_len); + iph = skb->nh.ipv6h; + + hdr_len = ip6_find_1stfragopt(skb, &prevhdr); + skb->nh.raw = prevhdr - x->props.header_len; + skb->h.raw = skb->data + hdr_len; + memmove(skb->data, iph, hdr_len); + return 0; +} + +/* Remove encapsulation header. + * + * The IP header will be moved over the top of the encapsulation header. + * + * On entry, skb->h shall point to where the IP header should be and skb->nh + * shall be set to where the IP header currently is. skb->data shall point + * to the start of the payload. + */ +static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb) +{ + int ihl = skb->data - skb->h.raw; + + if (skb->h.raw != skb->nh.raw) + skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl); + skb->nh.ipv6h->payload_len = htons(skb->len + ihl - + sizeof(struct ipv6hdr)); + skb->h.raw = skb->data; + return 0; +} + +static struct xfrm_mode xfrm6_transport_mode = { + .input = xfrm6_transport_input, + .output = xfrm6_transport_output, + .owner = THIS_MODULE, + .encap = XFRM_MODE_TRANSPORT, +}; + +static int __init xfrm6_transport_init(void) +{ + return xfrm_register_mode(&xfrm6_transport_mode, AF_INET6); +} + +static void __exit xfrm6_transport_exit(void) +{ + int err; + + err = xfrm_unregister_mode(&xfrm6_transport_mode, AF_INET6); + BUG_ON(err); +} + +module_init(xfrm6_transport_init); +module_exit(xfrm6_transport_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TRANSPORT); diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c new file mode 100644 index 0000000..8af79be --- /dev/null +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -0,0 +1,121 @@ +/* + * xfrm6_mode_tunnel.c - Tunnel mode encapsulation for IPv6. + * + * Copyright (C) 2002 USAGI/WIDE Project + * Copyright (c) 2004-2006 Herbert Xu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) +{ + struct ipv6hdr *outer_iph = skb->nh.ipv6h; + struct ipv6hdr *inner_iph = skb->h.ipv6h; + + if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph))) + IP6_ECN_set_ce(inner_iph); +} + +/* Add encapsulation header. + * + * The top IP header will be constructed per RFC 2401. The following fields + * in it shall be filled in by x->type->output: + * payload_len + * + * On exit, skb->h will be set to the start of the encapsulation header to be + * filled in by x->type->output and skb->nh will be set to the nextheader field + * of the extension header directly preceding the encapsulation header, or in + * its absence, that of the top IP header. The value of skb->data will always + * point to the top IP header. + */ +static int xfrm6_tunnel_output(struct sk_buff *skb) +{ + struct dst_entry *dst = skb->dst; + struct xfrm_state *x = dst->xfrm; + struct ipv6hdr *iph, *top_iph; + int dsfield; + + skb_push(skb, x->props.header_len); + iph = skb->nh.ipv6h; + + skb->nh.raw = skb->data; + top_iph = skb->nh.ipv6h; + skb->nh.raw = &top_iph->nexthdr; + skb->h.ipv6h = top_iph + 1; + + top_iph->version = 6; + top_iph->priority = iph->priority; + top_iph->flow_lbl[0] = iph->flow_lbl[0]; + top_iph->flow_lbl[1] = iph->flow_lbl[1]; + top_iph->flow_lbl[2] = iph->flow_lbl[2]; + dsfield = ipv6_get_dsfield(top_iph); + dsfield = INET_ECN_encapsulate(dsfield, dsfield); + if (x->props.flags & XFRM_STATE_NOECN) + dsfield &= ~INET_ECN_MASK; + ipv6_change_dsfield(top_iph, 0, dsfield); + top_iph->nexthdr = IPPROTO_IPV6; + top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT); + ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); + ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); + return 0; +} + +static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) +{ + int err = -EINVAL; + + if (skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPV6) + goto out; + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto out; + + if (skb_cloned(skb) && + (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + goto out; + + if (x->props.flags & XFRM_STATE_DECAP_DSCP) + ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h); + if (!(x->props.flags & XFRM_STATE_NOECN)) + ipip6_ecn_decapsulate(skb); + skb->mac.raw = memmove(skb->data - skb->mac_len, + skb->mac.raw, skb->mac_len); + skb->nh.raw = skb->data; + err = 0; + +out: + return err; +} + +static struct xfrm_mode xfrm6_tunnel_mode = { + .input = xfrm6_tunnel_input, + .output = xfrm6_tunnel_output, + .owner = THIS_MODULE, + .encap = XFRM_MODE_TUNNEL, +}; + +static int __init xfrm6_tunnel_init(void) +{ + return xfrm_register_mode(&xfrm6_tunnel_mode, AF_INET6); +} + +static void __exit xfrm6_tunnel_exit(void) +{ + int err; + + err = xfrm_unregister_mode(&xfrm6_tunnel_mode, AF_INET6); + BUG_ON(err); +} + +module_init(xfrm6_tunnel_init); +module_exit(xfrm6_tunnel_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TUNNEL); diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 8024217..48fccb1 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -14,68 +14,9 @@ #include #include #include #include -#include -#include #include #include -/* Add encapsulation header. - * - * In transport mode, the IP header and mutable extension headers will be moved - * forward to make space for the encapsulation header. - * - * In tunnel mode, the top IP header will be constructed per RFC 2401. - * The following fields in it shall be filled in by x->type->output: - * payload_len - * - * On exit, skb->h will be set to the start of the encapsulation header to be - * filled in by x->type->output and skb->nh will be set to the nextheader field - * of the extension header directly preceding the encapsulation header, or in - * its absence, that of the top IP header. The value of skb->data will always - * point to the top IP header. - */ -static void xfrm6_encap(struct sk_buff *skb) -{ - struct dst_entry *dst = skb->dst; - struct xfrm_state *x = dst->xfrm; - struct ipv6hdr *iph, *top_iph; - int dsfield; - - skb_push(skb, x->props.header_len); - iph = skb->nh.ipv6h; - - if (!x->props.mode) { - u8 *prevhdr; - int hdr_len; - - hdr_len = ip6_find_1stfragopt(skb, &prevhdr); - skb->nh.raw = prevhdr - x->props.header_len; - skb->h.raw = skb->data + hdr_len; - memmove(skb->data, iph, hdr_len); - return; - } - - skb->nh.raw = skb->data; - top_iph = skb->nh.ipv6h; - skb->nh.raw = &top_iph->nexthdr; - skb->h.ipv6h = top_iph + 1; - - top_iph->version = 6; - top_iph->priority = iph->priority; - top_iph->flow_lbl[0] = iph->flow_lbl[0]; - top_iph->flow_lbl[1] = iph->flow_lbl[1]; - top_iph->flow_lbl[2] = iph->flow_lbl[2]; - dsfield = ipv6_get_dsfield(top_iph); - dsfield = INET_ECN_encapsulate(dsfield, dsfield); - if (x->props.flags & XFRM_STATE_NOECN) - dsfield &= ~INET_ECN_MASK; - ipv6_change_dsfield(top_iph, 0, dsfield); - top_iph->nexthdr = IPPROTO_IPV6; - top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT); - ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr); - ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr); -} - static int xfrm6_tunnel_check_size(struct sk_buff *skb) { int mtu, ret = 0; @@ -118,7 +59,9 @@ static int xfrm6_output_one(struct sk_bu if (err) goto error; - xfrm6_encap(skb); + err = x->mode->output(skb); + if (err) + goto error; err = x->type->output(x, skb); if (err) @@ -151,7 +94,7 @@ error_nolock: goto out_exit; } -static int xfrm6_output_finish(struct sk_buff *skb) +static int xfrm6_output_finish2(struct sk_buff *skb) { int err; @@ -167,7 +110,7 @@ static int xfrm6_output_finish(struct sk return dst_output(skb); err = nf_hook(PF_INET6, NF_IP6_POST_ROUTING, &skb, NULL, - skb->dst->dev, xfrm6_output_finish); + skb->dst->dev, xfrm6_output_finish2); if (unlikely(err != 1)) break; } @@ -175,6 +118,41 @@ static int xfrm6_output_finish(struct sk return err; } +static int xfrm6_output_finish(struct sk_buff *skb) +{ + struct sk_buff *segs; + + if (!skb_shinfo(skb)->gso_size) + return xfrm6_output_finish2(skb); + + skb->protocol = htons(ETH_P_IP); + segs = skb_gso_segment(skb, 0); + kfree_skb(skb); + if (unlikely(IS_ERR(segs))) + return PTR_ERR(segs); + + do { + struct sk_buff *nskb = segs->next; + int err; + + segs->next = NULL; + err = xfrm6_output_finish2(segs); + + if (unlikely(err)) { + while ((segs = nskb)) { + nskb = segs->next; + segs->next = NULL; + kfree_skb(segs); + } + return err; + } + + segs = nskb; + } while (segs); + + return 0; +} + int xfrm6_output(struct sk_buff *skb) { return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, NULL, skb->dst->dev, diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 88c840f..73cd250 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include @@ -23,8 +22,6 @@ #include static struct dst_ops xfrm6_dst_ops; static struct xfrm_policy_afinfo xfrm6_policy_afinfo; -static struct xfrm_type_map xfrm6_type_map = { .lock = RW_LOCK_UNLOCKED }; - static int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl) { int err = 0; @@ -249,9 +246,7 @@ _decode_session6(struct sk_buff *skb, st static inline int xfrm6_garbage_collect(void) { - read_lock(&xfrm6_policy_afinfo.lock); xfrm6_policy_afinfo.garbage_collect(); - read_unlock(&xfrm6_policy_afinfo.lock); return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2); } @@ -311,8 +306,6 @@ static struct dst_ops xfrm6_dst_ops = { static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { .family = AF_INET6, - .lock = RW_LOCK_UNLOCKED, - .type_map = &xfrm6_type_map, .dst_ops = &xfrm6_dst_ops, .dst_lookup = xfrm6_dst_lookup, .find_bundle = __xfrm6_find_bundle, diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c index a572302..b33296b 100644 --- a/net/ipv6/xfrm6_state.c +++ b/net/ipv6/xfrm6_state.c @@ -135,7 +135,6 @@ __xfrm6_find_acq(u8 mode, u32 reqid, u8 static struct xfrm_state_afinfo xfrm6_state_afinfo = { .family = AF_INET6, - .lock = RW_LOCK_UNLOCKED, .init_tempsel = __xfrm6_init_tempsel, .state_lookup = __xfrm6_state_lookup, .find_acq = __xfrm6_find_acq, diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c index d37768e..6b44fe8 100644 --- a/net/ipv6/xfrm6_tunnel.c +++ b/net/ipv6/xfrm6_tunnel.c @@ -21,7 +21,6 @@ * Based on net/ipv4/xfrm4_tunnel.c * */ -#include #include #include #include diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c index 811d998..aa34ff4 100644 --- a/net/ipx/af_ipx.c +++ b/net/ipx/af_ipx.c @@ -28,7 +28,6 @@ * See net/ipx/ChangeLog. */ -#include #include #include #include diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c index 1f73d9e..4c0c712 100644 --- a/net/ipx/ipx_proc.c +++ b/net/ipx/ipx_proc.c @@ -4,7 +4,6 @@ * Copyright(C) Arnaldo Carvalho de Melo , 2002 */ -#include #include #ifdef CONFIG_PROC_FS #include diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c index a394c6f..a30dbb1 100644 --- a/net/ipx/ipx_route.c +++ b/net/ipx/ipx_route.c @@ -7,7 +7,6 @@ * See net/ipx/ChangeLog. */ -#include #include #include #include @@ -238,7 +237,7 @@ #endif /* CONFIG_IPX_INTERN */ } /* Apply checksum. Not allowed on 802.3 links. */ - if (sk->sk_no_check || intrfc->if_dlink_type == IPX_FRAME_8023) + if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023)) ipx->ipx_checksum = 0xFFFF; else ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr)); diff --git a/net/ipx/sysctl_net_ipx.c b/net/ipx/sysctl_net_ipx.c index 510eda9..fa57473 100644 --- a/net/ipx/sysctl_net_ipx.c +++ b/net/ipx/sysctl_net_ipx.c @@ -6,7 +6,6 @@ * Added /proc/sys/net/ipx/ipx_pprop_broadcasting - acme March 4, 2001 */ -#include #include #include diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index 2f37c9f..7fae48a 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -42,7 +42,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c index 2868819..9c4a902 100644 --- a/net/irda/ircomm/ircomm_core.c +++ b/net/irda/ircomm/ircomm_core.c @@ -29,7 +29,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c index 6f20b42..b400f27 100644 --- a/net/irda/ircomm/ircomm_tty.c +++ b/net/irda/ircomm/ircomm_tty.c @@ -30,7 +30,6 @@ * ********************************************************************/ -#include #include #include #include @@ -124,7 +123,6 @@ static int __init ircomm_tty_init(void) driver->owner = THIS_MODULE; driver->driver_name = "ircomm"; driver->name = "ircomm"; - driver->devfs_name = "ircomm"; driver->major = IRCOMM_TTY_MAJOR; driver->minor_start = IRCOMM_TTY_MINOR; driver->type = TTY_DRIVER_TYPE_SERIAL; diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index e3debbd..ba40e54 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -29,7 +29,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/iriap.c b/net/irda/iriap.c index 2d2e2b1..a047265 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -24,7 +24,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c index f8e6cb0..95cf123 100644 --- a/net/irda/irlan/irlan_client.c +++ b/net/irda/irlan/irlan_client.c @@ -173,13 +173,14 @@ void irlan_client_discovery_indication(d rcu_read_lock(); self = irlan_get_any(); if (self) { - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;); IRDA_DEBUG(1, "%s(), Found instance (%08x)!\n", __FUNCTION__ , daddr); irlan_client_wakeup(self, saddr, daddr); } +IRDA_ASSERT_LABEL(out:) rcu_read_unlock(); } diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c index 657d122..bd659dd 100644 --- a/net/irda/irlan/irlan_common.c +++ b/net/irda/irlan/irlan_common.c @@ -23,7 +23,6 @@ * ********************************************************************/ -#include #include #include diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c index 953e255..b0ccc45 100644 --- a/net/irda/irlan/irlan_eth.c +++ b/net/irda/irlan/irlan_eth.c @@ -25,7 +25,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/irlap.c b/net/irda/irlap.c index a165286..cade355 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -29,7 +29,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c index a505b54..99faff6 100644 --- a/net/irda/irlap_event.c +++ b/net/irda/irlap_event.c @@ -25,7 +25,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c index c19e9ce..129ad64 100644 --- a/net/irda/irlmp.c +++ b/net/irda/irlmp.c @@ -24,7 +24,6 @@ * ********************************************************************/ -#include #include #include #include @@ -44,6 +43,8 @@ #include #include #include +#include + static __u8 irlmp_find_free_slsap(void); static int irlmp_slsap_inuse(__u8 slsap_sel); @@ -840,6 +841,7 @@ void irlmp_do_expiry(void) void irlmp_do_discovery(int nslots) { struct lap_cb *lap; + __u16 *data_hintsp; /* Make sure the value is sane */ if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){ @@ -849,7 +851,8 @@ void irlmp_do_discovery(int nslots) } /* Construct new discovery info to be used by IrLAP, */ - u16ho(irlmp->discovery_cmd.data.hints) = irlmp->hints.word; + data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints; + put_unaligned(irlmp->hints.word, data_hintsp); /* * Set character set for device name (we use ASCII), and diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c index 26649f6..4c90dd1 100644 --- a/net/irda/irlmp_event.c +++ b/net/irda/irlmp_event.c @@ -24,7 +24,6 @@ * ********************************************************************/ -#include #include #include diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c index 91cd268..39761a1 100644 --- a/net/irda/irlmp_frame.c +++ b/net/irda/irlmp_frame.c @@ -24,7 +24,6 @@ * ********************************************************************/ -#include #include #include diff --git a/net/irda/irmod.c b/net/irda/irmod.c index 634901d..2869b16 100644 --- a/net/irda/irmod.c +++ b/net/irda/irmod.c @@ -31,7 +31,6 @@ * Jean II */ -#include #include #include diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h index e4fe1e8..8088752 100644 --- a/net/irda/irnet/irnet.h +++ b/net/irda/irnet/irnet.h @@ -244,12 +244,10 @@ #include #include #include #include -#include #include #include #include #include -#include #include /* isspace() */ #include #include diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index 1b1c419..86805c3 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -23,7 +23,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/irttp.c b/net/irda/irttp.c index 8aff254..49c51c5 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -24,7 +24,6 @@ * ********************************************************************/ -#include #include #include #include diff --git a/net/irda/qos.c b/net/irda/qos.c index ddfb5c5..95a69c0 100644 --- a/net/irda/qos.c +++ b/net/irda/qos.c @@ -30,7 +30,6 @@ * ********************************************************************/ -#include #include #include diff --git a/net/irda/timer.c b/net/irda/timer.c index 0e17f97..3871a2b 100644 --- a/net/irda/timer.c +++ b/net/irda/timer.c @@ -25,7 +25,6 @@ ********************************************************************/ #include -#include #include #include diff --git a/net/key/af_key.c b/net/key/af_key.c index 8595822..3a95b2e 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -14,7 +14,6 @@ * Derek Atkins */ -#include #include #include #include @@ -1454,21 +1453,23 @@ static int pfkey_delete(struct sock *sk, if (x == NULL) return -ESRCH; + if ((err = security_xfrm_state_delete(x))) + goto out; + if (xfrm_state_kern(x)) { - xfrm_state_put(x); - return -EPERM; + err = -EPERM; + goto out; } err = xfrm_state_delete(x); - if (err < 0) { - xfrm_state_put(x); - return err; - } + if (err < 0) + goto out; c.seq = hdr->sadb_msg_seq; c.pid = hdr->sadb_msg_pid; c.event = XFRM_MSG_DELSA; km_state_notify(x, &c); +out: xfrm_state_put(x); return err; @@ -2274,11 +2275,14 @@ static int pfkey_spddelete(struct sock * err = 0; + if ((err = security_xfrm_policy_delete(xp))) + goto out; c.seq = hdr->sadb_msg_seq; c.pid = hdr->sadb_msg_pid; c.event = XFRM_MSG_DELPOLICY; km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c); +out: xfrm_pol_put(xp); return err; } diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c index 5a04db7..d6cfe84 100644 --- a/net/llc/af_llc.c +++ b/net/llc/af_llc.c @@ -20,7 +20,6 @@ * * See the GNU General Public License for more details. */ -#include #include #include #include @@ -674,7 +673,7 @@ static int llc_ui_recvmsg(struct kiocb * lock_sock(sk); copied = -ENOTCONN; - if (sk->sk_state == TCP_LISTEN) + if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN)) goto out; timeo = sock_rcvtimeo(sk, nonblock); @@ -733,7 +732,7 @@ static int llc_ui_recvmsg(struct kiocb * if (sk->sk_shutdown & RCV_SHUTDOWN) break; - if (sk->sk_state == TCP_CLOSE) { + if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSE) { if (!sock_flag(sk, SOCK_DONE)) { /* * This occurs when user tries to read @@ -789,7 +788,7 @@ static int llc_ui_recvmsg(struct kiocb * continue; if (!(flags & MSG_PEEK)) { - sk_eat_skb(sk, skb); + sk_eat_skb(sk, skb, 0); *seq = 0; } } while (len > 0); diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c index ba90f7f..a899171 100644 --- a/net/llc/llc_if.c +++ b/net/llc/llc_if.c @@ -11,7 +11,6 @@ * * See the GNU General Public License for more details. */ -#include #include #include #include @@ -26,8 +25,6 @@ #include #include #include -u8 llc_mac_null_var[IFHWADDRLEN]; - /** * llc_build_and_send_pkt - Connection data sending for upper layers. * @sk: connection diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c index d62e0f9..94d2368 100644 --- a/net/llc/llc_input.c +++ b/net/llc/llc_input.c @@ -142,6 +142,8 @@ int llc_rcv(struct sk_buff *skb, struct struct llc_sap *sap; struct llc_pdu_sn *pdu; int dest; + int (*rcv)(struct sk_buff *, struct net_device *, + struct packet_type *, struct net_device *); /* * When the interface is in promisc. mode, drop all the crap that it @@ -169,9 +171,11 @@ int llc_rcv(struct sk_buff *skb, struct * First the upper layer protocols that don't need the full * LLC functionality */ - if (sap->rcv_func) { - sap->rcv_func(skb, dev, pt, orig_dev); - goto out_put; + rcv = rcu_dereference(sap->rcv_func); + if (rcv) { + struct sk_buff *cskb = skb_clone(skb, GFP_ATOMIC); + if (cskb) + rcv(cskb, dev, pt, orig_dev); } dest = llc_pdu_type(skb); if (unlikely(!dest || !llc_type_handlers[dest - 1])) diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c index bd531cb..19308fe 100644 --- a/net/llc/llc_proc.c +++ b/net/llc/llc_proc.c @@ -12,7 +12,6 @@ * See the GNU General Public License for more details. */ -#include #include #include #include diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c index 4029cee..20c4eb5 100644 --- a/net/llc/llc_sap.c +++ b/net/llc/llc_sap.c @@ -282,7 +282,7 @@ static void llc_sap_rcv(struct llc_sap * * mac, and local sap. Returns pointer for socket found, %NULL otherwise. */ static struct sock *llc_lookup_dgram(struct llc_sap *sap, - struct llc_addr *laddr) + const struct llc_addr *laddr) { struct sock *rc; struct hlist_node *node; @@ -304,19 +304,62 @@ found: return rc; } +/** + * llc_sap_mcast - Deliver multicast PDU's to all matching datagram sockets. + * @sap: SAP + * @laddr: address of local LLC (MAC + SAP) + * + * Search socket list of the SAP and finds connections with same sap. + * Deliver clone to each. + */ +static void llc_sap_mcast(struct llc_sap *sap, + const struct llc_addr *laddr, + struct sk_buff *skb) +{ + struct sock *sk; + struct hlist_node *node; + + read_lock_bh(&sap->sk_list.lock); + sk_for_each(sk, node, &sap->sk_list.list) { + struct llc_sock *llc = llc_sk(sk); + struct sk_buff *skb1; + + if (sk->sk_type != SOCK_DGRAM) + continue; + + if (llc->laddr.lsap != laddr->lsap) + continue; + + skb1 = skb_clone(skb, GFP_ATOMIC); + if (!skb1) + break; + + sock_hold(sk); + skb_set_owner_r(skb1, sk); + llc_sap_rcv(sap, skb1); + sock_put(sk); + } + read_unlock_bh(&sap->sk_list.lock); +} + + void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb) { struct llc_addr laddr; - struct sock *sk; llc_pdu_decode_da(skb, laddr.mac); llc_pdu_decode_dsap(skb, &laddr.lsap); - sk = llc_lookup_dgram(sap, &laddr); - if (sk) { - skb_set_owner_r(skb, sk); - llc_sap_rcv(sap, skb); - sock_put(sk); - } else + if (llc_mac_multicast(laddr.mac)) { + llc_sap_mcast(sap, &laddr, skb); kfree_skb(skb); + } else { + struct sock *sk = llc_lookup_dgram(sap, &laddr); + if (sk) { + skb_set_owner_r(skb, sk); + llc_sap_rcv(sap, skb); + sock_put(sk); + } else + kfree_skb(skb); + } } diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c index f37dbf8..8275bd3 100644 --- a/net/llc/llc_station.c +++ b/net/llc/llc_station.c @@ -11,7 +11,6 @@ * * See the GNU General Public License for more details. */ -#include #include #include #include diff --git a/net/llc/sysctl_net_llc.c b/net/llc/sysctl_net_llc.c index d1eaddb..45d7dd9 100644 --- a/net/llc/sysctl_net_llc.c +++ b/net/llc/sysctl_net_llc.c @@ -4,7 +4,6 @@ * Arnaldo Carvalho de Melo */ -#include #include #include #include diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index e2893ef..42a178a 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -60,6 +60,18 @@ config NF_CONNTRACK_MARK of packets, but this mark value is kept in the conntrack session instead of the individual packets. +config NF_CONNTRACK_SECMARK + bool 'Connection tracking security mark support' + depends on NF_CONNTRACK && NETWORK_SECMARK + help + This option enables security markings to be applied to + connections. Typically they are copied to connections from + packets using the CONNSECMARK target and copied back from + connections to packets with the same target, with the packets + being originally labeled via SECMARK. + + If unsure, say 'N'. + config NF_CONNTRACK_EVENTS bool "Connection tracking events (EXPERIMENTAL)" depends on EXPERIMENTAL && NF_CONNTRACK @@ -174,6 +186,26 @@ config NETFILTER_XT_TARGET_NOTRACK If you want to compile it as a module, say M here and read . If unsure, say `N'. +config NETFILTER_XT_TARGET_SECMARK + tristate '"SECMARK" target support' + depends on NETFILTER_XTABLES && NETWORK_SECMARK + help + The SECMARK target allows security marking of network + packets, for use with security subsystems. + + To compile it as a module, choose M here. If unsure, say N. + +config NETFILTER_XT_TARGET_CONNSECMARK + tristate '"CONNSECMARK" target support' + depends on NETFILTER_XTABLES && (NF_CONNTRACK_SECMARK || IP_NF_CONNTRACK_SECMARK) + help + The CONNSECMARK target copies security markings from packets + to connections, and restores security markings from connections + to packets (if the packets are not already marked). This would + normally be used in conjunction with the SECMARK target. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_MATCH_COMMENT tristate '"comment" match support' depends on NETFILTER_XTABLES @@ -329,6 +361,16 @@ config NETFILTER_XT_MATCH_PKTTYPE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_QUOTA + tristate '"quota" match support' + depends on NETFILTER_XTABLES + help + This option adds a `quota' match, which allows to match on a + byte counter. + + If you want to compile it as a module, say M here and read + . If unsure, say `N'. + config NETFILTER_XT_MATCH_REALM tristate '"realm" match support' depends on NETFILTER_XTABLES @@ -365,6 +407,15 @@ config NETFILTER_XT_MATCH_STATE To compile it as a module, choose M here. If unsure, say N. +config NETFILTER_XT_MATCH_STATISTIC + tristate '"statistic" match support' + depends on NETFILTER_XTABLES + help + This option adds a `statistic' match, which allows you to match + on packets periodically or randomly with a given percentage. + + To compile it as a module, choose M here. If unsure, say N. + config NETFILTER_XT_MATCH_STRING tristate '"string" match support' depends on NETFILTER_XTABLES diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 95b7e41..6fa4b75 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_CONNMAR obj-$(CONFIG_NETFILTER_XT_TARGET_MARK) += xt_MARK.o obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o +obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o +obj-$(CONFIG_NETFILTER_XT_TARGET_CONNSECMARK) += xt_CONNSECMARK.o # matches obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o @@ -44,9 +46,11 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_MARK) += obj-$(CONFIG_NETFILTER_XT_MATCH_MULTIPORT) += xt_multiport.o obj-$(CONFIG_NETFILTER_XT_MATCH_POLICY) += xt_policy.o obj-$(CONFIG_NETFILTER_XT_MATCH_PKTTYPE) += xt_pkttype.o +obj-$(CONFIG_NETFILTER_XT_MATCH_QUOTA) += xt_quota.o obj-$(CONFIG_NETFILTER_XT_MATCH_REALM) += xt_realm.o obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) += xt_sctp.o obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o +obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 8455a32..5d29d5e 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -10,7 +10,6 @@ * 15-Mar-2000: Added NF_REPEAT --RR. * 08-May-2003: Internal logging interface added by Jozsef Kadlecsik. */ -#include #include #include #include diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index f9b83f9..8f22619 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -29,7 +29,6 @@ * Derived from net/ipv4/netfilter/ip_conntrack_core.c */ -#include #include #include #include @@ -990,6 +989,9 @@ init_conntrack(const struct nf_conntrack #ifdef CONFIG_NF_CONNTRACK_MARK conntrack->mark = exp->master->mark; #endif +#ifdef CONFIG_NF_CONNTRACK_SECMARK + conntrack->secmark = exp->master->secmark; +#endif nf_conntrack_get(&conntrack->master->ct_general); NF_CT_STAT_INC(expect_new); } else @@ -1396,6 +1398,12 @@ void __nf_ct_refresh_acct(struct nf_conn write_lock_bh(&nf_conntrack_lock); + /* Only update if this is not a fixed timeout */ + if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) { + write_unlock_bh(&nf_conntrack_lock); + return; + } + /* If not in hash table, timer will not be active yet */ if (!nf_ct_is_confirmed(ct)) { ct->timeout.expires = extra_jiffies; diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index e38a4b5..960972d 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -15,7 +15,6 @@ * Derived from net/ipv4/netfilter/ip_conntrack_ftp.c */ -#include #include #include #include @@ -67,37 +66,48 @@ static int try_epsv_response(const char char); static struct ftp_search { - enum ip_conntrack_dir dir; const char *pattern; size_t plen; char skip; char term; enum ip_ct_ftp_type ftptype; int (*getnum)(const char *, size_t, struct nf_conntrack_man *, char); -} search[] = { - { - IP_CT_DIR_ORIGINAL, - "PORT", sizeof("PORT") - 1, ' ', '\r', - IP_CT_FTP_PORT, - try_rfc959, +} search[IP_CT_DIR_MAX][2] = { + [IP_CT_DIR_ORIGINAL] = { + { + .pattern = "PORT", + .plen = sizeof("PORT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = IP_CT_FTP_PORT, + .getnum = try_rfc959, + }, + { + .pattern = "EPRT", + .plen = sizeof("EPRT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = IP_CT_FTP_EPRT, + .getnum = try_eprt, + }, }, - { - IP_CT_DIR_REPLY, - "227 ", sizeof("227 ") - 1, '(', ')', - IP_CT_FTP_PASV, - try_rfc959, - }, - { - IP_CT_DIR_ORIGINAL, - "EPRT", sizeof("EPRT") - 1, ' ', '\r', - IP_CT_FTP_EPRT, - try_eprt, - }, - { - IP_CT_DIR_REPLY, - "229 ", sizeof("229 ") - 1, '(', ')', - IP_CT_FTP_EPSV, - try_epsv_response, + [IP_CT_DIR_REPLY] = { + { + .pattern = "227 ", + .plen = sizeof("227 ") - 1, + .skip = '(', + .term = ')', + .ftptype = IP_CT_FTP_PASV, + .getnum = try_rfc959, + }, + { + .pattern = "229 ", + .plen = sizeof("229 ") - 1, + .skip = '(', + .term = ')', + .ftptype = IP_CT_FTP_EPSV, + .getnum = try_epsv_response, + }, }, }; @@ -492,17 +502,15 @@ static int help(struct sk_buff **pskb, memcpy(cmd.u3.all, &ct->tuplehash[dir].tuple.src.u3.all, sizeof(cmd.u3.all)); - for (i = 0; i < ARRAY_SIZE(search); i++) { - if (search[i].dir != dir) continue; - + for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { found = find_pattern(fb_ptr, datalen, - search[i].pattern, - search[i].plen, - search[i].skip, - search[i].term, + search[dir][i].pattern, + search[dir][i].plen, + search[dir][i].skip, + search[dir][i].term, &matchoff, &matchlen, &cmd, - search[i].getnum); + search[dir][i].getnum); if (found) break; } if (found == -1) { @@ -512,7 +520,7 @@ static int help(struct sk_buff **pskb, this case. */ if (net_ratelimit()) printk("conntrack_ftp: partial %s %u+%u\n", - search[i].pattern, + search[dir][i].pattern, ntohl(th->seq), datalen); ret = NF_DROP; goto out; @@ -597,7 +605,7 @@ static int help(struct sk_buff **pskb, /* Now, NAT might want to mangle the packet, and register the * (possibly changed) expectation itself. */ if (nf_nat_ftp_hook) - ret = nf_nat_ftp_hook(pskb, ctinfo, search[i].ftptype, + ret = nf_nat_ftp_hook(pskb, ctinfo, search[dir][i].ftptype, matchoff, matchlen, exp, &seq); else { /* Can't expect this? Best to drop packet now. */ diff --git a/net/netfilter/nf_conntrack_l3proto_generic.c b/net/netfilter/nf_conntrack_l3proto_generic.c index 3fc58e4..21e0bc9 100644 --- a/net/netfilter/nf_conntrack_l3proto_generic.c +++ b/net/netfilter/nf_conntrack_l3proto_generic.c @@ -15,7 +15,6 @@ * Yasuyuki Kozakai @USAGI */ -#include #include #include #include diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index bd10eb9..af48459 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -29,6 +29,7 @@ #include #include #include #include +#include #include #include @@ -407,6 +408,8 @@ #endif /* CONFIG_NF_CONNTRACK_EVENTS */ static int ctnetlink_done(struct netlink_callback *cb) { + if (cb->args[1]) + nf_ct_put((struct nf_conn *)cb->args[1]); DEBUGP("entered %s\n", __FUNCTION__); return 0; } @@ -416,10 +419,9 @@ #define L3PROTO(ct) ct->tuplehash[IP_CT_ static int ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { - struct nf_conn *ct = NULL; + struct nf_conn *ct, *last; struct nf_conntrack_tuple_hash *h; struct list_head *i; - u_int32_t *id = (u_int32_t *) &cb->args[1]; struct nfgenmsg *nfmsg = NLMSG_DATA(cb->nlh); u_int8_t l3proto = nfmsg->nfgen_family; @@ -427,7 +429,9 @@ ctnetlink_dump_table(struct sk_buff *skb cb->args[0], *id); read_lock_bh(&nf_conntrack_lock); - for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++, *id = 0) { + for (; cb->args[0] < nf_conntrack_htable_size; cb->args[0]++) { +restart: + last = (struct nf_conn *)cb->args[1]; list_for_each_prev(i, &nf_conntrack_hash[cb->args[0]]) { h = (struct nf_conntrack_tuple_hash *) i; if (DIRECTION(h) != IP_CT_DIR_ORIGINAL) @@ -438,17 +442,30 @@ ctnetlink_dump_table(struct sk_buff *skb * then dump everything. */ if (l3proto && L3PROTO(ct) != l3proto) continue; - if (ct->id <= *id) - continue; + if (last != NULL) { + if (ct == last) { + nf_ct_put(last); + cb->args[1] = 0; + last = NULL; + } else + continue; + } if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, IPCTNL_MSG_CT_NEW, - 1, ct) < 0) + 1, ct) < 0) { + nf_conntrack_get(&ct->ct_general); + cb->args[1] = (unsigned long)ct; goto out; - *id = ct->id; + } + } + if (last != NULL) { + nf_ct_put(last); + cb->args[1] = 0; + goto restart; } } -out: +out: read_unlock_bh(&nf_conntrack_lock); DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id); @@ -641,7 +658,7 @@ static const size_t cta_min_nat[CTA_NAT_ }; static inline int -ctnetlink_parse_nat(struct nfattr *cda[], +ctnetlink_parse_nat(struct nfattr *nat, const struct nf_conn *ct, struct ip_nat_range *range) { struct nfattr *tb[CTA_NAT_MAX]; @@ -651,7 +668,7 @@ ctnetlink_parse_nat(struct nfattr *cda[] memset(range, 0, sizeof(*range)); - nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]); + nfattr_parse_nested(tb, CTA_NAT_MAX, nat); if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat)) return -EINVAL; @@ -866,39 +883,30 @@ ctnetlink_change_status(struct nf_conn * /* ASSURED bit can only be set */ return -EINVAL; - if (cda[CTA_NAT-1]) { + if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { #ifndef CONFIG_IP_NF_NAT_NEEDED return -EINVAL; #else - unsigned int hooknum; struct ip_nat_range range; - if (ctnetlink_parse_nat(cda, ct, &range) < 0) - return -EINVAL; - - DEBUGP("NAT: %u.%u.%u.%u-%u.%u.%u.%u:%u-%u\n", - NIPQUAD(range.min_ip), NIPQUAD(range.max_ip), - htons(range.min.all), htons(range.max.all)); - - /* This is tricky but it works. ip_nat_setup_info needs the - * hook number as parameter, so let's do the correct - * conversion and run away */ - if (status & IPS_SRC_NAT_DONE) - hooknum = NF_IP_POST_ROUTING; /* IP_NAT_MANIP_SRC */ - else if (status & IPS_DST_NAT_DONE) - hooknum = NF_IP_PRE_ROUTING; /* IP_NAT_MANIP_DST */ - else - return -EINVAL; /* Missing NAT flags */ - - DEBUGP("NAT status: %lu\n", - status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK)); - - if (ip_nat_initialized(ct, HOOK2MANIP(hooknum))) - return -EEXIST; - ip_nat_setup_info(ct, &range, hooknum); - - DEBUGP("NAT status after setup_info: %lu\n", - ct->status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK)); + if (cda[CTA_NAT_DST-1]) { + if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct, + &range) < 0) + return -EINVAL; + if (ip_nat_initialized(ct, + HOOK2MANIP(NF_IP_PRE_ROUTING))) + return -EEXIST; + ip_nat_setup_info(ct, &range, hooknum); + } + if (cda[CTA_NAT_SRC-1]) { + if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct, + &range) < 0) + return -EINVAL; + if (ip_nat_initialized(ct, + HOOK2MANIP(NF_IP_POST_ROUTING))) + return -EEXIST; + ip_nat_setup_info(ct, &range, hooknum); + } #endif } @@ -1122,7 +1130,7 @@ ctnetlink_new_conntrack(struct sock *ctn /* implicit 'else' */ /* we only allow nat config for new conntracks */ - if (cda[CTA_NAT-1]) { + if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) { err = -EINVAL; goto out_unlock; } diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 0c6da49..9bd8a78 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -28,6 +28,8 @@ #include #include #include #include +#include +#include #include #include @@ -259,7 +261,7 @@ static int do_basic_checks(struct nf_con } DEBUGP("Basic checks passed\n"); - return 0; + return count == 0; } static int new_state(enum ip_conntrack_dir dir, diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 69899f2..af8adcb 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -24,7 +24,6 @@ * version 2.2 */ -#include #include #include #include @@ -828,8 +827,9 @@ static int tcp_error(struct sk_buff *skb * and moreover root might send raw packets. */ /* FIXME: Source route IP option packets --RR */ - if (((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) || - (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) && + if (nf_conntrack_checksum && + ((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) || + (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) && nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) { if (LOG_INVALID(IPPROTO_TCP)) nf_log_packet(pf, 0, skb, NULL, NULL, NULL, diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index d93edbf..ae07ebe 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -134,7 +134,8 @@ static int udp_error(struct sk_buff *skb * because the semantic of CHECKSUM_HW is different there * and moreover root might send raw packets. * FIXME: Source route IP option packets --RR */ - if (((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) || + if (nf_conntrack_checksum && + ((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) || (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) && nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) { if (LOG_INVALID(IPPROTO_UDP)) diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 408960c..5fcab2e 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -17,7 +17,6 @@ * Derived from net/ipv4/netfilter/ip_conntrack_standalone.c */ -#include #include #include #include @@ -213,6 +212,11 @@ #if defined(CONFIG_NF_CONNTRACK_MARK) return -ENOSPC; #endif +#ifdef CONFIG_NF_CONNTRACK_SECMARK + if (seq_printf(s, "secmark=%u ", conntrack->secmark)) + return -ENOSPC; +#endif + if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use))) return -ENOSPC; @@ -455,6 +459,8 @@ extern unsigned int nf_ct_generic_timeou static int log_invalid_proto_min = 0; static int log_invalid_proto_max = 255; +int nf_conntrack_checksum = 1; + static struct ctl_table_header *nf_ct_sysctl_header; static ctl_table nf_ct_sysctl_table[] = { @@ -483,6 +489,14 @@ static ctl_table nf_ct_sysctl_table[] = .proc_handler = &proc_dointvec, }, { + .ctl_name = NET_NF_CONNTRACK_CHECKSUM, + .procname = "nf_conntrack_checksum", + .data = &nf_conntrack_checksum, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = NET_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT, .procname = "nf_conntrack_tcp_timeout_syn_sent", .data = &nf_ct_tcp_timeout_syn_sent, @@ -851,6 +865,7 @@ EXPORT_SYMBOL(nf_ct_proto_put); EXPORT_SYMBOL(nf_ct_l3proto_find_get); EXPORT_SYMBOL(nf_ct_l3proto_put); EXPORT_SYMBOL(nf_ct_l3protos); +EXPORT_SYMBOL_GPL(nf_conntrack_checksum); EXPORT_SYMBOL(nf_conntrack_expect_alloc); EXPORT_SYMBOL(nf_conntrack_expect_put); EXPORT_SYMBOL(nf_conntrack_expect_related); diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 6bdee29..86e392b 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -1,7 +1,6 @@ #ifndef _NF_INTERNALS_H #define _NF_INTERNALS_H -#include #include #include #include diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 3e76bd0..8901b3a 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index ee8f708..bb6fcee 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/net/netfilter/nf_sockopt.c b/net/netfilter/nf_sockopt.c index 0a63d7d..c2e44e9 100644 --- a/net/netfilter/nf_sockopt.c +++ b/net/netfilter/nf_sockopt.c @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index b88e82a..52fdfa2 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -14,7 +14,6 @@ * of the GNU General Public License, incorporated herein by reference. */ -#include #include #include #include @@ -229,7 +228,7 @@ static int nfnetlink_rcv_msg(struct sk_b NFNL_SUBSYS_ID(nlh->nlmsg_type), NFNL_MSG_TYPE(nlh->nlmsg_type)); - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) { + if (security_netlink_recv(skb, CAP_NET_ADMIN)) { DEBUGP("missing CAP_NET_ADMIN\n"); *errp = -EPERM; return -1; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 86a4ac3..49ef41e 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -680,11 +680,19 @@ dev_cmp(struct nfqnl_queue_entry *entry, if (entinf->indev) if (entinf->indev->ifindex == ifindex) return 1; - if (entinf->outdev) if (entinf->outdev->ifindex == ifindex) return 1; - +#ifdef CONFIG_BRIDGE_NETFILTER + if (entry->skb->nf_bridge) { + if (entry->skb->nf_bridge->physindev && + entry->skb->nf_bridge->physindev->ifindex == ifindex) + return 1; + if (entry->skb->nf_bridge->physoutdev && + entry->skb->nf_bridge->physoutdev->ifindex == ifindex) + return 1; + } +#endif return 0; } diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index 99293c6..174e8f9 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c new file mode 100644 index 0000000..8c011e0 --- /dev/null +++ b/net/netfilter/xt_CONNSECMARK.c @@ -0,0 +1,155 @@ +/* + * This module is used to copy security markings from packets + * to connections, and restore security markings from connections + * back to packets. This would normally be performed in conjunction + * with the SECMARK target and state match. + * + * Based somewhat on CONNMARK: + * Copyright (C) 2002,2004 MARA Systems AB + * by Henrik Nordstrom + * + * (C) 2006 Red Hat, Inc., James Morris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include + +#define PFX "CONNSECMARK: " + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Morris "); +MODULE_DESCRIPTION("ip[6]tables CONNSECMARK module"); +MODULE_ALIAS("ipt_CONNSECMARK"); +MODULE_ALIAS("ip6t_CONNSECMARK"); + +/* + * If the packet has a security mark and the connection does not, copy + * the security mark from the packet to the connection. + */ +static void secmark_save(struct sk_buff *skb) +{ + if (skb->secmark) { + u32 *connsecmark; + enum ip_conntrack_info ctinfo; + + connsecmark = nf_ct_get_secmark(skb, &ctinfo); + if (connsecmark && !*connsecmark) + if (*connsecmark != skb->secmark) + *connsecmark = skb->secmark; + } +} + +/* + * If packet has no security mark, and the connection does, restore the + * security mark from the connection to the packet. + */ +static void secmark_restore(struct sk_buff *skb) +{ + if (!skb->secmark) { + u32 *connsecmark; + enum ip_conntrack_info ctinfo; + + connsecmark = nf_ct_get_secmark(skb, &ctinfo); + if (connsecmark && *connsecmark) + if (skb->secmark != *connsecmark) + skb->secmark = *connsecmark; + } +} + +static unsigned int target(struct sk_buff **pskb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const struct xt_target *target, + const void *targinfo, void *userinfo) +{ + struct sk_buff *skb = *pskb; + const struct xt_connsecmark_target_info *info = targinfo; + + switch (info->mode) { + case CONNSECMARK_SAVE: + secmark_save(skb); + break; + + case CONNSECMARK_RESTORE: + secmark_restore(skb); + break; + + default: + BUG(); + } + + return XT_CONTINUE; +} + +static int checkentry(const char *tablename, const void *entry, + const struct xt_target *target, void *targinfo, + unsigned int targinfosize, unsigned int hook_mask) +{ + struct xt_connsecmark_target_info *info = targinfo; + + switch (info->mode) { + case CONNSECMARK_SAVE: + case CONNSECMARK_RESTORE: + break; + + default: + printk(KERN_INFO PFX "invalid mode: %hu\n", info->mode); + return 0; + } + + return 1; +} + +static struct xt_target ipt_connsecmark_reg = { + .name = "CONNSECMARK", + .target = target, + .targetsize = sizeof(struct xt_connsecmark_target_info), + .table = "mangle", + .checkentry = checkentry, + .me = THIS_MODULE, + .family = AF_INET, + .revision = 0, +}; + +static struct xt_target ip6t_connsecmark_reg = { + .name = "CONNSECMARK", + .target = target, + .targetsize = sizeof(struct xt_connsecmark_target_info), + .table = "mangle", + .checkentry = checkentry, + .me = THIS_MODULE, + .family = AF_INET6, + .revision = 0, +}; + +static int __init xt_connsecmark_init(void) +{ + int err; + + need_conntrack(); + + err = xt_register_target(&ipt_connsecmark_reg); + if (err) + return err; + + err = xt_register_target(&ip6t_connsecmark_reg); + if (err) + xt_unregister_target(&ipt_connsecmark_reg); + + return err; +} + +static void __exit xt_connsecmark_fini(void) +{ + xt_unregister_target(&ip6t_connsecmark_reg); + xt_unregister_target(&ipt_connsecmark_reg); +} + +module_init(xt_connsecmark_init); +module_exit(xt_connsecmark_fini); diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c new file mode 100644 index 0000000..c2ce9c4 --- /dev/null +++ b/net/netfilter/xt_SECMARK.c @@ -0,0 +1,156 @@ +/* + * Module for modifying the secmark field of the skb, for use by + * security subsystems. + * + * Based on the nfmark match by: + * (C) 1999-2001 Marc Boucher + * + * (C) 2006 Red Hat, Inc., James Morris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("James Morris "); +MODULE_DESCRIPTION("ip[6]tables SECMARK modification module"); +MODULE_ALIAS("ipt_SECMARK"); +MODULE_ALIAS("ip6t_SECMARK"); + +#define PFX "SECMARK: " + +static u8 mode; + +static unsigned int target(struct sk_buff **pskb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const struct xt_target *target, + const void *targinfo, void *userinfo) +{ + u32 secmark = 0; + const struct xt_secmark_target_info *info = targinfo; + + BUG_ON(info->mode != mode); + + switch (mode) { + case SECMARK_MODE_SEL: + secmark = info->u.sel.selsid; + break; + + default: + BUG(); + } + + if ((*pskb)->secmark != secmark) + (*pskb)->secmark = secmark; + + return XT_CONTINUE; +} + +static int checkentry_selinux(struct xt_secmark_target_info *info) +{ + int err; + struct xt_secmark_target_selinux_info *sel = &info->u.sel; + + err = selinux_string_to_sid(sel->selctx, &sel->selsid); + if (err) { + if (err == -EINVAL) + printk(KERN_INFO PFX "invalid SELinux context \'%s\'\n", + sel->selctx); + return 0; + } + + if (!sel->selsid) { + printk(KERN_INFO PFX "unable to map SELinux context \'%s\'\n", + sel->selctx); + return 0; + } + + err = selinux_relabel_packet_permission(sel->selsid); + if (err) { + printk(KERN_INFO PFX "unable to obtain relabeling permission\n"); + return 0; + } + + return 1; +} + +static int checkentry(const char *tablename, const void *entry, + const struct xt_target *target, void *targinfo, + unsigned int targinfosize, unsigned int hook_mask) +{ + struct xt_secmark_target_info *info = targinfo; + + if (mode && mode != info->mode) { + printk(KERN_INFO PFX "mode already set to %hu cannot mix with " + "rules for mode %hu\n", mode, info->mode); + return 0; + } + + switch (info->mode) { + case SECMARK_MODE_SEL: + if (!checkentry_selinux(info)) + return 0; + break; + + default: + printk(KERN_INFO PFX "invalid mode: %hu\n", info->mode); + return 0; + } + + if (!mode) + mode = info->mode; + return 1; +} + +static struct xt_target ipt_secmark_reg = { + .name = "SECMARK", + .target = target, + .targetsize = sizeof(struct xt_secmark_target_info), + .table = "mangle", + .checkentry = checkentry, + .me = THIS_MODULE, + .family = AF_INET, + .revision = 0, +}; + +static struct xt_target ip6t_secmark_reg = { + .name = "SECMARK", + .target = target, + .targetsize = sizeof(struct xt_secmark_target_info), + .table = "mangle", + .checkentry = checkentry, + .me = THIS_MODULE, + .family = AF_INET6, + .revision = 0, +}; + +static int __init xt_secmark_init(void) +{ + int err; + + err = xt_register_target(&ipt_secmark_reg); + if (err) + return err; + + err = xt_register_target(&ip6t_secmark_reg); + if (err) + xt_unregister_target(&ipt_secmark_reg); + + return err; +} + +static void __exit xt_secmark_fini(void) +{ + xt_unregister_target(&ip6t_secmark_reg); + xt_unregister_target(&ipt_secmark_reg); +} + +module_init(xt_secmark_init); +module_exit(xt_secmark_fini); diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c index dc26a27..56324c8 100644 --- a/net/netfilter/xt_connmark.c +++ b/net/netfilter/xt_connmark.c @@ -58,7 +58,7 @@ checkentry(const char *tablename, unsigned int matchsize, unsigned int hook_mask) { - struct xt_connmark_info *cm = (struct xt_connmark_info *)matchinfo; + struct xt_connmark_info *cm = matchinfo; if (cm->mark > 0xffffffff || cm->mask > 0xffffffff) { printk(KERN_WARNING "connmark: only support 32bit mark\n"); diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index dfb10b6..2e2f825 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -101,8 +101,7 @@ match(const struct sk_buff *skb, unsigned int protoff, int *hotdrop) { - const struct xt_dccp_info *info = - (const struct xt_dccp_info *)matchinfo; + const struct xt_dccp_info *info = matchinfo; struct dccp_hdr _dh, *dh; if (offset) diff --git a/net/netfilter/xt_mark.c b/net/netfilter/xt_mark.c index 8b385a3..876bc57 100644 --- a/net/netfilter/xt_mark.c +++ b/net/netfilter/xt_mark.c @@ -42,7 +42,7 @@ checkentry(const char *tablename, unsigned int matchsize, unsigned int hook_mask) { - struct xt_mark_info *minfo = (struct xt_mark_info *) matchinfo; + const struct xt_mark_info *minfo = matchinfo; if (minfo->mark > 0xffffffff || minfo->mask > 0xffffffff) { printk(KERN_WARNING "mark: only supports 32bit mark\n"); diff --git a/net/netfilter/xt_multiport.c b/net/netfilter/xt_multiport.c index b56cd2b..1ff0a25 100644 --- a/net/netfilter/xt_multiport.c +++ b/net/netfilter/xt_multiport.c @@ -1,4 +1,4 @@ -/* Kernel module to match one of a list of TCP/UDP ports: ports are in +/* Kernel module to match one of a list of TCP/UDP/SCTP/DCCP ports: ports are in the same place so we can treat them as equal. */ /* (C) 1999-2001 Paul `Rusty' Russell @@ -160,8 +160,9 @@ check(u_int16_t proto, u_int8_t match_flags, u_int8_t count) { - /* Must specify proto == TCP/UDP, no unknown flags or bad count */ - return (proto == IPPROTO_TCP || proto == IPPROTO_UDP) + /* Must specify supported protocol, no unknown flags or bad count */ + return (proto == IPPROTO_TCP || proto == IPPROTO_UDP + || proto == IPPROTO_SCTP || proto == IPPROTO_DCCP) && !(ip_invflags & XT_INV_PROTO) && (match_flags == XT_MULTIPORT_SOURCE || match_flags == XT_MULTIPORT_DESTINATION diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c index a3aa62f..ba1ca03 100644 --- a/net/netfilter/xt_policy.c +++ b/net/netfilter/xt_policy.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include diff --git a/net/netfilter/xt_quota.c b/net/netfilter/xt_quota.c new file mode 100644 index 0000000..4cdba74 --- /dev/null +++ b/net/netfilter/xt_quota.c @@ -0,0 +1,96 @@ +/* + * netfilter module to enforce network quotas + * + * Sam Johnston + */ +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sam Johnston "); + +static DEFINE_SPINLOCK(quota_lock); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + const struct xt_match *match, const void *matchinfo, + int offset, unsigned int protoff, int *hotdrop) +{ + struct xt_quota_info *q = ((struct xt_quota_info *)matchinfo)->master; + int ret = q->flags & XT_QUOTA_INVERT ? 1 : 0; + + spin_lock_bh("a_lock); + if (q->quota >= skb->len) { + q->quota -= skb->len; + ret ^= 1; + } else { + /* we do not allow even small packets from now on */ + q->quota = 0; + } + spin_unlock_bh("a_lock); + + return ret; +} + +static int +checkentry(const char *tablename, const void *entry, + const struct xt_match *match, void *matchinfo, + unsigned int matchsize, unsigned int hook_mask) +{ + struct xt_quota_info *q = (struct xt_quota_info *)matchinfo; + + if (q->flags & ~XT_QUOTA_MASK) + return 0; + /* For SMP, we only want to use one set of counters. */ + q->master = q; + return 1; +} + +static struct xt_match quota_match = { + .name = "quota", + .family = AF_INET, + .match = match, + .matchsize = sizeof(struct xt_quota_info), + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static struct xt_match quota_match6 = { + .name = "quota", + .family = AF_INET6, + .match = match, + .matchsize = sizeof(struct xt_quota_info), + .checkentry = checkentry, + .me = THIS_MODULE +}; + +static int __init xt_quota_init(void) +{ + int ret; + + ret = xt_register_match("a_match); + if (ret) + goto err1; + ret = xt_register_match("a_match6); + if (ret) + goto err2; + return ret; + +err2: + xt_unregister_match("a_match); +err1: + return ret; +} + +static void __exit xt_quota_fini(void) +{ + xt_unregister_match("a_match6); + xt_unregister_match("a_match); +} + +module_init(xt_quota_init); +module_exit(xt_quota_fini); diff --git a/net/netfilter/xt_sctp.c b/net/netfilter/xt_sctp.c index 34bd872..843383e 100644 --- a/net/netfilter/xt_sctp.c +++ b/net/netfilter/xt_sctp.c @@ -62,7 +62,7 @@ #endif do { sch = skb_header_pointer(skb, offset, sizeof(_sch), &_sch); - if (sch == NULL) { + if (sch == NULL || sch->length == 0) { duprintf("Dropping invalid SCTP packet.\n"); *hotdrop = 1; return 0; @@ -129,11 +129,9 @@ match(const struct sk_buff *skb, unsigned int protoff, int *hotdrop) { - const struct xt_sctp_info *info; + const struct xt_sctp_info *info = matchinfo; sctp_sctphdr_t _sh, *sh; - info = (const struct xt_sctp_info *)matchinfo; - if (offset) { duprintf("Dropping non-first fragment.. FIXME\n"); return 0; @@ -153,7 +151,7 @@ match(const struct sk_buff *skb, && SCCHECK(((ntohs(sh->dest) >= info->dpts[0]) && (ntohs(sh->dest) <= info->dpts[1])), XT_SCTP_DEST_PORTS, info->flags, info->invflags) - && SCCHECK(match_packet(skb, protoff, + && SCCHECK(match_packet(skb, protoff + sizeof (sctp_sctphdr_t), info->chunkmap, info->chunk_match_type, info->flag_info, info->flag_count, hotdrop), diff --git a/net/netfilter/xt_statistic.c b/net/netfilter/xt_statistic.c new file mode 100644 index 0000000..de1037f --- /dev/null +++ b/net/netfilter/xt_statistic.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2006 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on ipt_random and ipt_nth by Fabrice MARIE . + */ + +#include +#include +#include +#include + +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_DESCRIPTION("xtables statistical match module"); +MODULE_ALIAS("ipt_statistic"); +MODULE_ALIAS("ip6t_statistic"); + +static DEFINE_SPINLOCK(nth_lock); + +static int +match(const struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + const struct xt_match *match, const void *matchinfo, + int offset, unsigned int protoff, int *hotdrop) +{ + struct xt_statistic_info *info = (struct xt_statistic_info *)matchinfo; + int ret = info->flags & XT_STATISTIC_INVERT ? 1 : 0; + + switch (info->mode) { + case XT_STATISTIC_MODE_RANDOM: + if ((net_random() & 0x7FFFFFFF) < info->u.random.probability) + ret ^= 1; + break; + case XT_STATISTIC_MODE_NTH: + info = info->master; + spin_lock_bh(&nth_lock); + if (info->u.nth.count++ == info->u.nth.every) { + info->u.nth.count = 0; + ret ^= 1; + } + spin_unlock_bh(&nth_lock); + break; + } + + return ret; +} + +static int +checkentry(const char *tablename, const void *entry, + const struct xt_match *match, void *matchinfo, + unsigned int matchsize, unsigned int hook_mask) +{ + struct xt_statistic_info *info = (struct xt_statistic_info *)matchinfo; + + if (info->mode > XT_STATISTIC_MODE_MAX || + info->flags & ~XT_STATISTIC_MASK) + return 0; + info->master = info; + return 1; +} + +static struct xt_match statistic_match = { + .name = "statistic", + .match = match, + .matchsize = sizeof(struct xt_statistic_info), + .checkentry = checkentry, + .family = AF_INET, + .me = THIS_MODULE, +}; + +static struct xt_match statistic_match6 = { + .name = "statistic", + .match = match, + .matchsize = sizeof(struct xt_statistic_info), + .checkentry = checkentry, + .family = AF_INET6, + .me = THIS_MODULE, +}; + +static int __init xt_statistic_init(void) +{ + int ret; + + ret = xt_register_match(&statistic_match); + if (ret) + goto err1; + + ret = xt_register_match(&statistic_match6); + if (ret) + goto err2; + return ret; +err2: + xt_unregister_match(&statistic_match); +err1: + return ret; +} + +static void __exit xt_statistic_fini(void) +{ + xt_unregister_match(&statistic_match6); + xt_unregister_match(&statistic_match); +} + +module_init(xt_statistic_init); +module_exit(xt_statistic_fini); diff --git a/net/netfilter/xt_string.c b/net/netfilter/xt_string.c index 79d9ea6..0ebb6ac 100644 --- a/net/netfilter/xt_string.c +++ b/net/netfilter/xt_string.c @@ -30,8 +30,8 @@ static int match(const struct sk_buff *s unsigned int protoff, int *hotdrop) { + const struct xt_string_info *conf = matchinfo; struct ts_state state; - struct xt_string_info *conf = (struct xt_string_info *) matchinfo; memset(&state, 0, sizeof(struct ts_state)); diff --git a/net/netfilter/xt_tcpudp.c b/net/netfilter/xt_tcpudp.c index 1b61dac..a9a63aa 100644 --- a/net/netfilter/xt_tcpudp.c +++ b/net/netfilter/xt_tcpudp.c @@ -260,7 +260,7 @@ static int __init xt_tcpudp_init(void) return ret; out_unreg_udp: - xt_unregister_match(&tcp_matchstruct); + xt_unregister_match(&udp_matchstruct); out_unreg_tcp6: xt_unregister_match(&tcp6_matchstruct); out_unreg_tcp: diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 3862e73..55c0adc 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -21,7 +21,6 @@ * mandatory if CONFIG_NET=y these days */ -#include #include #include @@ -157,7 +156,7 @@ static void netlink_sock_destruct(struct static void netlink_table_grab(void) { - write_lock_bh(&nl_table_lock); + write_lock_irq(&nl_table_lock); if (atomic_read(&nl_table_users)) { DECLARE_WAITQUEUE(wait, current); @@ -167,9 +166,9 @@ static void netlink_table_grab(void) set_current_state(TASK_UNINTERRUPTIBLE); if (atomic_read(&nl_table_users) == 0) break; - write_unlock_bh(&nl_table_lock); + write_unlock_irq(&nl_table_lock); schedule(); - write_lock_bh(&nl_table_lock); + write_lock_irq(&nl_table_lock); } __set_current_state(TASK_RUNNING); @@ -179,7 +178,7 @@ static void netlink_table_grab(void) static __inline__ void netlink_table_ungrab(void) { - write_unlock_bh(&nl_table_lock); + write_unlock_irq(&nl_table_lock); wake_up(&nl_table_wait); } diff --git a/net/netlink/attr.c b/net/netlink/attr.c index fffef4a..dddbd15 100644 --- a/net/netlink/attr.c +++ b/net/netlink/attr.c @@ -5,7 +5,6 @@ * Alexey Kuznetsov */ -#include #include #include #include diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c index f329b72..a298f77 100644 --- a/net/netlink/genetlink.c +++ b/net/netlink/genetlink.c @@ -5,7 +5,6 @@ * Thomas Graf */ -#include #include #include #include @@ -320,7 +319,7 @@ static int genl_rcv_msg(struct sk_buff * goto errout; } - if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb)) { + if ((ops->flags & GENL_ADMIN_PERM) && security_netlink_recv(skb, CAP_NET_ADMIN)) { err = -EPERM; goto errout; } diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 3669cb9..389a411 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -8,7 +8,6 @@ * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) * Copyright Darryl Miles G7LED (dlm@g7led.demon.co.uk) */ -#include #include #include #include @@ -801,7 +800,7 @@ static int nr_accept(struct socket *sock /* Now attach up the new socket */ kfree_skb(skb); - sk->sk_ack_backlog--; + sk_acceptq_removed(sk); newsock->sk = newsk; out: @@ -986,7 +985,7 @@ int nr_rx_frame(struct sk_buff *skb, str nr_make->vr = 0; nr_make->vl = 0; nr_make->state = NR_STATE_3; - sk->sk_ack_backlog++; + sk_acceptq_added(sk); nr_insert_socket(make); diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c index 621e558..9b8eb54 100644 --- a/net/netrom/nr_dev.c +++ b/net/netrom/nr_dev.c @@ -6,7 +6,6 @@ * * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ -#include #include #include #include diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index b3b9097..c11737f 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -725,15 +725,17 @@ void nr_link_failed(ax25_cb *ax25, int r struct nr_node *nr_node = NULL; spin_lock_bh(&nr_neigh_list_lock); - nr_neigh_for_each(s, node, &nr_neigh_list) + nr_neigh_for_each(s, node, &nr_neigh_list) { if (s->ax25 == ax25) { nr_neigh_hold(s); nr_neigh = s; break; } + } spin_unlock_bh(&nr_neigh_list_lock); - if (nr_neigh == NULL) return; + if (nr_neigh == NULL) + return; nr_neigh->ax25 = NULL; ax25_cb_put(ax25); @@ -743,11 +745,13 @@ void nr_link_failed(ax25_cb *ax25, int r return; } spin_lock_bh(&nr_node_list_lock); - nr_node_for_each(nr_node, node, &nr_node_list) + nr_node_for_each(nr_node, node, &nr_node_list) { nr_node_lock(nr_node); - if (nr_node->which < nr_node->count && nr_node->routes[nr_node->which].neighbour == nr_neigh) + if (nr_node->which < nr_node->count && + nr_node->routes[nr_node->which].neighbour == nr_neigh) nr_node->which++; nr_node_unlock(nr_node); + } spin_unlock_bh(&nr_node_list_lock); nr_neigh_put(nr_neigh); } diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9db7dbd..f9cef36 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -49,7 +49,6 @@ * */ -#include #include #include #include diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 55564ef..d0a67bb 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -10,7 +10,6 @@ * Copyright (C) Tomi Manninen OH2BNS (oh2bns@sral.fi) */ -#include #include #include #include @@ -753,7 +752,7 @@ static int rose_connect(struct socket *s rose_insert_socket(sk); /* Finish the bind */ } - +rose_try_next_neigh: rose->dest_addr = addr->srose_addr; rose->dest_call = addr->srose_call; rose->rand = ((long)rose & 0xFFFF) + rose->lci; @@ -811,6 +810,11 @@ static int rose_connect(struct socket *s } if (sk->sk_state != TCP_ESTABLISHED) { + /* Try next neighbour */ + rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic); + if (rose->neighbour) + goto rose_try_next_neigh; + /* No more neighbour */ sock->state = SS_UNCONNECTED; return sock_error(sk); /* Always set at this point */ } diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c index 2a1bf8e..7c279e2 100644 --- a/net/rose/rose_dev.c +++ b/net/rose/rose_dev.c @@ -6,7 +6,6 @@ * * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) */ -#include #include #include #include @@ -60,6 +59,7 @@ static int rose_rebuild_header(struct sk struct net_device_stats *stats = netdev_priv(dev); unsigned char *bp = (unsigned char *)skb->data; struct sk_buff *skbn; + unsigned int len; #ifdef CONFIG_INET if (arp_find(bp + 7, skb)) { @@ -76,6 +76,8 @@ #ifdef CONFIG_INET kfree_skb(skb); + len = skbn->len; + if (!rose_route_frame(skbn, NULL)) { kfree_skb(skbn); stats->tx_errors++; @@ -83,7 +85,7 @@ #ifdef CONFIG_INET } stats->tx_packets++; - stats->tx_bytes += skbn->len; + stats->tx_bytes += len; #endif return 1; } diff --git a/net/rxrpc/call.c b/net/rxrpc/call.c index c4aeb7d..d07122b 100644 --- a/net/rxrpc/call.c +++ b/net/rxrpc/call.c @@ -1098,8 +1098,7 @@ static void rxrpc_call_receive_data_pack call->app_ready_seq = pmsg->seq; call->app_ready_qty += pmsg->dsize; - list_del_init(&pmsg->link); - list_add_tail(&pmsg->link, &call->app_readyq); + list_move_tail(&pmsg->link, &call->app_readyq); } /* see if we've got the last packet yet */ diff --git a/net/rxrpc/connection.c b/net/rxrpc/connection.c index 0e0a455..573b572 100644 --- a/net/rxrpc/connection.c +++ b/net/rxrpc/connection.c @@ -402,8 +402,7 @@ void rxrpc_put_connection(struct rxrpc_c /* move to graveyard queue */ _debug("burying connection: {%08x}", ntohl(conn->conn_id)); - list_del(&conn->link); - list_add_tail(&conn->link, &peer->conn_graveyard); + list_move_tail(&conn->link, &peer->conn_graveyard); rxrpc_krxtimod_add_timer(&conn->timeout, rxrpc_conn_timeout * HZ); diff --git a/net/rxrpc/krxsecd.c b/net/rxrpc/krxsecd.c index 1aadd02..cea4eb5 100644 --- a/net/rxrpc/krxsecd.c +++ b/net/rxrpc/krxsecd.c @@ -160,8 +160,7 @@ void rxrpc_krxsecd_clear_transport(struc list_for_each_safe(_p, _n, &rxrpc_krxsecd_initmsgq) { msg = list_entry(_p, struct rxrpc_message, link); if (msg->trans == trans) { - list_del(&msg->link); - list_add_tail(&msg->link, &tmp); + list_move_tail(&msg->link, &tmp); atomic_dec(&rxrpc_krxsecd_qcount); } } diff --git a/net/rxrpc/rxrpc_syms.c b/net/rxrpc/rxrpc_syms.c index 56adf16..9896fd8 100644 --- a/net/rxrpc/rxrpc_syms.c +++ b/net/rxrpc/rxrpc_syms.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include diff --git a/net/rxrpc/sysctl.c b/net/rxrpc/sysctl.c index fbf9872..6374df7 100644 --- a/net/rxrpc/sysctl.c +++ b/net/rxrpc/sysctl.c @@ -9,7 +9,6 @@ * 2 of the License, or (at your option) any later version. */ -#include #include #include #include diff --git a/net/sched/Kconfig b/net/sched/Kconfig index 13eeee5..8298ea9 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -305,7 +305,7 @@ config NET_CLS_U32 tristate "Universal 32bit comparisons w/ hashing (U32)" select NET_CLS ---help--- - Say Y here to be able to classify packetes using a universal + Say Y here to be able to classify packets using a universal 32bit pieces based comparison scheme. To compile this code as a module, choose M here: the @@ -485,7 +485,7 @@ config NET_ACT_IPT tristate "IPtables targets" depends on NET_CLS_ACT && NETFILTER && IP_NF_IPTABLES ---help--- - Say Y here to be able to invoke iptables targets after succesful + Say Y here to be able to invoke iptables targets after successful classification. To compile this code as a module, choose M here: the @@ -537,8 +537,8 @@ config NET_ESTIMATOR ---help--- Say Y here to allow using rate estimators to estimate the current rate-of-flow for network devices, queues, etc. This module is - automaticaly selected if needed but can be selected manually for - statstical purposes. + automatically selected if needed but can be selected manually for + statistical purposes. endif # NET_SCHED diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 2ffa11c..599423c 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -251,15 +250,17 @@ tcf_action_dump(struct sk_buff *skb, str RTA_PUT(skb, a->order, 0, NULL); err = tcf_action_dump_1(skb, a, bind, ref); if (err < 0) - goto rtattr_failure; + goto errout; r->rta_len = skb->tail - (u8*)r; } return 0; rtattr_failure: + err = -EINVAL; +errout: skb_trim(skb, b - skb->data); - return -err; + return err; } struct tc_action *tcf_action_init_1(struct rtattr *rta, struct rtattr *est, @@ -306,6 +307,7 @@ #ifdef CONFIG_KMOD goto err_mod; } #endif + *err = -ENOENT; goto err_out; } @@ -777,7 +779,7 @@ replay: return ret; } -static char * +static struct rtattr * find_dump_kind(struct nlmsghdr *n) { struct rtattr *tb1, *tb2[TCA_ACT_MAX+1]; @@ -805,7 +807,7 @@ find_dump_kind(struct nlmsghdr *n) return NULL; kind = tb2[TCA_ACT_KIND-1]; - return (char *) RTA_DATA(kind); + return kind; } static int @@ -818,16 +820,15 @@ tc_dump_action(struct sk_buff *skb, stru struct tc_action a; int ret = 0; struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh); - char *kind = find_dump_kind(cb->nlh); + struct rtattr *kind = find_dump_kind(cb->nlh); if (kind == NULL) { printk("tc_dump_action: action bad kind\n"); return 0; } - a_o = tc_lookup_action_n(kind); + a_o = tc_lookup_action(kind); if (a_o == NULL) { - printk("failed to find %s\n", kind); return 0; } @@ -835,7 +836,7 @@ tc_dump_action(struct sk_buff *skb, stru a.ops = a_o; if (a_o->walk == NULL) { - printk("tc_dump_action: %s !capable of dumping table\n", kind); + printk("tc_dump_action: %s !capable of dumping table\n", a_o->kind); goto rtattr_failure; } diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index a1e68f7..e75a147 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 37640c6..d799e01 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 4fcccbd..fc56204 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 1742a68..58b3a86 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 24c348f..47e00bd 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index e5f2e1f..17105c8 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -10,7 +10,6 @@ * */ -#include #include #include #include diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b4d89fb..7e14f14 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index dfb300b..61507f0 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -9,7 +9,6 @@ * Authors: Thomas Graf */ -#include #include #include #include diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 7547048..d41de91 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -18,7 +18,6 @@ * */ -#include #include #include #include diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 520ff71..c2e7190 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 572f06b..ba87419 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -65,7 +65,6 @@ Well, as result, despite its simplicity, we get a pretty powerful classification engine. */ -#include struct rsvp_head { diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 9f92117..7870e7b 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -4,7 +4,6 @@ * Written 1998,1999 by Werner Almesberger, EPFL ICA */ -#include #include #include #include diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 78e0525..d712edc 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/net/sched/em_cmp.c b/net/sched/em_cmp.c index bf1f00f..8ed93c3 100644 --- a/net/sched/em_cmp.c +++ b/net/sched/em_cmp.c @@ -9,7 +9,6 @@ * Authors: Thomas Graf */ -#include #include #include #include diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index 700844d..6983729 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -58,7 +58,6 @@ * only available if that subsytem is enabled in the kernel. */ -#include #include #include #include diff --git a/net/sched/em_nbyte.c b/net/sched/em_nbyte.c index 71ea926..cc80bab 100644 --- a/net/sched/em_nbyte.c +++ b/net/sched/em_nbyte.c @@ -9,7 +9,6 @@ * Authors: Thomas Graf */ -#include #include #include #include diff --git a/net/sched/em_text.c b/net/sched/em_text.c index 77beabc..aa17d8f 100644 --- a/net/sched/em_text.c +++ b/net/sched/em_text.c @@ -9,7 +9,6 @@ * Authors: Thomas Graf */ -#include #include #include #include diff --git a/net/sched/em_u32.c b/net/sched/em_u32.c index 34e7e51..e3ddfce 100644 --- a/net/sched/em_u32.c +++ b/net/sched/em_u32.c @@ -12,7 +12,6 @@ * Based on net/sched/cls_u32.c */ -#include #include #include #include diff --git a/net/sched/ematch.c b/net/sched/ematch.c index 5cb956b..2405a86 100644 --- a/net/sched/ematch.c +++ b/net/sched/ematch.c @@ -81,7 +81,6 @@ * open up a beer to watch the compilation going. */ -#include #include #include #include diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 31570b9..c7844ba 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -15,7 +15,6 @@ * Jamal Hadi Salim : 990601: ingress support */ -#include #include #include #include diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index ac7cb60..dbf44da 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -3,7 +3,6 @@ /* Written 1998-2000 by Werner Almesberger, EPFL ICA */ -#include #include #include #include diff --git a/net/sched/sch_blackhole.c b/net/sched/sch_blackhole.c index 81f0b83..cb0c456 100644 --- a/net/sched/sch_blackhole.c +++ b/net/sched/sch_blackhole.c @@ -11,7 +11,6 @@ * Note: Quantum tunneling is not supported. */ -#include #include #include #include diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 6cd8170..80b7f6a 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -10,7 +10,6 @@ * */ -#include #include #include #include diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index f6320ca..11c8a21 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -3,7 +3,6 @@ /* Written 1998-2000 by Werner Almesberger, EPFL ICA */ -#include #include #include #include diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index 033083b..c2689f4 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -9,7 +9,6 @@ * Authors: Alexey Kuznetsov, */ -#include #include #include #include diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 138ea92..d735f51 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -72,9 +71,9 @@ void qdisc_unlock_tree(struct net_device dev->queue_lock serializes queue accesses for this device AND dev->qdisc pointer itself. - dev->xmit_lock serializes accesses to device driver. + netif_tx_lock serializes accesses to device driver. - dev->queue_lock and dev->xmit_lock are mutually exclusive, + dev->queue_lock and netif_tx_lock are mutually exclusive, if one is grabbed, another must be free. */ @@ -90,14 +89,17 @@ void qdisc_unlock_tree(struct net_device NOTE: Called under dev->queue_lock with locally disabled BH. */ -int qdisc_restart(struct net_device *dev) +static inline int qdisc_restart(struct net_device *dev) { struct Qdisc *q = dev->qdisc; struct sk_buff *skb; /* Dequeue packet */ - if ((skb = q->dequeue(q)) != NULL) { + if (((skb = dev->gso_skb)) || ((skb = q->dequeue(q)))) { unsigned nolock = (dev->features & NETIF_F_LLTX); + + dev->gso_skb = NULL; + /* * When the driver has LLTX set it does its own locking * in start_xmit. No need to add additional overhead by @@ -108,7 +110,7 @@ int qdisc_restart(struct net_device *dev * will be requeued. */ if (!nolock) { - if (!spin_trylock(&dev->xmit_lock)) { + if (!netif_tx_trylock(dev)) { collision: /* So, someone grabbed the driver. */ @@ -126,8 +128,6 @@ int qdisc_restart(struct net_device *dev __get_cpu_var(netdev_rx_stat).cpu_collision++; goto requeue; } - /* Remember that the driver is grabbed by us. */ - dev->xmit_lock_owner = smp_processor_id(); } { @@ -136,14 +136,11 @@ int qdisc_restart(struct net_device *dev if (!netif_queue_stopped(dev)) { int ret; - if (netdev_nit) - dev_queue_xmit_nit(skb, dev); - ret = dev->hard_start_xmit(skb, dev); + ret = dev_hard_start_xmit(skb, dev); if (ret == NETDEV_TX_OK) { if (!nolock) { - dev->xmit_lock_owner = -1; - spin_unlock(&dev->xmit_lock); + netif_tx_unlock(dev); } spin_lock(&dev->queue_lock); return -1; @@ -157,8 +154,7 @@ int qdisc_restart(struct net_device *dev /* NETDEV_TX_BUSY - we need to requeue */ /* Release the driver */ if (!nolock) { - dev->xmit_lock_owner = -1; - spin_unlock(&dev->xmit_lock); + netif_tx_unlock(dev); } spin_lock(&dev->queue_lock); q = dev->qdisc; @@ -175,7 +171,10 @@ int qdisc_restart(struct net_device *dev */ requeue: - q->ops->requeue(skb, q); + if (skb->next) + dev->gso_skb = skb; + else + q->ops->requeue(skb, q); netif_schedule(dev); return 1; } @@ -183,11 +182,23 @@ requeue: return q->q.qlen; } +void __qdisc_run(struct net_device *dev) +{ + if (unlikely(dev->qdisc == &noop_qdisc)) + goto out; + + while (qdisc_restart(dev) < 0 && !netif_queue_stopped(dev)) + /* NOTHING */; + +out: + clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); +} + static void dev_watchdog(unsigned long arg) { struct net_device *dev = (struct net_device *)arg; - spin_lock(&dev->xmit_lock); + netif_tx_lock(dev); if (dev->qdisc != &noop_qdisc) { if (netif_device_present(dev) && netif_running(dev) && @@ -203,7 +214,7 @@ static void dev_watchdog(unsigned long a dev_hold(dev); } } - spin_unlock(&dev->xmit_lock); + netif_tx_unlock(dev); dev_put(dev); } @@ -227,17 +238,17 @@ void __netdev_watchdog_up(struct net_dev static void dev_watchdog_up(struct net_device *dev) { - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); __netdev_watchdog_up(dev); - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); } static void dev_watchdog_down(struct net_device *dev) { - spin_lock_bh(&dev->xmit_lock); + netif_tx_lock_bh(dev); if (del_timer(&dev->watchdog_timer)) dev_put(dev); - spin_unlock_bh(&dev->xmit_lock); + netif_tx_unlock_bh(dev); } void netif_carrier_on(struct net_device *dev) @@ -579,10 +590,17 @@ void dev_deactivate(struct net_device *d dev_watchdog_down(dev); - while (test_bit(__LINK_STATE_SCHED, &dev->state)) + /* Wait for outstanding dev_queue_xmit calls. */ + synchronize_rcu(); + + /* Wait for outstanding qdisc_run calls. */ + while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) yield(); - spin_unlock_wait(&dev->xmit_lock); + if (dev->gso_skb) { + kfree_skb(dev->gso_skb); + dev->gso_skb = NULL; + } } void dev_init_scheduler(struct net_device *dev) @@ -624,6 +642,5 @@ EXPORT_SYMBOL(qdisc_create_dflt); EXPORT_SYMBOL(qdisc_alloc); EXPORT_SYMBOL(qdisc_destroy); EXPORT_SYMBOL(qdisc_reset); -EXPORT_SYMBOL(qdisc_restart); EXPORT_SYMBOL(qdisc_lock_tree); EXPORT_SYMBOL(qdisc_unlock_tree); diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c index 29a2dd9..0cafdd5 100644 --- a/net/sched/sch_gred.c +++ b/net/sched/sch_gred.c @@ -18,7 +18,6 @@ * For all the glorious comments look at include/net/red.h */ -#include #include #include #include diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index f1c7bd2..6b1b4a9 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -50,7 +50,6 @@ */ #include -#include #include #include #include diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 3ec95df..34afe41 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -27,7 +27,6 @@ * * $Id: sch_htb.c,v 1.25 2003/12/07 11:08:25 devik Exp devik $ */ -#include #include #include #include diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 8edc32a..c3242f7 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -7,7 +7,6 @@ * Authors: Jamal Hadi Salim 1999 */ -#include #include #include #include diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 5a4a4d0..c5bd806 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -13,7 +13,6 @@ * Catalin(ux aka Dino) BOIE */ -#include #include #include #include diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 3395ca7..a5fa03c 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -11,7 +11,6 @@ * Init -- EINVAL when opt undefined */ -#include #include #include #include diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 2be563c..d65cadd 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -14,7 +14,6 @@ * J Hadi Salim 980816: ECN support */ -#include #include #include #include diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index e057768..d0d6e59 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -9,7 +9,6 @@ * Authors: Alexey Kuznetsov, */ -#include #include #include #include diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index d8e03c7..d9a5d29 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -12,7 +12,6 @@ * */ -#include #include #include #include diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index 79b8ef3..4c16ad5 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -302,20 +302,17 @@ restart: switch (teql_resolve(skb, skb_res, slave)) { case 0: - if (spin_trylock(&slave->xmit_lock)) { - slave->xmit_lock_owner = smp_processor_id(); + if (netif_tx_trylock(slave)) { if (!netif_queue_stopped(slave) && slave->hard_start_xmit(skb, slave) == 0) { - slave->xmit_lock_owner = -1; - spin_unlock(&slave->xmit_lock); + netif_tx_unlock(slave); master->slaves = NEXT_SLAVE(q); netif_wake_queue(dev); master->stats.tx_packets++; master->stats.tx_bytes += len; return 0; } - slave->xmit_lock_owner = -1; - spin_unlock(&slave->xmit_lock); + netif_tx_unlock(slave); } if (netif_queue_stopped(dev)) busy = 1; diff --git a/net/sctp/input.c b/net/sctp/input.c index 1662f9c..42b66e7 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -141,7 +141,8 @@ int sctp_rcv(struct sk_buff *skb) __skb_pull(skb, skb->h.raw - skb->data); if (skb->len < sizeof(struct sctphdr)) goto discard_it; - if (sctp_rcv_checksum(skb) < 0) + if ((skb->ip_summed != CHECKSUM_UNNECESSARY) && + (sctp_rcv_checksum(skb) < 0)) goto discard_it; skb_pull(skb, sizeof(struct sctphdr)); @@ -170,7 +171,8 @@ int sctp_rcv(struct sk_buff *skb) * IP broadcast addresses cannot be used in an SCTP transport * address." */ - if (!af->addr_valid(&src, NULL) || !af->addr_valid(&dest, NULL)) + if (!af->addr_valid(&src, NULL, skb) || + !af->addr_valid(&dest, NULL, skb)) goto discard_it; asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport); diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index c20d282..8ef0807 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -523,7 +523,9 @@ static int sctp_v6_available(union sctp_ * Return 0 - If the address is a non-unicast or an illegal address. * Return 1 - If the address is a unicast. */ -static int sctp_v6_addr_valid(union sctp_addr *addr, struct sctp_sock *sp) +static int sctp_v6_addr_valid(union sctp_addr *addr, + struct sctp_sock *sp, + const struct sk_buff *skb) { int ret = ipv6_addr_type(&addr->v6.sin6_addr); @@ -537,7 +539,7 @@ static int sctp_v6_addr_valid(union sctp if (sp && ipv6_only_sock(sctp_opt2sk(sp))) return 0; sctp_v6_map_v4(addr); - return sctp_get_af_specific(AF_INET)->addr_valid(addr, sp); + return sctp_get_af_specific(AF_INET)->addr_valid(addr, sp, skb); } /* Is this a non-unicast address */ diff --git a/net/sctp/output.c b/net/sctp/output.c index 437cba7..cdc5a39 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -295,14 +295,14 @@ int sctp_packet_transmit(struct sctp_pac struct sctp_transport *tp = packet->transport; struct sctp_association *asoc = tp->asoc; struct sctphdr *sh; - __u32 crc32; + __u32 crc32 = 0; struct sk_buff *nskb; struct sctp_chunk *chunk, *tmp; struct sock *sk; int err = 0; int padding; /* How much padding do we need? */ __u8 has_data = 0; - struct dst_entry *dst; + struct dst_entry *dst = tp->dst; SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet); @@ -327,6 +327,19 @@ int sctp_packet_transmit(struct sctp_pac */ skb_set_owner_w(nskb, sk); + /* The 'obsolete' field of dst is set to 2 when a dst is freed. */ + if (!dst || (dst->obsolete > 1)) { + dst_release(dst); + sctp_transport_route(tp, NULL, sctp_sk(sk)); + if (asoc && (asoc->param_flags & SPP_PMTUD_ENABLE)) { + sctp_assoc_sync_pmtu(asoc); + } + } + nskb->dst = dst_clone(tp->dst); + if (!nskb->dst) + goto no_route; + dst = nskb->dst; + /* Build the SCTP header. */ sh = (struct sctphdr *)skb_push(nskb, sizeof(struct sctphdr)); sh->source = htons(packet->source_port); @@ -350,7 +363,8 @@ int sctp_packet_transmit(struct sctp_pac * Note: Adler-32 is no longer applicable, as has been replaced * by CRC32-C as described in . */ - crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr)); + if (!(dst->dev->features & NETIF_F_NO_CSUM)) + crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr)); /** * 6.10 Bundling @@ -402,9 +416,14 @@ int sctp_packet_transmit(struct sctp_pac if (padding) memset(skb_put(chunk->skb, padding), 0, padding); - crc32 = sctp_update_copy_cksum(skb_put(nskb, chunk->skb->len), - chunk->skb->data, - chunk->skb->len, crc32); + if (dst->dev->features & NETIF_F_NO_CSUM) + memcpy(skb_put(nskb, chunk->skb->len), + chunk->skb->data, chunk->skb->len); + else + crc32 = sctp_update_copy_cksum(skb_put(nskb, + chunk->skb->len), + chunk->skb->data, + chunk->skb->len, crc32); SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d\n", "*** Chunk", chunk, @@ -427,7 +446,8 @@ int sctp_packet_transmit(struct sctp_pac } /* Perform final transformation on checksum. */ - crc32 = sctp_end_cksum(crc32); + if (!(dst->dev->features & NETIF_F_NO_CSUM)) + crc32 = sctp_end_cksum(crc32); /* 3) Put the resultant value into the checksum field in the * common header, and leave the rest of the bits unchanged. @@ -477,20 +497,6 @@ int sctp_packet_transmit(struct sctp_pac } } - dst = tp->dst; - /* The 'obsolete' field of dst is set to 2 when a dst is freed. */ - if (!dst || (dst->obsolete > 1)) { - dst_release(dst); - sctp_transport_route(tp, NULL, sctp_sk(sk)); - if (asoc->param_flags & SPP_PMTUD_ENABLE) { - sctp_assoc_sync_pmtu(asoc); - } - } - - nskb->dst = dst_clone(tp->dst); - if (!nskb->dst) - goto no_route; - SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb len %d\n", nskb->len); diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index f148f95..e5faa35 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1262,6 +1262,7 @@ #endif /* SCTP_DEBUG */ if (!tchunk->tsn_gap_acked && !tchunk->resent && tchunk->rtt_in_progress) { + tchunk->rtt_in_progress = 0; rtt = jiffies - tchunk->sent_at; sctp_transport_update_rto(transport, rtt); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 2088aa9..816c033 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -365,12 +365,18 @@ static int sctp_v4_is_any(const union sc * Return 0 - If the address is a non-unicast or an illegal address. * Return 1 - If the address is a unicast. */ -static int sctp_v4_addr_valid(union sctp_addr *addr, struct sctp_sock *sp) +static int sctp_v4_addr_valid(union sctp_addr *addr, + struct sctp_sock *sp, + const struct sk_buff *skb) { /* Is this a non-unicast address or a unusable SCTP address? */ if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) return 0; + /* Is this a broadcast address? */ + if (skb && ((struct rtable *)skb->dst)->rt_flags & RTCF_BROADCAST) + return 0; + return 1; } diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 5e0de3c..2a87736 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1402,14 +1402,14 @@ struct sctp_association *sctp_unpack_coo sg.length = bodysize; key = (char *)ep->secret_key[ep->current_key]; - memset(digest, 0x00, sizeof(digest)); + memset(digest, 0x00, SCTP_SIGNATURE_SIZE); sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, 1, digest); if (memcmp(digest, cookie->signature, SCTP_SIGNATURE_SIZE)) { /* Try the previous key. */ key = (char *)ep->secret_key[ep->last_key]; - memset(digest, 0x00, sizeof(digest)); + memset(digest, 0x00, SCTP_SIGNATURE_SIZE); sctp_crypto_hmac(sctp_sk(ep->base.sk)->hmac, key, &keylen, &sg, 1, digest); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8bc2792..9e58144 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -5293,10 +5293,18 @@ static int sctp_eat_data(const struct sc * seems a bit troublesome in that frag_point varies based on * PMTU. In cases, such as loopback, this might be a rather * large spill over. + * NOTE: If we have a full receive buffer here, we only renege if + * our receiver can still make progress without the tsn being + * received. We do this because in the event that the associations + * receive queue is empty we are filling a leading gap, and since + * reneging moves the gap to the end of the tsn stream, we are likely + * to stall again very shortly. Avoiding the renege when we fill a + * leading gap is a good heuristic for avoiding such steady state + * stalls. */ if (!asoc->rwnd || asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point) || - rcvbuf_over) { + (rcvbuf_over && (!skb_queue_len(&sk->sk_receive_queue)))) { /* If this is the next TSN, consider reneging to make * room. Note: Playing nice with a confused sender. A diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 174d4d3..0a2c71d 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -57,7 +57,6 @@ * be incorporated into the next SCTP release. */ -#include #include #include #include @@ -172,7 +171,7 @@ static inline int sctp_verify_addr(struc return -EINVAL; /* Is this a valid SCTP address? */ - if (!af->addr_valid(addr, sctp_sk(sk))) + if (!af->addr_valid(addr, sctp_sk(sk), NULL)) return -EINVAL; if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr))) @@ -2530,8 +2529,32 @@ static int sctp_setsockopt_associnfo(str /* Set the values to the specific association */ if (asoc) { - if (assocparams.sasoc_asocmaxrxt != 0) + if (assocparams.sasoc_asocmaxrxt != 0) { + __u32 path_sum = 0; + int paths = 0; + struct list_head *pos; + struct sctp_transport *peer_addr; + + list_for_each(pos, &asoc->peer.transport_addr_list) { + peer_addr = list_entry(pos, + struct sctp_transport, + transports); + path_sum += peer_addr->pathmaxrxt; + paths++; + } + + /* Only validate asocmaxrxt if we have more then + * one path/transport. We do this because path + * retransmissions are only counted when we have more + * then one path. + */ + if (paths > 1 && + assocparams.sasoc_asocmaxrxt > path_sum) + return -EINVAL; + asoc->max_retrans = assocparams.sasoc_asocmaxrxt; + } + if (assocparams.sasoc_cookie_life != 0) { asoc->cookie_life.tv_sec = assocparams.sasoc_cookie_life / 1000; diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index ba97f97..ee23678 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -51,6 +51,8 @@ #include static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event, struct sctp_association *asoc); static void sctp_ulpevent_release_data(struct sctp_ulpevent *event); +static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event); + /* Initialize an ULP event from an given skb. */ SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags) @@ -883,6 +885,7 @@ static void sctp_ulpevent_receive_data(s static void sctp_ulpevent_release_data(struct sctp_ulpevent *event) { struct sk_buff *skb, *frag; + unsigned int len; /* Current stack structures assume that the rcv buffer is * per socket. For UDP style sockets this is not true as @@ -892,7 +895,30 @@ static void sctp_ulpevent_release_data(s */ skb = sctp_event2skb(event); - sctp_assoc_rwnd_increase(event->asoc, skb_headlen(skb)); + len = skb->len; + + if (!skb->data_len) + goto done; + + /* Don't forget the fragments. */ + for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) { + /* NOTE: skb_shinfos are recursive. Although IP returns + * skb's with only 1 level of fragments, SCTP reassembly can + * increase the levels. + */ + sctp_ulpevent_release_frag_data(sctp_skb2event(frag)); + } + +done: + sctp_assoc_rwnd_increase(event->asoc, len); + sctp_ulpevent_release_owner(event); +} + +static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event) +{ + struct sk_buff *skb, *frag; + + skb = sctp_event2skb(event); if (!skb->data_len) goto done; @@ -903,7 +929,7 @@ static void sctp_ulpevent_release_data(s * skb's with only 1 level of fragments, SCTP reassembly can * increase the levels. */ - sctp_ulpevent_release_data(sctp_skb2event(frag)); + sctp_ulpevent_release_frag_data(sctp_skb2event(frag)); } done: diff --git a/net/socket.c b/net/socket.c index 02948b6..b4848ce 100644 --- a/net/socket.c +++ b/net/socket.c @@ -58,7 +58,6 @@ * Based upon Swansea University Computer Society NET3.039 */ -#include #include #include #include @@ -335,10 +334,11 @@ static struct super_operations sockfs_op .statfs = simple_statfs, }; -static struct super_block *sockfs_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int sockfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC); + return get_sb_pseudo(fs_type, "socket:", &sockfs_ops, SOCKFS_MAGIC, + mnt); } static struct vfsmount *sock_mnt __read_mostly; diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index 129e2bd..b8714a8 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c @@ -169,7 +169,7 @@ gss_import_sec_context_kerberos(const vo } ctx_id->internal_ctx_id = ctx; - dprintk("RPC: Succesfully imported new context.\n"); + dprintk("RPC: Successfully imported new context.\n"); return 0; out_err_free_key2: diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index f433112..2f31216 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c @@ -70,7 +70,7 @@ #ifdef RPC_DEBUG # define RPCDBG_FACILITY RPCDBG_AUTH #endif -spinlock_t krb5_seq_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(krb5_seq_lock); u32 gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text, diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c index f8bac6c..d88468d 100644 --- a/net/sunrpc/auth_gss/gss_mech_switch.c +++ b/net/sunrpc/auth_gss/gss_mech_switch.c @@ -224,7 +224,8 @@ EXPORT_SYMBOL(gss_service_to_auth_domain void gss_mech_put(struct gss_api_mech * gm) { - module_put(gm->gm_owner); + if (gm) + module_put(gm->gm_owner); } EXPORT_SYMBOL(gss_mech_put); @@ -307,8 +308,7 @@ gss_delete_sec_context(struct gss_ctx ** (*context_handle)->mech_type->gm_ops ->gss_delete_sec_context((*context_handle) ->internal_ctx_id); - if ((*context_handle)->mech_type) - gss_mech_put((*context_handle)->mech_type); + gss_mech_put((*context_handle)->mech_type); kfree(*context_handle); *context_handle=NULL; return GSS_S_COMPLETE; diff --git a/net/sunrpc/auth_gss/gss_spkm3_mech.c b/net/sunrpc/auth_gss/gss_spkm3_mech.c index 5bf11cc..3d0432a 100644 --- a/net/sunrpc/auth_gss/gss_spkm3_mech.c +++ b/net/sunrpc/auth_gss/gss_spkm3_mech.c @@ -201,7 +201,7 @@ gss_import_sec_context_spkm3(const void ctx_id->internal_ctx_id = ctx; - dprintk("Succesfully imported new spkm context.\n"); + dprintk("Successfully imported new spkm context.\n"); return 0; out_err_free_key2: diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index d51e316..94217ec 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -425,6 +425,7 @@ static int rsc_parse(struct cache_detail struct rsc rsci, *rscp = NULL; time_t expiry; int status = -EINVAL; + struct gss_api_mech *gm = NULL; memset(&rsci, 0, sizeof(rsci)); /* context handle */ @@ -453,7 +454,6 @@ static int rsc_parse(struct cache_detail set_bit(CACHE_NEGATIVE, &rsci.h.flags); else { int N, i; - struct gss_api_mech *gm; /* gid */ if (get_int(&mesg, &rsci.cred.cr_gid)) @@ -488,21 +488,17 @@ static int rsc_parse(struct cache_detail status = -EINVAL; /* mech-specific data: */ len = qword_get(&mesg, buf, mlen); - if (len < 0) { - gss_mech_put(gm); + if (len < 0) goto out; - } status = gss_import_sec_context(buf, len, gm, &rsci.mechctx); - if (status) { - gss_mech_put(gm); + if (status) goto out; - } - gss_mech_put(gm); } rsci.h.expiry_time = expiry; rscp = rsc_update(&rsci, rscp); status = 0; out: + gss_mech_put(gm); rsc_free(&rsci); if (rscp) cache_put(&rscp->h, &rsc_cache); @@ -836,6 +832,74 @@ out: return stat; } +static inline int +total_buf_len(struct xdr_buf *buf) +{ + return buf->head[0].iov_len + buf->page_len + buf->tail[0].iov_len; +} + +static void +fix_priv_head(struct xdr_buf *buf, int pad) +{ + if (buf->page_len == 0) { + /* We need to adjust head and buf->len in tandem in this + * case to make svc_defer() work--it finds the original + * buffer start using buf->len - buf->head[0].iov_len. */ + buf->head[0].iov_len -= pad; + } +} + +static int +unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx) +{ + u32 priv_len, maj_stat; + int pad, saved_len, remaining_len, offset; + + rqstp->rq_sendfile_ok = 0; + + priv_len = ntohl(svc_getu32(&buf->head[0])); + if (rqstp->rq_deferred) { + /* Already decrypted last time through! The sequence number + * check at out_seq is unnecessary but harmless: */ + goto out_seq; + } + /* buf->len is the number of bytes from the original start of the + * request to the end, where head[0].iov_len is just the bytes + * not yet read from the head, so these two values are different: */ + remaining_len = total_buf_len(buf); + if (priv_len > remaining_len) + return -EINVAL; + pad = remaining_len - priv_len; + buf->len -= pad; + fix_priv_head(buf, pad); + + /* Maybe it would be better to give gss_unwrap a length parameter: */ + saved_len = buf->len; + buf->len = priv_len; + maj_stat = gss_unwrap(ctx, 0, buf); + pad = priv_len - buf->len; + buf->len = saved_len; + buf->len -= pad; + /* The upper layers assume the buffer is aligned on 4-byte boundaries. + * In the krb5p case, at least, the data ends up offset, so we need to + * move it around. */ + /* XXX: This is very inefficient. It would be better to either do + * this while we encrypt, or maybe in the receive code, if we can peak + * ahead and work out the service and mechanism there. */ + offset = buf->head[0].iov_len % 4; + if (offset) { + buf->buflen = RPCSVC_MAXPAYLOAD; + xdr_shift_buf(buf, offset); + fix_priv_head(buf, pad); + } + if (maj_stat != GSS_S_COMPLETE) + return -EINVAL; +out_seq: + if (ntohl(svc_getu32(&buf->head[0])) != seq) + return -EINVAL; + return 0; +} + struct gss_svc_data { /* decoded gss client cred: */ struct rpc_gss_wire_cred clcred; @@ -1051,7 +1115,14 @@ svcauth_gss_accept(struct svc_rqst *rqst svc_putu32(resv, 0); break; case RPC_GSS_SVC_PRIVACY: - /* currently unsupported */ + if (unwrap_priv_data(rqstp, &rqstp->rq_arg, + gc->gc_seq, rsci->mechctx)) + goto auth_err; + /* placeholders for length and seq. number: */ + svcdata->body_start = resv->iov_base + resv->iov_len; + svc_putu32(resv, 0); + svc_putu32(resv, 0); + break; default: goto auth_err; } @@ -1076,8 +1147,8 @@ out: return ret; } -static int -svcauth_gss_release(struct svc_rqst *rqstp) +static inline int +svcauth_gss_wrap_resp_integ(struct svc_rqst *rqstp) { struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; struct rpc_gss_wire_cred *gc = &gsd->clcred; @@ -1089,69 +1160,147 @@ svcauth_gss_release(struct svc_rqst *rqs int integ_offset, integ_len; int stat = -EINVAL; + p = gsd->body_start; + gsd->body_start = NULL; + /* move accept_stat to right place: */ + memcpy(p, p + 2, 4); + /* Don't wrap in failure case: */ + /* Counting on not getting here if call was not even accepted! */ + if (*p != rpc_success) { + resbuf->head[0].iov_len -= 2 * 4; + goto out; + } + p++; + integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; + integ_len = resbuf->len - integ_offset; + BUG_ON(integ_len % 4); + *p++ = htonl(integ_len); + *p++ = htonl(gc->gc_seq); + if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, + integ_len)) + BUG(); + if (resbuf->page_len == 0 + && resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE + < PAGE_SIZE) { + BUG_ON(resbuf->tail[0].iov_len); + /* Use head for everything */ + resv = &resbuf->head[0]; + } else if (resbuf->tail[0].iov_base == NULL) { + if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE) + goto out_err; + resbuf->tail[0].iov_base = resbuf->head[0].iov_base + + resbuf->head[0].iov_len; + resbuf->tail[0].iov_len = 0; + rqstp->rq_restailpage = 0; + resv = &resbuf->tail[0]; + } else { + resv = &resbuf->tail[0]; + } + mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; + if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) + goto out_err; + svc_putu32(resv, htonl(mic.len)); + memset(mic.data + mic.len, 0, + round_up_to_quad(mic.len) - mic.len); + resv->iov_len += XDR_QUADLEN(mic.len) << 2; + /* not strictly required: */ + resbuf->len += XDR_QUADLEN(mic.len) << 2; + BUG_ON(resv->iov_len > PAGE_SIZE); +out: + stat = 0; +out_err: + return stat; +} + +static inline int +svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp) +{ + struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; + struct rpc_gss_wire_cred *gc = &gsd->clcred; + struct xdr_buf *resbuf = &rqstp->rq_res; + struct page **inpages = NULL; + u32 *p; + int offset, *len; + int pad; + + p = gsd->body_start; + gsd->body_start = NULL; + /* move accept_stat to right place: */ + memcpy(p, p + 2, 4); + /* Don't wrap in failure case: */ + /* Counting on not getting here if call was not even accepted! */ + if (*p != rpc_success) { + resbuf->head[0].iov_len -= 2 * 4; + return 0; + } + p++; + len = p++; + offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; + *p++ = htonl(gc->gc_seq); + inpages = resbuf->pages; + /* XXX: Would be better to write some xdr helper functions for + * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ + if (resbuf->tail[0].iov_base && rqstp->rq_restailpage == 0) { + BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base + + PAGE_SIZE); + BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base); + if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len + + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) + return -ENOMEM; + memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE, + resbuf->tail[0].iov_base, + resbuf->tail[0].iov_len); + resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE; + } + if (resbuf->tail[0].iov_base == NULL) { + if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE) + return -ENOMEM; + resbuf->tail[0].iov_base = resbuf->head[0].iov_base + + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; + resbuf->tail[0].iov_len = 0; + rqstp->rq_restailpage = 0; + } + if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) + return -ENOMEM; + *len = htonl(resbuf->len - offset); + pad = 3 - ((resbuf->len - offset - 1)&3); + p = (u32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len); + memset(p, 0, pad); + resbuf->tail[0].iov_len += pad; + resbuf->len += pad; + return 0; +} + +static int +svcauth_gss_release(struct svc_rqst *rqstp) +{ + struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; + struct rpc_gss_wire_cred *gc = &gsd->clcred; + struct xdr_buf *resbuf = &rqstp->rq_res; + int stat = -EINVAL; + if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; /* Release can be called twice, but we only wrap once. */ if (gsd->body_start == NULL) goto out; /* normally not set till svc_send, but we need it here: */ - resbuf->len = resbuf->head[0].iov_len - + resbuf->page_len + resbuf->tail[0].iov_len; + /* XXX: what for? Do we mess it up the moment we call svc_putu32 + * or whatever? */ + resbuf->len = total_buf_len(resbuf); switch (gc->gc_svc) { case RPC_GSS_SVC_NONE: break; case RPC_GSS_SVC_INTEGRITY: - p = gsd->body_start; - gsd->body_start = NULL; - /* move accept_stat to right place: */ - memcpy(p, p + 2, 4); - /* don't wrap in failure case: */ - /* Note: counting on not getting here if call was not even - * accepted! */ - if (*p != rpc_success) { - resbuf->head[0].iov_len -= 2 * 4; - goto out; - } - p++; - integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; - integ_len = resbuf->len - integ_offset; - BUG_ON(integ_len % 4); - *p++ = htonl(integ_len); - *p++ = htonl(gc->gc_seq); - if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, - integ_len)) - BUG(); - if (resbuf->page_len == 0 - && resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE - < PAGE_SIZE) { - BUG_ON(resbuf->tail[0].iov_len); - /* Use head for everything */ - resv = &resbuf->head[0]; - } else if (resbuf->tail[0].iov_base == NULL) { - if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE - > PAGE_SIZE) - goto out_err; - resbuf->tail[0].iov_base = - resbuf->head[0].iov_base - + resbuf->head[0].iov_len; - resbuf->tail[0].iov_len = 0; - rqstp->rq_restailpage = 0; - resv = &resbuf->tail[0]; - } else { - resv = &resbuf->tail[0]; - } - mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; - if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) + stat = svcauth_gss_wrap_resp_integ(rqstp); + if (stat) goto out_err; - svc_putu32(resv, htonl(mic.len)); - memset(mic.data + mic.len, 0, - round_up_to_quad(mic.len) - mic.len); - resv->iov_len += XDR_QUADLEN(mic.len) << 2; - /* not strictly required: */ - resbuf->len += XDR_QUADLEN(mic.len) << 2; - BUG_ON(resv->iov_len > PAGE_SIZE); break; case RPC_GSS_SVC_PRIVACY: + stat = svcauth_gss_wrap_resp_priv(rqstp); + if (stat) + goto out_err; + break; default: goto out_err; } diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index f56767a..2eccffa 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -118,6 +118,8 @@ struct rpc_auth null_auth = { .au_cslack = 4, .au_rslack = 2, .au_ops = &authnull_ops, + .au_flavor = RPC_AUTH_NULL, + .au_count = ATOMIC_INIT(0), }; static diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index df14b6b..74c7406 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -225,6 +225,7 @@ struct rpc_auth unix_auth = { .au_cslack = UNX_WRITESLACK, .au_rslack = 2, /* assume AUTH_NULL verf */ .au_ops = &authunix_ops, + .au_flavor = RPC_AUTH_UNIX, .au_count = ATOMIC_INIT(0), .au_credcache = &unix_cred_cache, }; diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c index d25b054..623180f 100644 --- a/net/sunrpc/pmap_clnt.c +++ b/net/sunrpc/pmap_clnt.c @@ -6,7 +6,6 @@ * Copyright (C) 1996, Olaf Kirch */ -#include #include #include #include diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index cc673dd..dc6cb93 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -8,7 +8,6 @@ * Copyright (c) 2002, Trond Myklebust * */ -#include #include #include #include @@ -439,7 +438,7 @@ struct vfsmount *rpc_get_mount(void) { int err; - err = simple_pin_fs("rpc_pipefs", &rpc_mount, &rpc_mount_count); + err = simple_pin_fs(&rpc_pipe_fs_type, &rpc_mount, &rpc_mount_count); if (err != 0) return ERR_PTR(err); return rpc_mount; @@ -516,7 +515,7 @@ rpc_depopulate(struct dentry *parent) struct dentry *dentry, *dvec[10]; int n = 0; - mutex_lock(&dir->i_mutex); + mutex_lock_nested(&dir->i_mutex, I_MUTEX_CHILD); repeat: spin_lock(&dcache_lock); list_for_each_safe(pos, next, &parent->d_subdirs) { @@ -632,7 +631,7 @@ rpc_lookup_negative(char *path, struct n if ((error = rpc_lookup_parent(path, nd)) != 0) return ERR_PTR(error); dir = nd->dentry->d_inode; - mutex_lock(&dir->i_mutex); + mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd->last.name, nd->dentry, nd->last.len); if (IS_ERR(dentry)) goto out_err; @@ -694,7 +693,7 @@ rpc_rmdir(char *path) if ((error = rpc_lookup_parent(path, &nd)) != 0) return error; dir = nd.dentry->d_inode; - mutex_lock(&dir->i_mutex); + mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd.last.name, nd.dentry, nd.last.len); if (IS_ERR(dentry)) { error = PTR_ERR(dentry); @@ -755,7 +754,7 @@ rpc_unlink(char *path) if ((error = rpc_lookup_parent(path, &nd)) != 0) return error; dir = nd.dentry->d_inode; - mutex_lock(&dir->i_mutex); + mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(nd.last.name, nd.dentry, nd.last.len); if (IS_ERR(dentry)) { error = PTR_ERR(dentry); @@ -815,11 +814,11 @@ out: return -ENOMEM; } -static struct super_block * +static int rpc_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) + int flags, const char *dev_name, void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, rpc_fill_super); + return get_sb_single(fs_type, flags, data, rpc_fill_super, mnt); } static struct file_system_type rpc_pipe_fs_type = { diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c index 769114f..f38f939 100644 --- a/net/sunrpc/sunrpc_syms.c +++ b/net/sunrpc/sunrpc_syms.c @@ -6,7 +6,6 @@ * Copyright (C) 1997 Olaf Kirch */ -#include #include #include diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index b08419e..01ba60a 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -280,7 +280,10 @@ svc_process(struct svc_serv *serv, struc rqstp->rq_res.page_base = 0; rqstp->rq_res.page_len = 0; rqstp->rq_res.buflen = PAGE_SIZE; + rqstp->rq_res.tail[0].iov_base = NULL; rqstp->rq_res.tail[0].iov_len = 0; + /* Will be turned off only in gss privacy case: */ + rqstp->rq_sendfile_ok = 1; /* tcp needs a space for the record length... */ if (rqstp->rq_prot == IPPROTO_TCP) svc_putu32(resv, 0); diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c index 1065904..d89b048 100644 --- a/net/sunrpc/sysctl.c +++ b/net/sunrpc/sysctl.c @@ -7,7 +7,6 @@ * impossible at the moment. */ -#include #include #include #include diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index ca4bfa5..6ac4510 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -191,7 +191,6 @@ _shift_data_right_pages(struct page **pa do { /* Are any pointers crossing a page boundary? */ if (pgto_base == 0) { - flush_dcache_page(*pgto); pgto_base = PAGE_CACHE_SIZE; pgto--; } @@ -211,11 +210,11 @@ _shift_data_right_pages(struct page **pa vto = kmap_atomic(*pgto, KM_USER0); vfrom = kmap_atomic(*pgfrom, KM_USER1); memmove(vto + pgto_base, vfrom + pgfrom_base, copy); + flush_dcache_page(*pgto); kunmap_atomic(vfrom, KM_USER1); kunmap_atomic(vto, KM_USER0); } while ((len -= copy) != 0); - flush_dcache_page(*pgto); } /* @@ -568,8 +567,7 @@ EXPORT_SYMBOL(xdr_inline_decode); * * Moves data beyond the current pointer position from the XDR head[] buffer * into the page list. Any data that lies beyond current position + "len" - * bytes is moved into the XDR tail[]. The current pointer is then - * repositioned at the beginning of the XDR tail. + * bytes is moved into the XDR tail[]. */ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) { @@ -606,6 +604,31 @@ void xdr_read_pages(struct xdr_stream *x } EXPORT_SYMBOL(xdr_read_pages); +/** + * xdr_enter_page - decode data from the XDR page + * @xdr: pointer to xdr_stream struct + * @len: number of bytes of page data + * + * Moves data beyond the current pointer position from the XDR head[] buffer + * into the page list. Any data that lies beyond current position + "len" + * bytes is moved into the XDR tail[]. The current pointer is then + * repositioned at the beginning of the first XDR page. + */ +void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) +{ + char * kaddr = page_address(xdr->buf->pages[0]); + xdr_read_pages(xdr, len); + /* + * Position current pointer at beginning of tail, and + * set remaining message length. + */ + if (len > PAGE_CACHE_SIZE - xdr->buf->page_base) + len = PAGE_CACHE_SIZE - xdr->buf->page_base; + xdr->p = (uint32_t *)(kaddr + xdr->buf->page_base); + xdr->end = (uint32_t *)((char *)xdr->p + len); +} +EXPORT_SYMBOL(xdr_enter_page); + static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0}; void diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 4dd5b3c..02060d0 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -41,7 +41,7 @@ #include #include #include #include -#include +#include #include #include @@ -830,7 +830,7 @@ static inline u32 xprt_alloc_xid(struct static inline void xprt_init_xid(struct rpc_xprt *xprt) { - get_random_bytes(&xprt->xid, sizeof(xprt->xid)); + xprt->xid = net_random(); } static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 4b4e7df..21006b1 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -930,6 +930,13 @@ static void xs_udp_timer(struct rpc_task xprt_adjust_cwnd(task, -ETIMEDOUT); } +static unsigned short xs_get_random_port(void) +{ + unsigned short range = xprt_max_resvport - xprt_min_resvport; + unsigned short rand = (unsigned short) net_random() % range; + return rand + xprt_min_resvport; +} + /** * xs_set_port - reset the port number in the remote endpoint address * @xprt: generic transport @@ -1275,7 +1282,7 @@ int xs_setup_udp(struct rpc_xprt *xprt, memset(xprt->slot, 0, slot_table_size); xprt->prot = IPPROTO_UDP; - xprt->port = xprt_max_resvport; + xprt->port = xs_get_random_port(); xprt->tsh_size = 0; xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0; /* XXX: header size can vary due to auth type, IPv6, etc. */ @@ -1317,7 +1324,7 @@ int xs_setup_tcp(struct rpc_xprt *xprt, memset(xprt->slot, 0, slot_table_size); xprt->prot = IPPROTO_TCP; - xprt->port = xprt_max_resvport; + xprt->port = xs_get_random_port(); xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32); xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0; xprt->max_payload = RPC_MAX_FRAGMENT_SIZE; diff --git a/net/sysctl_net.c b/net/sysctl_net.c index 58a1b6b..cd4eafb 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -12,7 +12,6 @@ * */ -#include #include #include diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 2c4ecbe..1bb7570 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -49,13 +49,19 @@ #include "bearer.h" #include "name_table.h" #include "bcast.h" - #define MAX_PKT_DEFAULT_MCAST 1500 /* bcast link max packet size (fixed) */ #define BCLINK_WIN_DEFAULT 20 /* bcast link window size (default) */ #define BCLINK_LOG_BUF_SIZE 0 +/* + * Loss rate for incoming broadcast frames; used to test retransmission code. + * Set to N to cause every N'th frame to be discarded; 0 => don't discard any. + */ + +#define TIPC_BCAST_LOSS_RATE 0 + /** * struct bcbearer_pair - a pair of bearers used by broadcast link * @primary: pointer to primary bearer @@ -75,7 +81,14 @@ struct bcbearer_pair { * @bearer: (non-standard) broadcast bearer structure * @media: (non-standard) broadcast media structure * @bpairs: array of bearer pairs - * @bpairs_temp: array of bearer pairs used during creation of "bpairs" + * @bpairs_temp: temporary array of bearer pairs used by tipc_bcbearer_sort() + * @remains: temporary node map used by tipc_bcbearer_send() + * @remains_new: temporary node map used tipc_bcbearer_send() + * + * Note: The fields labelled "temporary" are incorporated into the bearer + * to avoid consuming potentially limited stack space through the use of + * large local variables within multicast routines. Concurrent access is + * prevented through use of the spinlock "bc_lock". */ struct bcbearer { @@ -83,6 +96,8 @@ struct bcbearer { struct media media; struct bcbearer_pair bpairs[MAX_BEARERS]; struct bcbearer_pair bpairs_temp[TIPC_MAX_LINK_PRI + 1]; + struct node_map remains; + struct node_map remains_new; }; /** @@ -102,7 +117,7 @@ struct bclink { static struct bcbearer *bcbearer = NULL; static struct bclink *bclink = NULL; static struct link *bcl = NULL; -static spinlock_t bc_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(bc_lock); char tipc_bclink_name[] = "multicast-link"; @@ -165,21 +180,18 @@ static int bclink_ack_allowed(u32 n) * @after: sequence number of last packet to *not* retransmit * @to: sequence number of last packet to retransmit * - * Called with 'node' locked, bc_lock unlocked + * Called with bc_lock locked */ static void bclink_retransmit_pkt(u32 after, u32 to) { struct sk_buff *buf; - spin_lock_bh(&bc_lock); buf = bcl->first_out; while (buf && less_eq(buf_seqno(buf), after)) { buf = buf->next; } - if (buf != NULL) - tipc_link_retransmit(bcl, buf, mod(to - after)); - spin_unlock_bh(&bc_lock); + tipc_link_retransmit(bcl, buf, mod(to - after)); } /** @@ -346,8 +358,10 @@ static void tipc_bclink_peek_nack(u32 de for (; buf; buf = buf->next) { u32 seqno = buf_seqno(buf); - if (mod(seqno - prev) != 1) + if (mod(seqno - prev) != 1) { buf = NULL; + break; + } if (seqno == gap_after) break; prev = seqno; @@ -399,7 +413,10 @@ int tipc_bclink_send_msg(struct sk_buff */ void tipc_bclink_recv_pkt(struct sk_buff *buf) -{ +{ +#if (TIPC_BCAST_LOSS_RATE) + static int rx_count = 0; +#endif struct tipc_msg *msg = buf_msg(buf); struct node* node = tipc_node_find(msg_prevnode(msg)); u32 next_in; @@ -420,9 +437,13 @@ void tipc_bclink_recv_pkt(struct sk_buff tipc_node_lock(node); tipc_bclink_acknowledge(node, msg_bcast_ack(msg)); tipc_node_unlock(node); + spin_lock_bh(&bc_lock); bcl->stats.recv_nacks++; + bcl->owner->next = node; /* remember requestor */ bclink_retransmit_pkt(msg_bcgap_after(msg), msg_bcgap_to(msg)); + bcl->owner->next = NULL; + spin_unlock_bh(&bc_lock); } else { tipc_bclink_peek_nack(msg_destnode(msg), msg_bcast_tag(msg), @@ -433,6 +454,14 @@ void tipc_bclink_recv_pkt(struct sk_buff return; } +#if (TIPC_BCAST_LOSS_RATE) + if (++rx_count == TIPC_BCAST_LOSS_RATE) { + rx_count = 0; + buf_discard(buf); + return; + } +#endif + tipc_node_lock(node); receive: deferred = node->bclink.deferred_head; @@ -531,12 +560,8 @@ static int tipc_bcbearer_send(struct sk_ { static int send_count = 0; - struct node_map *remains; - struct node_map *remains_new; - struct node_map *remains_tmp; int bp_index; int swap_time; - int err; /* Prepare buffer for broadcasting (if first time trying to send it) */ @@ -557,9 +582,7 @@ static int tipc_bcbearer_send(struct sk_ /* Send buffer over bearers until all targets reached */ - remains = kmalloc(sizeof(struct node_map), GFP_ATOMIC); - remains_new = kmalloc(sizeof(struct node_map), GFP_ATOMIC); - *remains = tipc_cltr_bcast_nodes; + bcbearer->remains = tipc_cltr_bcast_nodes; for (bp_index = 0; bp_index < MAX_BEARERS; bp_index++) { struct bearer *p = bcbearer->bpairs[bp_index].primary; @@ -568,8 +591,8 @@ static int tipc_bcbearer_send(struct sk_ if (!p) break; /* no more bearers to try */ - tipc_nmap_diff(remains, &p->nodes, remains_new); - if (remains_new->count == remains->count) + tipc_nmap_diff(&bcbearer->remains, &p->nodes, &bcbearer->remains_new); + if (bcbearer->remains_new.count == bcbearer->remains.count) continue; /* bearer pair doesn't add anything */ if (!p->publ.blocked && @@ -587,27 +610,17 @@ swap: bcbearer->bpairs[bp_index].primary = s; bcbearer->bpairs[bp_index].secondary = p; update: - if (remains_new->count == 0) { - err = TIPC_OK; - goto out; - } + if (bcbearer->remains_new.count == 0) + return TIPC_OK; - /* swap map */ - remains_tmp = remains; - remains = remains_new; - remains_new = remains_tmp; + bcbearer->remains = bcbearer->remains_new; } /* Unable to reach all targets */ bcbearer->bearer.publ.blocked = 1; bcl->stats.bearer_congs++; - err = ~TIPC_OK; - - out: - kfree(remains_new); - kfree(remains); - return err; + return ~TIPC_OK; } /** @@ -765,7 +778,7 @@ int tipc_bclink_init(void) bclink = kmalloc(sizeof(*bclink), GFP_ATOMIC); if (!bcbearer || !bclink) { nomem: - warn("Memory squeeze; Failed to create multicast link\n"); + warn("Multicast link creation failed, no memory\n"); kfree(bcbearer); bcbearer = NULL; kfree(bclink); @@ -783,7 +796,7 @@ int tipc_bclink_init(void) memset(bclink, 0, sizeof(struct bclink)); INIT_LIST_HEAD(&bcl->waiting_ports); bcl->next_out_no = 1; - bclink->node.lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&bclink->node.lock); bcl->owner = &bclink->node; bcl->max_pkt = MAX_PKT_DEFAULT_MCAST; tipc_link_set_queue_limits(bcl, BCLINK_WIN_DEFAULT); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 0e3be2a..b243d9d 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -180,7 +180,7 @@ static inline void tipc_port_list_add(st if (!item->next) { item->next = kmalloc(sizeof(*item), GFP_ATOMIC); if (!item->next) { - warn("Memory squeeze: multicast destination port list is incomplete\n"); + warn("Incomplete multicast delivery, no memory\n"); return; } item->next->next = NULL; diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index e213a8e..7ef17a4 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -112,39 +112,42 @@ int tipc_register_media(u32 media_type, goto exit; if (!media_name_valid(name)) { - warn("Media registration error: illegal name <%s>\n", name); + warn("Media <%s> rejected, illegal name\n", name); goto exit; } if (!bcast_addr) { - warn("Media registration error: no broadcast address supplied\n"); + warn("Media <%s> rejected, no broadcast address\n", name); goto exit; } if ((bearer_priority < TIPC_MIN_LINK_PRI) && (bearer_priority > TIPC_MAX_LINK_PRI)) { - warn("Media registration error: priority %u\n", bearer_priority); + warn("Media <%s> rejected, illegal priority (%u)\n", name, + bearer_priority); goto exit; } if ((link_tolerance < TIPC_MIN_LINK_TOL) || (link_tolerance > TIPC_MAX_LINK_TOL)) { - warn("Media registration error: tolerance %u\n", link_tolerance); + warn("Media <%s> rejected, illegal tolerance (%u)\n", name, + link_tolerance); goto exit; } media_id = media_count++; if (media_id >= MAX_MEDIA) { - warn("Attempt to register more than %u media\n", MAX_MEDIA); + warn("Media <%s> rejected, media limit reached (%u)\n", name, + MAX_MEDIA); media_count--; goto exit; } for (i = 0; i < media_id; i++) { if (media_list[i].type_id == media_type) { - warn("Attempt to register second media with type %u\n", + warn("Media <%s> rejected, duplicate type (%u)\n", name, media_type); media_count--; goto exit; } if (!strcmp(name, media_list[i].name)) { - warn("Attempt to re-register media name <%s>\n", name); + warn("Media <%s> rejected, duplicate name\n", name); media_count--; goto exit; } @@ -283,6 +286,9 @@ static struct bearer *bearer_find(const struct bearer *b_ptr; u32 i; + if (tipc_mode != TIPC_NET_MODE) + return NULL; + for (i = 0, b_ptr = tipc_bearers; i < MAX_BEARERS; i++, b_ptr++) { if (b_ptr->active && (!strcmp(b_ptr->publ.name, name))) return b_ptr; @@ -475,26 +481,33 @@ int tipc_enable_bearer(const char *name, u32 i; int res = -EINVAL; - if (tipc_mode != TIPC_NET_MODE) + if (tipc_mode != TIPC_NET_MODE) { + warn("Bearer <%s> rejected, not supported in standalone mode\n", + name); return -ENOPROTOOPT; - - if (!bearer_name_validate(name, &b_name) || - !tipc_addr_domain_valid(bcast_scope) || - !in_scope(bcast_scope, tipc_own_addr)) + } + if (!bearer_name_validate(name, &b_name)) { + warn("Bearer <%s> rejected, illegal name\n", name); return -EINVAL; - + } + if (!tipc_addr_domain_valid(bcast_scope) || + !in_scope(bcast_scope, tipc_own_addr)) { + warn("Bearer <%s> rejected, illegal broadcast scope\n", name); + return -EINVAL; + } if ((priority < TIPC_MIN_LINK_PRI || priority > TIPC_MAX_LINK_PRI) && - (priority != TIPC_MEDIA_LINK_PRI)) + (priority != TIPC_MEDIA_LINK_PRI)) { + warn("Bearer <%s> rejected, illegal priority\n", name); return -EINVAL; + } write_lock_bh(&tipc_net_lock); - if (!tipc_bearers) - goto failed; m_ptr = media_find(b_name.media_name); if (!m_ptr) { - warn("No media <%s>\n", b_name.media_name); + warn("Bearer <%s> rejected, media <%s> not registered\n", name, + b_name.media_name); goto failed; } @@ -510,23 +523,24 @@ restart: continue; } if (!strcmp(name, tipc_bearers[i].publ.name)) { - warn("Bearer <%s> already enabled\n", name); + warn("Bearer <%s> rejected, already enabled\n", name); goto failed; } if ((tipc_bearers[i].priority == priority) && (++with_this_prio > 2)) { if (priority-- == 0) { - warn("Third bearer <%s> with priority %u, unable to lower to %u\n", - name, priority + 1, priority); + warn("Bearer <%s> rejected, duplicate priority\n", + name); goto failed; } - warn("Third bearer <%s> with priority %u, lowering to %u\n", + warn("Bearer <%s> priority adjustment required %u->%u\n", name, priority + 1, priority); goto restart; } } if (bearer_id >= MAX_BEARERS) { - warn("Attempt to enable more than %d bearers\n", MAX_BEARERS); + warn("Bearer <%s> rejected, bearer limit reached (%u)\n", + name, MAX_BEARERS); goto failed; } @@ -536,7 +550,7 @@ restart: strcpy(b_ptr->publ.name, name); res = m_ptr->enable_bearer(&b_ptr->publ); if (res) { - warn("Failed to enable bearer <%s>\n", name); + warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); goto failed; } @@ -552,7 +566,7 @@ restart: b_ptr->link_req = tipc_disc_init_link_req(b_ptr, &m_ptr->bcast_addr, bcast_scope, 2); } - b_ptr->publ.lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&b_ptr->publ.lock); write_unlock_bh(&tipc_net_lock); info("Enabled bearer <%s>, discovery domain %s, priority %u\n", name, addr_string_fill(addr_string, bcast_scope), priority); @@ -573,9 +587,6 @@ int tipc_block_bearer(const char *name) struct link *l_ptr; struct link *temp_l_ptr; - if (tipc_mode != TIPC_NET_MODE) - return -ENOPROTOOPT; - read_lock_bh(&tipc_net_lock); b_ptr = bearer_find(name); if (!b_ptr) { @@ -584,6 +595,7 @@ int tipc_block_bearer(const char *name) return -EINVAL; } + info("Blocking bearer <%s>\n", name); spin_lock_bh(&b_ptr->publ.lock); b_ptr->publ.blocked = 1; list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { @@ -595,7 +607,6 @@ int tipc_block_bearer(const char *name) } spin_unlock_bh(&b_ptr->publ.lock); read_unlock_bh(&tipc_net_lock); - info("Blocked bearer <%s>\n", name); return TIPC_OK; } @@ -611,15 +622,13 @@ static int bearer_disable(const char *na struct link *l_ptr; struct link *temp_l_ptr; - if (tipc_mode != TIPC_NET_MODE) - return -ENOPROTOOPT; - b_ptr = bearer_find(name); if (!b_ptr) { warn("Attempt to disable unknown bearer <%s>\n", name); return -EINVAL; } + info("Disabling bearer <%s>\n", name); tipc_disc_stop_link_req(b_ptr->link_req); spin_lock_bh(&b_ptr->publ.lock); b_ptr->link_req = NULL; @@ -635,7 +644,6 @@ static int bearer_disable(const char *na tipc_link_delete(l_ptr); } spin_unlock_bh(&b_ptr->publ.lock); - info("Disabled bearer <%s>\n", name); memset(b_ptr, 0, sizeof(struct bearer)); return TIPC_OK; } diff --git a/net/tipc/cluster.c b/net/tipc/cluster.c index 1aed815..1dcb694 100644 --- a/net/tipc/cluster.c +++ b/net/tipc/cluster.c @@ -60,8 +60,10 @@ struct cluster *tipc_cltr_create(u32 add int alloc; c_ptr = (struct cluster *)kmalloc(sizeof(*c_ptr), GFP_ATOMIC); - if (c_ptr == NULL) + if (c_ptr == NULL) { + warn("Cluster creation failure, no memory\n"); return NULL; + } memset(c_ptr, 0, sizeof(*c_ptr)); c_ptr->addr = tipc_addr(tipc_zone(addr), tipc_cluster(addr), 0); @@ -70,30 +72,32 @@ struct cluster *tipc_cltr_create(u32 add else max_nodes = tipc_max_nodes + 1; alloc = sizeof(void *) * (max_nodes + 1); + c_ptr->nodes = (struct node **)kmalloc(alloc, GFP_ATOMIC); if (c_ptr->nodes == NULL) { + warn("Cluster creation failure, no memory for node area\n"); kfree(c_ptr); return NULL; } - memset(c_ptr->nodes, 0, alloc); + memset(c_ptr->nodes, 0, alloc); + if (in_own_cluster(addr)) tipc_local_nodes = c_ptr->nodes; c_ptr->highest_slave = LOWEST_SLAVE - 1; c_ptr->highest_node = 0; z_ptr = tipc_zone_find(tipc_zone(addr)); - if (z_ptr == NULL) { + if (!z_ptr) { z_ptr = tipc_zone_create(addr); } - if (z_ptr != NULL) { - tipc_zone_attach_cluster(z_ptr, c_ptr); - c_ptr->owner = z_ptr; - } - else { + if (!z_ptr) { + kfree(c_ptr->nodes); kfree(c_ptr); - c_ptr = NULL; + return NULL; } + tipc_zone_attach_cluster(z_ptr, c_ptr); + c_ptr->owner = z_ptr; return c_ptr; } diff --git a/net/tipc/config.c b/net/tipc/config.c index 48b5de2..285e1bc 100644 --- a/net/tipc/config.c +++ b/net/tipc/config.c @@ -63,7 +63,7 @@ struct manager { static struct manager mng = { 0}; -static spinlock_t config_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(config_lock); static const void *req_tlv_area; /* request message TLV area */ static int req_tlv_space; /* request message TLV area size */ @@ -291,13 +291,22 @@ static struct sk_buff *cfg_set_own_addr( if (!tipc_addr_node_valid(addr)) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (node address)"); - if (tipc_own_addr) + if (tipc_mode == TIPC_NET_MODE) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED " (cannot change node address once assigned)"); + tipc_own_addr = addr; + + /* + * Must release all spinlocks before calling start_net() because + * Linux version of TIPC calls eth_media_start() which calls + * register_netdevice_notifier() which may block! + * + * Temporarily releasing the lock should be harmless for non-Linux TIPC, + * but Linux version of eth_media_start() should really be reworked + * so that it can be called with spinlocks held. + */ spin_unlock_bh(&config_lock); - tipc_core_stop_net(); - tipc_own_addr = addr; tipc_core_start_net(); spin_lock_bh(&config_lock); return tipc_cfg_reply_none(); @@ -350,50 +359,21 @@ static struct sk_buff *cfg_set_max_subsc static struct sk_buff *cfg_set_max_ports(void) { - int orig_mode; u32 value; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_UNSIGNED)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = *(u32 *)TLV_DATA(req_tlv_area); value = ntohl(value); + if (value == tipc_max_ports) + return tipc_cfg_reply_none(); if (value != delimit(value, 127, 65535)) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (max ports must be 127-65535)"); - - if (value == tipc_max_ports) - return tipc_cfg_reply_none(); - - if (atomic_read(&tipc_user_count) > 2) + if (tipc_mode != TIPC_NOT_RUNNING) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED - " (cannot change max ports while TIPC users exist)"); - - spin_unlock_bh(&config_lock); - orig_mode = tipc_get_mode(); - if (orig_mode == TIPC_NET_MODE) - tipc_core_stop_net(); - tipc_core_stop(); + " (cannot change max ports while TIPC is active)"); tipc_max_ports = value; - tipc_core_start(); - if (orig_mode == TIPC_NET_MODE) - tipc_core_start_net(); - spin_lock_bh(&config_lock); - return tipc_cfg_reply_none(); -} - -static struct sk_buff *set_net_max(int value, int *parameter) -{ - int orig_mode; - - if (value != *parameter) { - orig_mode = tipc_get_mode(); - if (orig_mode == TIPC_NET_MODE) - tipc_core_stop_net(); - *parameter = value; - if (orig_mode == TIPC_NET_MODE) - tipc_core_start_net(); - } - return tipc_cfg_reply_none(); } @@ -405,10 +385,16 @@ static struct sk_buff *cfg_set_max_zones return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = *(u32 *)TLV_DATA(req_tlv_area); value = ntohl(value); + if (value == tipc_max_zones) + return tipc_cfg_reply_none(); if (value != delimit(value, 1, 255)) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (max zones must be 1-255)"); - return set_net_max(value, &tipc_max_zones); + if (tipc_mode == TIPC_NET_MODE) + return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (cannot change max zones once TIPC has joined a network)"); + tipc_max_zones = value; + return tipc_cfg_reply_none(); } static struct sk_buff *cfg_set_max_clusters(void) @@ -419,8 +405,8 @@ static struct sk_buff *cfg_set_max_clust return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = *(u32 *)TLV_DATA(req_tlv_area); value = ntohl(value); - if (value != 1) - return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + if (value != delimit(value, 1, 1)) + return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (max clusters fixed at 1)"); return tipc_cfg_reply_none(); } @@ -433,10 +419,16 @@ static struct sk_buff *cfg_set_max_nodes return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = *(u32 *)TLV_DATA(req_tlv_area); value = ntohl(value); + if (value == tipc_max_nodes) + return tipc_cfg_reply_none(); if (value != delimit(value, 8, 2047)) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (max nodes must be 8-2047)"); - return set_net_max(value, &tipc_max_nodes); + if (tipc_mode == TIPC_NET_MODE) + return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (cannot change max nodes once TIPC has joined a network)"); + tipc_max_nodes = value; + return tipc_cfg_reply_none(); } static struct sk_buff *cfg_set_max_slaves(void) @@ -461,15 +453,16 @@ static struct sk_buff *cfg_set_netid(voi return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); value = *(u32 *)TLV_DATA(req_tlv_area); value = ntohl(value); + if (value == tipc_net_id) + return tipc_cfg_reply_none(); if (value != delimit(value, 1, 9999)) return tipc_cfg_reply_error_string(TIPC_CFG_INVALID_VALUE " (network id must be 1-9999)"); - - if (tipc_own_addr) + if (tipc_mode == TIPC_NET_MODE) return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED - " (cannot change network id once part of network)"); - - return set_net_max(value, &tipc_net_id); + " (cannot change network id once TIPC has joined a network)"); + tipc_net_id = value; + return tipc_cfg_reply_none(); } struct sk_buff *tipc_cfg_do_cmd(u32 orig_node, u16 cmd, const void *request_area, @@ -649,7 +642,7 @@ static void cfg_named_msg_event(void *us if ((size < sizeof(*req_hdr)) || (size != TCM_ALIGN(ntohl(req_hdr->tcm_len))) || (ntohs(req_hdr->tcm_flags) != TCM_F_REQUEST)) { - warn("discarded invalid configuration message\n"); + warn("Invalid configuration message discarded\n"); return; } diff --git a/net/tipc/core.c b/net/tipc/core.c index 3d0a8ee..0539a83 100644 --- a/net/tipc/core.c +++ b/net/tipc/core.c @@ -2,7 +2,7 @@ * net/tipc/core.c: TIPC module code * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,7 +57,7 @@ void tipc_socket_stop(void); int tipc_netlink_start(void); void tipc_netlink_stop(void); -#define MOD_NAME "tipc_start: " +#define TIPC_MOD_VER "1.6.1" #ifndef CONFIG_TIPC_ZONES #define CONFIG_TIPC_ZONES 3 @@ -191,14 +191,15 @@ static int __init tipc_init(void) int res; tipc_log_reinit(CONFIG_TIPC_LOG); - info("Activated (compiled " __DATE__ " " __TIME__ ")\n"); + info("Activated (version " TIPC_MOD_VER + " compiled " __DATE__ " " __TIME__ ")\n"); tipc_own_addr = 0; tipc_remote_management = 1; tipc_max_publications = 10000; tipc_max_subscriptions = 2000; tipc_max_ports = delimit(CONFIG_TIPC_PORTS, 127, 65536); - tipc_max_zones = delimit(CONFIG_TIPC_ZONES, 1, 511); + tipc_max_zones = delimit(CONFIG_TIPC_ZONES, 1, 255); tipc_max_clusters = delimit(CONFIG_TIPC_CLUSTERS, 1, 1); tipc_max_nodes = delimit(CONFIG_TIPC_NODES, 8, 2047); tipc_max_slaves = delimit(CONFIG_TIPC_SLAVE_NODES, 0, 2047); @@ -224,6 +225,7 @@ module_exit(tipc_exit); MODULE_DESCRIPTION("TIPC: Transparent Inter Process Communication"); MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(TIPC_MOD_VER); /* Native TIPC API for kernel-space applications (see tipc.h) */ diff --git a/net/tipc/core.h b/net/tipc/core.h index 1f2e8b2..762aac2 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -2,7 +2,7 @@ * net/tipc/core.h: Include file for TIPC global declarations * * Copyright (c) 2005-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -111,10 +111,6 @@ #endif #else -#ifndef DBG_OUTPUT -#define DBG_OUTPUT NULL -#endif - /* * TIPC debug support not included: * - system messages are printed to system console @@ -129,6 +125,19 @@ #define dbg(fmt, arg...) do {} while (0) #define msg_dbg(msg,txt) do {} while (0) #define dump(fmt,arg...) do {} while (0) + +/* + * TIPC_OUTPUT is defined to be the system console, while DBG_OUTPUT is + * the null print buffer. Thes ensures that any system or debug messages + * that are generated without using the above macros are handled correctly. + */ + +#undef TIPC_OUTPUT +#define TIPC_OUTPUT TIPC_CONS + +#undef DBG_OUTPUT +#define DBG_OUTPUT NULL + #endif @@ -288,7 +297,10 @@ static inline struct tipc_msg *buf_msg(s * buf_acquire - creates a TIPC message buffer * @size: message size (including TIPC header) * - * Returns a new buffer. Space is reserved for a data link header. + * Returns a new buffer with data pointers set to the specified size. + * + * NOTE: Headroom is reserved to allow prepending of a data link header. + * There may also be unrequested tailroom present at the buffer's end. */ static inline struct sk_buff *buf_acquire(u32 size) @@ -309,7 +321,7 @@ static inline struct sk_buff *buf_acquir * buf_discard - frees a TIPC message buffer * @skb: message buffer * - * Frees a new buffer. If passed NULL, just returns. + * Frees a message buffer. If passed NULL, just returns. */ static inline void buf_discard(struct sk_buff *skb) diff --git a/net/tipc/dbg.c b/net/tipc/dbg.c index 26ef95d..5513065 100644 --- a/net/tipc/dbg.c +++ b/net/tipc/dbg.c @@ -41,7 +41,7 @@ #include "dbg.h" #define MAX_STRING 512 static char print_string[MAX_STRING]; -static spinlock_t print_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(print_lock); static struct print_buf cons_buf = { NULL, 0, NULL, NULL }; struct print_buf *TIPC_CONS = &cons_buf; diff --git a/net/tipc/discover.c b/net/tipc/discover.c index 9260138..2b84412 100644 --- a/net/tipc/discover.c +++ b/net/tipc/discover.c @@ -2,7 +2,7 @@ * net/tipc/discover.c * * Copyright (c) 2003-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -176,7 +176,6 @@ void tipc_disc_recv_msg(struct sk_buff * n_ptr = tipc_node_create(orig); } if (n_ptr == NULL) { - warn("Memory squeeze; Failed to create node\n"); return; } spin_lock_bh(&n_ptr->lock); @@ -191,10 +190,8 @@ void tipc_disc_recv_msg(struct sk_buff * } addr = &link->media_addr; if (memcmp(addr, &media_addr, sizeof(*addr))) { - char addr_string[16]; - - warn("New bearer address for %s\n", - addr_string_fill(addr_string, orig)); + warn("Resetting link <%s>, peer interface address changed\n", + link->name); memcpy(addr, &media_addr, sizeof(*addr)); tipc_link_reset(link); } @@ -270,8 +267,8 @@ static void disc_timeout(struct link_req /* leave timer interval "as is" if already at a "normal" rate */ } else { req->timer_intv *= 2; - if (req->timer_intv > TIPC_LINK_REQ_SLOW) - req->timer_intv = TIPC_LINK_REQ_SLOW; + if (req->timer_intv > TIPC_LINK_REQ_FAST) + req->timer_intv = TIPC_LINK_REQ_FAST; if ((req->timer_intv == TIPC_LINK_REQ_FAST) && (req->bearer->nodes.count)) req->timer_intv = TIPC_LINK_REQ_SLOW; diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c index 7a25278..682da4a 100644 --- a/net/tipc/eth_media.c +++ b/net/tipc/eth_media.c @@ -2,7 +2,7 @@ * net/tipc/eth_media.c: Ethernet bearer support for TIPC * * Copyright (c) 2001-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -98,17 +98,19 @@ static int recv_msg(struct sk_buff *buf, u32 size; if (likely(eb_ptr->bearer)) { - size = msg_size((struct tipc_msg *)buf->data); - skb_trim(buf, size); - if (likely(buf->len == size)) { - buf->next = NULL; - tipc_recv_msg(buf, eb_ptr->bearer); - } else { - kfree_skb(buf); + if (likely(!dev->promiscuity) || + !memcmp(buf->mac.raw,dev->dev_addr,ETH_ALEN) || + !memcmp(buf->mac.raw,dev->broadcast,ETH_ALEN)) { + size = msg_size((struct tipc_msg *)buf->data); + skb_trim(buf, size); + if (likely(buf->len == size)) { + buf->next = NULL; + tipc_recv_msg(buf, eb_ptr->bearer); + return TIPC_OK; + } } - } else { - kfree_skb(buf); } + kfree_skb(buf); return TIPC_OK; } @@ -125,8 +127,7 @@ static int enable_bearer(struct tipc_bea /* Find device with specified name */ - while (dev && dev->name && - (memcmp(dev->name, driver_name, strlen(dev->name)))) { + while (dev && dev->name && strncmp(dev->name, driver_name, IFNAMSIZ)) { dev = dev->next; } if (!dev) @@ -252,7 +253,9 @@ int tipc_eth_media_start(void) if (eth_started) return -EINVAL; - memset(&bcast_addr, 0xff, sizeof(bcast_addr)); + bcast_addr.type = htonl(TIPC_MEDIA_TYPE_ETH); + memset(&bcast_addr.dev_addr, 0xff, ETH_ALEN); + memset(eth_bearers, 0, sizeof(eth_bearers)); res = tipc_register_media(TIPC_MEDIA_TYPE_ETH, "eth", diff --git a/net/tipc/handler.c b/net/tipc/handler.c index 966f70a..ae6ddf0 100644 --- a/net/tipc/handler.c +++ b/net/tipc/handler.c @@ -44,7 +44,7 @@ struct queue_item { static kmem_cache_t *tipc_queue_item_cache; static struct list_head signal_queue_head; -static spinlock_t qitem_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(qitem_lock); static int handler_enabled = 0; static void process_signal_queue(unsigned long dummy); diff --git a/net/tipc/link.c b/net/tipc/link.c index 784b24b..c10e18a 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -2,7 +2,7 @@ * net/tipc/link.c: TIPC link code * * Copyright (c) 1996-2006, Ericsson AB - * Copyright (c) 2004-2005, Wind River Systems + * Copyright (c) 2004-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -419,7 +419,7 @@ struct link *tipc_link_create(struct bea l_ptr = (struct link *)kmalloc(sizeof(*l_ptr), GFP_ATOMIC); if (!l_ptr) { - warn("Memory squeeze; Failed to create link\n"); + warn("Link creation failed, no memory\n"); return NULL; } memset(l_ptr, 0, sizeof(*l_ptr)); @@ -469,7 +469,7 @@ struct link *tipc_link_create(struct bea if (!pb) { kfree(l_ptr); - warn("Memory squeeze; Failed to create link\n"); + warn("Link creation failed, no memory for print buffer\n"); return NULL; } tipc_printbuf_init(&l_ptr->print_buf, pb, LINK_LOG_BUF_SIZE); @@ -574,7 +574,6 @@ void tipc_link_wakeup_ports(struct link break; list_del_init(&p_ptr->wait_list); p_ptr->congested_link = NULL; - assert(p_ptr->wakeup); spin_lock_bh(p_ptr->publ.lock); p_ptr->publ.congested = 0; p_ptr->wakeup(&p_ptr->publ); @@ -691,6 +690,7 @@ void tipc_link_reset(struct link *l_ptr) struct sk_buff *buf; u32 prev_state = l_ptr->state; u32 checkpoint = l_ptr->next_in_no; + int was_active_link = tipc_link_is_active(l_ptr); msg_set_session(l_ptr->pmsg, msg_session(l_ptr->pmsg) + 1); @@ -712,7 +712,7 @@ #if 0 tipc_printf(TIPC_CONS, "\nReset link <%s>\n", l_ptr->name); dbg_link_dump(); #endif - if (tipc_node_has_active_links(l_ptr->owner) && + if (was_active_link && tipc_node_has_active_links(l_ptr->owner) && l_ptr->owner->permit_changeover) { l_ptr->reset_checkpoint = checkpoint; l_ptr->exp_msg_count = START_CHANGEOVER; @@ -755,7 +755,7 @@ #endif static void link_activate(struct link *l_ptr) { - l_ptr->next_in_no = 1; + l_ptr->next_in_no = l_ptr->stats.recv_info = 1; tipc_node_link_up(l_ptr->owner, l_ptr); tipc_bearer_add_dest(l_ptr->b_ptr, l_ptr->addr); link_send_event(tipc_cfg_link_event, l_ptr, 1); @@ -820,6 +820,8 @@ static void link_state_event(struct link break; case RESET_MSG: dbg_link("RES -> RR\n"); + info("Resetting link <%s>, requested by peer\n", + l_ptr->name); tipc_link_reset(l_ptr); l_ptr->state = RESET_RESET; l_ptr->fsm_msg_cnt = 0; @@ -844,6 +846,8 @@ static void link_state_event(struct link break; case RESET_MSG: dbg_link("RES -> RR\n"); + info("Resetting link <%s>, requested by peer " + "while probing\n", l_ptr->name); tipc_link_reset(l_ptr); l_ptr->state = RESET_RESET; l_ptr->fsm_msg_cnt = 0; @@ -875,6 +879,8 @@ static void link_state_event(struct link } else { /* Link has failed */ dbg_link("-> RU (%u probes unanswered)\n", l_ptr->fsm_msg_cnt); + warn("Resetting link <%s>, peer not responding\n", + l_ptr->name); tipc_link_reset(l_ptr); l_ptr->state = RESET_UNKNOWN; l_ptr->fsm_msg_cnt = 0; @@ -982,17 +988,20 @@ static int link_bundle_buf(struct link * struct tipc_msg *bundler_msg = buf_msg(bundler); struct tipc_msg *msg = buf_msg(buf); u32 size = msg_size(msg); - u32 to_pos = align(msg_size(bundler_msg)); - u32 rest = link_max_pkt(l_ptr) - to_pos; + u32 bundle_size = msg_size(bundler_msg); + u32 to_pos = align(bundle_size); + u32 pad = to_pos - bundle_size; if (msg_user(bundler_msg) != MSG_BUNDLER) return 0; if (msg_type(bundler_msg) != OPEN_MSG) return 0; - if (rest < align(size)) + if (skb_tailroom(bundler) < (pad + size)) + return 0; + if (link_max_pkt(l_ptr) < (to_pos + size)) return 0; - skb_put(bundler, (to_pos - msg_size(bundler_msg)) + size); + skb_put(bundler, pad + size); memcpy(bundler->data + to_pos, buf->data, size); msg_set_size(bundler_msg, to_pos + size); msg_set_msgcnt(bundler_msg, msg_msgcnt(bundler_msg) + 1); @@ -1050,7 +1059,7 @@ int tipc_link_send_buf(struct link *l_pt msg_dbg(msg, "TIPC: Congestion, throwing away\n"); buf_discard(buf); if (imp > CONN_MANAGER) { - warn("Resetting <%s>, send queue full", l_ptr->name); + warn("Resetting link <%s>, send queue full", l_ptr->name); tipc_link_reset(l_ptr); } return dsz; @@ -1135,9 +1144,13 @@ int tipc_link_send(struct sk_buff *buf, if (n_ptr) { tipc_node_lock(n_ptr); l_ptr = n_ptr->active_links[selector & 1]; - dbg("tipc_link_send: found link %x for dest %x\n", l_ptr, dest); if (l_ptr) { + dbg("tipc_link_send: found link %x for dest %x\n", l_ptr, dest); res = tipc_link_send_buf(l_ptr, buf); + } else { + dbg("Attempt to send msg to unreachable node:\n"); + msg_dbg(buf_msg(buf),">>>"); + buf_discard(buf); } tipc_node_unlock(n_ptr); } else { @@ -1242,8 +1255,6 @@ int tipc_link_send_sections_fast(struct int res; u32 selector = msg_origport(hdr) & 1; - assert(destaddr != tipc_own_addr); - again: /* * Try building message using port's max_pkt hint. @@ -1604,40 +1615,121 @@ void tipc_link_push_queue(struct link *l tipc_bearer_schedule(l_ptr->b_ptr, l_ptr); } +static void link_reset_all(unsigned long addr) +{ + struct node *n_ptr; + char addr_string[16]; + u32 i; + + read_lock_bh(&tipc_net_lock); + n_ptr = tipc_node_find((u32)addr); + if (!n_ptr) { + read_unlock_bh(&tipc_net_lock); + return; /* node no longer exists */ + } + + tipc_node_lock(n_ptr); + + warn("Resetting all links to %s\n", + addr_string_fill(addr_string, n_ptr->addr)); + + for (i = 0; i < MAX_BEARERS; i++) { + if (n_ptr->links[i]) { + link_print(n_ptr->links[i], TIPC_OUTPUT, + "Resetting link\n"); + tipc_link_reset(n_ptr->links[i]); + } + } + + tipc_node_unlock(n_ptr); + read_unlock_bh(&tipc_net_lock); +} + +static void link_retransmit_failure(struct link *l_ptr, struct sk_buff *buf) +{ + struct tipc_msg *msg = buf_msg(buf); + + warn("Retransmission failure on link <%s>\n", l_ptr->name); + tipc_msg_print(TIPC_OUTPUT, msg, ">RETR-FAIL>"); + + if (l_ptr->addr) { + + /* Handle failure on standard link */ + + link_print(l_ptr, TIPC_OUTPUT, "Resetting link\n"); + tipc_link_reset(l_ptr); + + } else { + + /* Handle failure on broadcast link */ + + struct node *n_ptr; + char addr_string[16]; + + tipc_printf(TIPC_OUTPUT, "Msg seq number: %u, ", msg_seqno(msg)); + tipc_printf(TIPC_OUTPUT, "Outstanding acks: %u\n", (u32)TIPC_SKB_CB(buf)->handle); + + n_ptr = l_ptr->owner->next; + tipc_node_lock(n_ptr); + + addr_string_fill(addr_string, n_ptr->addr); + tipc_printf(TIPC_OUTPUT, "Multicast link info for %s\n", addr_string); + tipc_printf(TIPC_OUTPUT, "Supported: %d, ", n_ptr->bclink.supported); + tipc_printf(TIPC_OUTPUT, "Acked: %u\n", n_ptr->bclink.acked); + tipc_printf(TIPC_OUTPUT, "Last in: %u, ", n_ptr->bclink.last_in); + tipc_printf(TIPC_OUTPUT, "Gap after: %u, ", n_ptr->bclink.gap_after); + tipc_printf(TIPC_OUTPUT, "Gap to: %u\n", n_ptr->bclink.gap_to); + tipc_printf(TIPC_OUTPUT, "Nack sync: %u\n\n", n_ptr->bclink.nack_sync); + + tipc_k_signal((Handler)link_reset_all, (unsigned long)n_ptr->addr); + + tipc_node_unlock(n_ptr); + + l_ptr->stale_count = 0; + } +} + void tipc_link_retransmit(struct link *l_ptr, struct sk_buff *buf, u32 retransmits) { struct tipc_msg *msg; + if (!buf) + return; + + msg = buf_msg(buf); + dbg("Retransmitting %u in link %x\n", retransmits, l_ptr); - if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr) && buf && !skb_cloned(buf)) { - msg_dbg(buf_msg(buf), ">NO_RETR->BCONG>"); - dbg_print_link(l_ptr, " "); - l_ptr->retransm_queue_head = msg_seqno(buf_msg(buf)); - l_ptr->retransm_queue_size = retransmits; - return; + if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) { + if (!skb_cloned(buf)) { + msg_dbg(msg, ">NO_RETR->BCONG>"); + dbg_print_link(l_ptr, " "); + l_ptr->retransm_queue_head = msg_seqno(msg); + l_ptr->retransm_queue_size = retransmits; + return; + } else { + /* Don't retransmit if driver already has the buffer */ + } + } else { + /* Detect repeated retransmit failures on uncongested bearer */ + + if (l_ptr->last_retransmitted == msg_seqno(msg)) { + if (++l_ptr->stale_count > 100) { + link_retransmit_failure(l_ptr, buf); + return; + } + } else { + l_ptr->last_retransmitted = msg_seqno(msg); + l_ptr->stale_count = 1; + } } + while (retransmits && (buf != l_ptr->next_out) && buf && !skb_cloned(buf)) { msg = buf_msg(buf); msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in); if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) { - /* Catch if retransmissions fail repeatedly: */ - if (l_ptr->last_retransmitted == msg_seqno(msg)) { - if (++l_ptr->stale_count > 100) { - tipc_msg_print(TIPC_CONS, buf_msg(buf), ">RETR>"); - info("...Retransmitted %u times\n", - l_ptr->stale_count); - link_print(l_ptr, TIPC_CONS, "Resetting Link\n"); - tipc_link_reset(l_ptr); - break; - } - } else { - l_ptr->stale_count = 0; - } - l_ptr->last_retransmitted = msg_seqno(msg); - msg_dbg(buf_msg(buf), ">RETR>"); buf = buf->next; retransmits--; @@ -1650,6 +1742,7 @@ void tipc_link_retransmit(struct link *l return; } } + l_ptr->retransm_queue_head = l_ptr->retransm_queue_size = 0; } @@ -1720,6 +1813,11 @@ #endif link_recv_non_seq(buf); continue; } + + if (unlikely(!msg_short(msg) && + (msg_destnode(msg) != tipc_own_addr))) + goto cont; + n_ptr = tipc_node_find(msg_prevnode(msg)); if (unlikely(!n_ptr)) goto cont; @@ -2140,7 +2238,7 @@ static void link_recv_proto_msg(struct l if (msg_linkprio(msg) && (msg_linkprio(msg) != l_ptr->priority)) { - warn("Changing prio <%s>: %u->%u\n", + warn("Resetting link <%s>, priority change %u->%u\n", l_ptr->name, l_ptr->priority, msg_linkprio(msg)); l_ptr->priority = msg_linkprio(msg); tipc_link_reset(l_ptr); /* Enforce change to take effect */ @@ -2209,17 +2307,22 @@ void tipc_link_tunnel(struct link *l_ptr u32 length = msg_size(msg); tunnel = l_ptr->owner->active_links[selector & 1]; - if (!tipc_link_is_up(tunnel)) + if (!tipc_link_is_up(tunnel)) { + warn("Link changeover error, " + "tunnel link no longer available\n"); return; + } msg_set_size(tunnel_hdr, length + INT_H_SIZE); buf = buf_acquire(length + INT_H_SIZE); - if (!buf) + if (!buf) { + warn("Link changeover error, " + "unable to send tunnel msg\n"); return; + } memcpy(buf->data, (unchar *)tunnel_hdr, INT_H_SIZE); memcpy(buf->data + INT_H_SIZE, (unchar *)msg, length); dbg("%c->%c:", l_ptr->b_ptr->net_plane, tunnel->b_ptr->net_plane); msg_dbg(buf_msg(buf), ">SEND>"); - assert(tunnel); tipc_link_send_buf(tunnel, buf); } @@ -2235,23 +2338,27 @@ void tipc_link_changeover(struct link *l u32 msgcount = l_ptr->out_queue_size; struct sk_buff *crs = l_ptr->first_out; struct link *tunnel = l_ptr->owner->active_links[0]; - int split_bundles = tipc_node_has_redundant_links(l_ptr->owner); struct tipc_msg tunnel_hdr; + int split_bundles; if (!tunnel) return; - if (!l_ptr->owner->permit_changeover) + if (!l_ptr->owner->permit_changeover) { + warn("Link changeover error, " + "peer did not permit changeover\n"); return; + } msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL, ORIGINAL_MSG, TIPC_OK, INT_H_SIZE, l_ptr->addr); msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id); msg_set_msgcnt(&tunnel_hdr, msgcount); + dbg("Link changeover requires %u tunnel messages\n", msgcount); + if (!l_ptr->first_out) { struct sk_buff *buf; - assert(!msgcount); buf = buf_acquire(INT_H_SIZE); if (buf) { memcpy(buf->data, (unchar *)&tunnel_hdr, INT_H_SIZE); @@ -2261,10 +2368,15 @@ void tipc_link_changeover(struct link *l msg_dbg(&tunnel_hdr, "EMPTY>SEND>"); tipc_link_send_buf(tunnel, buf); } else { - warn("Memory squeeze; link changeover failed\n"); + warn("Link changeover error, " + "unable to send changeover msg\n"); } return; } + + split_bundles = (l_ptr->owner->active_links[0] != + l_ptr->owner->active_links[1]); + while (crs) { struct tipc_msg *msg = buf_msg(crs); @@ -2310,7 +2422,8 @@ void tipc_link_send_duplicate(struct lin msg_set_size(&tunnel_hdr, length + INT_H_SIZE); outbuf = buf_acquire(length + INT_H_SIZE); if (outbuf == NULL) { - warn("Memory squeeze; buffer duplication failed\n"); + warn("Link changeover error, " + "unable to send duplicate msg\n"); return; } memcpy(outbuf->data, (unchar *)&tunnel_hdr, INT_H_SIZE); @@ -2364,11 +2477,15 @@ static int link_recv_changeover_msg(stru u32 msg_count = msg_msgcnt(tunnel_msg); dest_link = (*l_ptr)->owner->links[msg_bearer_id(tunnel_msg)]; - assert(dest_link != *l_ptr); if (!dest_link) { msg_dbg(tunnel_msg, "NOLINK/\n", + (*l_ptr)->name); + goto exit; + } dbg("%c<-%c:", dest_link->b_ptr->net_plane, (*l_ptr)->b_ptr->net_plane); *l_ptr = dest_link; @@ -2381,7 +2498,7 @@ static int link_recv_changeover_msg(stru } *buf = buf_extract(tunnel_buf,INT_H_SIZE); if (*buf == NULL) { - warn("Memory squeeze; failed to extract msg\n"); + warn("Link changeover error, duplicate msg dropped\n"); goto exit; } msg_dbg(tunnel_msg, "TNL, changeover initiated by peer\n", + dest_link->name); tipc_link_reset(dest_link); dest_link->exp_msg_count = msg_count; + dbg("Expecting %u tunnelled messages\n", msg_count); if (!msg_count) goto exit; } else if (dest_link->exp_msg_count == START_CHANGEOVER) { msg_dbg(tunnel_msg, "BLK/FIRST/exp_msg_count = msg_count; + dbg("Expecting %u tunnelled messages\n", msg_count); if (!msg_count) goto exit; } @@ -2407,6 +2528,8 @@ static int link_recv_changeover_msg(stru /* Receive original message */ if (dest_link->exp_msg_count == 0) { + warn("Link switchover error, " + "got too many tunnelled messages\n"); msg_dbg(tunnel_msg, "OVERDUE/DROP/type, publ->lower, publ->upper); publ->key += 1222345; p = tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node, publ->ref, publ->key); - assert(p == publ); write_unlock_bh(&tipc_nametbl_lock); - kfree(publ); + + if (p != publ) { + err("Unable to remove publication from failed node\n" + "(type=%u, lower=%u, node=0x%x, ref=%u, key=%u)\n", + publ->type, publ->lower, publ->node, publ->ref, publ->key); + } + + if (p) { + kfree(p); + } } /** @@ -275,9 +283,15 @@ void tipc_named_recv(struct sk_buff *buf if (publ) { tipc_nodesub_unsubscribe(&publ->subscr); kfree(publ); + } else { + err("Unable to remove publication by node 0x%x\n" + "(type=%u, lower=%u, ref=%u, key=%u)\n", + msg_orignode(msg), + ntohl(item->type), ntohl(item->lower), + ntohl(item->ref), ntohl(item->key)); } } else { - warn("tipc_named_recv: unknown msg\n"); + warn("Unrecognized name table message received\n"); } item++; } diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index d129422..a6926ff 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -71,7 +71,7 @@ struct sub_seq { * @sseq: pointer to dynamically-sized array of sub-sequences of this 'type'; * sub-sequences are sorted in ascending order * @alloc: number of sub-sequences currently in array - * @first_free: upper bound of highest sub-sequence + 1 + * @first_free: array index of first unused sub-sequence entry * @ns_list: links to adjacent name sequences in hash chain * @subscriptions: list of subscriptions for this 'type' * @lock: spinlock controlling access to name sequence structure @@ -101,7 +101,7 @@ struct name_table { static struct name_table table = { NULL } ; static atomic_t rsv_publ_ok = ATOMIC_INIT(0); -rwlock_t tipc_nametbl_lock = RW_LOCK_UNLOCKED; +DEFINE_RWLOCK(tipc_nametbl_lock); static int hash(int x) @@ -120,7 +120,7 @@ static struct publication *publ_create(u struct publication *publ = (struct publication *)kmalloc(sizeof(*publ), GFP_ATOMIC); if (publ == NULL) { - warn("Memory squeeze; failed to create publication\n"); + warn("Publication creation failure, no memory\n"); return NULL; } @@ -165,17 +165,17 @@ static struct name_seq *tipc_nameseq_cre struct sub_seq *sseq = tipc_subseq_alloc(1); if (!nseq || !sseq) { - warn("Memory squeeze; failed to create name sequence\n"); + warn("Name sequence creation failed, no memory\n"); kfree(nseq); kfree(sseq); return NULL; } memset(nseq, 0, sizeof(*nseq)); - nseq->lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&nseq->lock); nseq->type = type; nseq->sseqs = sseq; - dbg("tipc_nameseq_create() nseq = %x type %u, ssseqs %x, ff: %u\n", + dbg("tipc_nameseq_create(): nseq = %p, type %u, ssseqs %p, ff: %u\n", nseq, type, nseq->sseqs, nseq->first_free); nseq->alloc = 1; INIT_HLIST_NODE(&nseq->ns_list); @@ -253,16 +253,16 @@ static struct publication *tipc_nameseq_ struct sub_seq *sseq; int created_subseq = 0; - assert(nseq->first_free <= nseq->alloc); sseq = nameseq_find_subseq(nseq, lower); - dbg("nameseq_ins: for seq %x,<%u,%u>, found sseq %x\n", + dbg("nameseq_ins: for seq %p, {%u,%u}, found sseq %p\n", nseq, type, lower, sseq); if (sseq) { /* Lower end overlaps existing entry => need an exact match */ if ((sseq->lower != lower) || (sseq->upper != upper)) { - warn("Overlapping publ <%u,%u,%u>\n", type, lower, upper); + warn("Cannot publish {%u,%u,%u}, overlap error\n", + type, lower, upper); return NULL; } } else { @@ -277,25 +277,27 @@ static struct publication *tipc_nameseq_ if ((inspos < nseq->first_free) && (upper >= nseq->sseqs[inspos].lower)) { - warn("Overlapping publ <%u,%u,%u>\n", type, lower, upper); + warn("Cannot publish {%u,%u,%u}, overlap error\n", + type, lower, upper); return NULL; } /* Ensure there is space for new sub-sequence */ if (nseq->first_free == nseq->alloc) { - struct sub_seq *sseqs = nseq->sseqs; - nseq->sseqs = tipc_subseq_alloc(nseq->alloc * 2); - if (nseq->sseqs != NULL) { - memcpy(nseq->sseqs, sseqs, - nseq->alloc * sizeof (struct sub_seq)); - kfree(sseqs); - dbg("Allocated %u sseqs\n", nseq->alloc); - nseq->alloc *= 2; - } else { - warn("Memory squeeze; failed to create sub-sequence\n"); + struct sub_seq *sseqs = tipc_subseq_alloc(nseq->alloc * 2); + + if (!sseqs) { + warn("Cannot publish {%u,%u,%u}, no memory\n", + type, lower, upper); return NULL; } + dbg("Allocated %u more sseqs\n", nseq->alloc); + memcpy(sseqs, nseq->sseqs, + nseq->alloc * sizeof(struct sub_seq)); + kfree(nseq->sseqs); + nseq->sseqs = sseqs; + nseq->alloc *= 2; } dbg("Have %u sseqs for type %u\n", nseq->alloc, type); @@ -311,7 +313,7 @@ static struct publication *tipc_nameseq_ sseq->upper = upper; created_subseq = 1; } - dbg("inserting (%u %u %u) from %x:%u into sseq %x(%u,%u) of seq %x\n", + dbg("inserting {%u,%u,%u} from <0x%x:%u> into sseq %p(%u,%u) of seq %p\n", type, lower, upper, node, port, sseq, sseq->lower, sseq->upper, nseq); @@ -320,7 +322,7 @@ static struct publication *tipc_nameseq_ publ = publ_create(type, lower, upper, scope, node, port, key); if (!publ) return NULL; - dbg("inserting publ %x, node=%x publ->node=%x, subscr->node=%x\n", + dbg("inserting publ %p, node=0x%x publ->node=0x%x, subscr->node=%p\n", publ, node, publ->node, publ->subscr.node); if (!sseq->zone_list) @@ -367,45 +369,47 @@ static struct publication *tipc_nameseq_ /** * tipc_nameseq_remove_publ - + * + * NOTE: There may be cases where TIPC is asked to remove a publication + * that is not in the name table. For example, if another node issues a + * publication for a name sequence that overlaps an existing name sequence + * the publication will not be recorded, which means the publication won't + * be found when the name sequence is later withdrawn by that node. + * A failed withdraw request simply returns a failure indication and lets the + * caller issue any error or warning messages associated with such a problem. */ static struct publication *tipc_nameseq_remove_publ(struct name_seq *nseq, u32 inst, u32 node, u32 ref, u32 key) { struct publication *publ; + struct publication *curr; struct publication *prev; struct sub_seq *sseq = nameseq_find_subseq(nseq, inst); struct sub_seq *free; struct subscription *s, *st; int removed_subseq = 0; - assert(nseq); - - if (!sseq) { - int i; - - warn("Withdraw unknown <%u,%u>?\n", nseq->type, inst); - assert(nseq->sseqs); - dbg("Dumping subseqs %x for %x, alloc = %u,ff=%u\n", - nseq->sseqs, nseq, nseq->alloc, - nseq->first_free); - for (i = 0; i < nseq->first_free; i++) { - dbg("Subseq %u(%x): lower = %u,upper = %u\n", - i, &nseq->sseqs[i], nseq->sseqs[i].lower, - nseq->sseqs[i].upper); - } + if (!sseq) return NULL; - } - dbg("nameseq_remove: seq: %x, sseq %x, <%u,%u> key %u\n", + + dbg("tipc_nameseq_remove_publ: seq: %p, sseq %p, {%u,%u}, key %u\n", nseq, sseq, nseq->type, inst, key); + /* Remove publication from zone scope list */ + prev = sseq->zone_list; publ = sseq->zone_list->zone_list_next; while ((publ->key != key) || (publ->ref != ref) || (publ->node && (publ->node != node))) { prev = publ; publ = publ->zone_list_next; - assert(prev != sseq->zone_list); + if (prev == sseq->zone_list) { + + /* Prevent endless loop if publication not found */ + + return NULL; + } } if (publ != sseq->zone_list) prev->zone_list_next = publ->zone_list_next; @@ -416,14 +420,24 @@ static struct publication *tipc_nameseq_ sseq->zone_list = NULL; } + /* Remove publication from cluster scope list, if present */ + if (in_own_cluster(node)) { prev = sseq->cluster_list; - publ = sseq->cluster_list->cluster_list_next; - while ((publ->key != key) || (publ->ref != ref) || - (publ->node && (publ->node != node))) { - prev = publ; - publ = publ->cluster_list_next; - assert(prev != sseq->cluster_list); + curr = sseq->cluster_list->cluster_list_next; + while (curr != publ) { + prev = curr; + curr = curr->cluster_list_next; + if (prev == sseq->cluster_list) { + + /* Prevent endless loop for malformed list */ + + err("Unable to de-list cluster publication\n" + "{%u%u}, node=0x%x, ref=%u, key=%u)\n", + publ->type, publ->lower, publ->node, + publ->ref, publ->key); + goto end_cluster; + } } if (publ != sseq->cluster_list) prev->cluster_list_next = publ->cluster_list_next; @@ -434,15 +448,26 @@ static struct publication *tipc_nameseq_ sseq->cluster_list = NULL; } } +end_cluster: + + /* Remove publication from node scope list, if present */ if (node == tipc_own_addr) { prev = sseq->node_list; - publ = sseq->node_list->node_list_next; - while ((publ->key != key) || (publ->ref != ref) || - (publ->node && (publ->node != node))) { - prev = publ; - publ = publ->node_list_next; - assert(prev != sseq->node_list); + curr = sseq->node_list->node_list_next; + while (curr != publ) { + prev = curr; + curr = curr->node_list_next; + if (prev == sseq->node_list) { + + /* Prevent endless loop for malformed list */ + + err("Unable to de-list node publication\n" + "{%u%u}, node=0x%x, ref=%u, key=%u)\n", + publ->type, publ->lower, publ->node, + publ->ref, publ->key); + goto end_node; + } } if (publ != sseq->node_list) prev->node_list_next = publ->node_list_next; @@ -453,22 +478,18 @@ static struct publication *tipc_nameseq_ sseq->node_list = NULL; } } - assert(!publ->node || (publ->node == node)); - assert(publ->ref == ref); - assert(publ->key == key); +end_node: - /* - * Contract subseq list if no more publications: - */ - if (!sseq->node_list && !sseq->cluster_list && !sseq->zone_list) { + /* Contract subseq list if no more publications for that subseq */ + + if (!sseq->zone_list) { free = &nseq->sseqs[nseq->first_free--]; memmove(sseq, sseq + 1, (free - (sseq + 1)) * sizeof (*sseq)); removed_subseq = 1; } - /* - * Any subscriptions waiting ? - */ + /* Notify any waiting subscriptions */ + list_for_each_entry_safe(s, st, &nseq->subscriptions, nameseq_list) { tipc_subscr_report_overlap(s, publ->lower, @@ -478,6 +499,7 @@ static struct publication *tipc_nameseq_ publ->node, removed_subseq); } + return publ; } @@ -530,7 +552,7 @@ static struct name_seq *nametbl_find_seq seq_head = &table.types[hash(type)]; hlist_for_each_entry(ns, seq_node, seq_head, ns_list) { if (ns->type == type) { - dbg("found %x\n", ns); + dbg("found %p\n", ns); return ns; } } @@ -543,22 +565,21 @@ struct publication *tipc_nametbl_insert_ { struct name_seq *seq = nametbl_find_seq(type); - dbg("ins_publ: <%u,%x,%x> found %x\n", type, lower, upper, seq); + dbg("tipc_nametbl_insert_publ: {%u,%u,%u} found %p\n", type, lower, upper, seq); if (lower > upper) { - warn("Failed to publish illegal <%u,%u,%u>\n", + warn("Failed to publish illegal {%u,%u,%u}\n", type, lower, upper); return NULL; } - dbg("Publishing <%u,%u,%u> from %x\n", type, lower, upper, node); + dbg("Publishing {%u,%u,%u} from 0x%x\n", type, lower, upper, node); if (!seq) { seq = tipc_nameseq_create(type, &table.types[hash(type)]); - dbg("tipc_nametbl_insert_publ: created %x\n", seq); + dbg("tipc_nametbl_insert_publ: created %p\n", seq); } if (!seq) return NULL; - assert(seq->type == type); return tipc_nameseq_insert_publ(seq, type, lower, upper, scope, node, port, key); } @@ -572,7 +593,7 @@ struct publication *tipc_nametbl_remove_ if (!seq) return NULL; - dbg("Withdrawing <%u,%u> from %x\n", type, lower, node); + dbg("Withdrawing {%u,%u} from 0x%x\n", type, lower, node); publ = tipc_nameseq_remove_publ(seq, lower, node, ref, key); if (!seq->first_free && list_empty(&seq->subscriptions)) { @@ -738,12 +759,12 @@ struct publication *tipc_nametbl_publish struct publication *publ; if (table.local_publ_count >= tipc_max_publications) { - warn("Failed publish: max %u local publication\n", + warn("Publication failed, local publication limit reached (%u)\n", tipc_max_publications); return NULL; } if ((type < TIPC_RESERVED_TYPES) && !atomic_read(&rsv_publ_ok)) { - warn("Failed to publish reserved name <%u,%u,%u>\n", + warn("Publication failed, reserved name {%u,%u,%u}\n", type, lower, upper); return NULL; } @@ -767,10 +788,10 @@ int tipc_nametbl_withdraw(u32 type, u32 { struct publication *publ; - dbg("tipc_nametbl_withdraw:<%d,%d,%d>\n", type, lower, key); + dbg("tipc_nametbl_withdraw: {%u,%u}, key=%u\n", type, lower, key); write_lock_bh(&tipc_nametbl_lock); publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key); - if (publ) { + if (likely(publ)) { table.local_publ_count--; if (publ->scope != TIPC_NODE_SCOPE) tipc_named_withdraw(publ); @@ -780,6 +801,9 @@ int tipc_nametbl_withdraw(u32 type, u32 return 1; } write_unlock_bh(&tipc_nametbl_lock); + err("Unable to remove local publication\n" + "(type=%u, lower=%u, ref=%u, key=%u)\n", + type, lower, ref, key); return 0; } @@ -787,8 +811,7 @@ int tipc_nametbl_withdraw(u32 type, u32 * tipc_nametbl_subscribe - add a subscription object to the name table */ -void -tipc_nametbl_subscribe(struct subscription *s) +void tipc_nametbl_subscribe(struct subscription *s) { u32 type = s->seq.type; struct name_seq *seq; @@ -800,11 +823,13 @@ tipc_nametbl_subscribe(struct subscripti } if (seq){ spin_lock_bh(&seq->lock); - dbg("tipc_nametbl_subscribe:found %x for <%u,%u,%u>\n", + dbg("tipc_nametbl_subscribe:found %p for {%u,%u,%u}\n", seq, type, s->seq.lower, s->seq.upper); - assert(seq->type == type); tipc_nameseq_subscribe(seq, s); spin_unlock_bh(&seq->lock); + } else { + warn("Failed to create subscription for {%u,%u,%u}\n", + s->seq.type, s->seq.lower, s->seq.upper); } write_unlock_bh(&tipc_nametbl_lock); } @@ -813,8 +838,7 @@ tipc_nametbl_subscribe(struct subscripti * tipc_nametbl_unsubscribe - remove a subscription object from name table */ -void -tipc_nametbl_unsubscribe(struct subscription *s) +void tipc_nametbl_unsubscribe(struct subscription *s) { struct name_seq *seq; @@ -1049,35 +1073,20 @@ int tipc_nametbl_init(void) void tipc_nametbl_stop(void) { - struct hlist_head *seq_head; - struct hlist_node *seq_node; - struct hlist_node *tmp; - struct name_seq *seq; u32 i; if (!table.types) return; + /* Verify name table is empty, then release it */ + write_lock_bh(&tipc_nametbl_lock); for (i = 0; i < tipc_nametbl_size; i++) { - seq_head = &table.types[i]; - hlist_for_each_entry_safe(seq, seq_node, tmp, seq_head, ns_list) { - struct sub_seq *sseq = seq->sseqs; - - for (; sseq != &seq->sseqs[seq->first_free]; sseq++) { - struct publication *publ = sseq->zone_list; - assert(publ); - do { - struct publication *next = - publ->zone_list_next; - kfree(publ); - publ = next; - } - while (publ != sseq->zone_list); - } - } + if (!hlist_empty(&table.types[i])) + err("tipc_nametbl_stop(): hash chain %u is non-null\n", i); } kfree(table.types); table.types = NULL; write_unlock_bh(&tipc_nametbl_lock); } + diff --git a/net/tipc/net.c b/net/tipc/net.c index f7c8223..e5a359a 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c @@ -115,7 +115,7 @@ #include "config.h" * - A local spin_lock protecting the queue of subscriber events. */ -rwlock_t tipc_net_lock = RW_LOCK_UNLOCKED; +DEFINE_RWLOCK(tipc_net_lock); struct network tipc_net = { NULL }; struct node *tipc_net_select_remote_node(u32 addr, u32 ref) diff --git a/net/tipc/node.c b/net/tipc/node.c index 0d5db06..fc6d096 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -2,7 +2,7 @@ * net/tipc/node.c: TIPC node management routines * * Copyright (c) 2000-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -61,34 +61,37 @@ struct node *tipc_node_create(u32 addr) struct node **curr_node; n_ptr = kmalloc(sizeof(*n_ptr),GFP_ATOMIC); - if (n_ptr != NULL) { - memset(n_ptr, 0, sizeof(*n_ptr)); - n_ptr->addr = addr; - n_ptr->lock = SPIN_LOCK_UNLOCKED; - INIT_LIST_HEAD(&n_ptr->nsub); - - c_ptr = tipc_cltr_find(addr); - if (c_ptr == NULL) - c_ptr = tipc_cltr_create(addr); - if (c_ptr != NULL) { - n_ptr->owner = c_ptr; - tipc_cltr_attach_node(c_ptr, n_ptr); - n_ptr->last_router = -1; - - /* Insert node into ordered list */ - for (curr_node = &tipc_nodes; *curr_node; - curr_node = &(*curr_node)->next) { - if (addr < (*curr_node)->addr) { - n_ptr->next = *curr_node; - break; - } - } - (*curr_node) = n_ptr; - } else { - kfree(n_ptr); - n_ptr = NULL; - } - } + if (!n_ptr) { + warn("Node creation failed, no memory\n"); + return NULL; + } + + c_ptr = tipc_cltr_find(addr); + if (!c_ptr) { + c_ptr = tipc_cltr_create(addr); + } + if (!c_ptr) { + kfree(n_ptr); + return NULL; + } + + memset(n_ptr, 0, sizeof(*n_ptr)); + n_ptr->addr = addr; + spin_lock_init(&n_ptr->lock); + INIT_LIST_HEAD(&n_ptr->nsub); + n_ptr->owner = c_ptr; + tipc_cltr_attach_node(c_ptr, n_ptr); + n_ptr->last_router = -1; + + /* Insert node into ordered list */ + for (curr_node = &tipc_nodes; *curr_node; + curr_node = &(*curr_node)->next) { + if (addr < (*curr_node)->addr) { + n_ptr->next = *curr_node; + break; + } + } + (*curr_node) = n_ptr; return n_ptr; } @@ -122,6 +125,8 @@ void tipc_node_link_up(struct node *n_pt { struct link **active = &n_ptr->active_links[0]; + n_ptr->working_links++; + info("Established link <%s> on network plane %c\n", l_ptr->name, l_ptr->b_ptr->net_plane); @@ -132,7 +137,7 @@ void tipc_node_link_up(struct node *n_pt return; } if (l_ptr->priority < active[0]->priority) { - info("Link is standby\n"); + info("New link <%s> becomes standby\n", l_ptr->name); return; } tipc_link_send_duplicate(active[0], l_ptr); @@ -140,8 +145,9 @@ void tipc_node_link_up(struct node *n_pt active[0] = l_ptr; return; } - info("Link <%s> on network plane %c becomes standby\n", - active[0]->name, active[0]->b_ptr->net_plane); + info("Old link <%s> becomes standby\n", active[0]->name); + if (active[1] != active[0]) + info("Old link <%s> becomes standby\n", active[1]->name); active[0] = active[1] = l_ptr; } @@ -181,6 +187,8 @@ void tipc_node_link_down(struct node *n_ { struct link **active; + n_ptr->working_links--; + if (!tipc_link_is_active(l_ptr)) { info("Lost standby link <%s> on network plane %c\n", l_ptr->name, l_ptr->b_ptr->net_plane); @@ -210,8 +218,7 @@ int tipc_node_has_active_links(struct no int tipc_node_has_redundant_links(struct node *n_ptr) { - return (tipc_node_has_active_links(n_ptr) && - (n_ptr->active_links[0] != n_ptr->active_links[1])); + return (n_ptr->working_links > 1); } static int tipc_node_has_active_routes(struct node *n_ptr) @@ -234,7 +241,6 @@ struct node *tipc_node_attach_link(struc u32 bearer_id = l_ptr->b_ptr->identity; char addr_string[16]; - assert(bearer_id < MAX_BEARERS); if (n_ptr->link_cnt >= 2) { char addr_string[16]; @@ -249,7 +255,7 @@ struct node *tipc_node_attach_link(struc n_ptr->link_cnt++; return n_ptr; } - err("Attempt to establish second link on <%s> to <%s> \n", + err("Attempt to establish second link on <%s> to %s \n", l_ptr->b_ptr->publ.name, addr_string_fill(addr_string, l_ptr->addr)); } @@ -314,7 +320,7 @@ static void node_established_contact(str struct cluster *c_ptr; dbg("node_established_contact:-> %x\n", n_ptr->addr); - if (!tipc_node_has_active_routes(n_ptr)) { + if (!tipc_node_has_active_routes(n_ptr) && in_own_cluster(n_ptr->addr)) { tipc_k_signal((Handler)tipc_named_node_up, n_ptr->addr); } @@ -586,6 +592,7 @@ struct sk_buff *tipc_node_get_nodes(cons struct sk_buff *buf; struct node *n_ptr; struct tipc_node_info node_info; + u32 payload_size; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); @@ -602,8 +609,11 @@ struct sk_buff *tipc_node_get_nodes(cons /* For now, get space for all other nodes (will need to modify this when slave nodes are supported */ - buf = tipc_cfg_reply_alloc(TLV_SPACE(sizeof(node_info)) * - (tipc_max_nodes - 1)); + payload_size = TLV_SPACE(sizeof(node_info)) * (tipc_max_nodes - 1); + if (payload_size > 32768u) + return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (too many nodes)"); + buf = tipc_cfg_reply_alloc(payload_size); if (!buf) return NULL; @@ -627,6 +637,7 @@ struct sk_buff *tipc_node_get_links(cons struct sk_buff *buf; struct node *n_ptr; struct tipc_link_info link_info; + u32 payload_size; if (!TLV_CHECK(req_tlv_area, req_tlv_space, TIPC_TLV_NET_ADDR)) return tipc_cfg_reply_error_string(TIPC_CFG_TLV_ERROR); @@ -639,12 +650,15 @@ struct sk_buff *tipc_node_get_links(cons if (!tipc_nodes) return tipc_cfg_reply_none(); - - /* For now, get space for 2 links to all other nodes + bcast link - (will need to modify this when slave nodes are supported */ - - buf = tipc_cfg_reply_alloc(TLV_SPACE(sizeof(link_info)) * - (2 * (tipc_max_nodes - 1) + 1)); + + /* Get space for all unicast links + multicast link */ + + payload_size = TLV_SPACE(sizeof(link_info)) * + (tipc_net.zones[tipc_zone(tipc_own_addr)]->links + 1); + if (payload_size > 32768u) + return tipc_cfg_reply_error_string(TIPC_CFG_NOT_SUPPORTED + " (too many links)"); + buf = tipc_cfg_reply_alloc(payload_size); if (!buf) return NULL; diff --git a/net/tipc/node.h b/net/tipc/node.h index 781126e..a07cc79 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -51,6 +51,7 @@ #include "bearer.h" * @nsub: list of "node down" subscriptions monitoring node * @active_links: pointers to active links to node * @links: pointers to all links to node + * @working_links: number of working links to node (both active and standby) * @link_cnt: number of links to node * @permit_changeover: non-zero if node has redundant links to this system * @routers: bitmap (used for multicluster communication) @@ -76,6 +77,7 @@ struct node { struct link *active_links[2]; struct link *links[MAX_BEARERS]; int link_cnt; + int working_links; int permit_changeover; u32 routers[512/32]; int last_router; diff --git a/net/tipc/node_subscr.c b/net/tipc/node_subscr.c index cff4068..cc3fff3 100644 --- a/net/tipc/node_subscr.c +++ b/net/tipc/node_subscr.c @@ -47,18 +47,19 @@ #include "addr.h" void tipc_nodesub_subscribe(struct node_subscr *node_sub, u32 addr, void *usr_handle, net_ev_handler handle_down) { - node_sub->node = NULL; - if (addr == tipc_own_addr) + if (addr == tipc_own_addr) { + node_sub->node = NULL; return; - if (!tipc_addr_node_valid(addr)) { - warn("node_subscr with illegal %x\n", addr); + } + + node_sub->node = tipc_node_find(addr); + if (!node_sub->node) { + warn("Node subscription rejected, unknown node 0x%x\n", addr); return; } - node_sub->handle_node_down = handle_down; node_sub->usr_handle = usr_handle; - node_sub->node = tipc_node_find(addr); - assert(node_sub->node); + tipc_node_lock(node_sub->node); list_add_tail(&node_sub->nodesub_list, &node_sub->node->nsub); tipc_node_unlock(node_sub->node); diff --git a/net/tipc/port.c b/net/tipc/port.c index 67e96cb..3251c8d 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -57,8 +57,8 @@ #define MAX_REJECT_SIZE 1024 static struct sk_buff *msg_queue_head = NULL; static struct sk_buff *msg_queue_tail = NULL; -spinlock_t tipc_port_list_lock = SPIN_LOCK_UNLOCKED; -static spinlock_t queue_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(tipc_port_list_lock); +static DEFINE_SPINLOCK(queue_lock); static LIST_HEAD(ports); static void port_handle_node_down(unsigned long ref); @@ -168,7 +168,6 @@ void tipc_port_recv_mcast(struct sk_buff struct port_list *item = dp; int cnt = 0; - assert(buf); msg = buf_msg(buf); /* Create destination port list, if one wasn't supplied */ @@ -196,7 +195,7 @@ void tipc_port_recv_mcast(struct sk_buff struct sk_buff *b = skb_clone(buf, GFP_ATOMIC); if (b == NULL) { - warn("Buffer allocation failure\n"); + warn("Unable to deliver multicast message(s)\n"); msg_dbg(msg, "LOST:"); goto exit; } @@ -228,14 +227,14 @@ u32 tipc_createport_raw(void *usr_handle u32 ref; p_ptr = kmalloc(sizeof(*p_ptr), GFP_ATOMIC); - if (p_ptr == NULL) { - warn("Memory squeeze; failed to create port\n"); + if (!p_ptr) { + warn("Port creation failed, no memory\n"); return 0; } memset(p_ptr, 0, sizeof(*p_ptr)); ref = tipc_ref_acquire(p_ptr, &p_ptr->publ.lock); if (!ref) { - warn("Reference Table Exhausted\n"); + warn("Port creation failed, reference table exhausted\n"); kfree(p_ptr); return 0; } @@ -810,18 +809,20 @@ static void port_dispatcher_sigh(void *d void *usr_handle; int connected; int published; + u32 message_type; struct sk_buff *next = buf->next; struct tipc_msg *msg = buf_msg(buf); u32 dref = msg_destport(msg); + message_type = msg_type(msg); + if (message_type > TIPC_DIRECT_MSG) + goto reject; /* Unsupported message type */ + p_ptr = tipc_port_lock(dref); - if (!p_ptr) { - /* Port deleted while msg in queue */ - tipc_reject_msg(buf, TIPC_ERR_NO_PORT); - buf = next; - continue; - } + if (!p_ptr) + goto reject; /* Port deleted while msg in queue */ + orig.ref = msg_origport(msg); orig.node = msg_orignode(msg); up_ptr = p_ptr->user_port; @@ -832,7 +833,7 @@ static void port_dispatcher_sigh(void *d if (unlikely(msg_errcode(msg))) goto err; - switch (msg_type(msg)) { + switch (message_type) { case TIPC_CONN_MSG:{ tipc_conn_msg_event cb = up_ptr->conn_msg_cb; @@ -874,6 +875,7 @@ static void port_dispatcher_sigh(void *d &orig); break; } + case TIPC_MCAST_MSG: case TIPC_NAMED_MSG:{ tipc_named_msg_event cb = up_ptr->named_msg_cb; @@ -886,7 +888,8 @@ static void port_dispatcher_sigh(void *d goto reject; dseq.type = msg_nametype(msg); dseq.lower = msg_nameinst(msg); - dseq.upper = dseq.lower; + dseq.upper = (message_type == TIPC_NAMED_MSG) + ? dseq.lower : msg_nameupper(msg); skb_pull(buf, msg_hdr_sz(msg)); cb(usr_handle, dref, &buf, msg_data(msg), msg_data_sz(msg), msg_importance(msg), @@ -899,7 +902,7 @@ static void port_dispatcher_sigh(void *d buf = next; continue; err: - switch (msg_type(msg)) { + switch (message_type) { case TIPC_CONN_MSG:{ tipc_conn_shutdown_event cb = @@ -931,6 +934,7 @@ err: msg_data_sz(msg), msg_errcode(msg), &orig); break; } + case TIPC_MCAST_MSG: case TIPC_NAMED_MSG:{ tipc_named_msg_err_event cb = up_ptr->named_err_cb; @@ -940,7 +944,8 @@ err: break; dseq.type = msg_nametype(msg); dseq.lower = msg_nameinst(msg); - dseq.upper = dseq.lower; + dseq.upper = (message_type == TIPC_NAMED_MSG) + ? dseq.lower : msg_nameupper(msg); skb_pull(buf, msg_hdr_sz(msg)); cb(usr_handle, dref, &buf, msg_data(msg), msg_data_sz(msg), msg_errcode(msg), &dseq); @@ -1054,7 +1059,8 @@ int tipc_createport(u32 user_ref, u32 ref; up_ptr = (struct user_port *)kmalloc(sizeof(*up_ptr), GFP_ATOMIC); - if (up_ptr == NULL) { + if (!up_ptr) { + warn("Port creation failed, no memory\n"); return -ENOMEM; } ref = tipc_createport_raw(NULL, port_dispatcher, port_wakeup, importance); @@ -1165,8 +1171,6 @@ int tipc_withdraw(u32 ref, unsigned int p_ptr = tipc_port_lock(ref); if (!p_ptr) return -EINVAL; - if (!p_ptr->publ.published) - goto exit; if (!seq) { list_for_each_entry_safe(publ, tpubl, &p_ptr->publications, pport_list) { @@ -1193,7 +1197,6 @@ int tipc_withdraw(u32 ref, unsigned int } if (list_empty(&p_ptr->publications)) p_ptr->publ.published = 0; -exit: tipc_port_unlock(p_ptr); return res; } diff --git a/net/tipc/ref.c b/net/tipc/ref.c index 33bbf50..596d3c8 100644 --- a/net/tipc/ref.c +++ b/net/tipc/ref.c @@ -63,7 +63,7 @@ #include "bcast.h" struct ref_table tipc_ref_table = { NULL }; -static rwlock_t ref_table_lock = RW_LOCK_UNLOCKED; +static DEFINE_RWLOCK(ref_table_lock); /** * tipc_ref_table_init - create reference table for objects @@ -87,7 +87,7 @@ int tipc_ref_table_init(u32 requested_si index_mask = sz - 1; for (i = sz - 1; i >= 0; i--) { table[i].object = NULL; - table[i].lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&table[i].lock); table[i].data.next_plus_upper = (start & ~index_mask) + i - 1; } tipc_ref_table.entries = table; @@ -127,7 +127,14 @@ u32 tipc_ref_acquire(void *object, spinl u32 next_plus_upper; u32 reference = 0; - assert(tipc_ref_table.entries && object); + if (!object) { + err("Attempt to acquire reference to non-existent object\n"); + return 0; + } + if (!tipc_ref_table.entries) { + err("Reference table not found during acquisition attempt\n"); + return 0; + } write_lock_bh(&ref_table_lock); if (tipc_ref_table.first_free) { @@ -162,15 +169,28 @@ void tipc_ref_discard(u32 ref) u32 index; u32 index_mask; - assert(tipc_ref_table.entries); - assert(ref != 0); + if (!ref) { + err("Attempt to discard reference 0\n"); + return; + } + if (!tipc_ref_table.entries) { + err("Reference table not found during discard attempt\n"); + return; + } write_lock_bh(&ref_table_lock); index_mask = tipc_ref_table.index_mask; index = ref & index_mask; entry = &(tipc_ref_table.entries[index]); - assert(entry->object != 0); - assert(entry->data.reference == ref); + + if (!entry->object) { + err("Attempt to discard reference to non-existent object\n"); + goto exit; + } + if (entry->data.reference != ref) { + err("Attempt to discard non-existent reference\n"); + goto exit; + } /* mark entry as unused */ entry->object = NULL; @@ -184,6 +204,7 @@ void tipc_ref_discard(u32 ref) /* increment upper bits of entry to invalidate subsequent references */ entry->data.next_plus_upper = (ref & ~index_mask) + (index_mask + 1); +exit: write_unlock_bh(&ref_table_lock); } diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 648a734..32d7784 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -169,12 +169,6 @@ static int tipc_create(struct socket *so struct sock *sk; u32 ref; - if ((sock->type != SOCK_STREAM) && - (sock->type != SOCK_SEQPACKET) && - (sock->type != SOCK_DGRAM) && - (sock->type != SOCK_RDM)) - return -EPROTOTYPE; - if (unlikely(protocol != 0)) return -EPROTONOSUPPORT; @@ -199,6 +193,9 @@ static int tipc_create(struct socket *so sock->ops = &msg_ops; sock->state = SS_READY; break; + default: + tipc_deleteport(ref); + return -EPROTOTYPE; } sk = sk_alloc(AF_TIPC, GFP_KERNEL, &tipc_proto, 1); @@ -426,7 +423,7 @@ static int dest_name_check(struct sockad if (copy_from_user(&hdr, m->msg_iov[0].iov_base, sizeof(hdr))) return -EFAULT; - if ((ntohs(hdr.tcm_type) & 0xC000) & (!capable(CAP_NET_ADMIN))) + if ((ntohs(hdr.tcm_type) & 0xC000) && (!capable(CAP_NET_ADMIN))) return -EACCES; return 0; @@ -437,7 +434,7 @@ static int dest_name_check(struct sockad * @iocb: (unused) * @sock: socket structure * @m: message to send - * @total_len: (unused) + * @total_len: length of message * * Message must have an destination specified explicitly. * Used for SOCK_RDM and SOCK_DGRAM messages, @@ -458,7 +455,8 @@ static int send_msg(struct kiocb *iocb, if (unlikely(!dest)) return -EDESTADDRREQ; - if (unlikely(dest->family != AF_TIPC)) + if (unlikely((m->msg_namelen < sizeof(*dest)) || + (dest->family != AF_TIPC))) return -EINVAL; needs_conn = (sock->state != SS_READY); @@ -470,6 +468,10 @@ static int send_msg(struct kiocb *iocb, if ((tsock->p->published) || ((sock->type == SOCK_STREAM) && (total_len != 0))) return -EOPNOTSUPP; + if (dest->addrtype == TIPC_ADDR_NAME) { + tsock->p->conn_type = dest->addr.name.name.type; + tsock->p->conn_instance = dest->addr.name.name.instance; + } } if (down_interruptible(&tsock->sem)) @@ -538,7 +540,7 @@ exit: * @iocb: (unused) * @sock: socket structure * @m: message to send - * @total_len: (unused) + * @total_len: length of message * * Used for SOCK_SEQPACKET messages and SOCK_STREAM data. * @@ -561,15 +563,15 @@ static int send_packet(struct kiocb *ioc return -ERESTARTSYS; } - if (unlikely(sock->state != SS_CONNECTED)) { - if (sock->state == SS_DISCONNECTING) - res = -EPIPE; - else - res = -ENOTCONN; - goto exit; - } - do { + if (unlikely(sock->state != SS_CONNECTED)) { + if (sock->state == SS_DISCONNECTING) + res = -EPIPE; + else + res = -ENOTCONN; + goto exit; + } + res = tipc_send(tsock->p->ref, m->msg_iovlen, m->msg_iov); if (likely(res != -ELINKCONG)) { exit: @@ -597,7 +599,8 @@ exit: * * Used for SOCK_STREAM data. * - * Returns the number of bytes sent on success, or errno otherwise + * Returns the number of bytes sent on success (or partial success), + * or errno if no data sent */ @@ -611,6 +614,7 @@ static int send_stream(struct kiocb *ioc char __user *curr_start; int curr_left; int bytes_to_send; + int bytes_sent; int res; if (likely(total_len <= TIPC_MAX_USER_MSG_SIZE)) @@ -633,11 +637,11 @@ static int send_stream(struct kiocb *ioc * of small iovec entries into send_packet(). */ - my_msg = *m; - curr_iov = my_msg.msg_iov; - curr_iovlen = my_msg.msg_iovlen; + curr_iov = m->msg_iov; + curr_iovlen = m->msg_iovlen; my_msg.msg_iov = &my_iov; my_msg.msg_iovlen = 1; + bytes_sent = 0; while (curr_iovlen--) { curr_start = curr_iov->iov_base; @@ -648,16 +652,18 @@ static int send_stream(struct kiocb *ioc ? curr_left : TIPC_MAX_USER_MSG_SIZE; my_iov.iov_base = curr_start; my_iov.iov_len = bytes_to_send; - if ((res = send_packet(iocb, sock, &my_msg, 0)) < 0) - return res; + if ((res = send_packet(iocb, sock, &my_msg, 0)) < 0) { + return bytes_sent ? bytes_sent : res; + } curr_left -= bytes_to_send; curr_start += bytes_to_send; + bytes_sent += bytes_to_send; } curr_iov++; } - return total_len; + return bytes_sent; } /** @@ -727,6 +733,7 @@ static int anc_data_recv(struct msghdr * u32 anc_data[3]; u32 err; u32 dest_type; + int has_name; int res; if (likely(m->msg_controllen == 0)) @@ -738,10 +745,10 @@ static int anc_data_recv(struct msghdr * if (unlikely(err)) { anc_data[0] = err; anc_data[1] = msg_data_sz(msg); - if ((res = put_cmsg(m, SOL_SOCKET, TIPC_ERRINFO, 8, anc_data))) + if ((res = put_cmsg(m, SOL_TIPC, TIPC_ERRINFO, 8, anc_data))) return res; if (anc_data[1] && - (res = put_cmsg(m, SOL_SOCKET, TIPC_RETDATA, anc_data[1], + (res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1], msg_data(msg)))) return res; } @@ -751,25 +758,28 @@ static int anc_data_recv(struct msghdr * dest_type = msg ? msg_type(msg) : TIPC_DIRECT_MSG; switch (dest_type) { case TIPC_NAMED_MSG: + has_name = 1; anc_data[0] = msg_nametype(msg); anc_data[1] = msg_namelower(msg); anc_data[2] = msg_namelower(msg); break; case TIPC_MCAST_MSG: + has_name = 1; anc_data[0] = msg_nametype(msg); anc_data[1] = msg_namelower(msg); anc_data[2] = msg_nameupper(msg); break; case TIPC_CONN_MSG: + has_name = (tport->conn_type != 0); anc_data[0] = tport->conn_type; anc_data[1] = tport->conn_instance; anc_data[2] = tport->conn_instance; break; default: - anc_data[0] = 0; + has_name = 0; } - if (anc_data[0] && - (res = put_cmsg(m, SOL_SOCKET, TIPC_DESTNAME, 12, anc_data))) + if (has_name && + (res = put_cmsg(m, SOL_TIPC, TIPC_DESTNAME, 12, anc_data))) return res; return 0; @@ -960,7 +970,7 @@ static int recv_stream(struct kiocb *ioc restart: if (unlikely((skb_queue_len(&sock->sk->sk_receive_queue) == 0) && (flags & MSG_DONTWAIT))) { - res = (sz_copied == 0) ? -EWOULDBLOCK : 0; + res = -EWOULDBLOCK; goto exit; } @@ -1051,7 +1061,7 @@ restart: exit: up(&tsock->sem); - return res ? res : sz_copied; + return sz_copied ? sz_copied : res; } /** @@ -1236,7 +1246,8 @@ static int connect(struct socket *sock, if (sock->state == SS_READY) return -EOPNOTSUPP; - /* MOVE THE REST OF THIS ERROR CHECKING TO send_msg()? */ + /* Issue Posix-compliant error code if socket is in the wrong state */ + if (sock->state == SS_LISTENING) return -EOPNOTSUPP; if (sock->state == SS_CONNECTING) @@ -1244,13 +1255,20 @@ static int connect(struct socket *sock, if (sock->state != SS_UNCONNECTED) return -EISCONN; - if ((dst->family != AF_TIPC) || - ((dst->addrtype != TIPC_ADDR_NAME) && (dst->addrtype != TIPC_ADDR_ID))) + /* + * Reject connection attempt using multicast address + * + * Note: send_msg() validates the rest of the address fields, + * so there's no need to do it here + */ + + if (dst->addrtype == TIPC_ADDR_MCAST) return -EINVAL; /* Send a 'SYN-' to destination */ m.msg_name = dest; + m.msg_namelen = destlen; if ((res = send_msg(NULL, sock, &m, 0)) < 0) { sock->state = SS_DISCONNECTING; return res; @@ -1269,10 +1287,6 @@ static int connect(struct socket *sock, msg = buf_msg(buf); res = auto_connect(sock, tsock, msg); if (!res) { - if (dst->addrtype == TIPC_ADDR_NAME) { - tsock->p->conn_type = dst->addr.name.name.type; - tsock->p->conn_instance = dst->addr.name.name.instance; - } if (!msg_data_sz(msg)) advance_queue(tsock); } @@ -1386,7 +1400,7 @@ exit: /** * shutdown - shutdown socket connection * @sock: socket structure - * @how: direction to close (always treated as read + write) + * @how: direction to close (unused; always treated as read + write) * * Terminates connection (if necessary), then purges socket's receive queue. * @@ -1469,7 +1483,8 @@ restart: * Returns 0 on success, errno otherwise */ -static int setsockopt(struct socket *sock, int lvl, int opt, char *ov, int ol) +static int setsockopt(struct socket *sock, + int lvl, int opt, char __user *ov, int ol) { struct tipc_sock *tsock = tipc_sk(sock->sk); u32 value; @@ -1525,7 +1540,8 @@ static int setsockopt(struct socket *soc * Returns 0 on success, errno otherwise */ -static int getsockopt(struct socket *sock, int lvl, int opt, char *ov, int *ol) +static int getsockopt(struct socket *sock, + int lvl, int opt, char __user *ov, int *ol) { struct tipc_sock *tsock = tipc_sk(sock->sk); int len; diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c index c5f026c..e19b4bc 100644 --- a/net/tipc/subscr.c +++ b/net/tipc/subscr.c @@ -266,7 +266,8 @@ static void subscr_subscribe(struct tipc /* Refuse subscription if global limit exceeded */ if (atomic_read(&topsrv.subscription_count) >= tipc_max_subscriptions) { - warn("Failed: max %u subscriptions\n", tipc_max_subscriptions); + warn("Subscription rejected, subscription limit reached (%u)\n", + tipc_max_subscriptions); subscr_terminate(subscriber); return; } @@ -274,8 +275,8 @@ static void subscr_subscribe(struct tipc /* Allocate subscription object */ sub = kmalloc(sizeof(*sub), GFP_ATOMIC); - if (sub == NULL) { - warn("Memory squeeze; ignoring subscription\n"); + if (!sub) { + warn("Subscription rejected, no memory\n"); subscr_terminate(subscriber); return; } @@ -298,8 +299,7 @@ static void subscr_subscribe(struct tipc if ((((sub->filter != TIPC_SUB_PORTS) && (sub->filter != TIPC_SUB_SERVICE))) || (sub->seq.lower > sub->seq.upper)) { - warn("Rejecting illegal subscription %u,%u,%u\n", - sub->seq.type, sub->seq.lower, sub->seq.upper); + warn("Subscription rejected, illegal request\n"); kfree(sub); subscr_terminate(subscriber); return; @@ -387,7 +387,7 @@ static void subscr_named_msg_event(void dbg("subscr_named_msg_event: orig = %x own = %x,\n", orig->node, tipc_own_addr); if (size && (size != sizeof(struct tipc_subscr))) { - warn("Received tipc_subscr of invalid size\n"); + warn("Subscriber rejected, invalid subscription size\n"); return; } @@ -395,7 +395,7 @@ static void subscr_named_msg_event(void subscriber = kmalloc(sizeof(struct subscriber), GFP_ATOMIC); if (subscriber == NULL) { - warn("Memory squeeze; ignoring subscriber setup\n"); + warn("Subscriber rejected, no memory\n"); return; } memset(subscriber, 0, sizeof(struct subscriber)); @@ -403,7 +403,7 @@ static void subscr_named_msg_event(void INIT_LIST_HEAD(&subscriber->subscriber_list); subscriber->ref = tipc_ref_acquire(subscriber, &subscriber->lock); if (subscriber->ref == 0) { - warn("Failed to acquire subscriber reference\n"); + warn("Subscriber rejected, reference table exhausted\n"); kfree(subscriber); return; } @@ -422,7 +422,7 @@ static void subscr_named_msg_event(void NULL, &subscriber->port_ref); if (subscriber->port_ref == 0) { - warn("Memory squeeze; failed to create subscription port\n"); + warn("Subscriber rejected, unable to create port\n"); tipc_ref_discard(subscriber->ref); kfree(subscriber); return; @@ -457,7 +457,7 @@ int tipc_subscr_start(void) int res = -1; memset(&topsrv, 0, sizeof (topsrv)); - topsrv.lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&topsrv.lock); INIT_LIST_HEAD(&topsrv.subscriber_list); spin_lock_bh(&topsrv.lock); diff --git a/net/tipc/user_reg.c b/net/tipc/user_reg.c index 3f3f933..1e3ae57 100644 --- a/net/tipc/user_reg.c +++ b/net/tipc/user_reg.c @@ -67,7 +67,7 @@ #define USER_LIST_SIZE ((MAX_USERID + 1) static struct tipc_user *users = NULL; static u32 next_free_user = MAX_USERID + 1; -static spinlock_t reg_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(reg_lock); /** * reg_init - create TIPC user registry (but don't activate it) diff --git a/net/tipc/zone.c b/net/tipc/zone.c index 2803e1b..316c487 100644 --- a/net/tipc/zone.c +++ b/net/tipc/zone.c @@ -44,19 +44,24 @@ #include "node.h" struct _zone *tipc_zone_create(u32 addr) { - struct _zone *z_ptr = NULL; + struct _zone *z_ptr; u32 z_num; - if (!tipc_addr_domain_valid(addr)) + if (!tipc_addr_domain_valid(addr)) { + err("Zone creation failed, invalid domain 0x%x\n", addr); return NULL; + } z_ptr = (struct _zone *)kmalloc(sizeof(*z_ptr), GFP_ATOMIC); - if (z_ptr != NULL) { - memset(z_ptr, 0, sizeof(*z_ptr)); - z_num = tipc_zone(addr); - z_ptr->addr = tipc_addr(z_num, 0, 0); - tipc_net.zones[z_num] = z_ptr; + if (!z_ptr) { + warn("Zone creation failed, insufficient memory\n"); + return NULL; } + + memset(z_ptr, 0, sizeof(*z_ptr)); + z_num = tipc_zone(addr); + z_ptr->addr = tipc_addr(z_num, 0, 0); + tipc_net.zones[z_num] = z_ptr; return z_ptr; } diff --git a/net/tipc/zone.h b/net/tipc/zone.h index 267999c..5ab3d08 100644 --- a/net/tipc/zone.h +++ b/net/tipc/zone.h @@ -2,7 +2,7 @@ * net/tipc/zone.h: Include file for TIPC zone management routines * * Copyright (c) 2000-2006, Ericsson AB - * Copyright (c) 2005, Wind River Systems + * Copyright (c) 2005-2006, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,7 +45,7 @@ #include "net.h" * struct _zone - TIPC zone structure * @addr: network address of zone * @clusters: array of pointers to all clusters within zone - * @links: (used for inter-zone communication) + * @links: number of (unicast) links to zone */ struct _zone { diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index d901465..f70475b 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -83,7 +83,6 @@ */ #include -#include #include #include #include @@ -128,6 +127,30 @@ #define unix_sockets_unbound (&unix_sock #define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash != UNIX_HASH_SIZE) +#ifdef CONFIG_SECURITY_NETWORK +static void unix_get_peersec_dgram(struct sk_buff *skb) +{ + int err; + + err = security_socket_getpeersec_dgram(skb, UNIXSECDATA(skb), + UNIXSECLEN(skb)); + if (err) + *(UNIXSECDATA(skb)) = NULL; +} + +static inline void unix_set_secdata(struct scm_cookie *scm, struct sk_buff *skb) +{ + scm->secdata = *UNIXSECDATA(skb); + scm->seclen = *UNIXSECLEN(skb); +} +#else +static inline void unix_get_peersec_dgram(struct sk_buff *skb) +{ } + +static inline void unix_set_secdata(struct scm_cookie *scm, struct sk_buff *skb) +{ } +#endif /* CONFIG_SECURITY_NETWORK */ + /* * SMP locking strategy: * hash table is protected with spinlock unix_table_lock @@ -542,6 +565,14 @@ static struct proto unix_proto = { .obj_size = sizeof(struct unix_sock), }; +/* + * AF_UNIX sockets do not interact with hardware, hence they + * dont trigger interrupts - so it's safe for them to have + * bh-unsafe locking for their sk_receive_queue.lock. Split off + * this special lock-class by reinitializing the spinlock key: + */ +static struct lock_class_key af_unix_sk_receive_queue_lock_key; + static struct sock * unix_create1(struct socket *sock) { struct sock *sk = NULL; @@ -557,6 +588,8 @@ static struct sock * unix_create1(struct atomic_inc(&unix_nr_socks); sock_init_data(sock,sk); + lockdep_set_class(&sk->sk_receive_queue.lock, + &af_unix_sk_receive_queue_lock_key); sk->sk_write_space = unix_write_space; sk->sk_max_ack_backlog = sysctl_unix_max_dgram_qlen; @@ -1022,7 +1055,7 @@ restart: goto out_unlock; } - unix_state_wlock(sk); + unix_state_wlock_nested(sk); if (sk->sk_state != st) { unix_state_wunlock(sk); @@ -1291,6 +1324,8 @@ static int unix_dgram_sendmsg(struct kio if (siocb->scm->fp) unix_attach_fds(siocb->scm, skb); + unix_get_peersec_dgram(skb); + skb->h.raw = skb->data; err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); if (err) @@ -1570,6 +1605,7 @@ static int unix_dgram_recvmsg(struct kio memset(&tmp_scm, 0, sizeof(tmp_scm)); } siocb->scm->creds = *UNIXCREDS(skb); + unix_set_secdata(siocb->scm, skb); if (!(flags & MSG_PEEK)) { diff --git a/net/wanrouter/af_wanpipe.c b/net/wanrouter/af_wanpipe.c index b126518..a690cf7 100644 --- a/net/wanrouter/af_wanpipe.c +++ b/net/wanrouter/af_wanpipe.c @@ -32,7 +32,6 @@ * ******************************************************************************/ -#include #include #include #include diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c index c34833d..ad8e8a7 100644 --- a/net/wanrouter/wanmain.c +++ b/net/wanrouter/wanmain.c @@ -42,7 +42,6 @@ * Jun 02, 1999 Gideon Hack Updates for Linux 2.0.X and 2.2.X kernels. *****************************************************************************/ -#include #include /* offsetof(), etc. */ #include #include /* return codes */ diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c index c28ba5a..930ea59 100644 --- a/net/wanrouter/wanproc.c +++ b/net/wanrouter/wanproc.c @@ -20,7 +20,6 @@ * Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE) *****************************************************************************/ -#include #include /* __initfunc et al. */ #include /* offsetof(), etc. */ #include /* return codes */ diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 282ce4e..52a2726 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -35,7 +35,6 @@ * response */ -#include #include #include #include diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c index adfe7b8..47b68a3 100644 --- a/net/x25/x25_dev.c +++ b/net/x25/x25_dev.c @@ -17,7 +17,6 @@ * 2000-09-04 Henner Eisen Prevent freeing a dangling skb. */ -#include #include #include #include diff --git a/net/x25/x25_proc.c b/net/x25/x25_proc.c index dfb8011..a11837d 100644 --- a/net/x25/x25_proc.c +++ b/net/x25/x25_proc.c @@ -17,7 +17,6 @@ * 2002/10/06 Arnaldo Carvalho de Melo seq_file support */ -#include #include #include #include diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c index 6c5d375..2a3fe98 100644 --- a/net/x25/x25_route.c +++ b/net/x25/x25_route.c @@ -17,7 +17,6 @@ * X.25 001 Jonathan Naylor Started coding. */ -#include #include #include #include diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 6ed3302..04e1aea 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -9,7 +9,6 @@ * any later version. */ -#include #include #include #include diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index b469c8b..405b741 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -13,7 +13,6 @@ * */ -#include #include #include #include @@ -46,45 +45,43 @@ static DEFINE_SPINLOCK(xfrm_policy_gc_lo static struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short family); static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo); +static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family); +static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo); int xfrm_register_type(struct xfrm_type *type, unsigned short family) { - struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); - struct xfrm_type_map *typemap; + struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family); + struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; - write_lock_bh(&typemap->lock); - if (likely(typemap->map[type->proto] == NULL)) - typemap->map[type->proto] = type; + if (likely(typemap[type->proto] == NULL)) + typemap[type->proto] = type; else err = -EEXIST; - write_unlock_bh(&typemap->lock); - xfrm_policy_put_afinfo(afinfo); + xfrm_policy_unlock_afinfo(afinfo); return err; } EXPORT_SYMBOL(xfrm_register_type); int xfrm_unregister_type(struct xfrm_type *type, unsigned short family) { - struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); - struct xfrm_type_map *typemap; + struct xfrm_policy_afinfo *afinfo = xfrm_policy_lock_afinfo(family); + struct xfrm_type **typemap; int err = 0; if (unlikely(afinfo == NULL)) return -EAFNOSUPPORT; typemap = afinfo->type_map; - write_lock_bh(&typemap->lock); - if (unlikely(typemap->map[type->proto] != type)) + if (unlikely(typemap[type->proto] != type)) err = -ENOENT; else - typemap->map[type->proto] = NULL; - write_unlock_bh(&typemap->lock); - xfrm_policy_put_afinfo(afinfo); + typemap[type->proto] = NULL; + xfrm_policy_unlock_afinfo(afinfo); return err; } EXPORT_SYMBOL(xfrm_unregister_type); @@ -92,7 +89,7 @@ EXPORT_SYMBOL(xfrm_unregister_type); struct xfrm_type *xfrm_get_type(u8 proto, unsigned short family) { struct xfrm_policy_afinfo *afinfo; - struct xfrm_type_map *typemap; + struct xfrm_type **typemap; struct xfrm_type *type; int modload_attempted = 0; @@ -102,11 +99,9 @@ retry: return NULL; typemap = afinfo->type_map; - read_lock(&typemap->lock); - type = typemap->map[proto]; + type = typemap[proto]; if (unlikely(type && !try_module_get(type->owner))) type = NULL; - read_unlock(&typemap->lock); if (!type && !modload_attempted) { xfrm_policy_put_afinfo(afinfo); request_module("xfrm-type-%d-%d", @@ -142,6 +137,89 @@ void xfrm_put_type(struct xfrm_type *typ module_put(type->owner); } +int xfrm_register_mode(struct xfrm_mode *mode, int family) +{ + struct xfrm_policy_afinfo *afinfo; + struct xfrm_mode **modemap; + int err; + + if (unlikely(mode->encap >= XFRM_MODE_MAX)) + return -EINVAL; + + afinfo = xfrm_policy_lock_afinfo(family); + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; + + err = -EEXIST; + modemap = afinfo->mode_map; + if (likely(modemap[mode->encap] == NULL)) { + modemap[mode->encap] = mode; + err = 0; + } + + xfrm_policy_unlock_afinfo(afinfo); + return err; +} +EXPORT_SYMBOL(xfrm_register_mode); + +int xfrm_unregister_mode(struct xfrm_mode *mode, int family) +{ + struct xfrm_policy_afinfo *afinfo; + struct xfrm_mode **modemap; + int err; + + if (unlikely(mode->encap >= XFRM_MODE_MAX)) + return -EINVAL; + + afinfo = xfrm_policy_lock_afinfo(family); + if (unlikely(afinfo == NULL)) + return -EAFNOSUPPORT; + + err = -ENOENT; + modemap = afinfo->mode_map; + if (likely(modemap[mode->encap] == mode)) { + modemap[mode->encap] = NULL; + err = 0; + } + + xfrm_policy_unlock_afinfo(afinfo); + return err; +} +EXPORT_SYMBOL(xfrm_unregister_mode); + +struct xfrm_mode *xfrm_get_mode(unsigned int encap, int family) +{ + struct xfrm_policy_afinfo *afinfo; + struct xfrm_mode *mode; + int modload_attempted = 0; + + if (unlikely(encap >= XFRM_MODE_MAX)) + return NULL; + +retry: + afinfo = xfrm_policy_get_afinfo(family); + if (unlikely(afinfo == NULL)) + return NULL; + + mode = afinfo->mode_map[encap]; + if (unlikely(mode && !try_module_get(mode->owner))) + mode = NULL; + if (!mode && !modload_attempted) { + xfrm_policy_put_afinfo(afinfo); + request_module("xfrm-mode-%d-%d", family, encap); + modload_attempted = 1; + goto retry; + } + + xfrm_policy_put_afinfo(afinfo); + return mode; +} + +void xfrm_put_mode(struct xfrm_mode *mode) +{ + module_put(mode->owner); +} + static inline unsigned long make_jiffies(long secs) { if (secs >= (MAX_SCHEDULE_TIMEOUT-1)/HZ) @@ -1306,17 +1384,31 @@ static struct xfrm_policy_afinfo *xfrm_p return NULL; read_lock(&xfrm_policy_afinfo_lock); afinfo = xfrm_policy_afinfo[family]; - if (likely(afinfo != NULL)) - read_lock(&afinfo->lock); - read_unlock(&xfrm_policy_afinfo_lock); + if (unlikely(!afinfo)) + read_unlock(&xfrm_policy_afinfo_lock); return afinfo; } static void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) { - if (unlikely(afinfo == NULL)) - return; - read_unlock(&afinfo->lock); + read_unlock(&xfrm_policy_afinfo_lock); +} + +static struct xfrm_policy_afinfo *xfrm_policy_lock_afinfo(unsigned int family) +{ + struct xfrm_policy_afinfo *afinfo; + if (unlikely(family >= NPROTO)) + return NULL; + write_lock_bh(&xfrm_policy_afinfo_lock); + afinfo = xfrm_policy_afinfo[family]; + if (unlikely(!afinfo)) + write_unlock_bh(&xfrm_policy_afinfo_lock); + return afinfo; +} + +static void xfrm_policy_unlock_afinfo(struct xfrm_policy_afinfo *afinfo) +{ + write_unlock_bh(&xfrm_policy_afinfo_lock); } static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 93a2f36..43f00fc 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -77,6 +77,8 @@ static void xfrm_state_gc_destroy(struct kfree(x->ealg); kfree(x->calg); kfree(x->encap); + if (x->mode) + xfrm_put_mode(x->mode); if (x->type) { x->type->destructor(x); xfrm_put_type(x->type); @@ -1103,17 +1105,14 @@ static struct xfrm_state_afinfo *xfrm_st return NULL; read_lock(&xfrm_state_afinfo_lock); afinfo = xfrm_state_afinfo[family]; - if (likely(afinfo != NULL)) - read_lock(&afinfo->lock); - read_unlock(&xfrm_state_afinfo_lock); + if (unlikely(!afinfo)) + read_unlock(&xfrm_state_afinfo_lock); return afinfo; } static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo) { - if (unlikely(afinfo == NULL)) - return; - read_unlock(&afinfo->lock); + read_unlock(&xfrm_state_afinfo_lock); } /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */ @@ -1165,8 +1164,6 @@ int xfrm_state_mtu(struct xfrm_state *x, return res; } -EXPORT_SYMBOL(xfrm_state_mtu); - int xfrm_init_state(struct xfrm_state *x) { struct xfrm_state_afinfo *afinfo; @@ -1196,6 +1193,10 @@ int xfrm_init_state(struct xfrm_state *x if (err) goto error; + x->mode = xfrm_get_mode(x->props.mode, family); + if (x->mode == NULL) + goto error; + x->km.state = XFRM_STATE_VALID; error: diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 81d1005..3e6a722 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -427,23 +427,25 @@ static int xfrm_del_sa(struct sk_buff *s if (x == NULL) return -ESRCH; + if ((err = security_xfrm_state_delete(x)) != 0) + goto out; + if (xfrm_state_kern(x)) { - xfrm_state_put(x); - return -EPERM; + err = -EPERM; + goto out; } err = xfrm_state_delete(x); - if (err < 0) { - xfrm_state_put(x); - return err; - } + if (err < 0) + goto out; c.seq = nlh->nlmsg_seq; c.pid = nlh->nlmsg_pid; c.event = nlh->nlmsg_type; km_state_notify(x, &c); - xfrm_state_put(x); +out: + xfrm_state_put(x); return err; } @@ -1055,6 +1057,8 @@ static int xfrm_get_policy(struct sk_buf MSG_DONTWAIT); } } else { + if ((err = security_xfrm_policy_delete(xp)) != 0) + goto out; c.data.byid = p->index; c.event = nlh->nlmsg_type; c.seq = nlh->nlmsg_seq; @@ -1064,6 +1068,7 @@ static int xfrm_get_policy(struct sk_buf xfrm_pol_put(xp); +out: return err; } @@ -1430,7 +1435,7 @@ static int xfrm_user_rcv_msg(struct sk_b link = &xfrm_dispatch[type]; /* All operations require privileges, even GET */ - if (security_netlink_recv(skb)) { + if (security_netlink_recv(skb, CAP_NET_ADMIN)) { *errp = -EPERM; return -1; } diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index b0d067b..2180c88 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -13,6 +13,10 @@ # contain a comma depfile = $(subst $(comma),_,$(@D)/.$(@F).d) ### +# filename of target with directory and extension stripped +basetarget = $(basename $(notdir $@)) + +### # Escape single quote for use in echo statements escsq = $(subst $(squote),'\$(squote)',$1) diff --git a/scripts/Makefile.build b/scripts/Makefile.build index e48e60d..3cb445c 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -8,7 +8,7 @@ PHONY := __build __build: # Read .config if it exist, otherwise ignore --include .config +-include include/config/auto.conf include scripts/Kbuild.include @@ -117,7 +117,7 @@ quiet_modtag := $(empty) $(empty) $(obj-m) : quiet_modtag := [M] # Default for not multi-part modules -modname = $(*F) +modname = $(basetarget) $(multi-objs-m) : modname = $(modname-multi) $(multi-objs-m:.o=.i) : modname = $(modname-multi) @@ -140,6 +140,15 @@ cmd_cc_i_c = $(CPP) $(c_flags) - %.i: %.c FORCE $(call if_changed_dep,cc_i_c) +quiet_cmd_cc_symtypes_c = SYM $(quiet_modtag) $@ +cmd_cc_symtypes_c = \ + $(CPP) -D__GENKSYMS__ $(c_flags) $< \ + | $(GENKSYMS) -T $@ >/dev/null; \ + test -s $@ || rm -f $@ + +%.symtypes : %.c FORCE + $(call if_changed_dep,cc_symtypes_c) + # C (.c) files # The C file is compiled and updated dependency information is generated. # (See cmd_cc_o_c + relevant part of rule_cc_o_c) @@ -166,7 +175,8 @@ cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D cmd_modversions = \ if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \ $(CPP) -D__GENKSYMS__ $(c_flags) $< \ - | $(GENKSYMS) -a $(ARCH) \ + | $(GENKSYMS) $(if $(KBUILD_SYMTYPES), \ + -T $(@D)/$(@F:.o=.symtypes)) -a $(ARCH) \ > $(@D)/.tmp_$(@F:.o=.ver); \ \ $(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) \ diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst new file mode 100644 index 0000000..aa9990a --- /dev/null +++ b/scripts/Makefile.headersinst @@ -0,0 +1,158 @@ +# ========================================================================== +# Installing headers +# +# header-y files will be installed verbatim +# unifdef-y are the files where unifdef will be run before installing files +# objhdr-y are generated files that will be installed verbatim +# +# ========================================================================== + +UNIFDEF := unifdef -U__KERNEL__ + +# Eliminate the contents of (and inclusions of) compiler.h +HDRSED := sed -e "s/ inline / __inline__ /g" \ + -e "s/[[:space:]]__user[[:space:]]\+/ /g" \ + -e "s/(__user[[:space:]]\+/ (/g" \ + -e "s/[[:space:]]__force[[:space:]]\+/ /g" \ + -e "s/(__force[[:space:]]\+/ (/g" \ + -e "s/[[:space:]]__iomem[[:space:]]\+/ /g" \ + -e "s/(__iomem[[:space:]]\+/ (/g" \ + -e "s/[[:space:]]__attribute_const__[[:space:]]\+/\ /g" \ + -e "s/[[:space:]]__attribute_const__$$//" \ + -e "/^\#include /d" + +_dst := $(if $(dst),$(dst),$(obj)) + +.PHONY: __headersinst +__headersinst: + + +ifeq (,$(patsubst include/asm/%,,$(obj)/)) +# For producing the generated stuff in include/asm for biarch builds, include +# both sets of Kbuild files; we'll generate anything which is mentioned in +# _either_ arch, and recurse into subdirectories which are mentioned in either +# arch. Since some directories may exist in one but not the other, we must +# use '-include'. +GENASM := 1 +archasm := $(subst include/asm,asm-$(ARCH),$(obj)) +altarchasm := $(subst include/asm,asm-$(ALTARCH),$(obj)) +-include $(srctree)/include/$(archasm)/Kbuild +-include $(srctree)/include/$(altarchasm)/Kbuild +else +include $(srctree)/$(obj)/Kbuild +endif + +include scripts/Kbuild.include + +# If this is include/asm-$(ARCH) and there's no $(ALTARCH), then +# override $(_dst) so that we install to include/asm directly. +ifeq ($(obj)$(ALTARCH),include/asm-$(ARCH)) + _dst := include/asm +endif + +header-y := $(sort $(header-y)) +unifdef-y := $(sort $(unifdef-y)) +subdir-y := $(patsubst %/,%,$(filter %/, $(header-y))) +header-y := $(filter-out %/, $(header-y)) +header-y := $(filter-out $(unifdef-y),$(header-y)) + +ifdef ALTARCH +ifeq ($(obj),include/asm-$(ARCH)) +altarch-y := altarch-dir +endif +endif + +# Make the definitions visible for recursive make invocations +export ALTARCH +export ARCHDEF +export ALTARCHDEF + +quiet_cmd_o_hdr_install = INSTALL $(_dst)/$@ + cmd_o_hdr_install = cp $(objtree)/$(obj)/$@ $(INSTALL_HDR_PATH)/$(_dst) + +quiet_cmd_headers_install = INSTALL $(_dst)/$@ + cmd_headers_install = $(HDRSED) $(srctree)/$(obj)/$@ \ + > $(INSTALL_HDR_PATH)/$(_dst)/$@ + +quiet_cmd_unifdef = UNIFDEF $(_dst)/$@ + cmd_unifdef = $(UNIFDEF) $(srctree)/$(obj)/$@ | $(HDRSED) \ + > $(INSTALL_HDR_PATH)/$(_dst)/$@ || : + +quiet_cmd_check = CHECK $(_dst)/$@ + cmd_check = $(srctree)/scripts/hdrcheck.sh \ + $(INSTALL_HDR_PATH)/include \ + $(INSTALL_HDR_PATH)/$(_dst)/$@ + +quiet_cmd_mkdir = MKDIR $@ + cmd_mkdir = mkdir -p $(INSTALL_HDR_PATH)/$@ + +quiet_cmd_gen = GEN $(_dst)/$@ + cmd_gen = \ +STUBDEF=__ASM_STUB_`echo $@ | tr a-z. A-Z_`; \ +(echo "/* File autogenerated by 'make headers_install' */" ; \ +echo "\#ifndef $$STUBDEF" ; \ +echo "\#define $$STUBDEF" ; \ +echo "\# if $(ARCHDEF)" ; \ +if [ -r $(srctree)/include/$(archasm)/$@ ]; then \ + echo "\# include <$(archasm)/$@>" ; \ +else \ + echo "\# error $(archasm)/$@ does not exist in" \ + "the $(ARCH) architecture" ; \ +fi ; \ +echo "\# elif $(ALTARCHDEF)" ; \ +if [ -r $(srctree)/include/$(altarchasm)/$@ ]; then \ + echo "\# include <$(altarchasm)/$@>" ; \ +else \ + echo "\# error $(altarchasm)/$@ does not exist in" \ + "the $(ALTARCH) architecture" ; \ +fi ; \ +echo "\# else" ; \ +echo "\# warning This machine appears to be" \ + "neither $(ARCH) nor $(ALTARCH)." ; \ +echo "\# endif" ; \ +echo "\#endif /* $$STUBDEF */" ; \ +) > $(INSTALL_HDR_PATH)/$(_dst)/$@ + +__headersinst: $(subdir-y) $(header-y) $(unifdef-y) $(altarch-y) $(objhdr-y) + +.PHONY: $(header-y) $(unifdef-y) $(subdir-y) + +ifdef HDRCHECK +# Rules for checking headers +$(objhdr-y) $(header-y) $(unifdef-y): + $(call cmd,check) +else +# Rules for installing headers + +$(objhdr-y) $(subdir-y) $(header-y) $(unifdef-y): $(_dst) + +.PHONY: $(_dst) +$(_dst): + $(call cmd,mkdir) + +ifdef GENASM +$(objhdr-y) $(header-y) $(unifdef-y): + $(call cmd,gen) + +else +$(objhdr-y): + $(call cmd,o_hdr_install) + +$(header-y): + $(call cmd,headers_install) + +$(unifdef-y): + $(call cmd,unifdef) +endif +endif + +hdrinst := -rR -f $(srctree)/scripts/Makefile.headersinst obj + +.PHONY: altarch-dir +altarch-dir: + $(Q)$(MAKE) $(hdrinst)=include/asm-$(ALTARCH) dst=include/asm-$(ALTARCH) + $(Q)$(MAKE) $(hdrinst)=include/asm dst=include/asm + +# Recursion +$(subdir-y): + $(Q)$(MAKE) $(hdrinst)=$(obj)/$@ dst=$(_dst)/$@ rel=../$(rel) diff --git a/scripts/Makefile.host b/scripts/Makefile.host index 2d51970..18ecd4d 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -33,8 +33,8 @@ # Note: Shared libraries consisting of C __hostprogs := $(sort $(hostprogs-y)$(hostprogs-m)) # hostprogs-y := tools/build may have been specified. Retreive directory -obj-dirs += $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f)))) -obj-dirs := $(strip $(sort $(filter-out ./,$(obj-dirs)))) +host-objdirs := $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f)))) +host-objdirs := $(strip $(sort $(filter-out ./,$(host-objdirs)))) # C code @@ -73,13 +73,17 @@ host-cxxmulti := $(addprefix $(obj)/,$(h host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs)) host-cshlib := $(addprefix $(obj)/,$(host-cshlib)) host-cshobjs := $(addprefix $(obj)/,$(host-cshobjs)) -obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) +host-objdirs := $(addprefix $(obj)/,$(host-objdirs)) + +obj-dirs += $(host-objdirs) ##### # Handle options to gcc. Support building with separate output directory -_hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) $(HOSTCFLAGS_$(*F).o) -_hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) $(HOSTCXXFLAGS_$(*F).o) +_hostc_flags = $(HOSTCFLAGS) $(HOST_EXTRACFLAGS) \ + $(HOSTCFLAGS_$(basetarget).o) +_hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \ + $(HOSTCXXFLAGS_$(basetarget).o) ifeq ($(KBUILD_SRC),) __hostc_flags = $(_hostc_flags) diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 2cb4935..fc498fe 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -82,12 +82,12 @@ # Note: It's possible that one object ge # than one module. In that case KBUILD_MODNAME will be set to foo_bar, # where foo and bar are the name of the modules. name-fix = $(subst $(comma),_,$(subst -,_,$1)) -basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(*F)))" +basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(basetarget)))" modname_flags = $(if $(filter 1,$(words $(modname))),\ -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))") -_c_flags = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o) -_a_flags = $(AFLAGS) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o) +_c_flags = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(basetarget).o) +_a_flags = $(AFLAGS) $(EXTRA_AFLAGS) $(AFLAGS_$(basetarget).o) _cpp_flags = $(CPPFLAGS) $(EXTRA_CPPFLAGS) $(CPPFLAGS_$(@F)) # If building the kernel in a separate objtree expand all occurrences diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index 2686dd5..f0ff248 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -17,7 +17,7 @@ __modinst: $(modules) @: quiet_cmd_modules_install = INSTALL $@ - cmd_modules_install = mkdir -p $(2); cp $@ $(2) + cmd_modules_install = mkdir -p $(2); cp $@ $(2) ; $(mod_strip_cmd) $(2)/$(notdir $@) # Modules built outside the kernel source tree go into extra by default INSTALL_MOD_DIR ?= extra diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 0e056cf..a495502 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -35,7 +35,7 @@ # where the CRC of each module is retrie PHONY := _modpost _modpost: __modpost -include .config +include include/config/auto.conf include scripts/Kbuild.include include scripts/Makefile.lib @@ -72,7 +72,7 @@ # Declare generated files as targets for # Step 5), compile all *.mod.c files # modname is set to make c_flags define KBUILD_MODNAME -modname = $(*F) +modname = $(notdir $(@:.mod.o=)) quiet_cmd_cc_o_c = CC $@ cmd_cc_o_c = $(CC) $(c_flags) $(CFLAGS_MODULE) \ diff --git a/scripts/basic/Makefile b/scripts/basic/Makefile index f22e94c..2f60070 100644 --- a/scripts/basic/Makefile +++ b/scripts/basic/Makefile @@ -1,17 +1,15 @@ ### # Makefile.basic list the most basic programs used during the build process. # The programs listed herein is what is needed to do the basic stuff, -# such as splitting .config and fix dependency file. +# such as fix dependency file. # This initial step is needed to avoid files to be recompiled # when kernel configuration changes (which is what happens when # .config is included by main Makefile. # --------------------------------------------------------------------------- # fixdep: Used to generate dependency information during build process -# split-include: Divide all config symbols up in a number of files in -# include/config/... # docproc: Used in Documentation/docbook -hostprogs-y := fixdep split-include docproc +hostprogs-y := fixdep docproc always := $(hostprogs-y) # fixdep is needed to compile other host programs diff --git a/scripts/basic/split-include.c b/scripts/basic/split-include.c deleted file mode 100644 index 459c452..0000000 --- a/scripts/basic/split-include.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * split-include.c - * - * Copyright abandoned, Michael Chastain, . - * This is a C version of syncdep.pl by Werner Almesberger. - * - * This program takes autoconf.h as input and outputs a directory full - * of one-line include files, merging onto the old values. - * - * Think of the configuration options as key-value pairs. Then there - * are five cases: - * - * key old value new value action - * - * KEY-1 VALUE-1 VALUE-1 leave file alone - * KEY-2 VALUE-2A VALUE-2B write VALUE-2B into file - * KEY-3 - VALUE-3 write VALUE-3 into file - * KEY-4 VALUE-4 - write an empty file - * KEY-5 (empty) - leave old empty file alone - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define ERROR_EXIT(strExit) \ - { \ - const int errnoSave = errno; \ - fprintf(stderr, "%s: ", str_my_name); \ - errno = errnoSave; \ - perror((strExit)); \ - exit(1); \ - } - - - -int main(int argc, const char * argv []) -{ - const char * str_my_name; - const char * str_file_autoconf; - const char * str_dir_config; - - FILE * fp_config; - FILE * fp_target; - FILE * fp_find; - - int buffer_size; - - char * line; - char * old_line; - char * list_target; - char * ptarget; - - struct stat stat_buf; - - /* Check arg count. */ - if (argc != 3) - { - fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]); - exit(1); - } - - str_my_name = argv[0]; - str_file_autoconf = argv[1]; - str_dir_config = argv[2]; - - /* Find a buffer size. */ - if (stat(str_file_autoconf, &stat_buf) != 0) - ERROR_EXIT(str_file_autoconf); - buffer_size = 2 * stat_buf.st_size + 4096; - - /* Allocate buffers. */ - if ( (line = malloc(buffer_size)) == NULL - || (old_line = malloc(buffer_size)) == NULL - || (list_target = malloc(buffer_size)) == NULL ) - ERROR_EXIT(str_file_autoconf); - - /* Open autoconfig file. */ - if ((fp_config = fopen(str_file_autoconf, "r")) == NULL) - ERROR_EXIT(str_file_autoconf); - - /* Make output directory if needed. */ - if (stat(str_dir_config, &stat_buf) != 0) - { - if (mkdir(str_dir_config, 0755) != 0) - ERROR_EXIT(str_dir_config); - } - - /* Change to output directory. */ - if (chdir(str_dir_config) != 0) - ERROR_EXIT(str_dir_config); - - /* Put initial separator into target list. */ - ptarget = list_target; - *ptarget++ = '\n'; - - /* Read config lines. */ - while (fgets(line, buffer_size, fp_config)) - { - const char * str_config; - int is_same; - int itarget; - - if (line[0] != '#') - continue; - if ((str_config = strstr(line, "CONFIG_")) == NULL) - continue; - - /* Make the output file name. */ - str_config += sizeof("CONFIG_") - 1; - for (itarget = 0; !isspace(str_config[itarget]); itarget++) - { - int c = (unsigned char) str_config[itarget]; - if (isupper(c)) c = tolower(c); - if (c == '_') c = '/'; - ptarget[itarget] = c; - } - ptarget[itarget++] = '.'; - ptarget[itarget++] = 'h'; - ptarget[itarget++] = '\0'; - - /* Check for existing file. */ - is_same = 0; - if ((fp_target = fopen(ptarget, "r")) != NULL) - { - fgets(old_line, buffer_size, fp_target); - if (fclose(fp_target) != 0) - ERROR_EXIT(ptarget); - if (!strcmp(line, old_line)) - is_same = 1; - } - - if (!is_same) - { - /* Auto-create directories. */ - int islash; - for (islash = 0; islash < itarget; islash++) - { - if (ptarget[islash] == '/') - { - ptarget[islash] = '\0'; - if (stat(ptarget, &stat_buf) != 0 - && mkdir(ptarget, 0755) != 0) - ERROR_EXIT( ptarget ); - ptarget[islash] = '/'; - } - } - - /* Write the file. */ - if ((fp_target = fopen(ptarget, "w" )) == NULL) - ERROR_EXIT(ptarget); - fputs(line, fp_target); - if (ferror(fp_target) || fclose(fp_target) != 0) - ERROR_EXIT(ptarget); - } - - /* Update target list */ - ptarget += itarget; - *(ptarget-1) = '\n'; - } - - /* - * Close autoconfig file. - * Terminate the target list. - */ - if (fclose(fp_config) != 0) - ERROR_EXIT(str_file_autoconf); - *ptarget = '\0'; - - /* - * Fix up existing files which have no new value. - * This is Case 4 and Case 5. - * - * I re-read the tree and filter it against list_target. - * This is crude. But it avoids data copies. Also, list_target - * is compact and contiguous, so it easily fits into cache. - * - * Notice that list_target contains strings separated by \n, - * with a \n before the first string and after the last. - * fgets gives the incoming names a terminating \n. - * So by having an initial \n, strstr will find exact matches. - */ - - fp_find = popen("find * -type f -name \"*.h\" -print", "r"); - if (fp_find == 0) - ERROR_EXIT( "find" ); - - line[0] = '\n'; - while (fgets(line+1, buffer_size, fp_find)) - { - if (strstr(list_target, line) == NULL) - { - /* - * This is an old file with no CONFIG_* flag in autoconf.h. - */ - - /* First strip the \n. */ - line[strlen(line)-1] = '\0'; - - /* Grab size. */ - if (stat(line+1, &stat_buf) != 0) - ERROR_EXIT(line); - - /* If file is not empty, make it empty and give it a fresh date. */ - if (stat_buf.st_size != 0) - { - if ((fp_target = fopen(line+1, "w")) == NULL) - ERROR_EXIT(line); - if (fclose(fp_target) != 0) - ERROR_EXIT(line); - } - } - } - - if (pclose(fp_find) != 0) - ERROR_EXIT("find"); - - return 0; -} diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index 75f21d8..ce59fc2 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -18,7 +18,8 @@ def getsizes(file): for l in os.popen("nm --size-sort " + file).readlines(): size, type, name = l[:-1].split() if type in "tTdDbB": - sym[name] = int(size, 16) + if "." in name: name = "static." + name.split(".")[0] + sym[name] = sym.get(name, 0) + int(size, 16) return sym old = getsizes(sys.argv[1]) diff --git a/scripts/checkstack.pl b/scripts/checkstack.pl index dadfa20..b349246 100755 --- a/scripts/checkstack.pl +++ b/scripts/checkstack.pl @@ -89,11 +89,21 @@ # main() # my $funcre = qr/^$x* <(.*)>:$/; my $func; +my $file, $lastslash; + while (my $line = ) { if ($line =~ m/$funcre/) { $func = $1; } - if ($line =~ m/$re/) { + elsif ($line =~ m/(.*):\s*file format/) { + $file = $1; + $file =~ s/\.ko//; + $lastslash = rindex($file, "/"); + if ($lastslash != -1) { + $file = substr($file, $lastslash + 1); + } + } + elsif ($line =~ m/$re/) { my $size = $1; $size = hex($size) if ($size =~ /^0x/); @@ -109,7 +119,7 @@ while (my $line = ) { $addr =~ s/ /0/g; $addr = "0x$addr"; - my $intro = "$addr $func:"; + my $intro = "$addr $func [$file]:"; my $padlen = 56 - length($intro); while ($padlen > 0) { $intro .= ' '; diff --git a/scripts/checkversion.pl b/scripts/checkversion.pl index 9f84e56..ec7d211 100755 --- a/scripts/checkversion.pl +++ b/scripts/checkversion.pl @@ -1,7 +1,7 @@ #! /usr/bin/perl # -# checkversion find uses of LINUX_VERSION_CODE, KERNEL_VERSION, or -# UTS_RELEASE without including , or cases of +# checkversion find uses of LINUX_VERSION_CODE or KERNEL_VERSION +# without including , or cases of # including that don't need it. # Copyright (C) 2003, Randy Dunlap @@ -41,8 +41,7 @@ foreach $file (@ARGV) } # Look for uses: LINUX_VERSION_CODE, KERNEL_VERSION, UTS_RELEASE - if (($_ =~ /LINUX_VERSION_CODE/) || ($_ =~ /\WKERNEL_VERSION/) || - ($_ =~ /UTS_RELEASE/)) { + if (($_ =~ /LINUX_VERSION_CODE/) || ($_ =~ /\WKERNEL_VERSION/)) { $fUseVersion = 1; last LINE if $iLinuxVersion; } diff --git a/scripts/export_report.pl b/scripts/export_report.pl new file mode 100644 index 0000000..9ed00d9 --- /dev/null +++ b/scripts/export_report.pl @@ -0,0 +1,169 @@ +#!/usr/bin/perl -w +# +# (C) Copyright IBM Corporation 2006. +# Released under GPL v2. +# Author : Ram Pai (linuxram@us.ibm.com) +# +# Usage: export_report.pl -k Module.symvers [-o report_file ] -f *.mod.c +# + +use Getopt::Std; +use strict; + +sub numerically { + my $no1 = (split /\s+/, $a)[1]; + my $no2 = (split /\s+/, $b)[1]; + return $no1 <=> $no2; +} + +sub alphabetically { + my ($module1, $value1) = @{$a}; + my ($module2, $value2) = @{$b}; + return $value1 <=> $value2 || $module2 cmp $module1; +} + +sub print_depends_on { + my ($href) = @_; + print "\n"; + while (my ($mod, $list) = each %$href) { + print "\t$mod:\n"; + foreach my $sym (sort numerically @{$list}) { + my ($symbol, $no) = split /\s+/, $sym; + printf("\t\t%-25s\t%-25d\n", $symbol, $no); + } + print "\n"; + } + print "\n"; + print "~"x80 , "\n"; +} + +sub usage { + print "Usage: @_ -h -k Module.symvers [ -o outputfile ] \n", + "\t-f: treat all the non-option argument as .mod.c files. ", + "Recommend using this as the last option\n", + "\t-h: print detailed help\n", + "\t-k: the path to Module.symvers file. By default uses ", + "the file from the current directory\n", + "\t-o outputfile: output the report to outputfile\n"; + exit 0; +} + +sub collectcfiles { + my @file = `cat .tmp_versions/*.mod | grep '.*\.ko\$'`; + @file = grep {s/\.ko/.mod.c/} @file; + chomp @file; + return @file; +} + +my (%SYMBOL, %MODULE, %opt, @allcfiles); + +if (not getopts('hk:o:f',\%opt) or defined $opt{'h'}) { + usage($0); +} + +if (defined $opt{'f'}) { + @allcfiles = @ARGV; +} else { + @allcfiles = collectcfiles(); +} + +if (not defined $opt{'k'}) { + $opt{'k'} = "Module.symvers"; +} + +unless (open(MODULE_SYMVERS, $opt{'k'})) { + die "Sorry, cannot open $opt{'k'}: $!\n"; +} + +if (defined $opt{'o'}) { + unless (open(OUTPUT_HANDLE, ">$opt{'o'}")) { + die "Sorry, cannot open $opt{'o'} $!\n"; + } + select OUTPUT_HANDLE; +} +# +# collect all the symbols and their attributes from the +# Module.symvers file +# +while ( ) { + chomp; + my (undef, $symbol, $module, $gpl) = split; + $SYMBOL { $symbol } = [ $module , "0" , $symbol, $gpl]; +} +close(MODULE_SYMVERS); + +# +# collect the usage count of each symbol. +# +foreach my $thismod (@allcfiles) { + unless (open(MODULE_MODULE, $thismod)) { + print "Sorry, cannot open $thismod: $!\n"; + next; + } + my $state=0; + while ( ) { + chomp; + if ($state eq 0) { + $state = 1 if ($_ =~ /static const struct modversion_info/); + next; + } + if ($state eq 1) { + $state = 2 if ($_ =~ /__attribute__\(\(section\("__versions"\)\)\)/); + next; + } + if ($state eq 2) { + if ( $_ !~ /0x[0-9a-f]{7,8},/ ) { + next; + } + my $sym = (split /([,"])/,)[4]; + my ($module, $value, $symbol, $gpl) = @{$SYMBOL{$sym}}; + $SYMBOL{ $sym } = [ $module, $value+1, $symbol, $gpl]; + push(@{$MODULE{$thismod}} , $sym); + } + } + if ($state ne 2) { + print "WARNING:$thismod is not built with CONFIG_MODVERSION enabled\n"; + } + close(MODULE_MODULE); +} + +print "\tThis file reports the exported symbols usage patterns by in-tree\n", + "\t\t\t\tmodules\n"; +printf("%s\n\n\n","x"x80); +printf("\t\t\t\tINDEX\n\n\n"); +printf("SECTION 1: Usage counts of all exported symbols\n"); +printf("SECTION 2: List of modules and the exported symbols they use\n"); +printf("%s\n\n\n","x"x80); +printf("SECTION 1:\tThe exported symbols and their usage count\n\n"); +printf("%-25s\t%-25s\t%-5s\t%-25s\n", "Symbol", "Module", "Usage count", + "export type"); + +# +# print the list of unused exported symbols +# +foreach my $list (sort alphabetically values(%SYMBOL)) { + my ($module, $value, $symbol, $gpl) = @{$list}; + printf("%-25s\t%-25s\t%-10s\t", $symbol, $module, $value); + if (defined $gpl) { + printf("%-25s\n",$gpl); + } else { + printf("\n"); + } +} +printf("%s\n\n\n","x"x80); + +printf("SECTION 2:\n\tThis section reports export-symbol-usage of in-kernel +modules. Each module lists the modules, and the symbols from that module that +it uses. Each listed symbol reports the number of modules using it\n"); + +print "~"x80 , "\n"; +while (my ($thismod, $list) = each %MODULE) { + my %depends; + $thismod =~ s/\.mod\.c/.ko/; + print "\t\t\t$thismod\n"; + foreach my $symbol (@{$list}) { + my ($module, $value, undef, $gpl) = @{$SYMBOL{$symbol}}; + push (@{$depends{"$module"}}, "$symbol $value"); + } + print_depends_on(\%depends); +} diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c index 5b0344e..b038182 100644 --- a/scripts/genksyms/genksyms.c +++ b/scripts/genksyms/genksyms.c @@ -42,7 +42,7 @@ static FILE *debugfile; int cur_line = 1; char *cur_filename; -static int flag_debug, flag_dump_defs, flag_warnings; +static int flag_debug, flag_dump_defs, flag_dump_types, flag_warnings; static const char *arch = ""; static const char *mod_prefix = ""; @@ -50,6 +50,7 @@ static int errors; static int nsyms; static struct symbol *expansion_trail; +static struct symbol *visited_symbols; static const char *const symbol_type_name[] = { "normal", "typedef", "enum", "struct", "union" @@ -176,6 +177,7 @@ struct symbol *add_symbol(const char *na sym->type = type; sym->defn = defn; sym->expansion_trail = NULL; + sym->visited = NULL; sym->is_extern = is_extern; sym->hash_next = symtab[h]; @@ -236,26 +238,11 @@ static int equal_list(struct string_list static void print_node(FILE * f, struct string_list *list) { - switch (list->tag) { - case SYM_STRUCT: - putc('s', f); - goto printit; - case SYM_UNION: - putc('u', f); - goto printit; - case SYM_ENUM: - putc('e', f); - goto printit; - case SYM_TYPEDEF: - putc('t', f); - goto printit; - - printit: + if (list->tag != SYM_NORMAL) { + putc(symbol_type_name[list->tag][0], f); putc('#', f); - case SYM_NORMAL: - fputs(list->string, f); - break; } + fputs(list->string, f); } static void print_list(FILE * f, struct string_list *list) @@ -287,9 +274,9 @@ static void print_list(FILE * f, struct } } -static unsigned long expand_and_crc_list(struct string_list *list, - unsigned long crc) +static unsigned long expand_and_crc_sym(struct symbol *sym, unsigned long crc) { + struct string_list *list = sym->defn; struct string_list **e, **b; struct string_list *tmp, **tmp2; int elem = 1; @@ -332,7 +319,7 @@ static unsigned long expand_and_crc_list } else { subsym->expansion_trail = expansion_trail; expansion_trail = subsym; - crc = expand_and_crc_list(subsym->defn, crc); + crc = expand_and_crc_sym(subsym, crc); } break; @@ -382,12 +369,22 @@ static unsigned long expand_and_crc_list } else { subsym->expansion_trail = expansion_trail; expansion_trail = subsym; - crc = expand_and_crc_list(subsym->defn, crc); + crc = expand_and_crc_sym(subsym, crc); } break; } } + { + static struct symbol **end = &visited_symbols; + + if (!sym->visited) { + *end = sym; + end = &sym->visited; + sym->visited = (struct symbol *)-1L; + } + } + return crc; } @@ -406,7 +403,7 @@ void export_symbol(const char *name) expansion_trail = (struct symbol *)-1L; - crc = expand_and_crc_list(sym->defn, 0xffffffff) ^ 0xffffffff; + crc = expand_and_crc_sym(sym, 0xffffffff) ^ 0xffffffff; sym = expansion_trail; while (sym != (struct symbol *)-1L) { @@ -464,6 +461,7 @@ #endif /* __GNU_LIBRARY__ */ int main(int argc, char **argv) { + FILE *dumpfile = NULL; int o; #ifdef __GNU_LIBRARY__ @@ -473,15 +471,16 @@ #ifdef __GNU_LIBRARY__ {"warnings", 0, 0, 'w'}, {"quiet", 0, 0, 'q'}, {"dump", 0, 0, 'D'}, + {"dump-types", 1, 0, 'T'}, {"version", 0, 0, 'V'}, {"help", 0, 0, 'h'}, {0, 0, 0, 0} }; - while ((o = getopt_long(argc, argv, "a:dwqVDk:p:", + while ((o = getopt_long(argc, argv, "a:dwqVDT:k:p:", &long_opts[0], NULL)) != EOF) #else /* __GNU_LIBRARY__ */ - while ((o = getopt(argc, argv, "a:dwqVDk:p:")) != EOF) + while ((o = getopt(argc, argv, "a:dwqVDT:k:p:")) != EOF) #endif /* __GNU_LIBRARY__ */ switch (o) { case 'a': @@ -502,6 +501,14 @@ #endif /* __GNU_LIBRARY__ */ case 'D': flag_dump_defs = 1; break; + case 'T': + flag_dump_types = 1; + dumpfile = fopen(optarg, "w"); + if (!dumpfile) { + perror(optarg); + return 1; + } + break; case 'h': genksyms_usage(); return 0; @@ -524,6 +531,24 @@ #endif /* __GNU_LIBRARY__ */ yyparse(); + if (flag_dump_types && visited_symbols) { + while (visited_symbols != (struct symbol *)-1L) { + struct symbol *sym = visited_symbols; + + if (sym->type != SYM_NORMAL) { + putc(symbol_type_name[sym->type][0], dumpfile); + putc('#', dumpfile); + } + fputs(sym->name, dumpfile); + putc(' ', dumpfile); + print_list(dumpfile, sym->defn); + putc('\n', dumpfile); + + visited_symbols = sym->visited; + sym->visited = NULL; + } + } + if (flag_debug) { fprintf(debugfile, "Hash table occupancy %d/%d = %g\n", nsyms, HASH_BUCKETS, diff --git a/scripts/genksyms/genksyms.h b/scripts/genksyms/genksyms.h index ab6f34f..2668287 100644 --- a/scripts/genksyms/genksyms.h +++ b/scripts/genksyms/genksyms.h @@ -41,6 +41,7 @@ struct symbol { enum symbol_type type; struct string_list *defn; struct symbol *expansion_trail; + struct symbol *visited; int is_extern; }; diff --git a/scripts/genksyms/lex.c_shipped b/scripts/genksyms/lex.c_shipped index 1218053..37ba982 100644 --- a/scripts/genksyms/lex.c_shipped +++ b/scripts/genksyms/lex.c_shipped @@ -2023,7 +2023,7 @@ repeat: break; default: - abort(); + exit(1); } fini: diff --git a/scripts/genksyms/lex.l b/scripts/genksyms/lex.l index fe0dfee..5e544a0 100644 --- a/scripts/genksyms/lex.l +++ b/scripts/genksyms/lex.l @@ -392,7 +392,7 @@ repeat: break; default: - abort(); + exit(1); } fini: diff --git a/scripts/hdrcheck.sh b/scripts/hdrcheck.sh new file mode 100755 index 0000000..b3bb683 --- /dev/null +++ b/scripts/hdrcheck.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +for FILE in `grep '^#include <' $2 | cut -f2 -d\< | cut -f1 -d\> | egrep ^linux\|^asm` ; do + if [ ! -r $1/$FILE ]; then + echo $2 requires $FILE, which does not exist + exit 1 + fi +done diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 8012d10..4dcb886 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -539,6 +539,7 @@ int main(int ac, char **av) name = av[i]; if (!name) { printf(_("%s: Kconfig file missing\n"), av[0]); + exit(1); } conf_parse(name); //zconfdump(stdout); @@ -573,7 +574,7 @@ int main(int ac, char **av) case set_random: name = getenv("KCONFIG_ALLCONFIG"); if (name && !stat(name, &tmpstat)) { - conf_read_simple(name); + conf_read_simple(name, S_DEF_USER); break; } switch (input_mode) { @@ -584,9 +585,9 @@ int main(int ac, char **av) default: break; } if (!stat(name, &tmpstat)) - conf_read_simple(name); + conf_read_simple(name, S_DEF_USER); else if (!stat("all.config", &tmpstat)) - conf_read_simple("all.config"); + conf_read_simple("all.config", S_DEF_USER); break; default: break; @@ -599,7 +600,15 @@ int main(int ac, char **av) input_mode = ask_silent; valid_stdin = 1; } - } + } else if (sym_change_count) { + name = getenv("KCONFIG_NOSILENTUPDATE"); + if (name && *name) { + fprintf(stderr, _("\n*** Kernel configuration requires explicit update.\n\n")); + return 1; + } + } else + goto skip_check; + do { conf_cnt = 0; check_conf(&rootmenu); @@ -608,5 +617,11 @@ int main(int ac, char **av) fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n")); return 1; } +skip_check: + if (input_mode == ask_silent && conf_write_autoconf()) { + fprintf(stderr, _("\n*** Error during writing of the kernel configuration.\n\n")); + return 1; + } + return 0; } diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 1b5df58..2ee48c3 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -20,19 +21,8 @@ static void conf_warning(const char *fmt static const char *conf_filename; static int conf_lineno, conf_warnings, conf_unsaved; -const char conf_def_filename[] = ".config"; - const char conf_defname[] = "arch/$ARCH/defconfig"; -const char *conf_confnames[] = { - ".config", - "/lib/modules/$UNAME_RELEASE/.config", - "/etc/kernel-config", - "/boot/config-$UNAME_RELEASE", - conf_defname, - NULL, -}; - static void conf_warning(const char *fmt, ...) { va_list ap; @@ -44,6 +34,13 @@ static void conf_warning(const char *fmt conf_warnings++; } +const char *conf_get_configname(void) +{ + char *name = getenv("KCONFIG_CONFIG"); + + return name ? name : ".config"; +} + static char *conf_expand_value(const char *in) { struct symbol *sym; @@ -86,51 +83,65 @@ char *conf_get_default_confname(void) return name; } -int conf_read_simple(const char *name) +int conf_read_simple(const char *name, int def) { FILE *in = NULL; char line[1024]; char *p, *p2; struct symbol *sym; - int i; + int i, def_flags; if (name) { in = zconf_fopen(name); } else { - const char **names = conf_confnames; - while ((name = *names++)) { - name = conf_expand_value(name); + struct property *prop; + + name = conf_get_configname(); + in = zconf_fopen(name); + if (in) + goto load; + sym_change_count++; + if (!sym_defconfig_list) + return 1; + + for_all_defaults(sym_defconfig_list, prop) { + if (expr_calc_value(prop->visible.expr) == no || + prop->expr->type != E_SYMBOL) + continue; + name = conf_expand_value(prop->expr->left.sym->name); in = zconf_fopen(name); if (in) { printf(_("#\n" - "# using defaults found in %s\n" - "#\n"), name); - break; + "# using defaults found in %s\n" + "#\n"), name); + goto load; } } } if (!in) return 1; +load: conf_filename = name; conf_lineno = 0; conf_warnings = 0; conf_unsaved = 0; + def_flags = SYMBOL_DEF << def; for_all_symbols(i, sym) { - sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED; + sym->flags |= SYMBOL_CHANGED; + sym->flags &= ~(def_flags|SYMBOL_VALID); if (sym_is_choice(sym)) - sym->flags &= ~SYMBOL_NEW; - sym->flags &= ~SYMBOL_VALID; + sym->flags |= def_flags; switch (sym->type) { case S_INT: case S_HEX: case S_STRING: - if (sym->user.val) - free(sym->user.val); + if (sym->def[def].val) + free(sym->def[def].val); default: - sym->user.val = NULL; - sym->user.tri = no; + sym->def[def].val = NULL; + sym->def[def].tri = no; } } @@ -147,19 +158,26 @@ int conf_read_simple(const char *name) *p++ = 0; if (strncmp(p, "is not set", 10)) continue; - sym = sym_find(line + 9); - if (!sym) { - conf_warning("trying to assign nonexistent symbol %s", line + 9); - break; - } else if (!(sym->flags & SYMBOL_NEW)) { + if (def == S_DEF_USER) { + sym = sym_find(line + 9); + if (!sym) { + conf_warning("trying to assign nonexistent symbol %s", line + 9); + break; + } + } else { + sym = sym_lookup(line + 9, 0); + if (sym->type == S_UNKNOWN) + sym->type = S_BOOLEAN; + } + if (sym->flags & def_flags) { conf_warning("trying to reassign symbol %s", sym->name); break; } switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: - sym->user.tri = no; - sym->flags &= ~SYMBOL_NEW; + sym->def[def].tri = no; + sym->flags |= def_flags; break; default: ; @@ -177,34 +195,48 @@ int conf_read_simple(const char *name) p2 = strchr(p, '\n'); if (p2) *p2 = 0; - sym = sym_find(line + 7); - if (!sym) { - conf_warning("trying to assign nonexistent symbol %s", line + 7); - break; - } else if (!(sym->flags & SYMBOL_NEW)) { + if (def == S_DEF_USER) { + sym = sym_find(line + 7); + if (!sym) { + conf_warning("trying to assign nonexistent symbol %s", line + 7); + break; + } + } else { + sym = sym_lookup(line + 7, 0); + if (sym->type == S_UNKNOWN) + sym->type = S_OTHER; + } + if (sym->flags & def_flags) { conf_warning("trying to reassign symbol %s", sym->name); break; } switch (sym->type) { case S_TRISTATE: if (p[0] == 'm') { - sym->user.tri = mod; - sym->flags &= ~SYMBOL_NEW; + sym->def[def].tri = mod; + sym->flags |= def_flags; break; } case S_BOOLEAN: if (p[0] == 'y') { - sym->user.tri = yes; - sym->flags &= ~SYMBOL_NEW; + sym->def[def].tri = yes; + sym->flags |= def_flags; break; } if (p[0] == 'n') { - sym->user.tri = no; - sym->flags &= ~SYMBOL_NEW; + sym->def[def].tri = no; + sym->flags |= def_flags; break; } conf_warning("symbol value '%s' invalid for %s", p, sym->name); break; + case S_OTHER: + if (*p != '"') { + for (p2 = p; *p2 && !isspace(*p2); p2++) + ; + sym->type = S_STRING; + goto done; + } case S_STRING: if (*p++ != '"') break; @@ -221,9 +253,10 @@ int conf_read_simple(const char *name) } case S_INT: case S_HEX: + done: if (sym_string_valid(sym, p)) { - sym->user.val = strdup(p); - sym->flags &= ~SYMBOL_NEW; + sym->def[def].val = strdup(p); + sym->flags |= def_flags; } else { conf_warning("symbol value '%s' invalid for %s", p, sym->name); continue; @@ -241,24 +274,24 @@ int conf_read_simple(const char *name) } if (sym && sym_is_choice_value(sym)) { struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym)); - switch (sym->user.tri) { + switch (sym->def[def].tri) { case no: break; case mod: - if (cs->user.tri == yes) { + if (cs->def[def].tri == yes) { conf_warning("%s creates inconsistent choice state", sym->name); - cs->flags |= SYMBOL_NEW; + cs->flags &= ~def_flags; } break; case yes: - if (cs->user.tri != no) { + if (cs->def[def].tri != no) { conf_warning("%s creates inconsistent choice state", sym->name); - cs->flags |= SYMBOL_NEW; + cs->flags &= ~def_flags; } else - cs->user.val = sym; + cs->def[def].val = sym; break; } - cs->user.tri = E_OR(cs->user.tri, sym->user.tri); + cs->def[def].tri = E_OR(cs->def[def].tri, sym->def[def].tri); } } fclose(in); @@ -273,9 +306,11 @@ int conf_read(const char *name) struct symbol *sym; struct property *prop; struct expr *e; - int i; + int i, flags; + + sym_change_count = 0; - if (conf_read_simple(name)) + if (conf_read_simple(name, S_DEF_USER)) return 1; for_all_symbols(i, sym) { @@ -287,12 +322,12 @@ int conf_read(const char *name) switch (sym->type) { case S_BOOLEAN: case S_TRISTATE: - if (sym->user.tri != sym_get_tristate_value(sym)) + if (sym->def[S_DEF_USER].tri != sym_get_tristate_value(sym)) break; if (!sym_is_choice(sym)) goto sym_ok; default: - if (!strcmp(sym->curr.val, sym->user.val)) + if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val)) goto sym_ok; break; } @@ -304,15 +339,13 @@ int conf_read(const char *name) sym_ok: if (sym_has_value(sym) && !sym_is_choice_value(sym)) { if (sym->visible == no) - sym->flags |= SYMBOL_NEW; + sym->flags &= ~SYMBOL_DEF_USER; switch (sym->type) { case S_STRING: case S_INT: case S_HEX: - if (!sym_string_within_range(sym, sym->user.val)) { - sym->flags |= SYMBOL_NEW; - sym->flags &= ~SYMBOL_VALID; - } + if (!sym_string_within_range(sym, sym->def[S_DEF_USER].val)) + sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER); default: break; } @@ -320,19 +353,21 @@ int conf_read(const char *name) if (!sym_is_choice(sym)) continue; prop = sym_get_choice_prop(sym); + flags = sym->flags; for (e = prop->expr; e; e = e->left.expr) if (e->right.sym->visible != no) - sym->flags |= e->right.sym->flags & SYMBOL_NEW; + flags &= e->right.sym->flags; + sym->flags |= flags & SYMBOL_DEF_USER; } - sym_change_count = conf_warnings || conf_unsaved; + sym_change_count += conf_warnings || conf_unsaved; return 0; } int conf_write(const char *name) { - FILE *out, *out_h; + FILE *out; struct symbol *sym; struct menu *menu; const char *basename; @@ -351,7 +386,7 @@ int conf_write(const char *name) if (!stat(name, &st) && S_ISDIR(st.st_mode)) { strcpy(dirname, name); strcat(dirname, "/"); - basename = conf_def_filename; + basename = conf_get_configname(); } else if ((slash = strrchr(name, '/'))) { int size = slash - name + 1; memcpy(dirname, name, size); @@ -359,23 +394,24 @@ int conf_write(const char *name) if (slash[1]) basename = slash + 1; else - basename = conf_def_filename; + basename = conf_get_configname(); } else basename = name; } else - basename = conf_def_filename; + basename = conf_get_configname(); - sprintf(newname, "%s.tmpconfig.%d", dirname, (int)getpid()); - out = fopen(newname, "w"); + sprintf(newname, "%s%s", dirname, basename); + env = getenv("KCONFIG_OVERWRITECONFIG"); + if (!env || !*env) { + sprintf(tmpname, "%s.tmpconfig.%d", dirname, (int)getpid()); + out = fopen(tmpname, "w"); + } else { + *tmpname = 0; + out = fopen(newname, "w"); + } if (!out) return 1; - out_h = NULL; - if (!name) { - out_h = fopen(".tmpconfig.h", "w"); - if (!out_h) - return 1; - file_write_dep(NULL); - } + sym = sym_lookup("KERNELVERSION", 0); sym_calc_value(sym); time(&now); @@ -391,16 +427,6 @@ int conf_write(const char *name) sym_get_string_value(sym), use_timestamp ? "# " : "", use_timestamp ? ctime(&now) : ""); - if (out_h) - fprintf(out_h, "/*\n" - " * Automatically generated C config: don't edit\n" - " * Linux kernel version: %s\n" - "%s%s" - " */\n" - "#define AUTOCONF_INCLUDED\n", - sym_get_string_value(sym), - use_timestamp ? " * " : "", - use_timestamp ? ctime(&now) : ""); if (!sym_change_count) sym_clear_all_valid(); @@ -416,11 +442,6 @@ int conf_write(const char *name) "#\n" "# %s\n" "#\n", str); - if (out_h) - fprintf(out_h, "\n" - "/*\n" - " * %s\n" - " */\n", str); } else if (!(sym->flags & SYMBOL_CHOICE)) { sym_calc_value(sym); if (!(sym->flags & SYMBOL_WRITE)) @@ -438,59 +459,39 @@ int conf_write(const char *name) switch (sym_get_tristate_value(sym)) { case no: fprintf(out, "# CONFIG_%s is not set\n", sym->name); - if (out_h) - fprintf(out_h, "#undef CONFIG_%s\n", sym->name); break; case mod: fprintf(out, "CONFIG_%s=m\n", sym->name); - if (out_h) - fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name); break; case yes: fprintf(out, "CONFIG_%s=y\n", sym->name); - if (out_h) - fprintf(out_h, "#define CONFIG_%s 1\n", sym->name); break; } break; case S_STRING: - // fix me str = sym_get_string_value(sym); fprintf(out, "CONFIG_%s=\"", sym->name); - if (out_h) - fprintf(out_h, "#define CONFIG_%s \"", sym->name); - do { + while (1) { l = strcspn(str, "\"\\"); if (l) { fwrite(str, l, 1, out); - if (out_h) - fwrite(str, l, 1, out_h); - } - str += l; - while (*str == '\\' || *str == '"') { - fprintf(out, "\\%c", *str); - if (out_h) - fprintf(out_h, "\\%c", *str); - str++; + str += l; } - } while (*str); + if (!*str) + break; + fprintf(out, "\\%c", *str++); + } fputs("\"\n", out); - if (out_h) - fputs("\"\n", out_h); break; case S_HEX: str = sym_get_string_value(sym); if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { fprintf(out, "CONFIG_%s=%s\n", sym->name, str); - if (out_h) - fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str); break; } case S_INT: str = sym_get_string_value(sym); fprintf(out, "CONFIG_%s=%s\n", sym->name, str); - if (out_h) - fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str); break; } } @@ -510,21 +511,253 @@ int conf_write(const char *name) } } fclose(out); - if (out_h) { - fclose(out_h); - rename(".tmpconfig.h", "include/linux/autoconf.h"); + + if (*tmpname) { + strcat(dirname, name ? name : conf_get_configname()); + strcat(dirname, ".old"); + rename(newname, dirname); + if (rename(tmpname, newname)) + return 1; } - if (!name || basename != conf_def_filename) { - if (!name) - name = conf_def_filename; - sprintf(tmpname, "%s.old", name); - rename(name, tmpname); + + printf(_("#\n" + "# configuration written to %s\n" + "#\n"), newname); + + sym_change_count = 0; + + return 0; +} + +int conf_split_config(void) +{ + char *name, path[128]; + char *s, *d, c; + struct symbol *sym; + struct stat sb; + int res, i, fd; + + name = getenv("KCONFIG_AUTOCONFIG"); + if (!name) + name = "include/config/auto.conf"; + conf_read_simple(name, S_DEF_AUTO); + + if (chdir("include/config")) + return 1; + + res = 0; + for_all_symbols(i, sym) { + sym_calc_value(sym); + if ((sym->flags & SYMBOL_AUTO) || !sym->name) + continue; + if (sym->flags & SYMBOL_WRITE) { + if (sym->flags & SYMBOL_DEF_AUTO) { + /* + * symbol has old and new value, + * so compare them... + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == + sym->def[S_DEF_AUTO].tri) + continue; + break; + case S_STRING: + case S_HEX: + case S_INT: + if (!strcmp(sym_get_string_value(sym), + sym->def[S_DEF_AUTO].val)) + continue; + break; + default: + break; + } + } else { + /* + * If there is no old value, only 'no' (unset) + * is allowed as new value. + */ + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + if (sym_get_tristate_value(sym) == no) + continue; + break; + default: + break; + } + } + } else if (!(sym->flags & SYMBOL_DEF_AUTO)) + /* There is neither an old nor a new value. */ + continue; + /* else + * There is an old value, but no new value ('no' (unset) + * isn't saved in auto.conf, so the old value is always + * different from 'no'). + */ + + /* Replace all '_' and append ".h" */ + s = sym->name; + d = path; + while ((c = *s++)) { + c = tolower(c); + *d++ = (c == '_') ? '/' : c; + } + strcpy(d, ".h"); + + /* Assume directory path already exists. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + if (errno != ENOENT) { + res = 1; + break; + } + /* + * Create directory components, + * unless they exist already. + */ + d = path; + while ((d = strchr(d, '/'))) { + *d = 0; + if (stat(path, &sb) && mkdir(path, 0755)) { + res = 1; + goto out; + } + *d++ = '/'; + } + /* Try it again. */ + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd == -1) { + res = 1; + break; + } + } + close(fd); } - sprintf(tmpname, "%s%s", dirname, basename); - if (rename(newname, tmpname)) +out: + if (chdir("../..")) return 1; - sym_change_count = 0; + return res; +} + +int conf_write_autoconf(void) +{ + struct symbol *sym; + const char *str; + char *name; + FILE *out, *out_h; + time_t now; + int i, l; + + sym_clear_all_valid(); + + file_write_dep("include/config/auto.conf.cmd"); + + if (conf_split_config()) + return 1; + + out = fopen(".tmpconfig", "w"); + if (!out) + return 1; + + out_h = fopen(".tmpconfig.h", "w"); + if (!out_h) { + fclose(out); + return 1; + } + + sym = sym_lookup("KERNELVERSION", 0); + sym_calc_value(sym); + time(&now); + fprintf(out, "#\n" + "# Automatically generated make config: don't edit\n" + "# Linux kernel version: %s\n" + "# %s" + "#\n", + sym_get_string_value(sym), ctime(&now)); + fprintf(out_h, "/*\n" + " * Automatically generated C config: don't edit\n" + " * Linux kernel version: %s\n" + " * %s" + " */\n" + "#define AUTOCONF_INCLUDED\n", + sym_get_string_value(sym), ctime(&now)); + + for_all_symbols(i, sym) { + sym_calc_value(sym); + if (!(sym->flags & SYMBOL_WRITE) || !sym->name) + continue; + switch (sym->type) { + case S_BOOLEAN: + case S_TRISTATE: + switch (sym_get_tristate_value(sym)) { + case no: + break; + case mod: + fprintf(out, "CONFIG_%s=m\n", sym->name); + fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name); + break; + case yes: + fprintf(out, "CONFIG_%s=y\n", sym->name); + fprintf(out_h, "#define CONFIG_%s 1\n", sym->name); + break; + } + break; + case S_STRING: + str = sym_get_string_value(sym); + fprintf(out, "CONFIG_%s=\"", sym->name); + fprintf(out_h, "#define CONFIG_%s \"", sym->name); + while (1) { + l = strcspn(str, "\"\\"); + if (l) { + fwrite(str, l, 1, out); + fwrite(str, l, 1, out_h); + str += l; + } + if (!*str) + break; + fprintf(out, "\\%c", *str); + fprintf(out_h, "\\%c", *str); + str++; + } + fputs("\"\n", out); + fputs("\"\n", out_h); + break; + case S_HEX: + str = sym_get_string_value(sym); + if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) { + fprintf(out, "CONFIG_%s=%s\n", sym->name, str); + fprintf(out_h, "#define CONFIG_%s 0x%s\n", sym->name, str); + break; + } + case S_INT: + str = sym_get_string_value(sym); + fprintf(out, "CONFIG_%s=%s\n", sym->name, str); + fprintf(out_h, "#define CONFIG_%s %s\n", sym->name, str); + break; + default: + break; + } + } + fclose(out); + fclose(out_h); + + name = getenv("KCONFIG_AUTOHEADER"); + if (!name) + name = "include/linux/autoconf.h"; + if (rename(".tmpconfig.h", name)) + return 1; + name = getenv("KCONFIG_AUTOCONFIG"); + if (!name) + name = "include/config/auto.conf"; + /* + * This must be the last step, kbuild has a dependency on auto.conf + * and this marks the successful completion of the previous steps. + */ + if (rename(".tmpconfig", name)) + return 1; return 0; } diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index 30e4f9d..6f98dbf 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -145,7 +145,8 @@ static void __expr_eliminate_eq(enum exp return; } if (e1->type == E_SYMBOL && e2->type == E_SYMBOL && - e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO))) + e1->left.sym == e2->left.sym && + (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no)) return; if (!expr_eq(e1, e2)) return; @@ -1012,73 +1013,73 @@ #else #endif } -void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken) +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken) { if (!e) { - fn(data, "y"); + fn(data, NULL, "y"); return; } if (expr_compare_type(prevtoken, e->type) > 0) - fn(data, "("); + fn(data, NULL, "("); switch (e->type) { case E_SYMBOL: if (e->left.sym->name) - fn(data, e->left.sym->name); + fn(data, e->left.sym, e->left.sym->name); else - fn(data, ""); + fn(data, NULL, ""); break; case E_NOT: - fn(data, "!"); + fn(data, NULL, "!"); expr_print(e->left.expr, fn, data, E_NOT); break; case E_EQUAL: - fn(data, e->left.sym->name); - fn(data, "="); - fn(data, e->right.sym->name); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, "="); + fn(data, e->right.sym, e->right.sym->name); break; case E_UNEQUAL: - fn(data, e->left.sym->name); - fn(data, "!="); - fn(data, e->right.sym->name); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, "!="); + fn(data, e->right.sym, e->right.sym->name); break; case E_OR: expr_print(e->left.expr, fn, data, E_OR); - fn(data, " || "); + fn(data, NULL, " || "); expr_print(e->right.expr, fn, data, E_OR); break; case E_AND: expr_print(e->left.expr, fn, data, E_AND); - fn(data, " && "); + fn(data, NULL, " && "); expr_print(e->right.expr, fn, data, E_AND); break; case E_CHOICE: - fn(data, e->right.sym->name); + fn(data, e->right.sym, e->right.sym->name); if (e->left.expr) { - fn(data, " ^ "); + fn(data, NULL, " ^ "); expr_print(e->left.expr, fn, data, E_CHOICE); } break; case E_RANGE: - fn(data, "["); - fn(data, e->left.sym->name); - fn(data, " "); - fn(data, e->right.sym->name); - fn(data, "]"); + fn(data, NULL, "["); + fn(data, e->left.sym, e->left.sym->name); + fn(data, NULL, " "); + fn(data, e->right.sym, e->right.sym->name); + fn(data, NULL, "]"); break; default: { char buf[32]; sprintf(buf, "", e->type); - fn(data, buf); + fn(data, NULL, buf); break; } } if (expr_compare_type(prevtoken, e->type) > 0) - fn(data, ")"); + fn(data, NULL, ")"); } -static void expr_print_file_helper(void *data, const char *str) +static void expr_print_file_helper(void *data, struct symbol *sym, const char *str) { fwrite(str, strlen(str), 1, data); } @@ -1088,7 +1089,7 @@ void expr_fprint(struct expr *e, FILE *o expr_print(e, expr_print_file_helper, out, E_NONE); } -static void expr_print_gstr_helper(void *data, const char *str) +static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str) { str_append((struct gstr*)data, str); } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 1b36ef1..6084525 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -63,12 +63,18 @@ enum symbol_type { S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER }; +enum { + S_DEF_USER, /* main user value */ + S_DEF_AUTO, +}; + struct symbol { struct symbol *next; char *name; char *help; enum symbol_type type; - struct symbol_value curr, user; + struct symbol_value curr; + struct symbol_value def[4]; tristate visible; int flags; struct property *prop; @@ -78,10 +84,7 @@ struct symbol { #define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER) -#define SYMBOL_YES 0x0001 -#define SYMBOL_MOD 0x0002 -#define SYMBOL_NO 0x0004 -#define SYMBOL_CONST 0x0007 +#define SYMBOL_CONST 0x0001 #define SYMBOL_CHECK 0x0008 #define SYMBOL_CHOICE 0x0010 #define SYMBOL_CHOICEVAL 0x0020 @@ -90,10 +93,14 @@ #define SYMBOL_VALID 0x0080 #define SYMBOL_OPTIONAL 0x0100 #define SYMBOL_WRITE 0x0200 #define SYMBOL_CHANGED 0x0400 -#define SYMBOL_NEW 0x0800 #define SYMBOL_AUTO 0x1000 #define SYMBOL_CHECKED 0x2000 #define SYMBOL_WARNED 0x8000 +#define SYMBOL_DEF 0x10000 +#define SYMBOL_DEF_USER 0x10000 +#define SYMBOL_DEF_AUTO 0x20000 +#define SYMBOL_DEF3 0x40000 +#define SYMBOL_DEF4 0x80000 #define SYMBOL_MAXLENGTH 256 #define SYMBOL_HASHSIZE 257 @@ -149,6 +156,7 @@ struct file *lookup_file(const char *nam extern struct symbol symbol_yes, symbol_no, symbol_mod; extern struct symbol *modules_sym; +extern struct symbol *sym_defconfig_list; extern int cdebug; struct expr *expr_alloc_symbol(struct symbol *sym); struct expr *expr_alloc_one(enum expr_type type, struct expr *ce); diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 665bd53..7b0d3a9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -114,12 +114,6 @@ const char *dbg_print_flags(int val) bzero(buf, 256); - if (val & SYMBOL_YES) - strcat(buf, "yes/"); - if (val & SYMBOL_MOD) - strcat(buf, "mod/"); - if (val & SYMBOL_NO) - strcat(buf, "no/"); if (val & SYMBOL_CONST) strcat(buf, "const/"); if (val & SYMBOL_CHECK) @@ -138,8 +132,6 @@ const char *dbg_print_flags(int val) strcat(buf, "write/"); if (val & SYMBOL_CHANGED) strcat(buf, "changed/"); - if (val & SYMBOL_NEW) - strcat(buf, "new/"); if (val & SYMBOL_AUTO) strcat(buf, "auto/"); @@ -1192,9 +1184,7 @@ static gchar **fill_row(struct menu *men row[COL_OPTION] = g_strdup_printf("%s %s", menu_get_prompt(menu), - sym ? (sym-> - flags & SYMBOL_NEW ? "(NEW)" : "") : - ""); + sym && sym_has_value(sym) ? "(NEW)" : ""); if (show_all && !menu_is_visible(menu)) row[COL_COLOR] = g_strdup("DarkGray"); diff --git a/scripts/kconfig/lex.zconf.c_shipped b/scripts/kconfig/lex.zconf.c_shipped index 24e3c8c..800f8c7 100644 --- a/scripts/kconfig/lex.zconf.c_shipped +++ b/scripts/kconfig/lex.zconf.c_shipped @@ -8,7 +8,7 @@ #define YY_INT_ALIGNED short int #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 -#define YY_FLEX_SUBMINOR_VERSION 31 +#define YY_FLEX_SUBMINOR_VERSION 33 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif @@ -30,7 +30,15 @@ #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ -#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L +#if __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; @@ -134,6 +142,10 @@ #ifndef YY_BUF_SIZE #define YY_BUF_SIZE 16384 #endif +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; @@ -267,7 +279,7 @@ int zconfleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; -static int yy_init = 1; /* whether we need to initialize */ +static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow zconfwrap()'s to do buffer switches @@ -820,6 +832,8 @@ #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif +static int yy_init_globals (void ); + /* Macros after this point can all be overridden by user definitions in * section 1. */ @@ -942,9 +956,9 @@ YY_DECL int str = 0; int ts, i; - if ( (yy_init) ) + if ( !(yy_init) ) { - (yy_init) = 0; + (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; @@ -1452,7 +1466,7 @@ static int yy_get_next_buffer (void) else { - size_t num_to_read = + int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) @@ -1969,16 +1983,16 @@ YY_BUFFER_STATE zconf_scan_buffer (char /** Setup the input buffer state to scan a string. The next call to zconflex() will * scan from a @e copy of @a str. - * @param yy_str a NUL-terminated string to scan + * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * zconf_scan_bytes() instead. */ -YY_BUFFER_STATE zconf_scan_string (yyconst char * yy_str ) +YY_BUFFER_STATE zconf_scan_string (yyconst char * yystr ) { - return zconf_scan_bytes(yy_str,strlen(yy_str) ); + return zconf_scan_bytes(yystr,strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to zconflex() will @@ -1988,7 +2002,7 @@ YY_BUFFER_STATE zconf_scan_string (yycon * * @return the newly allocated buffer state object. */ -YY_BUFFER_STATE zconf_scan_bytes (yyconst char * bytes, int len ) +YY_BUFFER_STATE zconf_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; @@ -1996,15 +2010,15 @@ YY_BUFFER_STATE zconf_scan_bytes (yycon int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = len + 2; + n = _yybytes_len + 2; buf = (char *) zconfalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" ); - for ( i = 0; i < len; ++i ) - buf[i] = bytes[i]; + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; - buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = zconf_scan_buffer(buf,n ); if ( ! b ) @@ -2125,6 +2139,34 @@ void zconfset_debug (int bdebug ) zconf_flex_debug = bdebug ; } +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from zconflex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + zconfin = stdin; + zconfout = stdout; +#else + zconfin = (FILE *) 0; + zconfout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * zconflex_init() + */ + return 0; +} + /* zconflex_destroy is for both reentrant and non-reentrant scanners. */ int zconflex_destroy (void) { @@ -2140,6 +2182,10 @@ int zconflex_destroy (void) zconffree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * zconflex() is called, initialization will occur. */ + yy_init_globals( ); + return 0; } @@ -2151,7 +2197,7 @@ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { register int i; - for ( i = 0; i < n; ++i ) + for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif @@ -2160,7 +2206,7 @@ #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { register int n; - for ( n = 0; s[n]; ++n ) + for ( n = 0; s[n]; ++n ) ; return n; @@ -2191,19 +2237,6 @@ void zconffree (void * ptr ) #define YYTABLES_NAME "yytables" -#undef YY_NEW_FILE -#undef YY_FLUSH_BUFFER -#undef yy_set_bol -#undef yy_new_buffer -#undef yy_set_interactive -#undef yytext_ptr -#undef YY_DO_BEFORE_ACTION - -#ifdef YY_DECL_IS_OURS -#undef YY_DECL_IS_OURS -#undef YY_DECL -#endif - void zconf_starthelp(void) { new_string(); diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 527f60c..2628023 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -40,6 +40,10 @@ #define N_(text) (text) #define TF_COMMAND 0x0001 #define TF_PARAM 0x0002 +#define TF_OPTION 0x0004 + +#define T_OPT_MODULES 1 +#define T_OPT_DEFCONFIG_LIST 2 struct kconf_id { int name; @@ -60,8 +64,6 @@ int zconf_lineno(void); char *zconf_curname(void); /* confdata.c */ -extern const char conf_def_filename[]; - char *conf_get_default_confname(void); /* kconfig_load.c */ @@ -78,6 +80,7 @@ struct property *menu_add_prop(enum prop struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); +void menu_add_option(int token, char *arg); void menu_finalize(struct menu *parent); void menu_set_type(int type); @@ -99,6 +102,7 @@ const char *str_get(struct gstr *gs); /* symbol.c */ void sym_init(void); void sym_clear_all_valid(void); +void sym_set_all_changed(void); void sym_set_changed(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); struct property *prop_alloc(enum prop_type type, struct symbol *sym); @@ -137,7 +141,7 @@ static inline bool sym_is_optional(struc static inline bool sym_has_value(struct symbol *sym) { - return sym->flags & SYMBOL_NEW ? false : true; + return sym->flags & SYMBOL_DEF_USER ? true : false; } #ifdef __cplusplus diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index b6a389c..a263746 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -2,8 +2,9 @@ /* confdata.c */ P(conf_parse,void,(const char *name)); P(conf_read,int,(const char *name)); -P(conf_read_simple,int,(const char *name)); +P(conf_read_simple,int,(const char *name, int)); P(conf_write,int,(const char *name)); +P(conf_write_autoconf,int,(void)); /* menu.c */ P(rootmenu,struct menu,); @@ -38,4 +39,4 @@ P(prop_get_type_name,const char *,(enum /* expr.c */ P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); -P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)); +P(expr_print,void,(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)); diff --git a/scripts/kconfig/lxdialog/checklist.c b/scripts/kconfig/lxdialog/checklist.c index be0200e..7988641 100644 --- a/scripts/kconfig/lxdialog/checklist.c +++ b/scripts/kconfig/lxdialog/checklist.c @@ -187,9 +187,12 @@ int dialog_checklist(const char *title, /* Print the list */ for (i = 0; i < max_choice; i++) { - print_item(list, items[(scroll + i) * 3 + 1], - status[i + scroll], i, i == choice); + if (i != choice) + print_item(list, items[(scroll + i) * 3 + 1], + status[i + scroll], i, 0); } + print_item(list, items[(scroll + choice) * 3 + 1], + status[choice + scroll], choice, 1); print_arrows(dialog, choice, item_no, scroll, box_y, box_x + check_x + 5, list_height); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 0fce20c..c86c27f 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -114,7 +114,7 @@ void menu_set_type(int type) sym->type = type; return; } - menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n", + menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'", sym->name ? sym->name : "", sym_type_name(sym->type), sym_type_name(type)); } @@ -124,15 +124,20 @@ struct property *menu_add_prop(enum prop struct property *prop = prop_alloc(type, current_entry->sym); prop->menu = current_entry; - prop->text = prompt; prop->expr = expr; prop->visible.expr = menu_check_dep(dep); if (prompt) { + if (isspace(*prompt)) { + prop_warn(prop, "leading whitespace ignored"); + while (isspace(*prompt)) + prompt++; + } if (current_entry->prompt) - menu_warn(current_entry, "prompt redefined\n"); + prop_warn(prop, "prompt redefined"); current_entry->prompt = prop; } + prop->text = prompt; return prop; } @@ -152,6 +157,24 @@ void menu_add_symbol(enum prop_type type menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep); } +void menu_add_option(int token, char *arg) +{ + struct property *prop; + + switch (token) { + case T_OPT_MODULES: + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(current_entry->sym); + break; + case T_OPT_DEFCONFIG_LIST: + if (!sym_defconfig_list) + sym_defconfig_list = current_entry->sym; + else if (sym_defconfig_list != current_entry->sym) + zconf_error("trying to redefine defconfig symbol"); + break; + } +} + static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2) { return sym2->type == S_INT || sym2->type == S_HEX || @@ -325,11 +348,10 @@ void menu_finalize(struct menu *parent) if (sym && !(sym->flags & SYMBOL_WARNED)) { if (sym->type == S_UNKNOWN) - menu_warn(parent, "config symbol defined " - "without type\n"); + menu_warn(parent, "config symbol defined without type"); if (sym_is_choice(sym) && !parent->prompt) - menu_warn(parent, "choice must have a prompt\n"); + menu_warn(parent, "choice must have a prompt"); /* Check properties connected to this symbol */ sym_check_prop(sym); diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 4590cd3..393f374 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -6,16 +6,20 @@ #include #include #include +#include #include #include #include -#include +#include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -32,32 +36,16 @@ # define _ qgettext #endif static QApplication *configApp; +static ConfigSettings *configSettings; static inline QString qgettext(const char* str) { - return QString::fromLocal8Bit(gettext(str)); + return QString::fromLocal8Bit(gettext(str)); } static inline QString qgettext(const QString& str) { - return QString::fromLocal8Bit(gettext(str.latin1())); -} - -ConfigSettings::ConfigSettings() - : showAll(false), showName(false), showRange(false), showData(false) -{ -} - -#if QT_VERSION >= 300 -/** - * Reads the list column settings from the application settings. - */ -void ConfigSettings::readListSettings() -{ - showAll = readBoolEntry("/kconfig/qconf/showAll", false); - showName = readBoolEntry("/kconfig/qconf/showName", false); - showRange = readBoolEntry("/kconfig/qconf/showRange", false); - showData = readBoolEntry("/kconfig/qconf/showData", false); + return QString::fromLocal8Bit(gettext(str.latin1())); } /** @@ -88,76 +76,7 @@ bool ConfigSettings::writeSizes(const QS stringList.push_back(QString::number(*it)); return writeEntry(key, stringList); } -#endif - - -/* - * update all the children of a menu entry - * removes/adds the entries from the parent widget as necessary - * - * parent: either the menu list widget or a menu entry widget - * menu: entry to be updated - */ -template -void ConfigList::updateMenuList(P* parent, struct menu* menu) -{ - struct menu* child; - ConfigItem* item; - ConfigItem* last; - bool visible; - enum prop_type type; - if (!menu) { - while ((item = parent->firstChild())) - delete item; - return; - } - - last = parent->firstChild(); - if (last && !last->goParent) - last = 0; - for (child = menu->list; child; child = child->next) { - item = last ? last->nextSibling() : parent->firstChild(); - type = child->prompt ? child->prompt->type : P_UNKNOWN; - - switch (mode) { - case menuMode: - if (!(child->flags & MENU_ROOT)) - goto hide; - break; - case symbolMode: - if (child->flags & MENU_ROOT) - goto hide; - break; - default: - break; - } - - visible = menu_is_visible(child); - if (showAll || visible) { - if (!item || item->menu != child) - item = new ConfigItem(parent, last, child, visible); - else - item->testUpdateMenu(visible); - - if (mode == fullMode || mode == menuMode || type != P_MENU) - updateMenuList(item, child); - else - updateMenuList(item, 0); - last = item; - continue; - } - hide: - if (item && item->menu == child) { - last = parent->firstChild(); - if (last == item) - last = 0; - else while (last->nextSibling() != item) - last = last->nextSibling(); - delete item; - } - } -} #if QT_VERSION >= 300 /* @@ -355,6 +274,12 @@ ConfigItem::~ConfigItem(void) } } +ConfigLineEdit::ConfigLineEdit(ConfigView* parent) + : Parent(parent) +{ + connect(this, SIGNAL(lostFocus()), SLOT(hide())); +} + void ConfigLineEdit::show(ConfigItem* i) { item = i; @@ -385,14 +310,14 @@ void ConfigLineEdit::keyPressEvent(QKeyE hide(); } -ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings) - : Parent(p), cview(cv), +ConfigList::ConfigList(ConfigView* p, const char *name) + : Parent(p, name), updateAll(false), symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no), choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no), menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void), showAll(false), showName(false), showRange(false), showData(false), - rootEntry(0) + rootEntry(0), headerPopup(0) { int i; @@ -406,11 +331,14 @@ ConfigList::ConfigList(ConfigView* p, Co connect(this, SIGNAL(selectionChanged(void)), SLOT(updateSelection(void))); - if (configSettings) { - showAll = configSettings->showAll; - showName = configSettings->showName; - showRange = configSettings->showRange; - showData = configSettings->showData; + if (name) { + configSettings->beginGroup(name); + showAll = configSettings->readBoolEntry("/showAll", false); + showName = configSettings->readBoolEntry("/showName", false); + showRange = configSettings->readBoolEntry("/showRange", false); + showData = configSettings->readBoolEntry("/showData", false); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); } for (i = 0; i < colNr; i++) @@ -441,6 +369,30 @@ void ConfigList::reinit(void) updateListAll(); } +void ConfigList::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/showName", showName); + configSettings->writeEntry("/showRange", showRange); + configSettings->writeEntry("/showData", showData); + configSettings->writeEntry("/showAll", showAll); + configSettings->endGroup(); + } +} + +ConfigItem* ConfigList::findConfigItem(struct menu *menu) +{ + ConfigItem* item = (ConfigItem*)menu->data; + + for (; item; item = item->nextItem) { + if (this == item->listView()) + break; + } + + return item; +} + void ConfigList::updateSelection(void) { struct menu *menu; @@ -450,9 +402,8 @@ void ConfigList::updateSelection(void) if (!item) return; - cview->setHelp(item); - menu = item->menu; + emit menuChanged(menu); if (!menu) return; type = menu->prompt ? menu->prompt->type : P_UNKNOWN; @@ -464,8 +415,20 @@ void ConfigList::updateList(ConfigItem* { ConfigItem* last = 0; - if (!rootEntry) - goto update; + if (!rootEntry) { + if (mode != listMode) + goto update; + QListViewItemIterator it(this); + ConfigItem* item; + + for (; it.current(); ++it) { + item = (ConfigItem*)it.current(); + if (!item->menu) + continue; + item->testUpdateMenu(menu_is_visible(item->menu)); + } + return; + } if (rootEntry != &rootmenu && (mode == singleMode || (mode == symbolMode && rootEntry->parent != &rootmenu))) { @@ -491,14 +454,6 @@ update: triggerUpdate(); } -void ConfigList::setAllOpen(bool open) -{ - QListViewItemIterator it(this); - - for (; it.current(); it++) - it.current()->setOpen(open); -} - void ConfigList::setValue(ConfigItem* item, tristate val) { struct symbol* sym; @@ -581,6 +536,7 @@ void ConfigList::setRootMenu(struct menu rootEntry = menu; updateListAll(); setSelected(currentItem(), hasFocus()); + ensureItemVisible(currentItem()); } void ConfigList::setParentMenu(void) @@ -603,6 +559,74 @@ void ConfigList::setParentMenu(void) } } +/* + * update all the children of a menu entry + * removes/adds the entries from the parent widget as necessary + * + * parent: either the menu list widget or a menu entry widget + * menu: entry to be updated + */ +template +void ConfigList::updateMenuList(P* parent, struct menu* menu) +{ + struct menu* child; + ConfigItem* item; + ConfigItem* last; + bool visible; + enum prop_type type; + + if (!menu) { + while ((item = parent->firstChild())) + delete item; + return; + } + + last = parent->firstChild(); + if (last && !last->goParent) + last = 0; + for (child = menu->list; child; child = child->next) { + item = last ? last->nextSibling() : parent->firstChild(); + type = child->prompt ? child->prompt->type : P_UNKNOWN; + + switch (mode) { + case menuMode: + if (!(child->flags & MENU_ROOT)) + goto hide; + break; + case symbolMode: + if (child->flags & MENU_ROOT) + goto hide; + break; + default: + break; + } + + visible = menu_is_visible(child); + if (showAll || visible) { + if (!item || item->menu != child) + item = new ConfigItem(parent, last, child, visible); + else + item->testUpdateMenu(visible); + + if (mode == fullMode || mode == menuMode || type != P_MENU) + updateMenuList(item, child); + else + updateMenuList(item, 0); + last = item; + continue; + } + hide: + if (item && item->menu == child) { + last = parent->firstChild(); + if (last == item) + last = 0; + else while (last->nextSibling() != item) + last = last->nextSibling(); + delete item; + } + } +} + void ConfigList::keyPressEvent(QKeyEvent* ev) { QListViewItem* i = currentItem(); @@ -610,7 +634,7 @@ void ConfigList::keyPressEvent(QKeyEvent struct menu *menu; enum prop_type type; - if (ev->key() == Key_Escape && mode != fullMode) { + if (ev->key() == Key_Escape && mode != fullMode && mode != listMode) { emit parentSelected(); ev->accept(); return; @@ -755,23 +779,62 @@ skip: void ConfigList::focusInEvent(QFocusEvent *e) { + struct menu *menu = NULL; + Parent::focusInEvent(e); - QListViewItem* item = currentItem(); - if (!item) - return; + ConfigItem* item = (ConfigItem *)currentItem(); + if (item) { + setSelected(item, TRUE); + menu = item->menu; + } + emit gotFocus(menu); +} - setSelected(item, TRUE); - emit gotFocus(); +void ConfigList::contextMenuEvent(QContextMenuEvent *e) +{ + if (e->y() <= header()->geometry().bottom()) { + if (!headerPopup) { + QAction *action; + + headerPopup = new QPopupMenu(this); + action = new QAction("Show Name", 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowName(bool))); + connect(parent(), SIGNAL(showNameChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showName); + action->addTo(headerPopup); + action = new QAction("Show Range", 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowRange(bool))); + connect(parent(), SIGNAL(showRangeChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showRange); + action->addTo(headerPopup); + action = new QAction("Show Data", 0, this); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), + parent(), SLOT(setShowData(bool))); + connect(parent(), SIGNAL(showDataChanged(bool)), + action, SLOT(setOn(bool))); + action->setOn(showData); + action->addTo(headerPopup); + } + headerPopup->exec(e->globalPos()); + e->accept(); + } else + e->ignore(); } ConfigView* ConfigView::viewList; -ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview, - ConfigSettings *configSettings) - : Parent(parent) +ConfigView::ConfigView(QWidget* parent, const char *name) + : Parent(parent, name) { - list = new ConfigList(this, cview, configSettings); + list = new ConfigList(this, name); lineEdit = new ConfigLineEdit(this); lineEdit->hide(); @@ -791,6 +854,50 @@ ConfigView::~ConfigView(void) } } +void ConfigView::setShowAll(bool b) +{ + if (list->showAll != b) { + list->showAll = b; + list->updateListAll(); + emit showAllChanged(b); + } +} + +void ConfigView::setShowName(bool b) +{ + if (list->showName != b) { + list->showName = b; + list->reinit(); + emit showNameChanged(b); + } +} + +void ConfigView::setShowRange(bool b) +{ + if (list->showRange != b) { + list->showRange = b; + list->reinit(); + emit showRangeChanged(b); + } +} + +void ConfigView::setShowData(bool b) +{ + if (list->showData != b) { + list->showData = b; + list->reinit(); + emit showDataChanged(b); + } +} + +void ConfigList::setAllOpen(bool open) +{ + QListViewItemIterator it(this); + + for (; it.current(); it++) + it.current()->setOpen(open); +} + void ConfigView::updateList(ConfigItem* item) { ConfigView* v; @@ -807,6 +914,347 @@ void ConfigView::updateListAll(void) v->list->updateListAll(); } +ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name) + : Parent(parent, name), menu(0) +{ + if (name) { + configSettings->beginGroup(name); + _showDebug = configSettings->readBoolEntry("/showDebug", false); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigInfoView::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/showDebug", showDebug()); + configSettings->endGroup(); + } +} + +void ConfigInfoView::setShowDebug(bool b) +{ + if (_showDebug != b) { + _showDebug = b; + if (menu) + menuInfo(); + else if (sym) + symbolInfo(); + emit showDebugChanged(b); + } +} + +void ConfigInfoView::setInfo(struct menu *m) +{ + if (menu == m) + return; + menu = m; + if (!menu) + clear(); + else + menuInfo(); +} + +void ConfigInfoView::setSource(const QString& name) +{ + const char *p = name.latin1(); + + menu = NULL; + sym = NULL; + + switch (p[0]) { + case 'm': + struct menu *m; + + if (sscanf(p, "m%p", &m) == 1 && menu != m) { + menu = m; + menuInfo(); + emit menuSelected(menu); + } + break; + case 's': + struct symbol *s; + + if (sscanf(p, "s%p", &s) == 1 && sym != s) { + sym = s; + symbolInfo(); + } + break; + } +} + +void ConfigInfoView::symbolInfo(void) +{ + QString str; + + str += "Symbol: "; + str += print_filter(sym->name); + str += "

value: "; + str += print_filter(sym_get_string_value(sym)); + str += "
visibility: "; + str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n"; + str += "
"; + str += debug_info(sym); + + setText(str); +} + +void ConfigInfoView::menuInfo(void) +{ + struct symbol* sym; + QString head, debug, help; + + sym = menu->sym; + if (sym) { + if (menu->prompt) { + head += ""; + head += print_filter(_(menu->prompt->text)); + head += ""; + if (sym->name) { + head += " ("; + if (showDebug()) + head += QString().sprintf("
", sym); + head += print_filter(sym->name); + if (showDebug()) + head += ""; + head += ")"; + } + } else if (sym->name) { + head += ""; + if (showDebug()) + head += QString().sprintf("", sym); + head += print_filter(sym->name); + if (showDebug()) + head += ""; + head += ""; + } + head += "

"; + + if (showDebug()) + debug = debug_info(sym); + + help = print_filter(_(sym->help)); + } else if (menu->prompt) { + head += ""; + head += print_filter(_(menu->prompt->text)); + head += "

"; + if (showDebug()) { + if (menu->prompt->visible.expr) { + debug += "  dep: "; + expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE); + debug += "

"; + } + } + } + if (showDebug()) + debug += QString().sprintf("defined at %s:%d

", menu->file->name, menu->lineno); + + setText(head + debug + help); +} + +QString ConfigInfoView::debug_info(struct symbol *sym) +{ + QString debug; + + debug += "type: "; + debug += print_filter(sym_type_name(sym->type)); + if (sym_is_choice(sym)) + debug += " (choice)"; + debug += "
"; + if (sym->rev_dep.expr) { + debug += "reverse dep: "; + expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE); + debug += "
"; + } + for (struct property *prop = sym->prop; prop; prop = prop->next) { + switch (prop->type) { + case P_PROMPT: + case P_MENU: + debug += QString().sprintf("prompt: ", prop->menu); + debug += print_filter(_(prop->text)); + debug += "
"; + break; + case P_DEFAULT: + debug += "default: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "
"; + break; + case P_CHOICE: + if (sym_is_choice(sym)) { + debug += "choice: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "
"; + } + break; + case P_SELECT: + debug += "select: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "
"; + break; + case P_RANGE: + debug += "range: "; + expr_print(prop->expr, expr_print_help, &debug, E_NONE); + debug += "
"; + break; + default: + debug += "unknown property: "; + debug += prop_get_type_name(prop->type); + debug += "
"; + } + if (prop->visible.expr) { + debug += "    dep: "; + expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE); + debug += "
"; + } + } + debug += "
"; + + return debug; +} + +QString ConfigInfoView::print_filter(const QString &str) +{ + QRegExp re("[<>&\"\\n]"); + QString res = str; + for (int i = 0; (i = res.find(re, i)) >= 0;) { + switch (res[i].latin1()) { + case '<': + res.replace(i, 1, "<"); + i += 4; + break; + case '>': + res.replace(i, 1, ">"); + i += 4; + break; + case '&': + res.replace(i, 1, "&"); + i += 5; + break; + case '"': + res.replace(i, 1, """); + i += 6; + break; + case '\n': + res.replace(i, 1, "
"); + i += 4; + break; + } + } + return res; +} + +void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str) +{ + QString* text = reinterpret_cast(data); + QString str2 = print_filter(str); + + if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) { + *text += QString().sprintf("", sym); + *text += str2; + *text += ""; + } else + *text += str2; +} + +QPopupMenu* ConfigInfoView::createPopupMenu(const QPoint& pos) +{ + QPopupMenu* popup = Parent::createPopupMenu(pos); + QAction* action = new QAction("Show Debug Info", 0, popup); + action->setToggleAction(TRUE); + connect(action, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool))); + connect(this, SIGNAL(showDebugChanged(bool)), action, SLOT(setOn(bool))); + action->setOn(showDebug()); + popup->insertSeparator(); + action->addTo(popup); + return popup; +} + +void ConfigInfoView::contentsContextMenuEvent(QContextMenuEvent *e) +{ + Parent::contentsContextMenuEvent(e); +} + +ConfigSearchWindow::ConfigSearchWindow(QWidget* parent, const char *name) + : Parent(parent, name), result(NULL) +{ + setCaption("Search Config"); + + QVBoxLayout* layout1 = new QVBoxLayout(this, 11, 6); + QHBoxLayout* layout2 = new QHBoxLayout(0, 0, 6); + layout2->addWidget(new QLabel("Find:", this)); + editField = new QLineEdit(this); + connect(editField, SIGNAL(returnPressed()), SLOT(search())); + layout2->addWidget(editField); + searchButton = new QPushButton("Search", this); + searchButton->setAutoDefault(FALSE); + connect(searchButton, SIGNAL(clicked()), SLOT(search())); + layout2->addWidget(searchButton); + layout1->addLayout(layout2); + + split = new QSplitter(this); + split->setOrientation(QSplitter::Vertical); + list = new ConfigView(split, name); + list->list->mode = listMode; + info = new ConfigInfoView(split, name); + connect(list->list, SIGNAL(menuChanged(struct menu *)), + info, SLOT(setInfo(struct menu *))); + layout1->addWidget(split); + + if (name) { + int x, y, width, height; + bool ok; + + configSettings->beginGroup(name); + width = configSettings->readNumEntry("/window width", parent->width() / 2); + height = configSettings->readNumEntry("/window height", parent->height() / 2); + resize(width, height); + x = configSettings->readNumEntry("/window x", 0, &ok); + if (ok) + y = configSettings->readNumEntry("/window y", 0, &ok); + if (ok) + move(x, y); + QValueList sizes = configSettings->readSizes("/split", &ok); + if (ok) + split->setSizes(sizes); + configSettings->endGroup(); + connect(configApp, SIGNAL(aboutToQuit()), SLOT(saveSettings())); + } +} + +void ConfigSearchWindow::saveSettings(void) +{ + if (name()) { + configSettings->beginGroup(name()); + configSettings->writeEntry("/window x", pos().x()); + configSettings->writeEntry("/window y", pos().y()); + configSettings->writeEntry("/window width", size().width()); + configSettings->writeEntry("/window height", size().height()); + configSettings->writeSizes("/split", split->sizes()); + configSettings->endGroup(); + } +} + +void ConfigSearchWindow::search(void) +{ + struct symbol **p; + struct property *prop; + ConfigItem *lastItem = NULL; + + free(result); + list->list->clear(); + + result = sym_re_search(editField->text().latin1()); + if (!result) + return; + for (p = result; *p; p++) { + for_all_prompts((*p), prop) + lastItem = new ConfigItem(list->list, lastItem, prop->menu, + menu_is_visible(prop->menu)); + } +} + /* * Construct the complete config widget */ @@ -818,42 +1266,30 @@ ConfigMainWindow::ConfigMainWindow(void) QWidget *d = configApp->desktop(); - ConfigSettings* configSettings = new ConfigSettings(); -#if QT_VERSION >= 300 - width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64); - height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64); + width = configSettings->readNumEntry("/window width", d->width() - 64); + height = configSettings->readNumEntry("/window height", d->height() - 64); resize(width, height); - x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok); + x = configSettings->readNumEntry("/window x", 0, &ok); if (ok) - y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok); + y = configSettings->readNumEntry("/window y", 0, &ok); if (ok) move(x, y); - showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false); - - // read list settings into configSettings, will be used later for ConfigList setup - configSettings->readListSettings(); -#else - width = d->width() - 64; - height = d->height() - 64; - resize(width, height); - showDebug = false; -#endif split1 = new QSplitter(this); split1->setOrientation(QSplitter::Horizontal); setCentralWidget(split1); - menuView = new ConfigView(split1, this, configSettings); + menuView = new ConfigView(split1, "menu"); menuList = menuView->list; split2 = new QSplitter(split1); split2->setOrientation(QSplitter::Vertical); // create config tree - configView = new ConfigView(split2, this, configSettings); + configView = new ConfigView(split2, "config"); configList = configView->list; - helpText = new QTextView(split2); + helpText = new ConfigInfoView(split2, "help"); helpText->setTextFormat(Qt::RichText); setTabOrder(configList, helpText); @@ -873,6 +1309,8 @@ #endif connect(saveAction, SIGNAL(activated()), SLOT(saveConfig())); QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this); connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs())); + QAction *searchAction = new QAction("Search", "&Search", CTRL+Key_F, this); + connect(searchAction, SIGNAL(activated()), SLOT(searchConfig())); QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this); connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView())); QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this); @@ -882,24 +1320,29 @@ #endif QAction *showNameAction = new QAction(NULL, "Show Name", 0, this); showNameAction->setToggleAction(TRUE); - showNameAction->setOn(configList->showName); - connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool))); + connect(showNameAction, SIGNAL(toggled(bool)), configView, SLOT(setShowName(bool))); + connect(configView, SIGNAL(showNameChanged(bool)), showNameAction, SLOT(setOn(bool))); + showNameAction->setOn(configView->showName()); QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this); showRangeAction->setToggleAction(TRUE); + connect(showRangeAction, SIGNAL(toggled(bool)), configView, SLOT(setShowRange(bool))); + connect(configView, SIGNAL(showRangeChanged(bool)), showRangeAction, SLOT(setOn(bool))); showRangeAction->setOn(configList->showRange); - connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool))); QAction *showDataAction = new QAction(NULL, "Show Data", 0, this); showDataAction->setToggleAction(TRUE); + connect(showDataAction, SIGNAL(toggled(bool)), configView, SLOT(setShowData(bool))); + connect(configView, SIGNAL(showDataChanged(bool)), showDataAction, SLOT(setOn(bool))); showDataAction->setOn(configList->showData); - connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool))); QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this); showAllAction->setToggleAction(TRUE); + connect(showAllAction, SIGNAL(toggled(bool)), configView, SLOT(setShowAll(bool))); + connect(showAllAction, SIGNAL(toggled(bool)), menuView, SLOT(setShowAll(bool))); showAllAction->setOn(configList->showAll); - connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool))); QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this); showDebugAction->setToggleAction(TRUE); - showDebugAction->setOn(showDebug); - connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool))); + connect(showDebugAction, SIGNAL(toggled(bool)), helpText, SLOT(setShowDebug(bool))); + connect(helpText, SIGNAL(showDebugChanged(bool)), showDebugAction, SLOT(setOn(bool))); + showDebugAction->setOn(helpText->showDebug()); QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this); connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro())); @@ -923,6 +1366,8 @@ #endif saveAction->addTo(config); saveAsAction->addTo(config); config->insertSeparator(); + searchAction->addTo(config); + config->insertSeparator(); quitAction->addTo(config); // create options menu @@ -942,20 +1387,27 @@ #endif showIntroAction->addTo(helpMenu); showAboutAction->addTo(helpMenu); + connect(configList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); connect(configList, SIGNAL(menuSelected(struct menu *)), SLOT(changeMenu(struct menu *))); connect(configList, SIGNAL(parentSelected()), SLOT(goBack())); + connect(menuList, SIGNAL(menuChanged(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); connect(menuList, SIGNAL(menuSelected(struct menu *)), SLOT(changeMenu(struct menu *))); - connect(configList, SIGNAL(gotFocus(void)), - SLOT(listFocusChanged(void))); - connect(menuList, SIGNAL(gotFocus(void)), + connect(configList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), + helpText, SLOT(setInfo(struct menu *))); + connect(menuList, SIGNAL(gotFocus(struct menu *)), SLOT(listFocusChanged(void))); + connect(helpText, SIGNAL(menuSelected(struct menu *)), + SLOT(setMenuLink(struct menu *))); -#if QT_VERSION >= 300 - QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol"); + QString listMode = configSettings->readEntry("/listMode", "symbol"); if (listMode == "single") showSingleView(); else if (listMode == "full") @@ -964,162 +1416,13 @@ #if QT_VERSION >= 300 showSplitView(); // UI setup done, restore splitter positions - QValueList sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok); + QValueList sizes = configSettings->readSizes("/split1", &ok); if (ok) split1->setSizes(sizes); - sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok); + sizes = configSettings->readSizes("/split2", &ok); if (ok) split2->setSizes(sizes); -#else - showSplitView(); -#endif - delete configSettings; -} - -static QString print_filter(const QString &str) -{ - QRegExp re("[<>&\"\\n]"); - QString res = str; - for (int i = 0; (i = res.find(re, i)) >= 0;) { - switch (res[i].latin1()) { - case '<': - res.replace(i, 1, "<"); - i += 4; - break; - case '>': - res.replace(i, 1, ">"); - i += 4; - break; - case '&': - res.replace(i, 1, "&"); - i += 5; - break; - case '"': - res.replace(i, 1, """); - i += 6; - break; - case '\n': - res.replace(i, 1, "
"); - i += 4; - break; - } - } - return res; -} - -static void expr_print_help(void *data, const char *str) -{ - reinterpret_cast(data)->append(print_filter(str)); -} - -/* - * display a new help entry as soon as a new menu entry is selected - */ -void ConfigMainWindow::setHelp(QListViewItem* item) -{ - struct symbol* sym; - struct menu* menu = 0; - - configList->parent()->lineEdit->hide(); - if (item) - menu = ((ConfigItem*)item)->menu; - if (!menu) { - helpText->setText(QString::null); - return; - } - - QString head, debug, help; - menu = ((ConfigItem*)item)->menu; - sym = menu->sym; - if (sym) { - if (menu->prompt) { - head += ""; - head += print_filter(_(menu->prompt->text)); - head += ""; - if (sym->name) { - head += " ("; - head += print_filter(_(sym->name)); - head += ")"; - } - } else if (sym->name) { - head += ""; - head += print_filter(_(sym->name)); - head += ""; - } - head += "

"; - - if (showDebug) { - debug += "type: "; - debug += print_filter(sym_type_name(sym->type)); - if (sym_is_choice(sym)) - debug += " (choice)"; - debug += "
"; - if (sym->rev_dep.expr) { - debug += "reverse dep: "; - expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE); - debug += "
"; - } - for (struct property *prop = sym->prop; prop; prop = prop->next) { - switch (prop->type) { - case P_PROMPT: - case P_MENU: - debug += "prompt: "; - debug += print_filter(_(prop->text)); - debug += "
"; - break; - case P_DEFAULT: - debug += "default: "; - expr_print(prop->expr, expr_print_help, &debug, E_NONE); - debug += "
"; - break; - case P_CHOICE: - if (sym_is_choice(sym)) { - debug += "choice: "; - expr_print(prop->expr, expr_print_help, &debug, E_NONE); - debug += "
"; - } - break; - case P_SELECT: - debug += "select: "; - expr_print(prop->expr, expr_print_help, &debug, E_NONE); - debug += "
"; - break; - case P_RANGE: - debug += "range: "; - expr_print(prop->expr, expr_print_help, &debug, E_NONE); - debug += "
"; - break; - default: - debug += "unknown property: "; - debug += prop_get_type_name(prop->type); - debug += "
"; - } - if (prop->visible.expr) { - debug += "    dep: "; - expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE); - debug += "
"; - } - } - debug += "
"; - } - - help = print_filter(_(sym->help)); - } else if (menu->prompt) { - head += ""; - head += print_filter(_(menu->prompt->text)); - head += "

"; - if (showDebug) { - if (menu->prompt->visible.expr) { - debug += "  dep: "; - expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE); - debug += "

"; - } - } - } - if (showDebug) - debug += QString().sprintf("defined at %s:%d

", menu->file->name, menu->lineno); - helpText->setText(head + debug + help); } void ConfigMainWindow::loadConfig(void) @@ -1147,21 +1450,73 @@ void ConfigMainWindow::saveConfigAs(void QMessageBox::information(this, "qconf", "Unable to save configuration!"); } +void ConfigMainWindow::searchConfig(void) +{ + if (!searchWindow) + searchWindow = new ConfigSearchWindow(this, "search"); + searchWindow->show(); +} + void ConfigMainWindow::changeMenu(struct menu *menu) { configList->setRootMenu(menu); backAction->setEnabled(TRUE); } -void ConfigMainWindow::listFocusChanged(void) +void ConfigMainWindow::setMenuLink(struct menu *menu) { - if (menuList->hasFocus()) { - if (menuList->mode == menuMode) + struct menu *parent; + ConfigList* list = NULL; + ConfigItem* item; + + if (!menu_is_visible(menu) && !configView->showAll()) + return; + + switch (configList->mode) { + case singleMode: + list = configList; + parent = menu_get_parent_menu(menu); + if (!parent) + return; + list->setRootMenu(parent); + break; + case symbolMode: + if (menu->flags & MENU_ROOT) { + configList->setRootMenu(menu); configList->clearSelection(); - setHelp(menuList->selectedItem()); - } else if (configList->hasFocus()) { - setHelp(configList->selectedItem()); + list = menuList; + } else { + list = configList; + parent = menu_get_parent_menu(menu->parent); + if (!parent) + return; + item = menuList->findConfigItem(parent); + if (item) { + menuList->setSelected(item, TRUE); + menuList->ensureItemVisible(item); + } + list->setRootMenu(parent); + } + break; + case fullMode: + list = configList; + break; } + + if (list) { + item = list->findConfigItem(menu); + if (item) { + list->setSelected(item, TRUE); + list->ensureItemVisible(item); + list->setFocus(); + } + } +} + +void ConfigMainWindow::listFocusChanged(void) +{ + if (menuList->mode == menuMode) + configList->clearSelection(); } void ConfigMainWindow::goBack(void) @@ -1223,53 +1578,6 @@ void ConfigMainWindow::showFullView(void configList->setFocus(); } -void ConfigMainWindow::setShowAll(bool b) -{ - if (configList->showAll == b) - return; - configList->showAll = b; - configList->updateListAll(); - menuList->showAll = b; - menuList->updateListAll(); -} - -void ConfigMainWindow::setShowDebug(bool b) -{ - if (showDebug == b) - return; - showDebug = b; -} - -void ConfigMainWindow::setShowName(bool b) -{ - if (configList->showName == b) - return; - configList->showName = b; - configList->reinit(); - menuList->showName = b; - menuList->reinit(); -} - -void ConfigMainWindow::setShowRange(bool b) -{ - if (configList->showRange == b) - return; - configList->showRange = b; - configList->reinit(); - menuList->showRange = b; - menuList->reinit(); -} - -void ConfigMainWindow::setShowData(bool b) -{ - if (configList->showData == b) - return; - configList->showData = b; - configList->reinit(); - menuList->showData = b; - menuList->reinit(); -} - /* * ask for saving configuration before quitting * TODO ask only when something changed @@ -1324,17 +1632,10 @@ void ConfigMainWindow::showAbout(void) void ConfigMainWindow::saveSettings(void) { -#if QT_VERSION >= 300 - ConfigSettings *configSettings = new ConfigSettings; - configSettings->writeEntry("/kconfig/qconf/window x", pos().x()); - configSettings->writeEntry("/kconfig/qconf/window y", pos().y()); - configSettings->writeEntry("/kconfig/qconf/window width", size().width()); - configSettings->writeEntry("/kconfig/qconf/window height", size().height()); - configSettings->writeEntry("/kconfig/qconf/showName", configList->showName); - configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange); - configSettings->writeEntry("/kconfig/qconf/showData", configList->showData); - configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll); - configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug); + configSettings->writeEntry("/window x", pos().x()); + configSettings->writeEntry("/window y", pos().y()); + configSettings->writeEntry("/window width", size().width()); + configSettings->writeEntry("/window height", size().height()); QString entry; switch(configList->mode) { @@ -1350,13 +1651,10 @@ #if QT_VERSION >= 300 entry = "full"; break; } - configSettings->writeEntry("/kconfig/qconf/listMode", entry); + configSettings->writeEntry("/listMode", entry); - configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes()); - configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes()); - - delete configSettings; -#endif + configSettings->writeSizes("/split1", split1->sizes()); + configSettings->writeSizes("/split2", split2->sizes()); } void fixup_rootmenu(struct menu *menu) @@ -1414,13 +1712,19 @@ #endif conf_read(NULL); //zconfdump(stdout); + configSettings = new ConfigSettings(); + configSettings->beginGroup("/kconfig/qconf"); v = new ConfigMainWindow(); //zconfdump(stdout); - v->show(); + configApp->setMainWidget(v); configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit())); configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings())); + v->show(); configApp->exec(); + configSettings->endGroup(); + delete configSettings; + return 0; } diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index e52f3e9..6a9e3b1 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -7,9 +7,25 @@ #include #if QT_VERSION >= 300 #include #else -class QSettings { }; +class QSettings { +public: + void beginGroup(const QString& group) { } + void endGroup(void) { } + bool readBoolEntry(const QString& key, bool def = FALSE, bool* ok = 0) const + { if (ok) *ok = FALSE; return def; } + int readNumEntry(const QString& key, int def = 0, bool* ok = 0) const + { if (ok) *ok = FALSE; return def; } + QString readEntry(const QString& key, const QString& def = QString::null, bool* ok = 0) const + { if (ok) *ok = FALSE; return def; } + QStringList readListEntry(const QString& key, bool* ok = 0) const + { if (ok) *ok = FALSE; return QStringList(); } + template + bool writeEntry(const QString& key, t value) + { return TRUE; } +}; #endif +class ConfigView; class ConfigList; class ConfigItem; class ConfigLineEdit; @@ -18,64 +34,38 @@ class ConfigMainWindow; class ConfigSettings : public QSettings { public: - ConfigSettings(); - -#if QT_VERSION >= 300 - void readListSettings(); QValueList readSizes(const QString& key, bool *ok); bool writeSizes(const QString& key, const QValueList& value); -#endif - - bool showAll; - bool showName; - bool showRange; - bool showData; -}; - -class ConfigView : public QVBox { - Q_OBJECT - typedef class QVBox Parent; -public: - ConfigView(QWidget* parent, ConfigMainWindow* cview, ConfigSettings* configSettings); - ~ConfigView(void); - static void updateList(ConfigItem* item); - static void updateListAll(void); - -public: - ConfigList* list; - ConfigLineEdit* lineEdit; - - static ConfigView* viewList; - ConfigView* nextView; }; enum colIdx { promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr }; enum listMode { - singleMode, menuMode, symbolMode, fullMode + singleMode, menuMode, symbolMode, fullMode, listMode }; class ConfigList : public QListView { Q_OBJECT typedef class QListView Parent; public: - ConfigList(ConfigView* p, ConfigMainWindow* cview, ConfigSettings *configSettings); + ConfigList(ConfigView* p, const char *name = 0); void reinit(void); ConfigView* parent(void) const { return (ConfigView*)Parent::parent(); } + ConfigItem* findConfigItem(struct menu *); protected: - ConfigMainWindow* cview; - void keyPressEvent(QKeyEvent *e); void contentsMousePressEvent(QMouseEvent *e); void contentsMouseReleaseEvent(QMouseEvent *e); void contentsMouseMoveEvent(QMouseEvent *e); void contentsMouseDoubleClickEvent(QMouseEvent *e); void focusInEvent(QFocusEvent *e); + void contextMenuEvent(QContextMenuEvent *e); + public slots: void setRootMenu(struct menu *menu); @@ -83,10 +73,12 @@ public slots: void setValue(ConfigItem* item, tristate val); void changeValue(ConfigItem* item); void updateSelection(void); + void saveSettings(void); signals: + void menuChanged(struct menu *menu); void menuSelected(struct menu *menu); void parentSelected(void); - void gotFocus(void); + void gotFocus(struct menu *); public: void updateListAll(void) @@ -137,6 +129,7 @@ public: struct menu *rootEntry; QColorGroup disabledColorGroup; QColorGroup inactivedColorGroup; + QPopupMenu* headerPopup; private: int colMap[colNr]; @@ -208,9 +201,7 @@ class ConfigLineEdit : public QLineEdit Q_OBJECT typedef class QLineEdit Parent; public: - ConfigLineEdit(ConfigView* parent) - : Parent(parent) - { } + ConfigLineEdit(ConfigView* parent); ConfigView* parent(void) const { return (ConfigView*)Parent::parent(); @@ -222,26 +213,104 @@ public: ConfigItem *item; }; +class ConfigView : public QVBox { + Q_OBJECT + typedef class QVBox Parent; +public: + ConfigView(QWidget* parent, const char *name = 0); + ~ConfigView(void); + static void updateList(ConfigItem* item); + static void updateListAll(void); + + bool showAll(void) const { return list->showAll; } + bool showName(void) const { return list->showName; } + bool showRange(void) const { return list->showRange; } + bool showData(void) const { return list->showData; } +public slots: + void setShowAll(bool); + void setShowName(bool); + void setShowRange(bool); + void setShowData(bool); +signals: + void showAllChanged(bool); + void showNameChanged(bool); + void showRangeChanged(bool); + void showDataChanged(bool); +public: + ConfigList* list; + ConfigLineEdit* lineEdit; + + static ConfigView* viewList; + ConfigView* nextView; +}; + +class ConfigInfoView : public QTextBrowser { + Q_OBJECT + typedef class QTextBrowser Parent; +public: + ConfigInfoView(QWidget* parent, const char *name = 0); + bool showDebug(void) const { return _showDebug; } + +public slots: + void setInfo(struct menu *menu); + void saveSettings(void); + void setSource(const QString& name); + void setShowDebug(bool); + +signals: + void showDebugChanged(bool); + void menuSelected(struct menu *); + +protected: + void symbolInfo(void); + void menuInfo(void); + QString debug_info(struct symbol *sym); + static QString print_filter(const QString &str); + static void expr_print_help(void *data, struct symbol *sym, const char *str); + QPopupMenu* createPopupMenu(const QPoint& pos); + void contentsContextMenuEvent(QContextMenuEvent *e); + + struct symbol *sym; + struct menu *menu; + bool _showDebug; +}; + +class ConfigSearchWindow : public QDialog { + Q_OBJECT + typedef class QDialog Parent; +public: + ConfigSearchWindow(QWidget* parent, const char *name = 0); + +public slots: + void saveSettings(void); + void search(void); + +protected: + QLineEdit* editField; + QPushButton* searchButton; + QSplitter* split; + ConfigView* list; + ConfigInfoView* info; + + struct symbol **result; +}; + class ConfigMainWindow : public QMainWindow { Q_OBJECT public: ConfigMainWindow(void); public slots: - void setHelp(QListViewItem* item); void changeMenu(struct menu *); + void setMenuLink(struct menu *); void listFocusChanged(void); void goBack(void); void loadConfig(void); void saveConfig(void); void saveConfigAs(void); + void searchConfig(void); void showSingleView(void); void showSplitView(void); void showFullView(void); - void setShowAll(bool); - void setShowDebug(bool); - void setShowRange(bool); - void setShowName(bool); - void setShowData(bool); void showIntro(void); void showAbout(void); void saveSettings(void); @@ -249,15 +318,14 @@ public slots: protected: void closeEvent(QCloseEvent *e); + ConfigSearchWindow *searchWindow; ConfigView *menuView; ConfigList *menuList; ConfigView *configView; ConfigList *configList; - QTextView *helpText; + ConfigInfoView *helpText; QToolBar *toolBar; QAction *backAction; QSplitter* split1; QSplitter* split2; - - bool showDebug; }; diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 3d7877a..ee225ce 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -15,15 +15,15 @@ #include "lkc.h" struct symbol symbol_yes = { .name = "y", .curr = { "y", yes }, - .flags = SYMBOL_YES|SYMBOL_VALID, + .flags = SYMBOL_CONST|SYMBOL_VALID, }, symbol_mod = { .name = "m", .curr = { "m", mod }, - .flags = SYMBOL_MOD|SYMBOL_VALID, + .flags = SYMBOL_CONST|SYMBOL_VALID, }, symbol_no = { .name = "n", .curr = { "n", no }, - .flags = SYMBOL_NO|SYMBOL_VALID, + .flags = SYMBOL_CONST|SYMBOL_VALID, }, symbol_empty = { .name = "", .curr = { "", no }, @@ -31,6 +31,7 @@ struct symbol symbol_yes = { }; int sym_change_count; +struct symbol *sym_defconfig_list; struct symbol *modules_sym; tristate modules_val; @@ -227,7 +228,7 @@ static struct symbol *sym_calc_choice(st struct expr *e; /* is the user choice visible? */ - def_sym = sym->user.val; + def_sym = sym->def[S_DEF_USER].val; if (def_sym) { sym_calc_visibility(def_sym); if (def_sym->visible != no) @@ -306,7 +307,7 @@ void sym_calc_value(struct symbol *sym) } else if (E_OR(sym->visible, sym->rev_dep.tri) != no) { sym->flags |= SYMBOL_WRITE; if (sym_has_value(sym)) - newval.tri = sym->user.tri; + newval.tri = sym->def[S_DEF_USER].tri; else if (!sym_is_choice(sym)) { prop = sym_get_default_prop(sym); if (prop) @@ -329,7 +330,7 @@ void sym_calc_value(struct symbol *sym) if (sym->visible != no) { sym->flags |= SYMBOL_WRITE; if (sym_has_value(sym)) { - newval.val = sym->user.val; + newval.val = sym->def[S_DEF_USER].val; break; } } @@ -352,10 +353,13 @@ void sym_calc_value(struct symbol *sym) sym->curr.val = sym_calc_choice(sym); sym_validate_range(sym); - if (memcmp(&oldval, &sym->curr, sizeof(oldval))) + if (memcmp(&oldval, &sym->curr, sizeof(oldval))) { sym_set_changed(sym); - if (modules_sym == sym) - modules_val = modules_sym->curr.tri; + if (modules_sym == sym) { + sym_set_all_changed(); + modules_val = modules_sym->curr.tri; + } + } if (sym_is_choice(sym)) { int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE); @@ -426,8 +430,8 @@ bool sym_set_tristate_value(struct symbo if (oldval != val && !sym_tristate_within_range(sym, val)) return false; - if (sym->flags & SYMBOL_NEW) { - sym->flags &= ~SYMBOL_NEW; + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } /* @@ -439,21 +443,18 @@ bool sym_set_tristate_value(struct symbo struct property *prop; struct expr *e; - cs->user.val = sym; - cs->flags &= ~SYMBOL_NEW; + cs->def[S_DEF_USER].val = sym; + cs->flags |= SYMBOL_DEF_USER; prop = sym_get_choice_prop(cs); for (e = prop->expr; e; e = e->left.expr) { if (e->right.sym->visible != no) - e->right.sym->flags &= ~SYMBOL_NEW; + e->right.sym->flags |= SYMBOL_DEF_USER; } } - sym->user.tri = val; - if (oldval != val) { + sym->def[S_DEF_USER].tri = val; + if (oldval != val) sym_clear_all_valid(); - if (sym == modules_sym) - sym_set_all_changed(); - } return true; } @@ -591,20 +592,20 @@ bool sym_set_string_value(struct symbol if (!sym_string_within_range(sym, newval)) return false; - if (sym->flags & SYMBOL_NEW) { - sym->flags &= ~SYMBOL_NEW; + if (!(sym->flags & SYMBOL_DEF_USER)) { + sym->flags |= SYMBOL_DEF_USER; sym_set_changed(sym); } - oldval = sym->user.val; + oldval = sym->def[S_DEF_USER].val; size = strlen(newval) + 1; if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) { size += 2; - sym->user.val = val = malloc(size); + sym->def[S_DEF_USER].val = val = malloc(size); *val++ = '0'; *val++ = 'x'; } else if (!oldval || strcmp(oldval, newval)) - sym->user.val = val = malloc(size); + sym->def[S_DEF_USER].val = val = malloc(size); else return true; @@ -679,7 +680,6 @@ struct symbol *sym_lookup(const char *na memset(symbol, 0, sizeof(*symbol)); symbol->name = new_name; symbol->type = S_UNKNOWN; - symbol->flags = SYMBOL_NEW; if (isconst) symbol->flags |= SYMBOL_CONST; diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c index 656d2c8..e3f28b9 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -44,7 +44,9 @@ int file_write_dep(const char *name) else fprintf(out, "\t%s\n", file->name); } - fprintf(out, "\n.config include/linux/autoconf.h: $(deps_config)\n\n$(deps_config):\n"); + fprintf(out, "\ninclude/config/auto.conf: \\\n" + "\t$(deps_config)\n\n" + "$(deps_config): ;\n"); fclose(out); rename("..config.tmp", name); return 0; diff --git a/scripts/kconfig/zconf.gperf b/scripts/kconfig/zconf.gperf index b032206..9b44c80 100644 --- a/scripts/kconfig/zconf.gperf +++ b/scripts/kconfig/zconf.gperf @@ -39,5 +39,8 @@ string, T_TYPE, TF_COMMAND, S_STRING select, T_SELECT, TF_COMMAND enable, T_SELECT, TF_COMMAND range, T_RANGE, TF_COMMAND +option, T_OPTION, TF_COMMAND on, T_ON, TF_PARAM +modules, T_OPT_MODULES, TF_OPTION +defconfig_list, T_OPT_DEFCONFIG_LIST,TF_OPTION %% diff --git a/scripts/kconfig/zconf.hash.c_shipped b/scripts/kconfig/zconf.hash.c_shipped index 345f0fc..47c8b5b 100644 --- a/scripts/kconfig/zconf.hash.c_shipped +++ b/scripts/kconfig/zconf.hash.c_shipped @@ -53,10 +53,10 @@ kconf_id_hash (register const char *str, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 25, 10, 15, - 0, 0, 5, 47, 0, 0, 47, 47, 0, 10, - 0, 20, 20, 20, 5, 0, 0, 20, 47, 47, - 20, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 47, 47, 25, 30, 15, + 0, 15, 0, 47, 5, 15, 47, 47, 30, 20, + 5, 0, 25, 15, 0, 0, 10, 35, 47, 47, + 5, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, @@ -88,69 +88,75 @@ kconf_id_hash (register const char *str, struct kconf_id_strings_t { - char kconf_id_strings_str2[sizeof("if")]; - char kconf_id_strings_str3[sizeof("int")]; - char kconf_id_strings_str4[sizeof("help")]; - char kconf_id_strings_str5[sizeof("endif")]; - char kconf_id_strings_str6[sizeof("select")]; - char kconf_id_strings_str7[sizeof("endmenu")]; - char kconf_id_strings_str8[sizeof("tristate")]; - char kconf_id_strings_str9[sizeof("endchoice")]; + char kconf_id_strings_str2[sizeof("on")]; + char kconf_id_strings_str6[sizeof("string")]; + char kconf_id_strings_str7[sizeof("default")]; + char kconf_id_strings_str8[sizeof("def_bool")]; char kconf_id_strings_str10[sizeof("range")]; - char kconf_id_strings_str11[sizeof("string")]; - char kconf_id_strings_str12[sizeof("default")]; - char kconf_id_strings_str13[sizeof("def_bool")]; - char kconf_id_strings_str14[sizeof("menu")]; - char kconf_id_strings_str16[sizeof("def_boolean")]; - char kconf_id_strings_str17[sizeof("def_tristate")]; - char kconf_id_strings_str18[sizeof("mainmenu")]; - char kconf_id_strings_str20[sizeof("menuconfig")]; - char kconf_id_strings_str21[sizeof("config")]; - char kconf_id_strings_str22[sizeof("on")]; - char kconf_id_strings_str23[sizeof("hex")]; - char kconf_id_strings_str26[sizeof("source")]; - char kconf_id_strings_str27[sizeof("depends")]; - char kconf_id_strings_str28[sizeof("optional")]; - char kconf_id_strings_str31[sizeof("enable")]; - char kconf_id_strings_str32[sizeof("comment")]; - char kconf_id_strings_str33[sizeof("requires")]; + char kconf_id_strings_str11[sizeof("def_boolean")]; + char kconf_id_strings_str12[sizeof("def_tristate")]; + char kconf_id_strings_str13[sizeof("hex")]; + char kconf_id_strings_str14[sizeof("defconfig_list")]; + char kconf_id_strings_str16[sizeof("option")]; + char kconf_id_strings_str17[sizeof("if")]; + char kconf_id_strings_str18[sizeof("optional")]; + char kconf_id_strings_str20[sizeof("endif")]; + char kconf_id_strings_str21[sizeof("choice")]; + char kconf_id_strings_str22[sizeof("endmenu")]; + char kconf_id_strings_str23[sizeof("requires")]; + char kconf_id_strings_str24[sizeof("endchoice")]; + char kconf_id_strings_str26[sizeof("config")]; + char kconf_id_strings_str27[sizeof("modules")]; + char kconf_id_strings_str28[sizeof("int")]; + char kconf_id_strings_str29[sizeof("menu")]; + char kconf_id_strings_str31[sizeof("prompt")]; + char kconf_id_strings_str32[sizeof("depends")]; + char kconf_id_strings_str33[sizeof("tristate")]; char kconf_id_strings_str34[sizeof("bool")]; + char kconf_id_strings_str35[sizeof("menuconfig")]; + char kconf_id_strings_str36[sizeof("select")]; char kconf_id_strings_str37[sizeof("boolean")]; - char kconf_id_strings_str41[sizeof("choice")]; - char kconf_id_strings_str46[sizeof("prompt")]; + char kconf_id_strings_str39[sizeof("help")]; + char kconf_id_strings_str41[sizeof("source")]; + char kconf_id_strings_str42[sizeof("comment")]; + char kconf_id_strings_str43[sizeof("mainmenu")]; + char kconf_id_strings_str46[sizeof("enable")]; }; static struct kconf_id_strings_t kconf_id_strings_contents = { - "if", - "int", - "help", - "endif", - "select", - "endmenu", - "tristate", - "endchoice", - "range", + "on", "string", "default", "def_bool", - "menu", + "range", "def_boolean", "def_tristate", - "mainmenu", - "menuconfig", - "config", - "on", "hex", - "source", - "depends", + "defconfig_list", + "option", + "if", "optional", - "enable", - "comment", + "endif", + "choice", + "endmenu", "requires", + "endchoice", + "config", + "modules", + "int", + "menu", + "prompt", + "depends", + "tristate", "bool", + "menuconfig", + "select", "boolean", - "choice", - "prompt" + "help", + "source", + "comment", + "mainmenu", + "enable" }; #define kconf_id_strings ((const char *) &kconf_id_strings_contents) #ifdef __GNUC__ @@ -161,9 +167,9 @@ kconf_id_lookup (register const char *st { enum { - TOTAL_KEYWORDS = 30, + TOTAL_KEYWORDS = 33, MIN_WORD_LENGTH = 2, - MAX_WORD_LENGTH = 12, + MAX_WORD_LENGTH = 14, MIN_HASH_VALUE = 2, MAX_HASH_VALUE = 46 }; @@ -171,43 +177,48 @@ kconf_id_lookup (register const char *st static struct kconf_id wordlist[] = { {-1}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4, T_HELP, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_SELECT, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_ON, TF_PARAM}, + {-1}, {-1}, {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_TYPE, TF_COMMAND, S_STRING}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, + {-1}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_TYPE, TF_COMMAND, S_STRING}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_MENU, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_TRISTATE}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_TYPE, TF_COMMAND, S_HEX}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_OPT_DEFCONFIG_LIST,TF_OPTION}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_MAINMENU, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_OPTION, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_IF, TF_COMMAND|TF_PARAM}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_OPTIONAL, TF_COMMAND}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_MENUCONFIG, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CONFIG, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ON, TF_PARAM}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_HEX}, - {-1}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SOURCE, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_DEPENDS, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPTIONAL, TF_COMMAND}, - {-1}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_REQUIRES, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_ENDIF, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CHOICE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ENDMENU, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_REQUIRES, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str24, T_ENDCHOICE, TF_COMMAND}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_CONFIG, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_OPT_MODULES, TF_OPTION}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_TYPE, TF_COMMAND, S_INT}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str29, T_MENU, TF_COMMAND}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_PROMPT, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_DEPENDS, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_TYPE, TF_COMMAND, S_TRISTATE}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34, T_TYPE, TF_COMMAND, S_BOOLEAN}, - {-1}, {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str35, T_MENUCONFIG, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str36, T_SELECT, TF_COMMAND}, {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_TYPE, TF_COMMAND, S_BOOLEAN}, - {-1}, {-1}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_CHOICE, TF_COMMAND}, - {-1}, {-1}, {-1}, {-1}, - {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_PROMPT, TF_COMMAND} + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str39, T_HELP, TF_COMMAND}, + {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_SOURCE, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str42, T_COMMENT, TF_COMMAND}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str43, T_MAINMENU, TF_COMMAND}, + {-1}, {-1}, + {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_SELECT, TF_COMMAND} }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) diff --git a/scripts/kconfig/zconf.tab.c_shipped b/scripts/kconfig/zconf.tab.c_shipped index ea7755d..2fb0a4f 100644 --- a/scripts/kconfig/zconf.tab.c_shipped +++ b/scripts/kconfig/zconf.tab.c_shipped @@ -1,7 +1,7 @@ -/* A Bison parser, made by GNU Bison 2.0. */ +/* A Bison parser, made by GNU Bison 2.1. */ /* Skeleton parser for Yacc-like parsing with Bison, - Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /* As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. @@ -36,6 +36,9 @@ /* Identify Bison output. */ #define YYBISON 1 +/* Bison version. */ +#define YYBISON_VERSION "2.1" + /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -82,19 +85,21 @@ # define YYTOKENTYPE T_DEFAULT = 276, T_SELECT = 277, T_RANGE = 278, - T_ON = 279, - T_WORD = 280, - T_WORD_QUOTE = 281, - T_UNEQUAL = 282, - T_CLOSE_PAREN = 283, - T_OPEN_PAREN = 284, - T_EOL = 285, - T_OR = 286, - T_AND = 287, - T_EQUAL = 288, - T_NOT = 289 + T_OPTION = 279, + T_ON = 280, + T_WORD = 281, + T_WORD_QUOTE = 282, + T_UNEQUAL = 283, + T_CLOSE_PAREN = 284, + T_OPEN_PAREN = 285, + T_EOL = 286, + T_OR = 287, + T_AND = 288, + T_EQUAL = 289, + T_NOT = 290 }; #endif +/* Tokens. */ #define T_MAINMENU 258 #define T_MENU 259 #define T_ENDMENU 260 @@ -116,17 +121,18 @@ #define T_TYPE 275 #define T_DEFAULT 276 #define T_SELECT 277 #define T_RANGE 278 -#define T_ON 279 -#define T_WORD 280 -#define T_WORD_QUOTE 281 -#define T_UNEQUAL 282 -#define T_CLOSE_PAREN 283 -#define T_OPEN_PAREN 284 -#define T_EOL 285 -#define T_OR 286 -#define T_AND 287 -#define T_EQUAL 288 -#define T_NOT 289 +#define T_OPTION 279 +#define T_ON 280 +#define T_WORD 281 +#define T_WORD_QUOTE 282 +#define T_UNEQUAL 283 +#define T_CLOSE_PAREN 284 +#define T_OPEN_PAREN 285 +#define T_EOL 286 +#define T_OR 287 +#define T_AND 288 +#define T_EQUAL 289 +#define T_NOT 290 @@ -187,6 +193,11 @@ #else # define YYERROR_VERBOSE 0 #endif +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + #if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) typedef union YYSTYPE { @@ -197,7 +208,7 @@ typedef union YYSTYPE { struct menu *menu; struct kconf_id *id; } YYSTYPE; -/* Line 190 of yacc.c. */ +/* Line 196 of yacc.c. */ # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 @@ -209,17 +220,36 @@ #endif /* Copy the second part of user declarations. */ -/* Line 213 of yacc.c. */ +/* Line 219 of yacc.c. */ -#if ! defined (yyoverflow) || YYERROR_VERBOSE +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) && (defined (__STDC__) || defined (__cplusplus)) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif -# ifndef YYFREE -# define YYFREE free +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif # endif -# ifndef YYMALLOC -# define YYMALLOC malloc +# ifndef YY_ +# define YY_(msgid) msgid # endif +#endif + +#if ! defined (yyoverflow) || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ @@ -229,6 +259,10 @@ # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # else # define YYSTACK_ALLOC alloca +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYINCLUDED_STDLIB_H +# endif # endif # endif # endif @@ -236,13 +270,39 @@ # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) -# else -# if defined (__STDC__) || defined (__cplusplus) -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2005 */ # endif +# else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM ((YYSIZE_T) -1) +# endif +# ifdef __cplusplus +extern "C" { +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if (! defined (malloc) && ! defined (YYINCLUDED_STDLIB_H) \ + && (defined (__STDC__) || defined (__cplusplus))) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if (! defined (free) && ! defined (YYINCLUDED_STDLIB_H) \ + && (defined (__STDC__) || defined (__cplusplus))) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifdef __cplusplus +} +# endif # endif #endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ @@ -277,7 +337,7 @@ # else # define YYCOPY(To, From, Count) \ do \ { \ - register YYSIZE_T yyi; \ + YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ @@ -312,22 +372,22 @@ #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 3 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 264 +#define YYLAST 275 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 35 +#define YYNTOKENS 36 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 42 +#define YYNNTS 45 /* YYNRULES -- Number of rules. */ -#define YYNRULES 104 +#define YYNRULES 110 /* YYNRULES -- Number of states. */ -#define YYNSTATES 175 +#define YYNSTATES 183 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 289 +#define YYMAXUTOK 290 -#define YYTRANSLATE(YYX) \ +#define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ @@ -361,7 +421,8 @@ static const unsigned char yytranslate[] 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34 + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35 }; #if YYDEBUG @@ -372,72 +433,75 @@ static const unsigned short int yyprhs[] 0, 0, 3, 5, 6, 9, 12, 15, 20, 23, 28, 33, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 67, 70, 74, 77, - 81, 84, 85, 88, 91, 94, 97, 100, 104, 109, - 114, 119, 125, 128, 131, 133, 137, 138, 141, 144, - 147, 150, 153, 158, 162, 165, 170, 171, 174, 178, - 180, 184, 185, 188, 191, 194, 198, 201, 203, 207, - 208, 211, 214, 217, 221, 225, 228, 231, 234, 235, - 238, 241, 244, 249, 253, 257, 258, 261, 263, 265, - 268, 271, 274, 276, 279, 280, 283, 285, 289, 293, - 297, 300, 304, 308, 310 + 81, 84, 85, 88, 91, 94, 97, 100, 103, 107, + 112, 117, 122, 128, 132, 133, 137, 138, 141, 144, + 147, 149, 153, 154, 157, 160, 163, 166, 169, 174, + 178, 181, 186, 187, 190, 194, 196, 200, 201, 204, + 207, 210, 214, 217, 219, 223, 224, 227, 230, 233, + 237, 241, 244, 247, 250, 251, 254, 257, 260, 265, + 269, 273, 274, 277, 279, 281, 284, 287, 290, 292, + 295, 296, 299, 301, 305, 309, 313, 316, 320, 324, + 326 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yysigned_char yyrhs[] = { - 36, 0, -1, 37, -1, -1, 37, 39, -1, 37, - 50, -1, 37, 61, -1, 37, 3, 71, 73, -1, - 37, 72, -1, 37, 25, 1, 30, -1, 37, 38, - 1, 30, -1, 37, 1, 30, -1, 16, -1, 19, + 37, 0, -1, 38, -1, -1, 38, 40, -1, 38, + 54, -1, 38, 65, -1, 38, 3, 75, 77, -1, + 38, 76, -1, 38, 26, 1, 31, -1, 38, 39, + 1, 31, -1, 38, 1, 31, -1, 16, -1, 19, -1, 20, -1, 22, -1, 18, -1, 23, -1, 21, - -1, 30, -1, 56, -1, 65, -1, 42, -1, 44, - -1, 63, -1, 25, 1, 30, -1, 1, 30, -1, - 10, 25, 30, -1, 41, 45, -1, 11, 25, 30, - -1, 43, 45, -1, -1, 45, 46, -1, 45, 69, - -1, 45, 67, -1, 45, 40, -1, 45, 30, -1, - 20, 70, 30, -1, 19, 71, 74, 30, -1, 21, - 75, 74, 30, -1, 22, 25, 74, 30, -1, 23, - 76, 76, 74, 30, -1, 7, 30, -1, 47, 51, - -1, 72, -1, 48, 53, 49, -1, -1, 51, 52, - -1, 51, 69, -1, 51, 67, -1, 51, 30, -1, - 51, 40, -1, 19, 71, 74, 30, -1, 20, 70, - 30, -1, 18, 30, -1, 21, 25, 74, 30, -1, - -1, 53, 39, -1, 14, 75, 73, -1, 72, -1, - 54, 57, 55, -1, -1, 57, 39, -1, 57, 61, - -1, 57, 50, -1, 4, 71, 30, -1, 58, 68, - -1, 72, -1, 59, 62, 60, -1, -1, 62, 39, - -1, 62, 61, -1, 62, 50, -1, 6, 71, 30, - -1, 9, 71, 30, -1, 64, 68, -1, 12, 30, - -1, 66, 13, -1, -1, 68, 69, -1, 68, 30, - -1, 68, 40, -1, 16, 24, 75, 30, -1, 16, - 75, 30, -1, 17, 75, 30, -1, -1, 71, 74, - -1, 25, -1, 26, -1, 5, 30, -1, 8, 30, - -1, 15, 30, -1, 30, -1, 73, 30, -1, -1, - 14, 75, -1, 76, -1, 76, 33, 76, -1, 76, - 27, 76, -1, 29, 75, 28, -1, 34, 75, -1, - 75, 31, 75, -1, 75, 32, 75, -1, 25, -1, - 26, -1 + -1, 31, -1, 60, -1, 69, -1, 43, -1, 45, + -1, 67, -1, 26, 1, 31, -1, 1, 31, -1, + 10, 26, 31, -1, 42, 46, -1, 11, 26, 31, + -1, 44, 46, -1, -1, 46, 47, -1, 46, 48, + -1, 46, 73, -1, 46, 71, -1, 46, 41, -1, + 46, 31, -1, 20, 74, 31, -1, 19, 75, 78, + 31, -1, 21, 79, 78, 31, -1, 22, 26, 78, + 31, -1, 23, 80, 80, 78, 31, -1, 24, 49, + 31, -1, -1, 49, 26, 50, -1, -1, 34, 75, + -1, 7, 31, -1, 51, 55, -1, 76, -1, 52, + 57, 53, -1, -1, 55, 56, -1, 55, 73, -1, + 55, 71, -1, 55, 31, -1, 55, 41, -1, 19, + 75, 78, 31, -1, 20, 74, 31, -1, 18, 31, + -1, 21, 26, 78, 31, -1, -1, 57, 40, -1, + 14, 79, 77, -1, 76, -1, 58, 61, 59, -1, + -1, 61, 40, -1, 61, 65, -1, 61, 54, -1, + 4, 75, 31, -1, 62, 72, -1, 76, -1, 63, + 66, 64, -1, -1, 66, 40, -1, 66, 65, -1, + 66, 54, -1, 6, 75, 31, -1, 9, 75, 31, + -1, 68, 72, -1, 12, 31, -1, 70, 13, -1, + -1, 72, 73, -1, 72, 31, -1, 72, 41, -1, + 16, 25, 79, 31, -1, 16, 79, 31, -1, 17, + 79, 31, -1, -1, 75, 78, -1, 26, -1, 27, + -1, 5, 31, -1, 8, 31, -1, 15, 31, -1, + 31, -1, 77, 31, -1, -1, 14, 79, -1, 80, + -1, 80, 34, 80, -1, 80, 28, 80, -1, 30, + 79, 29, -1, 35, 79, -1, 79, 32, 79, -1, + 79, 33, 79, -1, 26, -1, 27, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const unsigned short int yyrline[] = { - 0, 103, 103, 105, 107, 108, 109, 110, 111, 112, - 113, 117, 121, 121, 121, 121, 121, 121, 121, 125, - 126, 127, 128, 129, 130, 134, 135, 141, 149, 155, - 163, 173, 175, 176, 177, 178, 179, 182, 190, 196, - 206, 212, 220, 229, 234, 242, 245, 247, 248, 249, - 250, 251, 254, 260, 271, 277, 287, 289, 294, 302, - 310, 313, 315, 316, 317, 322, 329, 334, 342, 345, - 347, 348, 349, 352, 360, 367, 374, 380, 387, 389, - 390, 391, 394, 399, 404, 412, 414, 419, 420, 423, - 424, 425, 429, 430, 433, 434, 437, 438, 439, 440, - 441, 442, 443, 446, 447 + 0, 105, 105, 107, 109, 110, 111, 112, 113, 114, + 115, 119, 123, 123, 123, 123, 123, 123, 123, 127, + 128, 129, 130, 131, 132, 136, 137, 143, 151, 157, + 165, 175, 177, 178, 179, 180, 181, 182, 185, 193, + 199, 209, 215, 221, 224, 226, 237, 238, 243, 252, + 257, 265, 268, 270, 271, 272, 273, 274, 277, 283, + 294, 300, 310, 312, 317, 325, 333, 336, 338, 339, + 340, 345, 352, 357, 365, 368, 370, 371, 372, 375, + 383, 390, 397, 403, 410, 412, 413, 414, 417, 422, + 427, 435, 437, 442, 443, 446, 447, 448, 452, 453, + 456, 457, 460, 461, 462, 463, 464, 465, 466, 469, + 470 }; #endif -#if YYDEBUG || YYERROR_VERBOSE -/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { @@ -445,17 +509,18 @@ static const char *const yytname[] = "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG", "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS", "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT", - "T_SELECT", "T_RANGE", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL", - "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL", - "T_NOT", "$accept", "input", "stmt_list", "option_name", "common_stmt", - "option_error", "config_entry_start", "config_stmt", + "T_SELECT", "T_RANGE", "T_OPTION", "T_ON", "T_WORD", "T_WORD_QUOTE", + "T_UNEQUAL", "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", + "T_EQUAL", "T_NOT", "$accept", "input", "stmt_list", "option_name", + "common_stmt", "option_error", "config_entry_start", "config_stmt", "menuconfig_entry_start", "menuconfig_stmt", "config_option_list", - "config_option", "choice", "choice_entry", "choice_end", "choice_stmt", - "choice_option_list", "choice_option", "choice_block", "if_entry", - "if_end", "if_stmt", "if_block", "menu", "menu_entry", "menu_end", - "menu_stmt", "menu_block", "source_stmt", "comment", "comment_stmt", - "help_start", "help", "depends_list", "depends", "prompt_stmt_opt", - "prompt", "end", "nl", "if_expr", "expr", "symbol", 0 + "config_option", "symbol_option", "symbol_option_list", + "symbol_option_arg", "choice", "choice_entry", "choice_end", + "choice_stmt", "choice_option_list", "choice_option", "choice_block", + "if_entry", "if_end", "if_stmt", "if_block", "menu", "menu_entry", + "menu_end", "menu_stmt", "menu_block", "source_stmt", "comment", + "comment_stmt", "help_start", "help", "depends_list", "depends", + "prompt_stmt_opt", "prompt", "end", "nl", "if_expr", "expr", "symbol", 0 }; #endif @@ -467,24 +532,25 @@ static const unsigned short int yytoknum 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, - 285, 286, 287, 288, 289 + 285, 286, 287, 288, 289, 290 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const unsigned char yyr1[] = { - 0, 35, 36, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 38, 38, 38, 38, 38, 38, 38, 39, - 39, 39, 39, 39, 39, 40, 40, 41, 42, 43, - 44, 45, 45, 45, 45, 45, 45, 46, 46, 46, - 46, 46, 47, 48, 49, 50, 51, 51, 51, 51, - 51, 51, 52, 52, 52, 52, 53, 53, 54, 55, - 56, 57, 57, 57, 57, 58, 59, 60, 61, 62, - 62, 62, 62, 63, 64, 65, 66, 67, 68, 68, - 68, 68, 69, 69, 69, 70, 70, 71, 71, 72, - 72, 72, 73, 73, 74, 74, 75, 75, 75, 75, - 75, 75, 75, 76, 76 + 0, 36, 37, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 39, 39, 39, 39, 39, 39, 39, 40, + 40, 40, 40, 40, 40, 41, 41, 42, 43, 44, + 45, 46, 46, 46, 46, 46, 46, 46, 47, 47, + 47, 47, 47, 48, 49, 49, 50, 50, 51, 52, + 53, 54, 55, 55, 55, 55, 55, 55, 56, 56, + 56, 56, 57, 57, 58, 59, 60, 61, 61, 61, + 61, 62, 63, 64, 65, 66, 66, 66, 66, 67, + 68, 69, 70, 71, 72, 72, 72, 72, 73, 73, + 73, 74, 74, 75, 75, 76, 76, 76, 77, 77, + 78, 78, 79, 79, 79, 79, 79, 79, 79, 80, + 80 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -493,14 +559,15 @@ static const unsigned char yyr2[] = 0, 2, 1, 0, 2, 2, 2, 4, 2, 4, 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 3, - 2, 0, 2, 2, 2, 2, 2, 3, 4, 4, - 4, 5, 2, 2, 1, 3, 0, 2, 2, 2, - 2, 2, 4, 3, 2, 4, 0, 2, 3, 1, - 3, 0, 2, 2, 2, 3, 2, 1, 3, 0, - 2, 2, 2, 3, 3, 2, 2, 2, 0, 2, - 2, 2, 4, 3, 3, 0, 2, 1, 1, 2, - 2, 2, 1, 2, 0, 2, 1, 3, 3, 3, - 2, 3, 3, 1, 1 + 2, 0, 2, 2, 2, 2, 2, 2, 3, 4, + 4, 4, 5, 3, 0, 3, 0, 2, 2, 2, + 1, 3, 0, 2, 2, 2, 2, 2, 4, 3, + 2, 4, 0, 2, 3, 1, 3, 0, 2, 2, + 2, 3, 2, 1, 3, 0, 2, 2, 2, 3, + 3, 2, 2, 2, 0, 2, 2, 2, 4, 3, + 3, 0, 2, 1, 1, 2, 2, 2, 1, 2, + 0, 2, 1, 3, 3, 3, 2, 3, 3, 1, + 1 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -511,175 +578,164 @@ static const unsigned char yydefact[] = 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 16, 13, 14, 18, 15, 17, 0, 19, 0, 4, 31, 22, 31, - 23, 46, 56, 5, 61, 20, 78, 69, 6, 24, - 78, 21, 8, 11, 87, 88, 0, 0, 89, 0, - 42, 90, 0, 0, 0, 103, 104, 0, 0, 0, - 96, 91, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 92, 7, 65, 73, 74, 27, 29, 0, - 100, 0, 0, 58, 0, 0, 9, 10, 0, 0, - 0, 0, 0, 85, 0, 0, 0, 0, 36, 35, - 32, 0, 34, 33, 0, 0, 85, 0, 50, 51, - 47, 49, 48, 57, 45, 44, 62, 64, 60, 63, - 59, 80, 81, 79, 70, 72, 68, 71, 67, 93, - 99, 101, 102, 98, 97, 26, 76, 0, 0, 0, - 94, 0, 94, 94, 94, 0, 0, 77, 54, 94, - 0, 94, 0, 83, 84, 0, 0, 37, 86, 0, - 0, 94, 25, 0, 53, 0, 82, 95, 38, 39, - 40, 0, 52, 55, 41 + 23, 52, 62, 5, 67, 20, 84, 75, 6, 24, + 84, 21, 8, 11, 93, 94, 0, 0, 95, 0, + 48, 96, 0, 0, 0, 109, 110, 0, 0, 0, + 102, 97, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 98, 7, 71, 79, 80, 27, 29, 0, + 106, 0, 0, 64, 0, 0, 9, 10, 0, 0, + 0, 0, 0, 91, 0, 0, 0, 44, 0, 37, + 36, 32, 33, 0, 35, 34, 0, 0, 91, 0, + 56, 57, 53, 55, 54, 63, 51, 50, 68, 70, + 66, 69, 65, 86, 87, 85, 76, 78, 74, 77, + 73, 99, 105, 107, 108, 104, 103, 26, 82, 0, + 0, 0, 100, 0, 100, 100, 100, 0, 0, 0, + 83, 60, 100, 0, 100, 0, 89, 90, 0, 0, + 38, 92, 0, 0, 100, 46, 43, 25, 0, 59, + 0, 88, 101, 39, 40, 41, 0, 0, 45, 58, + 61, 42, 47 }; /* YYDEFGOTO[NTERM-NUM]. */ static const short int yydefgoto[] = { - -1, 1, 2, 25, 26, 99, 27, 28, 29, 30, - 64, 100, 31, 32, 114, 33, 66, 110, 67, 34, - 118, 35, 68, 36, 37, 126, 38, 70, 39, 40, - 41, 101, 102, 69, 103, 141, 142, 42, 73, 156, - 59, 60 + -1, 1, 2, 25, 26, 100, 27, 28, 29, 30, + 64, 101, 102, 148, 178, 31, 32, 116, 33, 66, + 112, 67, 34, 120, 35, 68, 36, 37, 128, 38, + 70, 39, 40, 41, 103, 104, 69, 105, 143, 144, + 42, 73, 159, 59, 60 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -78 +#define YYPACT_NINF -135 static const short int yypact[] = { - -78, 2, 159, -78, -21, 0, 0, -12, 0, 1, - 4, 0, 27, 38, 60, 58, -78, -78, -78, -78, - -78, -78, -78, 100, -78, 104, -78, -78, -78, -78, - -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, - -78, -78, -78, -78, -78, -78, 86, 113, -78, 114, - -78, -78, 125, 127, 128, -78, -78, 60, 60, 210, - 65, -78, 141, 142, 39, 103, 182, 200, 6, 66, - 6, 131, -78, 146, -78, -78, -78, -78, -78, 196, - -78, 60, 60, 146, 40, 40, -78, -78, 155, 156, - -2, 60, 0, 0, 60, 105, 40, 194, -78, -78, - -78, 206, -78, -78, 183, 0, 0, 195, -78, -78, - -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, - -78, -78, -78, -78, -78, -78, -78, -78, -78, -78, - -78, 197, -78, -78, -78, -78, -78, 60, 213, 216, - 212, 203, 212, 190, 212, 40, 208, -78, -78, 212, - 222, 212, 219, -78, -78, 60, 223, -78, -78, 224, - 225, 212, -78, 226, -78, 227, -78, 47, -78, -78, - -78, 228, -78, -78, -78 + -135, 2, 170, -135, -14, 56, 56, -8, 56, 24, + 67, 56, 7, 14, 62, 97, -135, -135, -135, -135, + -135, -135, -135, 156, -135, 166, -135, -135, -135, -135, + -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, -135, -135, -135, -135, -135, 138, 151, -135, 152, + -135, -135, 163, 167, 176, -135, -135, 62, 62, 185, + -19, -135, 188, 190, 42, 103, 194, 85, 70, 222, + 70, 132, -135, 191, -135, -135, -135, -135, -135, 127, + -135, 62, 62, 191, 104, 104, -135, -135, 193, 203, + 9, 62, 56, 56, 62, 161, 104, -135, 196, -135, + -135, -135, -135, 233, -135, -135, 204, 56, 56, 221, + -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, -135, -135, -135, -135, -135, -135, -135, -135, -135, + -135, -135, -135, 219, -135, -135, -135, -135, -135, 62, + 209, 212, 240, 224, 240, -1, 240, 104, 41, 225, + -135, -135, 240, 226, 240, 218, -135, -135, 62, 227, + -135, -135, 228, 229, 240, 230, -135, -135, 231, -135, + 232, -135, 112, -135, -135, -135, 234, 56, -135, -135, + -135, -135, -135 }; /* YYPGOTO[NTERM-NUM]. */ static const short int yypgoto[] = { - -78, -78, -78, -78, 164, -36, -78, -78, -78, -78, - 230, -78, -78, -78, -78, 29, -78, -78, -78, -78, - -78, -78, -78, -78, -78, -78, 59, -78, -78, -78, - -78, -78, 198, 220, 24, 157, -5, 169, 202, 74, - -53, -77 + -135, -135, -135, -135, 94, -45, -135, -135, -135, -135, + 237, -135, -135, -135, -135, -135, -135, -135, -54, -135, + -135, -135, -135, -135, -135, -135, -135, -135, -135, 1, + -135, -135, -135, -135, -135, 195, 235, -44, 159, -5, + 98, 210, -134, -53, -77 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -76 +#define YYTABLE_NINF -82 static const short int yytable[] = { - 46, 47, 3, 49, 79, 80, 52, 133, 134, 43, - 6, 7, 8, 9, 10, 11, 12, 13, 48, 145, - 14, 15, 137, 55, 56, 44, 45, 57, 131, 132, - 109, 50, 58, 122, 51, 122, 24, 138, 139, -28, - 88, 143, -28, -28, -28, -28, -28, -28, -28, -28, - -28, 89, 53, -28, -28, 90, 91, -28, 92, 93, - 94, 95, 96, 54, 97, 55, 56, 88, 161, 98, - -66, -66, -66, -66, -66, -66, -66, -66, 81, 82, - -66, -66, 90, 91, 152, 55, 56, 140, 61, 57, - 112, 97, 84, 123, 58, 123, 121, 117, 85, 125, - 149, 62, 167, -30, 88, 63, -30, -30, -30, -30, - -30, -30, -30, -30, -30, 89, 72, -30, -30, 90, - 91, -30, 92, 93, 94, 95, 96, 119, 97, 127, - 144, -75, 88, 98, -75, -75, -75, -75, -75, -75, - -75, -75, -75, 74, 75, -75, -75, 90, 91, -75, - -75, -75, -75, -75, -75, 76, 97, 77, 78, -2, - 4, 121, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 86, 87, 14, 15, 16, 129, 17, 18, 19, - 20, 21, 22, 88, 23, 135, 136, -43, -43, 24, - -43, -43, -43, -43, 89, 146, -43, -43, 90, 91, - 104, 105, 106, 107, 155, 7, 8, 97, 10, 11, - 12, 13, 108, 148, 14, 15, 158, 159, 160, 147, - 151, 81, 82, 163, 130, 165, 155, 81, 82, 82, - 24, 113, 116, 157, 124, 171, 115, 120, 162, 128, - 72, 81, 82, 153, 81, 82, 154, 81, 82, 166, - 81, 82, 164, 168, 169, 170, 172, 173, 174, 65, - 71, 83, 0, 150, 111 + 46, 47, 3, 49, 79, 80, 52, 135, 136, 84, + 161, 162, 163, 158, 119, 85, 127, 43, 168, 147, + 170, 111, 114, 48, 124, 125, 124, 125, 133, 134, + 176, 81, 82, 53, 139, 55, 56, 140, 141, 57, + 54, 145, -28, 88, 58, -28, -28, -28, -28, -28, + -28, -28, -28, -28, 89, 50, -28, -28, 90, 91, + -28, 92, 93, 94, 95, 96, 97, 165, 98, 121, + 164, 129, 166, 99, 6, 7, 8, 9, 10, 11, + 12, 13, 44, 45, 14, 15, 155, 142, 55, 56, + 7, 8, 57, 10, 11, 12, 13, 58, 51, 14, + 15, 24, 152, -30, 88, 172, -30, -30, -30, -30, + -30, -30, -30, -30, -30, 89, 24, -30, -30, 90, + 91, -30, 92, 93, 94, 95, 96, 97, 61, 98, + 55, 56, -81, 88, 99, -81, -81, -81, -81, -81, + -81, -81, -81, -81, 81, 82, -81, -81, 90, 91, + -81, -81, -81, -81, -81, -81, 132, 62, 98, 81, + 82, 115, 118, 123, 126, 117, 122, 63, 130, 72, + -2, 4, 182, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 74, 75, 14, 15, 16, 146, 17, 18, + 19, 20, 21, 22, 76, 88, 23, 149, 77, -49, + -49, 24, -49, -49, -49, -49, 89, 78, -49, -49, + 90, 91, 106, 107, 108, 109, 72, 81, 82, 86, + 98, 87, 131, 88, 137, 110, -72, -72, -72, -72, + -72, -72, -72, -72, 138, 151, -72, -72, 90, 91, + 156, 81, 82, 157, 81, 82, 150, 154, 98, 171, + 81, 82, 82, 123, 158, 160, 167, 169, 173, 174, + 175, 113, 179, 180, 177, 181, 65, 153, 0, 83, + 0, 0, 0, 0, 0, 71 }; static const short int yycheck[] = { - 5, 6, 0, 8, 57, 58, 11, 84, 85, 30, - 4, 5, 6, 7, 8, 9, 10, 11, 30, 96, - 14, 15, 24, 25, 26, 25, 26, 29, 81, 82, - 66, 30, 34, 69, 30, 71, 30, 90, 91, 0, - 1, 94, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 25, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 25, 25, 25, 26, 1, 145, 30, - 4, 5, 6, 7, 8, 9, 10, 11, 31, 32, - 14, 15, 16, 17, 137, 25, 26, 92, 30, 29, - 66, 25, 27, 69, 34, 71, 30, 68, 33, 70, - 105, 1, 155, 0, 1, 1, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 30, 14, 15, 16, - 17, 18, 19, 20, 21, 22, 23, 68, 25, 70, - 25, 0, 1, 30, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 30, 30, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 30, 25, 30, 30, 0, - 1, 30, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 30, 30, 14, 15, 16, 30, 18, 19, 20, - 21, 22, 23, 1, 25, 30, 30, 5, 6, 30, - 8, 9, 10, 11, 12, 1, 14, 15, 16, 17, - 18, 19, 20, 21, 14, 5, 6, 25, 8, 9, - 10, 11, 30, 30, 14, 15, 142, 143, 144, 13, - 25, 31, 32, 149, 28, 151, 14, 31, 32, 32, - 30, 67, 68, 30, 70, 161, 67, 68, 30, 70, - 30, 31, 32, 30, 31, 32, 30, 31, 32, 30, - 31, 32, 30, 30, 30, 30, 30, 30, 30, 29, - 40, 59, -1, 106, 66 + 5, 6, 0, 8, 57, 58, 11, 84, 85, 28, + 144, 145, 146, 14, 68, 34, 70, 31, 152, 96, + 154, 66, 66, 31, 69, 69, 71, 71, 81, 82, + 164, 32, 33, 26, 25, 26, 27, 90, 91, 30, + 26, 94, 0, 1, 35, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 31, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 26, 26, 68, + 147, 70, 31, 31, 4, 5, 6, 7, 8, 9, + 10, 11, 26, 27, 14, 15, 139, 92, 26, 27, + 5, 6, 30, 8, 9, 10, 11, 35, 31, 14, + 15, 31, 107, 0, 1, 158, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 31, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 31, 26, + 26, 27, 0, 1, 31, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 32, 33, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 29, 1, 26, 32, + 33, 67, 68, 31, 70, 67, 68, 1, 70, 31, + 0, 1, 177, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 31, 31, 14, 15, 16, 26, 18, 19, + 20, 21, 22, 23, 31, 1, 26, 1, 31, 5, + 6, 31, 8, 9, 10, 11, 12, 31, 14, 15, + 16, 17, 18, 19, 20, 21, 31, 32, 33, 31, + 26, 31, 31, 1, 31, 31, 4, 5, 6, 7, + 8, 9, 10, 11, 31, 31, 14, 15, 16, 17, + 31, 32, 33, 31, 32, 33, 13, 26, 26, 31, + 32, 33, 33, 31, 14, 31, 31, 31, 31, 31, + 31, 66, 31, 31, 34, 31, 29, 108, -1, 59, + -1, -1, -1, -1, -1, 40 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const unsigned char yystos[] = { - 0, 36, 37, 0, 1, 3, 4, 5, 6, 7, + 0, 37, 38, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 18, 19, 20, - 21, 22, 23, 25, 30, 38, 39, 41, 42, 43, - 44, 47, 48, 50, 54, 56, 58, 59, 61, 63, - 64, 65, 72, 30, 25, 26, 71, 71, 30, 71, - 30, 30, 71, 25, 25, 25, 26, 29, 34, 75, - 76, 30, 1, 1, 45, 45, 51, 53, 57, 68, - 62, 68, 30, 73, 30, 30, 30, 30, 30, 75, - 75, 31, 32, 73, 27, 33, 30, 30, 1, 12, - 16, 17, 19, 20, 21, 22, 23, 25, 30, 40, - 46, 66, 67, 69, 18, 19, 20, 21, 30, 40, - 52, 67, 69, 39, 49, 72, 39, 50, 55, 61, - 72, 30, 40, 69, 39, 50, 60, 61, 72, 30, - 28, 75, 75, 76, 76, 30, 30, 24, 75, 75, - 71, 70, 71, 75, 25, 76, 1, 13, 30, 71, - 70, 25, 75, 30, 30, 14, 74, 30, 74, 74, - 74, 76, 30, 74, 30, 74, 30, 75, 30, 30, - 30, 74, 30, 30, 30 + 21, 22, 23, 26, 31, 39, 40, 42, 43, 44, + 45, 51, 52, 54, 58, 60, 62, 63, 65, 67, + 68, 69, 76, 31, 26, 27, 75, 75, 31, 75, + 31, 31, 75, 26, 26, 26, 27, 30, 35, 79, + 80, 31, 1, 1, 46, 46, 55, 57, 61, 72, + 66, 72, 31, 77, 31, 31, 31, 31, 31, 79, + 79, 32, 33, 77, 28, 34, 31, 31, 1, 12, + 16, 17, 19, 20, 21, 22, 23, 24, 26, 31, + 41, 47, 48, 70, 71, 73, 18, 19, 20, 21, + 31, 41, 56, 71, 73, 40, 53, 76, 40, 54, + 59, 65, 76, 31, 41, 73, 40, 54, 64, 65, + 76, 31, 29, 79, 79, 80, 80, 31, 31, 25, + 79, 79, 75, 74, 75, 79, 26, 80, 49, 1, + 13, 31, 75, 74, 26, 79, 31, 31, 14, 78, + 31, 78, 78, 78, 80, 26, 31, 31, 78, 31, + 78, 31, 79, 31, 31, 31, 78, 34, 50, 31, + 31, 31, 75 }; -#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) -# define YYSIZE_T __SIZE_TYPE__ -#endif -#if ! defined (YYSIZE_T) && defined (size_t) -# define YYSIZE_T size_t -#endif -#if ! defined (YYSIZE_T) -# if defined (__STDC__) || defined (__cplusplus) -# include /* INFRINGES ON USER NAME SPACE */ -# define YYSIZE_T size_t -# endif -#endif -#if ! defined (YYSIZE_T) -# define YYSIZE_T unsigned int -#endif - #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) @@ -709,8 +765,8 @@ do \ goto yybackup; \ } \ else \ - { \ - yyerror ("syntax error: cannot back up");\ + { \ + yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) @@ -789,7 +845,7 @@ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ - yysymprint (stderr, \ + yysymprint (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ @@ -837,13 +893,13 @@ yy_reduce_print (yyrule) #endif { int yyi; - unsigned int yylno = yyrline[yyrule]; - YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu), ", yyrule - 1, yylno); /* Print the symbols being reduced, and their result. */ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) - YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); - YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); + YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname[yyr1[yyrule]]); } # define YY_REDUCE_PRINT(Rule) \ @@ -872,7 +928,7 @@ #endif if the built-in stack extension method is used). Do not make this value too large; the results are undefined if - SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH @@ -896,7 +952,7 @@ yystrlen (yystr) const char *yystr; # endif { - register const char *yys = yystr; + const char *yys = yystr; while (*yys++ != '\0') continue; @@ -921,8 +977,8 @@ yystpcpy (yydest, yysrc) const char *yysrc; # endif { - register char *yyd = yydest; - register const char *yys = yysrc; + char *yyd = yydest; + const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; @@ -932,7 +988,55 @@ # endif # endif # endif -#endif /* !YYERROR_VERBOSE */ +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + size_t yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +#endif /* YYERROR_VERBOSE */ @@ -998,7 +1102,7 @@ #endif switch (yytype) { - case 48: /* choice_entry */ + case 52: /* "choice_entry" */ { fprintf(stderr, "%s:%d: missing end statement for this entry\n", @@ -1008,7 +1112,7 @@ #endif }; break; - case 54: /* if_entry */ + case 58: /* "if_entry" */ { fprintf(stderr, "%s:%d: missing end statement for this entry\n", @@ -1018,7 +1122,7 @@ #endif }; break; - case 59: /* menu_entry */ + case 63: /* "menu_entry" */ { fprintf(stderr, "%s:%d: missing end statement for this entry\n", @@ -1082,13 +1186,13 @@ yyparse (void) #else int yyparse () - + ; #endif #endif { - register int yystate; - register int yyn; + int yystate; + int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; @@ -1106,12 +1210,12 @@ #endif /* The state stack. */ short int yyssa[YYINITDEPTH]; short int *yyss = yyssa; - register short int *yyssp; + short int *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; - register YYSTYPE *yyvsp; + YYSTYPE *yyvsp; @@ -1143,9 +1247,6 @@ #define YYPOPSTACK (yyvsp--, yyssp--) yyssp = yyss; yyvsp = yyvs; - - yyvsp[0] = yylval; - goto yysetstate; /*------------------------------------------------------------. @@ -1178,7 +1279,7 @@ #ifdef yyoverflow data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ - yyoverflow ("parser stack overflow", + yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), @@ -1189,11 +1290,11 @@ #ifdef yyoverflow } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE - goto yyoverflowlab; + goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) - goto yyoverflowlab; + goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; @@ -1203,7 +1304,7 @@ # else union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) - goto yyoverflowlab; + goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); @@ -1403,7 +1504,7 @@ yyreduce: ;} break; - case 37: + case 38: { menu_set_type((yyvsp[-2].id)->stype); @@ -1413,7 +1514,7 @@ yyreduce: ;} break; - case 38: + case 39: { menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr)); @@ -1421,7 +1522,7 @@ yyreduce: ;} break; - case 39: + case 40: { menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr)); @@ -1433,7 +1534,7 @@ yyreduce: ;} break; - case 40: + case 41: { menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr)); @@ -1441,7 +1542,7 @@ yyreduce: ;} break; - case 41: + case 42: { menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr)); @@ -1449,7 +1550,29 @@ yyreduce: ;} break; - case 42: + case 45: + + { + struct kconf_id *id = kconf_id_lookup((yyvsp[-1].string), strlen((yyvsp[-1].string))); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, (yyvsp[0].string)); + else + zconfprint("warning: ignoring unknown option %s", (yyvsp[-1].string)); + free((yyvsp[-1].string)); +;} + break; + + case 46: + + { (yyval.string) = NULL; ;} + break; + + case 47: + + { (yyval.string) = (yyvsp[0].string); ;} + break; + + case 48: { struct symbol *sym = sym_lookup(NULL, 0); @@ -1460,14 +1583,14 @@ yyreduce: ;} break; - case 43: + case 49: { (yyval.menu) = menu_add_menu(); ;} break; - case 44: + case 50: { if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) { @@ -1477,7 +1600,7 @@ yyreduce: ;} break; - case 52: + case 58: { menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr)); @@ -1485,7 +1608,7 @@ yyreduce: ;} break; - case 53: + case 59: { if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) { @@ -1498,7 +1621,7 @@ yyreduce: ;} break; - case 54: + case 60: { current_entry->sym->flags |= SYMBOL_OPTIONAL; @@ -1506,7 +1629,7 @@ yyreduce: ;} break; - case 55: + case 61: { if ((yyvsp[-3].id)->stype == S_UNKNOWN) { @@ -1518,7 +1641,7 @@ yyreduce: ;} break; - case 58: + case 64: { printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno()); @@ -1528,7 +1651,7 @@ yyreduce: ;} break; - case 59: + case 65: { if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) { @@ -1538,7 +1661,7 @@ yyreduce: ;} break; - case 65: + case 71: { menu_add_entry(NULL); @@ -1547,14 +1670,14 @@ yyreduce: ;} break; - case 66: + case 72: { (yyval.menu) = menu_add_menu(); ;} break; - case 67: + case 73: { if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) { @@ -1564,7 +1687,7 @@ yyreduce: ;} break; - case 73: + case 79: { printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string)); @@ -1572,7 +1695,7 @@ yyreduce: ;} break; - case 74: + case 80: { menu_add_entry(NULL); @@ -1581,14 +1704,14 @@ yyreduce: ;} break; - case 75: + case 81: { menu_end_entry(); ;} break; - case 76: + case 82: { printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno()); @@ -1596,14 +1719,14 @@ yyreduce: ;} break; - case 77: + case 83: { current_entry->sym->help = (yyvsp[0].string); ;} break; - case 82: + case 88: { menu_add_dep((yyvsp[-1].expr)); @@ -1611,7 +1734,7 @@ yyreduce: ;} break; - case 83: + case 89: { menu_add_dep((yyvsp[-1].expr)); @@ -1619,7 +1742,7 @@ yyreduce: ;} break; - case 84: + case 90: { menu_add_dep((yyvsp[-1].expr)); @@ -1627,87 +1750,88 @@ yyreduce: ;} break; - case 86: + case 92: { menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr)); ;} break; - case 89: + case 95: { (yyval.id) = (yyvsp[-1].id); ;} break; - case 90: + case 96: { (yyval.id) = (yyvsp[-1].id); ;} break; - case 91: + case 97: { (yyval.id) = (yyvsp[-1].id); ;} break; - case 94: + case 100: { (yyval.expr) = NULL; ;} break; - case 95: + case 101: { (yyval.expr) = (yyvsp[0].expr); ;} break; - case 96: + case 102: { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); ;} break; - case 97: + case 103: { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;} break; - case 98: + case 104: { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;} break; - case 99: + case 105: { (yyval.expr) = (yyvsp[-1].expr); ;} break; - case 100: + case 106: { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); ;} break; - case 101: + case 107: { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; - case 102: + case 108: { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; - case 103: + case 109: { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); ;} break; - case 104: + case 110: { (yyval.symbol) = sym_lookup((yyvsp[0].string), 1); free((yyvsp[0].string)); ;} break; + default: break; } -/* Line 1037 of yacc.c. */ +/* Line 1126 of yacc.c. */ yyvsp -= yylen; @@ -1747,12 +1871,36 @@ #if YYERROR_VERBOSE if (YYPACT_NINF < yyn && yyn < YYLAST) { - YYSIZE_T yysize = 0; int yytype = YYTRANSLATE (yychar); - const char* yyprefix; - char *yymsg; + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + char *yymsg = 0; +# define YYERROR_VERBOSE_ARGS_MAXIMUM 5 + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; +#if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +#endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; @@ -1760,48 +1908,68 @@ #if YYERROR_VERBOSE /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; - int yycount = 0; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); - yyprefix = ", expecting "; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { - yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); - yycount += 1; - if (yycount == 5) + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { - yysize = 0; + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; break; } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= yysize1 < yysize; + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; } - yysize += (sizeof ("syntax error, unexpected ") - + yystrlen (yytname[yytype])); - yymsg = (char *) YYSTACK_ALLOC (yysize); - if (yymsg != 0) - { - char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); - yyp = yystpcpy (yyp, yytname[yytype]); - if (yycount < 5) + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= yysize1 < yysize; + yysize = yysize1; + + if (!yysize_overflow && yysize <= YYSTACK_ALLOC_MAXIMUM) + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yymsg; + int yyi = 0; + while ((*yyp = *yyf)) { - yyprefix = ", expecting "; - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) - { - yyp = yystpcpy (yyp, yyprefix); - yyp = yystpcpy (yyp, yytname[yyx]); - yyprefix = " or "; - } + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } } yyerror (yymsg); YYSTACK_FREE (yymsg); } else - yyerror ("syntax error; also virtual memory exhausted"); + { + yyerror (YY_("syntax error")); + goto yyexhaustedlab; + } } else #endif /* YYERROR_VERBOSE */ - yyerror ("syntax error"); + yyerror (YY_("syntax error")); } @@ -1813,18 +1981,9 @@ #endif /* YYERROR_VERBOSE */ if (yychar <= YYEOF) { - /* If at end of input, pop the error token, - then the rest of the stack, then return failure. */ + /* Return failure if at end of input. */ if (yychar == YYEOF) - for (;;) - { - - YYPOPSTACK; - if (yyssp == yyss) - YYABORT; - yydestruct ("Error: popping", - yystos[*yyssp], yyvsp); - } + YYABORT; } else { @@ -1843,12 +2002,11 @@ #endif /* YYERROR_VERBOSE */ `---------------------------------------------------*/ yyerrorlab: -#ifdef __GNUC__ - /* Pacify GCC when the user code never invokes YYERROR and the label - yyerrorlab therefore never appears in user code. */ + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ if (0) goto yyerrorlab; -#endif yyvsp -= yylen; yyssp -= yylen; @@ -1911,23 +2069,29 @@ yyacceptlab: | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: - yydestruct ("Error: discarding lookahead", - yytoken, &yylval); - yychar = YYEMPTY; yyresult = 1; goto yyreturn; #ifndef yyoverflow -/*----------------------------------------------. -| yyoverflowlab -- parser overflow comes here. | -`----------------------------------------------*/ -yyoverflowlab: - yyerror ("parser stack overflow"); +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK; + } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); @@ -1948,7 +2112,9 @@ void conf_parse(const char *name) sym_init(); menu_init(); - modules_sym = sym_lookup("MODULES", 0); + modules_sym = sym_lookup(NULL, 0); + modules_sym->type = S_BOOLEAN; + modules_sym->flags |= SYMBOL_AUTO; rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); #if YYDEBUG @@ -1958,6 +2124,12 @@ #endif zconfparse(); if (zconfnerrs) exit(1); + if (!modules_sym->prop) { + struct property *prop; + + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(sym_lookup("MODULES", 0)); + } menu_finalize(&rootmenu); for_all_symbols(i, sym) { sym_check_deps(sym); diff --git a/scripts/kconfig/zconf.y b/scripts/kconfig/zconf.y index 1f61fba..ab44feb 100644 --- a/scripts/kconfig/zconf.y +++ b/scripts/kconfig/zconf.y @@ -71,6 +71,7 @@ #endif %token T_DEFAULT %token T_SELECT %token T_RANGE +%token T_OPTION %token T_ON %token T_WORD %token T_WORD_QUOTE @@ -91,6 +92,7 @@ #endif %type end %type option_name %type if_entry menu_entry choice_entry +%type symbol_option_arg %destructor { fprintf(stderr, "%s:%d: missing end statement for this entry\n", @@ -173,6 +175,7 @@ menuconfig_stmt: menuconfig_entry_start config_option_list: /* empty */ | config_option_list config_option + | config_option_list symbol_option | config_option_list depends | config_option_list help | config_option_list option_error @@ -215,6 +218,26 @@ config_option: T_RANGE symbol symbol if_ printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno()); }; +symbol_option: T_OPTION symbol_option_list T_EOL +; + +symbol_option_list: + /* empty */ + | symbol_option_list T_WORD symbol_option_arg +{ + struct kconf_id *id = kconf_id_lookup($2, strlen($2)); + if (id && id->flags & TF_OPTION) + menu_add_option(id->token, $3); + else + zconfprint("warning: ignoring unknown option %s", $2); + free($2); +}; + +symbol_option_arg: + /* empty */ { $$ = NULL; } + | T_EQUAL prompt { $$ = $2; } +; + /* choice entry */ choice: T_CHOICE T_EOL @@ -458,7 +481,9 @@ void conf_parse(const char *name) sym_init(); menu_init(); - modules_sym = sym_lookup("MODULES", 0); + modules_sym = sym_lookup(NULL, 0); + modules_sym->type = S_BOOLEAN; + modules_sym->flags |= SYMBOL_AUTO; rootmenu.prompt = menu_add_prompt(P_MENU, "Linux Kernel Configuration", NULL); #if YYDEBUG @@ -468,6 +493,12 @@ #endif zconfparse(); if (zconfnerrs) exit(1); + if (!modules_sym->prop) { + struct property *prop; + + prop = prop_alloc(P_DEFAULT, modules_sym); + prop->expr = expr_alloc_symbol(sym_lookup("MODULES", 0)); + } menu_finalize(&rootmenu); for_all_symbols(i, sym) { sym_check_deps(sym); diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 99fe4b7..f9460a6 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -253,6 +253,7 @@ # 2 - scanning field start. # 3 - scanning prototype. # 4 - documentation block my $state; +my $in_doc_sect; #declaration types: can be # 'function', 'struct', 'union', 'enum', 'typedef' @@ -1055,7 +1056,8 @@ sub output_struct_man(%) { # pointer-to-function print ".BI \" ".$1."\" ".$parameter." \") (".$2.")"."\"\n;\n"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { - print ".BI \" ".$1."\" ".$parameter.$2." \""."\"\n;\n"; + # bitfield + print ".BI \" ".$1."\ \" ".$parameter.$2." \""."\"\n;\n"; } else { $type =~ s/([^\*])$/$1 /; print ".BI \" ".$type."\" ".$parameter." \""."\"\n;\n"; @@ -1064,7 +1066,7 @@ sub output_struct_man(%) { } print "};\n.br\n"; - print ".SH Arguments\n"; + print ".SH Members\n"; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; @@ -1117,7 +1119,10 @@ sub output_function_text(%) { my %args = %{$_[0]}; my ($parameter, $section); - print "Function:\n\n"; + print "Name:\n\n"; + print $args{'function'}." - ".$args{'purpose'}."\n"; + + print "\nSynopsis:\n\n"; my $start=$args{'functiontype'}." ".$args{'function'}." ("; print $start; my $count = 0; @@ -1168,6 +1173,7 @@ sub output_enum_text(%) { my $count; print "Enum:\n\n"; + print "enum ".$args{'enum'}." - ".$args{'purpose'}."\n\n"; print "enum ".$args{'enum'}." {\n"; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { @@ -1196,7 +1202,7 @@ sub output_typedef_text(%) { my $count; print "Typedef:\n\n"; - print "typedef ".$args{'typedef'}."\n"; + print "typedef ".$args{'typedef'}." - ".$args{'purpose'}."\n"; output_section_text(@_); } @@ -1205,7 +1211,7 @@ sub output_struct_text(%) { my %args = %{$_[0]}; my ($parameter); - print $args{'type'}." ".$args{'struct'}.":\n\n"; + print $args{'type'}." ".$args{'struct'}." - ".$args{'purpose'}."\n\n"; print $args{'type'}." ".$args{'struct'}." {\n"; foreach $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { @@ -1673,6 +1679,9 @@ sub process_state3_type($$) { # replace <, >, and & sub xml_escape($) { my $text = shift; + if (($output_mode eq "text") || ($output_mode eq "man")) { + return $text; + } $text =~ s/\&/\\\\\\amp;/g; $text =~ s/\/\\\\\\gt;/g; @@ -1706,6 +1715,7 @@ sub process_file($) { if ($state == 0) { if (/$doc_start/o) { $state = 1; # next line is always the function name + $in_doc_sect = 0; } } elsif ($state == 1) { # this line is the function name (always) if (/$doc_block/o) { @@ -1756,12 +1766,20 @@ sub process_file($) { $newcontents = $2; if ($contents ne "") { + if (!$in_doc_sect && $verbose) { + print STDERR "Warning(${file}:$.): contents before sections\n"; + ++$warnings; + } dump_section($section, xml_escape($contents)); $section = $section_default; } + $in_doc_sect = 1; $contents = $newcontents; if ($contents ne "") { + if (substr($contents, 0, 1) eq " ") { + $contents = substr($contents, 1); + } $contents .= "\n"; } $section = $newsection; @@ -1776,7 +1794,7 @@ sub process_file($) { $prototype = ""; $state = 3; $brcount = 0; -# print STDERR "end of doc comment, looking for prototype\n"; +# print STDERR "end of doc comment, looking for prototype\n"; } elsif (/$doc_content/) { # miguel-style comment kludge, look for blank lines after # @parameter line to signify start of description @@ -1793,7 +1811,7 @@ # print STDERR "end of doc comment, print STDERR "Warning(${file}:$.): bad line: $_"; ++$warnings; } - } elsif ($state == 3) { # scanning for function { (end of prototype) + } elsif ($state == 3) { # scanning for function '{' (end of prototype) if ($decl_type eq 'function') { process_state3_function($_, $file); } else { diff --git a/scripts/mod/mk_elfconfig.c b/scripts/mod/mk_elfconfig.c index 3c92c83..725d61c 100644 --- a/scripts/mod/mk_elfconfig.c +++ b/scripts/mod/mk_elfconfig.c @@ -28,7 +28,7 @@ main(int argc, char **argv) printf("#define KERNEL_ELFCLASS ELFCLASS64\n"); break; default: - abort(); + exit(1); } switch (ei[EI_DATA]) { case ELFDATA2LSB: @@ -38,7 +38,7 @@ main(int argc, char **argv) printf("#define KERNEL_ELFDATA ELFDATA2MSB\n"); break; default: - abort(); + exit(1); } if (sizeof(unsigned long) == 4) { @@ -53,7 +53,7 @@ main(int argc, char **argv) else if (memcmp(endian_test.c, "\x02\x01", 2) == 0) printf("#define HOST_ELFDATA ELFDATA2LSB\n"); else - abort(); + exit(1); if ((strcmp(argv[1], "v850") == 0) || (strcmp(argv[1], "h8300") == 0)) printf("#define MODULE_SYMBOL_PREFIX \"_\"\n"); diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index d0f86ed..dfde0e8 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -13,6 +13,7 @@ #include #include "modpost.h" +#include "../../include/linux/license.h" /* Are we using CONFIG_MODVERSIONS? */ int modversions = 0; @@ -22,6 +23,11 @@ int have_vmlinux = 0; static int all_versions = 0; /* If we are modposting external module set to 1 */ static int external_module = 0; +/* How a symbol is exported */ +enum export { + export_plain, export_unused, export_gpl, + export_unused_gpl, export_gpl_future, export_unknown +}; void fatal(const char *fmt, ...) { @@ -97,6 +103,7 @@ static struct module *new_module(char *m /* add to list */ mod->name = p; + mod->gpl_compatible = -1; mod->next = modules; modules = mod; @@ -118,6 +125,7 @@ struct symbol { unsigned int kernel:1; /* 1 if symbol is from kernel * (only for external modules) **/ unsigned int preloaded:1; /* 1 if symbol from Module.symvers */ + enum export export; /* Type of export */ char name[0]; }; @@ -153,7 +161,8 @@ static struct symbol *alloc_symbol(const } /* For the hash of exported symbols */ -static struct symbol *new_symbol(const char *name, struct module *module) +static struct symbol *new_symbol(const char *name, struct module *module, + enum export export) { unsigned int hash; struct symbol *new; @@ -161,6 +170,7 @@ static struct symbol *new_symbol(const c hash = tdb_hash(name) % SYMBOL_HASH_SIZE; new = symbolhash[hash] = alloc_symbol(name, 0, symbolhash[hash]); new->module = module; + new->export = export; return new; } @@ -179,16 +189,63 @@ static struct symbol *find_symbol(const return NULL; } +static struct { + const char *str; + enum export export; +} export_list[] = { + { .str = "EXPORT_SYMBOL", .export = export_plain }, + { .str = "EXPORT_UNUSED_SYMBOL", .export = export_unused }, + { .str = "EXPORT_SYMBOL_GPL", .export = export_gpl }, + { .str = "EXPORT_UNUSED_SYMBOL_GPL", .export = export_unused_gpl }, + { .str = "EXPORT_SYMBOL_GPL_FUTURE", .export = export_gpl_future }, + { .str = "(unknown)", .export = export_unknown }, +}; + + +static const char *export_str(enum export ex) +{ + return export_list[ex].str; +} + +static enum export export_no(const char * s) +{ + int i; + if (!s) + return export_unknown; + for (i = 0; export_list[i].export != export_unknown; i++) { + if (strcmp(export_list[i].str, s) == 0) + return export_list[i].export; + } + return export_unknown; +} + +static enum export export_from_sec(struct elf_info *elf, Elf_Section sec) +{ + if (sec == elf->export_sec) + return export_plain; + else if (sec == elf->export_unused_sec) + return export_unused; + else if (sec == elf->export_gpl_sec) + return export_gpl; + else if (sec == elf->export_unused_gpl_sec) + return export_unused_gpl; + else if (sec == elf->export_gpl_future_sec) + return export_gpl_future; + else + return export_unknown; +} + /** * Add an exported symbol - it may have already been added without a * CRC, in this case just update the CRC **/ -static struct symbol *sym_add_exported(const char *name, struct module *mod) +static struct symbol *sym_add_exported(const char *name, struct module *mod, + enum export export) { struct symbol *s = find_symbol(name); if (!s) { - s = new_symbol(name, mod); + s = new_symbol(name, mod, export); } else { if (!s->preloaded) { warn("%s: '%s' exported twice. Previous export " @@ -200,16 +257,17 @@ static struct symbol *sym_add_exported(c s->preloaded = 0; s->vmlinux = is_vmlinux(mod->name); s->kernel = 0; + s->export = export; return s; } static void sym_update_crc(const char *name, struct module *mod, - unsigned int crc) + unsigned int crc, enum export export) { struct symbol *s = find_symbol(name); if (!s) - s = new_symbol(name, mod); + s = new_symbol(name, mod, export); s->crc = crc; s->crc_valid = 1; } @@ -283,7 +341,7 @@ static void parse_elf(struct elf_info *i hdr = grab_file(filename, &info->size); if (!hdr) { perror(filename); - abort(); + exit(1); } info->hdr = hdr; if (info->size < sizeof(*hdr)) @@ -309,13 +367,25 @@ static void parse_elf(struct elf_info *i for (i = 1; i < hdr->e_shnum; i++) { const char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + const char *secname; if (sechdrs[i].sh_offset > info->size) goto truncated; - if (strcmp(secstrings+sechdrs[i].sh_name, ".modinfo") == 0) { + secname = secstrings + sechdrs[i].sh_name; + if (strcmp(secname, ".modinfo") == 0) { info->modinfo = (void *)hdr + sechdrs[i].sh_offset; info->modinfo_len = sechdrs[i].sh_size; - } + } else if (strcmp(secname, "__ksymtab") == 0) + info->export_sec = i; + else if (strcmp(secname, "__ksymtab_unused") == 0) + info->export_unused_sec = i; + else if (strcmp(secname, "__ksymtab_gpl") == 0) + info->export_gpl_sec = i; + else if (strcmp(secname, "__ksymtab_unused_gpl") == 0) + info->export_unused_gpl_sec = i; + else if (strcmp(secname, "__ksymtab_gpl_future") == 0) + info->export_gpl_future_sec = i; + if (sechdrs[i].sh_type != SHT_SYMTAB) continue; @@ -353,6 +423,7 @@ static void handle_modversions(struct mo Elf_Sym *sym, const char *symname) { unsigned int crc; + enum export export = export_from_sec(info, sym->st_shndx); switch (sym->st_shndx) { case SHN_COMMON: @@ -362,7 +433,8 @@ static void handle_modversions(struct mo /* CRC'd symbol */ if (memcmp(symname, CRC_PFX, strlen(CRC_PFX)) == 0) { crc = (unsigned int) sym->st_value; - sym_update_crc(symname + strlen(CRC_PFX), mod, crc); + sym_update_crc(symname + strlen(CRC_PFX), mod, crc, + export); } break; case SHN_UNDEF: @@ -406,7 +478,8 @@ #endif default: /* All exported symbols */ if (memcmp(symname, KSYMTAB_PFX, strlen(KSYMTAB_PFX)) == 0) { - sym_add_exported(symname + strlen(KSYMTAB_PFX), mod); + sym_add_exported(symname + strlen(KSYMTAB_PFX), mod, + export); } if (strcmp(symname, MODULE_SYMBOL_PREFIX "init_module") == 0) mod->has_init = 1; @@ -437,13 +510,18 @@ static char *next_string(char *string, u return string; } -static char *get_modinfo(void *modinfo, unsigned long modinfo_len, - const char *tag) +static char *get_next_modinfo(void *modinfo, unsigned long modinfo_len, + const char *tag, char *info) { char *p; unsigned int taglen = strlen(tag); unsigned long size = modinfo_len; + if (info) { + size -= info - (char *)modinfo; + modinfo = next_string(info, &size); + } + for (p = modinfo; p; p = next_string(p, &size)) { if (strncmp(p, tag, taglen) == 0 && p[taglen] == '=') return p + taglen + 1; @@ -451,6 +529,13 @@ static char *get_modinfo(void *modinfo, return NULL; } +static char *get_modinfo(void *modinfo, unsigned long modinfo_len, + const char *tag) + +{ + return get_next_modinfo(modinfo, modinfo_len, tag, NULL); +} + /** * Test if string s ends in string sub * return 0 if match @@ -821,6 +906,10 @@ static int init_section_ref_ok(const cha ".pci_fixup_final", ".pdr", "__param", + "__ex_table", + ".fixup", + ".smp_locks", + ".plt", /* seen on ARCH=um build on x86_64. Harmless */ NULL }; /* Start of section names */ @@ -846,6 +935,8 @@ static int init_section_ref_ok(const cha for (s = namelist3; *s; s++) if (strstr(name, *s) != NULL) return 1; + if (strrcmp(name, ".init") == 0) + return 1; return 0; } @@ -892,6 +983,10 @@ static int exit_section_ref_ok(const cha ".exitcall.exit", ".eh_frame", ".stab", + "__ex_table", + ".fixup", + ".smp_locks", + ".plt", /* seen on ARCH=um build on x86_64. Harmless */ NULL }; /* Start of section names */ @@ -921,6 +1016,7 @@ static void read_symbols(char *modname) { const char *symname; char *version; + char *license; struct module *mod; struct elf_info info = { }; Elf_Sym *sym; @@ -936,6 +1032,18 @@ static void read_symbols(char *modname) mod->skip = 1; } + license = get_modinfo(info.modinfo, info.modinfo_len, "license"); + while (license) { + if (license_is_gpl_compatible(license)) + mod->gpl_compatible = 1; + else { + mod->gpl_compatible = 0; + break; + } + license = get_next_modinfo(info.modinfo, info.modinfo_len, + "license", license); + } + for (sym = info.symtab_start; sym < info.symtab_stop; sym++) { symname = info.strtab + sym->st_name; @@ -992,6 +1100,67 @@ void buf_write(struct buffer *buf, const buf->pos += len; } +static void check_for_gpl_usage(enum export exp, const char *m, const char *s) +{ + const char *e = is_vmlinux(m) ?"":".ko"; + + switch (exp) { + case export_gpl: + fatal("modpost: GPL-incompatible module %s%s " + "uses GPL-only symbol '%s'\n", m, e, s); + break; + case export_unused_gpl: + fatal("modpost: GPL-incompatible module %s%s " + "uses GPL-only symbol marked UNUSED '%s'\n", m, e, s); + break; + case export_gpl_future: + warn("modpost: GPL-incompatible module %s%s " + "uses future GPL-only symbol '%s'\n", m, e, s); + break; + case export_plain: + case export_unused: + case export_unknown: + /* ignore */ + break; + } +} + +static void check_for_unused(enum export exp, const char* m, const char* s) +{ + const char *e = is_vmlinux(m) ?"":".ko"; + + switch (exp) { + case export_unused: + case export_unused_gpl: + warn("modpost: module %s%s " + "uses symbol '%s' marked UNUSED\n", m, e, s); + break; + default: + /* ignore */ + break; + } +} + +static void check_exports(struct module *mod) +{ + struct symbol *s, *exp; + + for (s = mod->unres; s; s = s->next) { + const char *basename; + exp = find_symbol(s->name); + if (!exp || exp->module == mod) + continue; + basename = strrchr(mod->name, '/'); + if (basename) + basename++; + else + basename = mod->name; + if (!mod->gpl_compatible) + check_for_gpl_usage(exp->export, basename, exp->name); + check_for_unused(exp->export, basename, exp->name); + } +} + /** * Header for the generated file **/ @@ -1142,6 +1311,9 @@ static void write_if_changed(struct buff fclose(file); } +/* parse Module.symvers file. line format: + * 0x12345678symbolmodule[[export]something] + **/ static void read_dump(const char *fname, unsigned int kernel) { unsigned long size, pos = 0; @@ -1153,7 +1325,7 @@ static void read_dump(const char *fname, return; while ((line = get_next_line(&pos, file, size))) { - char *symname, *modname, *d; + char *symname, *modname, *d, *export, *end; unsigned int crc; struct module *mod; struct symbol *s; @@ -1164,8 +1336,10 @@ static void read_dump(const char *fname, if (!(modname = strchr(symname, '\t'))) goto fail; *modname++ = '\0'; - if (strchr(modname, '\t')) - goto fail; + if ((export = strchr(modname, '\t')) != NULL) + *export++ = '\0'; + if (export && ((end = strchr(export, '\t')) != NULL)) + *end = '\0'; crc = strtoul(line, &d, 16); if (*symname == '\0' || *modname == '\0' || *d != '\0') goto fail; @@ -1177,10 +1351,10 @@ static void read_dump(const char *fname, mod = new_module(NOFAIL(strdup(modname))); mod->skip = 1; } - s = sym_add_exported(symname, mod); + s = sym_add_exported(symname, mod, export_no(export)); s->kernel = kernel; s->preloaded = 1; - sym_update_crc(symname, mod, crc); + sym_update_crc(symname, mod, crc, export_no(export)); } return; fail: @@ -1210,9 +1384,10 @@ static void write_dump(const char *fname symbol = symbolhash[n]; while (symbol) { if (dump_sym(symbol)) - buf_printf(&buf, "0x%08x\t%s\t%s\n", + buf_printf(&buf, "0x%08x\t%s\t%s\t%s\n", symbol->crc, symbol->name, - symbol->module->name); + symbol->module->name, + export_str(symbol->export)); symbol = symbol->next; } } @@ -1263,6 +1438,12 @@ int main(int argc, char **argv) for (mod = modules; mod; mod = mod->next) { if (mod->skip) continue; + check_exports(mod); + } + + for (mod = modules; mod; mod = mod->next) { + if (mod->skip) + continue; buf.pos = 0; diff --git a/scripts/mod/modpost.h b/scripts/mod/modpost.h index 861d866..d398c61 100644 --- a/scripts/mod/modpost.h +++ b/scripts/mod/modpost.h @@ -100,6 +100,7 @@ buf_write(struct buffer *buf, const char struct module { struct module *next; const char *name; + int gpl_compatible; struct symbol *unres; int seen; int skip; @@ -115,6 +116,11 @@ struct elf_info { Elf_Shdr *sechdrs; Elf_Sym *symtab_start; Elf_Sym *symtab_stop; + Elf_Section export_sec; + Elf_Section export_unused_sec; + Elf_Section export_gpl_sec; + Elf_Section export_unused_gpl_sec; + Elf_Section export_gpl_future_sec; const char *strtab; char *modinfo; unsigned int modinfo_len; diff --git a/scripts/package/mkspec b/scripts/package/mkspec index 0b10387..df89284 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -73,8 +73,13 @@ echo "%ifarch ia64" echo 'cp $KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/efi/vmlinuz-$KERNELRELEASE" echo 'ln -s '"efi/vmlinuz-$KERNELRELEASE" '$RPM_BUILD_ROOT'"/boot/" echo "%else" +echo "%ifarch ppc64" +echo "cp vmlinux arch/powerpc/boot" +echo "cp arch/powerpc/boot/"'$KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" +echo "%else" echo 'cp $KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" echo "%endif" +echo "%endif" echo 'cp System.map $RPM_BUILD_ROOT'"/boot/System.map-$KERNELRELEASE" diff --git a/scripts/rt-tester/check-all.sh b/scripts/rt-tester/check-all.sh new file mode 100644 index 0000000..43098af --- /dev/null +++ b/scripts/rt-tester/check-all.sh @@ -0,0 +1,22 @@ + + +function testit () +{ + printf "%-30s: " $1 + ./rt-tester.py $1 | grep Pass +} + +testit t2-l1-2rt-sameprio.tst +testit t2-l1-pi.tst +testit t2-l1-signal.tst +#testit t2-l2-2rt-deadlock.tst +testit t3-l1-pi-1rt.tst +testit t3-l1-pi-2rt.tst +testit t3-l1-pi-3rt.tst +testit t3-l1-pi-signal.tst +testit t3-l1-pi-steal.tst +testit t3-l2-pi.tst +testit t4-l2-pi-deboost.tst +testit t5-l4-pi-boost-deboost.tst +testit t5-l4-pi-boost-deboost-setsched.tst + diff --git a/scripts/rt-tester/rt-tester.py b/scripts/rt-tester/rt-tester.py new file mode 100644 index 0000000..4c79660 --- /dev/null +++ b/scripts/rt-tester/rt-tester.py @@ -0,0 +1,222 @@ +#!/usr/bin/env python +# +# rt-mutex tester +# +# (C) 2006 Thomas Gleixner +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +import os +import sys +import getopt +import shutil +import string + +# Globals +quiet = 0 +test = 0 +comments = 0 + +sysfsprefix = "/sys/devices/system/rttest/rttest" +statusfile = "/status" +commandfile = "/command" + +# Command opcodes +cmd_opcodes = { + "schedother" : "1", + "schedfifo" : "2", + "lock" : "3", + "locknowait" : "4", + "lockint" : "5", + "lockintnowait" : "6", + "lockcont" : "7", + "unlock" : "8", + "lockbkl" : "9", + "unlockbkl" : "10", + "signal" : "11", + "resetevent" : "98", + "reset" : "99", + } + +test_opcodes = { + "prioeq" : ["P" , "eq" , None], + "priolt" : ["P" , "lt" , None], + "priogt" : ["P" , "gt" , None], + "nprioeq" : ["N" , "eq" , None], + "npriolt" : ["N" , "lt" , None], + "npriogt" : ["N" , "gt" , None], + "unlocked" : ["M" , "eq" , 0], + "trylock" : ["M" , "eq" , 1], + "blocked" : ["M" , "eq" , 2], + "blockedwake" : ["M" , "eq" , 3], + "locked" : ["M" , "eq" , 4], + "opcodeeq" : ["O" , "eq" , None], + "opcodelt" : ["O" , "lt" , None], + "opcodegt" : ["O" , "gt" , None], + "eventeq" : ["E" , "eq" , None], + "eventlt" : ["E" , "lt" , None], + "eventgt" : ["E" , "gt" , None], + } + +# Print usage information +def usage(): + print "rt-tester.py <-c -h -q -t> " + print " -c display comments after first command" + print " -h help" + print " -q quiet mode" + print " -t test mode (syntax check)" + print " testfile: read test specification from testfile" + print " otherwise from stdin" + return + +# Print progress when not in quiet mode +def progress(str): + if not quiet: + print str + +# Analyse a status value +def analyse(val, top, arg): + + intval = int(val) + + if top[0] == "M": + intval = intval / (10 ** int(arg)) + intval = intval % 10 + argval = top[2] + elif top[0] == "O": + argval = int(cmd_opcodes.get(arg, arg)) + else: + argval = int(arg) + + # progress("%d %s %d" %(intval, top[1], argval)) + + if top[1] == "eq" and intval == argval: + return 1 + if top[1] == "lt" and intval < argval: + return 1 + if top[1] == "gt" and intval > argval: + return 1 + return 0 + +# Parse the commandline +try: + (options, arguments) = getopt.getopt(sys.argv[1:],'chqt') +except getopt.GetoptError, ex: + usage() + sys.exit(1) + +# Parse commandline options +for option, value in options: + if option == "-c": + comments = 1 + elif option == "-q": + quiet = 1 + elif option == "-t": + test = 1 + elif option == '-h': + usage() + sys.exit(0) + +# Select the input source +if arguments: + try: + fd = open(arguments[0]) + except Exception,ex: + sys.stderr.write("File not found %s\n" %(arguments[0])) + sys.exit(1) +else: + fd = sys.stdin + +linenr = 0 + +# Read the test patterns +while 1: + + linenr = linenr + 1 + line = fd.readline() + if not len(line): + break + + line = line.strip() + parts = line.split(":") + + if not parts or len(parts) < 1: + continue + + if len(parts[0]) == 0: + continue + + if parts[0].startswith("#"): + if comments > 1: + progress(line) + continue + + if comments == 1: + comments = 2 + + progress(line) + + cmd = parts[0].strip().lower() + opc = parts[1].strip().lower() + tid = parts[2].strip() + dat = parts[3].strip() + + try: + # Test or wait for a status value + if cmd == "t" or cmd == "w": + testop = test_opcodes[opc] + + fname = "%s%s%s" %(sysfsprefix, tid, statusfile) + if test: + print fname + continue + + while 1: + query = 1 + fsta = open(fname, 'r') + status = fsta.readline().strip() + fsta.close() + stat = status.split(",") + for s in stat: + s = s.strip() + if s.startswith(testop[0]): + # Seperate status value + val = s[2:].strip() + query = analyse(val, testop, dat) + break + if query or cmd == "t": + break + + progress(" " + status) + + if not query: + sys.stderr.write("Test failed in line %d\n" %(linenr)) + sys.exit(1) + + # Issue a command to the tester + elif cmd == "c": + cmdnr = cmd_opcodes[opc] + # Build command string and sys filename + cmdstr = "%s:%s" %(cmdnr, dat) + fname = "%s%s%s" %(sysfsprefix, tid, commandfile) + if test: + print fname + continue + fcmd = open(fname, 'w') + fcmd.write(cmdstr) + fcmd.close() + + except Exception,ex: + sys.stderr.write(str(ex)) + sys.stderr.write("\nSyntax error in line %d\n" %(linenr)) + if not test: + fd.close() + sys.exit(1) + +# Normal exit pass +print "Pass" +sys.exit(0) + + diff --git a/scripts/rt-tester/t2-l1-2rt-sameprio.tst b/scripts/rt-tester/t2-l1-2rt-sameprio.tst new file mode 100644 index 0000000..8821f27 --- /dev/null +++ b/scripts/rt-tester/t2-l1-2rt-sameprio.tst @@ -0,0 +1,99 @@ +# +# RT-Mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal 0 +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 2 threads 1 lock +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedfifo: 0: 80 +C: schedfifo: 1: 80 + +# T0 lock L0 +C: locknowait: 0: 0 +C: locknowait: 1: 0 +W: locked: 0: 0 +W: blocked: 1: 0 +T: prioeq: 0: 80 + +# T0 unlock L0 +C: unlock: 0: 0 +W: locked: 1: 0 + +# Verify T0 +W: unlocked: 0: 0 +T: prioeq: 0: 80 + +# Unlock +C: unlock: 1: 0 +W: unlocked: 1: 0 + +# T1,T0 lock L0 +C: locknowait: 1: 0 +C: locknowait: 0: 0 +W: locked: 1: 0 +W: blocked: 0: 0 +T: prioeq: 1: 80 + +# T1 unlock L0 +C: unlock: 1: 0 +W: locked: 0: 0 + +# Verify T1 +W: unlocked: 1: 0 +T: prioeq: 1: 80 + +# Unlock and exit +C: unlock: 0: 0 +W: unlocked: 0: 0 + diff --git a/scripts/rt-tester/t2-l1-pi.tst b/scripts/rt-tester/t2-l1-pi.tst new file mode 100644 index 0000000..cde1f18 --- /dev/null +++ b/scripts/rt-tester/t2-l1-pi.tst @@ -0,0 +1,82 @@ +# +# RT-Mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal 0 +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 2 threads 1 lock with priority inversion +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedfifo: 1: 80 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: locknowait: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 80 + +# T0 unlock L0 +C: unlock: 0: 0 +W: locked: 1: 0 + +# Verify T1 +W: unlocked: 0: 0 +T: priolt: 0: 1 + +# Unlock and exit +C: unlock: 1: 0 +W: unlocked: 1: 0 + diff --git a/scripts/rt-tester/t2-l1-signal.tst b/scripts/rt-tester/t2-l1-signal.tst new file mode 100644 index 0000000..3ab0bfc --- /dev/null +++ b/scripts/rt-tester/t2-l1-signal.tst @@ -0,0 +1,77 @@ +# +# RT-Mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal 0 +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 2 threads 1 lock with priority inversion +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedother: 1: 0 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: lockintnowait: 1: 0 +W: blocked: 1: 0 + +# Interrupt T1 +C: signal: 1: 0 +W: unlocked: 1: 0 +T: opcodeeq: 1: -4 + +# Unlock and exit +C: unlock: 0: 0 +W: unlocked: 0: 0 diff --git a/scripts/rt-tester/t2-l2-2rt-deadlock.tst b/scripts/rt-tester/t2-l2-2rt-deadlock.tst new file mode 100644 index 0000000..f4b5d5d --- /dev/null +++ b/scripts/rt-tester/t2-l2-2rt-deadlock.tst @@ -0,0 +1,89 @@ +# +# RT-Mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal 0 +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 2 threads 2 lock +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedfifo: 0: 80 +C: schedfifo: 1: 80 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L1 +C: locknowait: 1: 1 +W: locked: 1: 1 + +# T0 lock L1 +C: lockintnowait: 0: 1 +W: blocked: 0: 1 + +# T1 lock L0 +C: lockintnowait: 1: 0 +W: blocked: 1: 0 + +# Make deadlock go away +C: signal: 1: 0 +W: unlocked: 1: 0 +C: signal: 0: 0 +W: unlocked: 0: 1 + +# Unlock and exit +C: unlock: 0: 0 +W: unlocked: 0: 0 +C: unlock: 1: 1 +W: unlocked: 1: 1 + diff --git a/scripts/rt-tester/t3-l1-pi-1rt.tst b/scripts/rt-tester/t3-l1-pi-1rt.tst new file mode 100644 index 0000000..63440ca --- /dev/null +++ b/scripts/rt-tester/t3-l1-pi-1rt.tst @@ -0,0 +1,92 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 3 threads 1 lock PI +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedother: 1: 0 +C: schedfifo: 2: 82 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: locknowait: 1: 0 +W: blocked: 1: 0 +T: priolt: 0: 1 + +# T2 lock L0 +C: locknowait: 2: 0 +W: blocked: 2: 0 +T: prioeq: 0: 82 + +# T0 unlock L0 +C: unlock: 0: 0 + +# Wait until T2 got the lock +W: locked: 2: 0 +W: unlocked: 0: 0 +T: priolt: 0: 1 + +# T2 unlock L0 +C: unlock: 2: 0 + +W: unlocked: 2: 0 +W: locked: 1: 0 + +C: unlock: 1: 0 +W: unlocked: 1: 0 diff --git a/scripts/rt-tester/t3-l1-pi-2rt.tst b/scripts/rt-tester/t3-l1-pi-2rt.tst new file mode 100644 index 0000000..e5816fe --- /dev/null +++ b/scripts/rt-tester/t3-l1-pi-2rt.tst @@ -0,0 +1,93 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 3 threads 1 lock PI +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedfifo: 1: 81 +C: schedfifo: 2: 82 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: locknowait: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 81 + +# T2 lock L0 +C: locknowait: 2: 0 +W: blocked: 2: 0 +T: prioeq: 0: 82 +T: prioeq: 1: 81 + +# T0 unlock L0 +C: unlock: 0: 0 + +# Wait until T2 got the lock +W: locked: 2: 0 +W: unlocked: 0: 0 +T: priolt: 0: 1 + +# T2 unlock L0 +C: unlock: 2: 0 + +W: unlocked: 2: 0 +W: locked: 1: 0 + +C: unlock: 1: 0 +W: unlocked: 1: 0 diff --git a/scripts/rt-tester/t3-l1-pi-3rt.tst b/scripts/rt-tester/t3-l1-pi-3rt.tst new file mode 100644 index 0000000..718b82b --- /dev/null +++ b/scripts/rt-tester/t3-l1-pi-3rt.tst @@ -0,0 +1,92 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 3 threads 1 lock PI +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedfifo: 0: 80 +C: schedfifo: 1: 81 +C: schedfifo: 2: 82 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: locknowait: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 81 + +# T2 lock L0 +C: locknowait: 2: 0 +W: blocked: 2: 0 +T: prioeq: 0: 82 + +# T0 unlock L0 +C: unlock: 0: 0 + +# Wait until T2 got the lock +W: locked: 2: 0 +W: unlocked: 0: 0 +T: prioeq: 0: 80 + +# T2 unlock L0 +C: unlock: 2: 0 + +W: locked: 1: 0 +W: unlocked: 2: 0 + +C: unlock: 1: 0 +W: unlocked: 1: 0 diff --git a/scripts/rt-tester/t3-l1-pi-signal.tst b/scripts/rt-tester/t3-l1-pi-signal.tst new file mode 100644 index 0000000..c6e2135 --- /dev/null +++ b/scripts/rt-tester/t3-l1-pi-signal.tst @@ -0,0 +1,98 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# Reset event counter +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set priorities +C: schedother: 0: 0 +C: schedfifo: 1: 80 +C: schedfifo: 2: 81 + +# T0 lock L0 +C: lock: 0: 0 +W: locked: 0: 0 + +# T1 lock L0, no wait in the wakeup path +C: locknowait: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 80 +T: prioeq: 1: 80 + +# T2 lock L0 interruptible, no wait in the wakeup path +C: lockintnowait: 2: 0 +W: blocked: 2: 0 +T: prioeq: 0: 81 +T: prioeq: 1: 80 + +# Interrupt T2 +C: signal: 2: 2 +W: unlocked: 2: 0 +T: prioeq: 1: 80 +T: prioeq: 0: 80 + +T: locked: 0: 0 +T: blocked: 1: 0 + +# T0 unlock L0 +C: unlock: 0: 0 + +# Wait until T1 has locked L0 and exit +W: locked: 1: 0 +W: unlocked: 0: 0 +T: priolt: 0: 1 + +C: unlock: 1: 0 +W: unlocked: 1: 0 + + + diff --git a/scripts/rt-tester/t3-l1-pi-steal.tst b/scripts/rt-tester/t3-l1-pi-steal.tst new file mode 100644 index 0000000..f53749d --- /dev/null +++ b/scripts/rt-tester/t3-l1-pi-steal.tst @@ -0,0 +1,96 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 3 threads 1 lock PI steal pending ownership +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedfifo: 1: 80 +C: schedfifo: 2: 81 + +# T0 lock L0 +C: lock: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: lock: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 80 + +# T0 unlock L0 +C: unlock: 0: 0 + +# Wait until T1 is in the wakeup loop +W: blockedwake: 1: 0 +T: priolt: 0: 1 + +# T2 lock L0 +C: lock: 2: 0 +# T1 leave wakeup loop +C: lockcont: 1: 0 + +# T2 must have the lock and T1 must be blocked +W: locked: 2: 0 +W: blocked: 1: 0 + +# T2 unlock L0 +C: unlock: 2: 0 + +# Wait until T1 is in the wakeup loop and let it run +W: blockedwake: 1: 0 +C: lockcont: 1: 0 +W: locked: 1: 0 +C: unlock: 1: 0 +W: unlocked: 1: 0 diff --git a/scripts/rt-tester/t3-l2-pi.tst b/scripts/rt-tester/t3-l2-pi.tst new file mode 100644 index 0000000..cdc3e4f --- /dev/null +++ b/scripts/rt-tester/t3-l2-pi.tst @@ -0,0 +1,92 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 3 threads 2 lock PI +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedother: 1: 0 +C: schedfifo: 2: 82 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L0 +C: locknowait: 1: 0 +W: blocked: 1: 0 +T: priolt: 0: 1 + +# T2 lock L0 +C: locknowait: 2: 0 +W: blocked: 2: 0 +T: prioeq: 0: 82 + +# T0 unlock L0 +C: unlock: 0: 0 + +# Wait until T2 got the lock +W: locked: 2: 0 +W: unlocked: 0: 0 +T: priolt: 0: 1 + +# T2 unlock L0 +C: unlock: 2: 0 + +W: unlocked: 2: 0 +W: locked: 1: 0 + +C: unlock: 1: 0 +W: unlocked: 1: 0 diff --git a/scripts/rt-tester/t4-l2-pi-deboost.tst b/scripts/rt-tester/t4-l2-pi-deboost.tst new file mode 100644 index 0000000..baa1413 --- /dev/null +++ b/scripts/rt-tester/t4-l2-pi-deboost.tst @@ -0,0 +1,123 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 4 threads 2 lock PI +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedother: 1: 0 +C: schedfifo: 2: 82 +C: schedfifo: 3: 83 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L1 +C: locknowait: 1: 1 +W: locked: 1: 1 + +# T3 lock L0 +C: lockintnowait: 3: 0 +W: blocked: 3: 0 +T: prioeq: 0: 83 + +# T0 lock L1 +C: lock: 0: 1 +W: blocked: 0: 1 +T: prioeq: 1: 83 + +# T1 unlock L1 +C: unlock: 1: 1 + +# Wait until T0 is in the wakeup code +W: blockedwake: 0: 1 + +# Verify that T1 is unboosted +W: unlocked: 1: 1 +T: priolt: 1: 1 + +# T2 lock L1 (T0 is boosted and pending owner !) +C: locknowait: 2: 1 +W: blocked: 2: 1 +T: prioeq: 0: 83 + +# Interrupt T3 and wait until T3 returned +C: signal: 3: 0 +W: unlocked: 3: 0 + +# Verify prio of T0 (still pending owner, +# but T2 is enqueued due to the previous boost by T3 +T: prioeq: 0: 82 + +# Let T0 continue +C: lockcont: 0: 1 +W: locked: 0: 1 + +# Unlock L1 and let T2 get L1 +C: unlock: 0: 1 +W: locked: 2: 1 + +# Verify that T0 is unboosted +W: unlocked: 0: 1 +T: priolt: 0: 1 + +# Unlock everything and exit +C: unlock: 2: 1 +W: unlocked: 2: 1 + +C: unlock: 0: 0 +W: unlocked: 0: 0 + diff --git a/scripts/rt-tester/t5-l4-pi-boost-deboost-setsched.tst b/scripts/rt-tester/t5-l4-pi-boost-deboost-setsched.tst new file mode 100644 index 0000000..e6ec0c8 --- /dev/null +++ b/scripts/rt-tester/t5-l4-pi-boost-deboost-setsched.tst @@ -0,0 +1,183 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 5 threads 4 lock PI - modify priority of blocked threads +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedfifo: 1: 81 +C: schedfifo: 2: 82 +C: schedfifo: 3: 83 +C: schedfifo: 4: 84 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L1 +C: locknowait: 1: 1 +W: locked: 1: 1 + +# T1 lock L0 +C: lockintnowait: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 81 + +# T2 lock L2 +C: locknowait: 2: 2 +W: locked: 2: 2 + +# T2 lock L1 +C: lockintnowait: 2: 1 +W: blocked: 2: 1 +T: prioeq: 0: 82 +T: prioeq: 1: 82 + +# T3 lock L3 +C: locknowait: 3: 3 +W: locked: 3: 3 + +# T3 lock L2 +C: lockintnowait: 3: 2 +W: blocked: 3: 2 +T: prioeq: 0: 83 +T: prioeq: 1: 83 +T: prioeq: 2: 83 + +# T4 lock L3 +C: lockintnowait: 4: 3 +W: blocked: 4: 3 +T: prioeq: 0: 84 +T: prioeq: 1: 84 +T: prioeq: 2: 84 +T: prioeq: 3: 84 + +# Reduce prio of T4 +C: schedfifo: 4: 80 +T: prioeq: 0: 83 +T: prioeq: 1: 83 +T: prioeq: 2: 83 +T: prioeq: 3: 83 +T: prioeq: 4: 80 + +# Increase prio of T4 +C: schedfifo: 4: 84 +T: prioeq: 0: 84 +T: prioeq: 1: 84 +T: prioeq: 2: 84 +T: prioeq: 3: 84 +T: prioeq: 4: 84 + +# Reduce prio of T3 +C: schedfifo: 3: 80 +T: prioeq: 0: 84 +T: prioeq: 1: 84 +T: prioeq: 2: 84 +T: prioeq: 3: 84 +T: prioeq: 4: 84 + +# Increase prio of T3 +C: schedfifo: 3: 85 +T: prioeq: 0: 85 +T: prioeq: 1: 85 +T: prioeq: 2: 85 +T: prioeq: 3: 85 +T: prioeq: 4: 84 + +# Reduce prio of T3 +C: schedfifo: 3: 83 +T: prioeq: 0: 84 +T: prioeq: 1: 84 +T: prioeq: 2: 84 +T: prioeq: 3: 84 +T: prioeq: 4: 84 + +# Signal T4 +C: signal: 4: 0 +W: unlocked: 4: 3 +T: prioeq: 0: 83 +T: prioeq: 1: 83 +T: prioeq: 2: 83 +T: prioeq: 3: 83 + +# Signal T3 +C: signal: 3: 0 +W: unlocked: 3: 2 +T: prioeq: 0: 82 +T: prioeq: 1: 82 +T: prioeq: 2: 82 + +# Signal T2 +C: signal: 2: 0 +W: unlocked: 2: 1 +T: prioeq: 0: 81 +T: prioeq: 1: 81 + +# Signal T1 +C: signal: 1: 0 +W: unlocked: 1: 0 +T: priolt: 0: 1 + +# Unlock and exit +C: unlock: 3: 3 +C: unlock: 2: 2 +C: unlock: 1: 1 +C: unlock: 0: 0 + +W: unlocked: 3: 3 +W: unlocked: 2: 2 +W: unlocked: 1: 1 +W: unlocked: 0: 0 + diff --git a/scripts/rt-tester/t5-l4-pi-boost-deboost.tst b/scripts/rt-tester/t5-l4-pi-boost-deboost.tst new file mode 100644 index 0000000..ca64f8b --- /dev/null +++ b/scripts/rt-tester/t5-l4-pi-boost-deboost.tst @@ -0,0 +1,143 @@ +# +# rt-mutex test +# +# Op: C(ommand)/T(est)/W(ait) +# | opcode +# | | threadid: 0-7 +# | | | opcode argument +# | | | | +# C: lock: 0: 0 +# +# Commands +# +# opcode opcode argument +# schedother nice value +# schedfifo priority +# lock lock nr (0-7) +# locknowait lock nr (0-7) +# lockint lock nr (0-7) +# lockintnowait lock nr (0-7) +# lockcont lock nr (0-7) +# unlock lock nr (0-7) +# lockbkl lock nr (0-7) +# unlockbkl lock nr (0-7) +# signal thread to signal (0-7) +# reset 0 +# resetevent 0 +# +# Tests / Wait +# +# opcode opcode argument +# +# prioeq priority +# priolt priority +# priogt priority +# nprioeq normal priority +# npriolt normal priority +# npriogt normal priority +# locked lock nr (0-7) +# blocked lock nr (0-7) +# blockedwake lock nr (0-7) +# unlocked lock nr (0-7) +# lockedbkl dont care +# blockedbkl dont care +# unlockedbkl dont care +# opcodeeq command opcode or number +# opcodelt number +# opcodegt number +# eventeq number +# eventgt number +# eventlt number + +# +# 5 threads 4 lock PI +# +C: resetevent: 0: 0 +W: opcodeeq: 0: 0 + +# Set schedulers +C: schedother: 0: 0 +C: schedfifo: 1: 81 +C: schedfifo: 2: 82 +C: schedfifo: 3: 83 +C: schedfifo: 4: 84 + +# T0 lock L0 +C: locknowait: 0: 0 +W: locked: 0: 0 + +# T1 lock L1 +C: locknowait: 1: 1 +W: locked: 1: 1 + +# T1 lock L0 +C: lockintnowait: 1: 0 +W: blocked: 1: 0 +T: prioeq: 0: 81 + +# T2 lock L2 +C: locknowait: 2: 2 +W: locked: 2: 2 + +# T2 lock L1 +C: lockintnowait: 2: 1 +W: blocked: 2: 1 +T: prioeq: 0: 82 +T: prioeq: 1: 82 + +# T3 lock L3 +C: locknowait: 3: 3 +W: locked: 3: 3 + +# T3 lock L2 +C: lockintnowait: 3: 2 +W: blocked: 3: 2 +T: prioeq: 0: 83 +T: prioeq: 1: 83 +T: prioeq: 2: 83 + +# T4 lock L3 +C: lockintnowait: 4: 3 +W: blocked: 4: 3 +T: prioeq: 0: 84 +T: prioeq: 1: 84 +T: prioeq: 2: 84 +T: prioeq: 3: 84 + +# Signal T4 +C: signal: 4: 0 +W: unlocked: 4: 3 +T: prioeq: 0: 83 +T: prioeq: 1: 83 +T: prioeq: 2: 83 +T: prioeq: 3: 83 + +# Signal T3 +C: signal: 3: 0 +W: unlocked: 3: 2 +T: prioeq: 0: 82 +T: prioeq: 1: 82 +T: prioeq: 2: 82 + +# Signal T2 +C: signal: 2: 0 +W: unlocked: 2: 1 +T: prioeq: 0: 81 +T: prioeq: 1: 81 + +# Signal T1 +C: signal: 1: 0 +W: unlocked: 1: 0 +T: priolt: 0: 1 + +# Unlock and exit +C: unlock: 3: 3 +C: unlock: 2: 2 +C: unlock: 1: 1 +C: unlock: 0: 0 + +W: unlocked: 3: 3 +W: unlocked: 2: 2 +W: unlocked: 1: 1 +W: unlocked: 0: 0 + diff --git a/scripts/setlocalversion b/scripts/setlocalversion index 9a23825..82e4993 100644 --- a/scripts/setlocalversion +++ b/scripts/setlocalversion @@ -11,12 +11,12 @@ cd "${1:-.}" || usage # Check for git and a git repo. if head=`git rev-parse --verify HEAD 2>/dev/null`; then # Do we have an untagged version? - if [ "`git name-rev --tags HEAD`" = "HEAD undefined" ]; then + if git name-rev --tags HEAD | grep -E '^HEAD[[:space:]]+(.*~[0-9]*|undefined)$' > /dev/null; then printf '%s%s' -g `echo "$head" | cut -c1-8` fi # Are there uncommitted changes? - if git diff-files | read dummy; then + if git diff-index HEAD | read dummy; then printf '%s' -dirty fi fi diff --git a/security/Kconfig b/security/Kconfig index 34f5934..67785df 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -22,16 +22,22 @@ config KEYS If you are unsure as to whether this is required, answer N. config KEYS_DEBUG_PROC_KEYS - bool "Enable the /proc/keys file by which all keys may be viewed" + bool "Enable the /proc/keys file by which keys may be viewed" depends on KEYS help - This option turns on support for the /proc/keys file through which - all the keys on the system can be listed. + This option turns on support for the /proc/keys file - through which + can be listed all the keys on the system that are viewable by the + reading process. - This option is a slight security risk in that it makes it possible - for anyone to see all the keys on the system. Normally the manager - pretends keys that are inaccessible to a process don't exist as far - as that process is concerned. + The only keys included in the list are those that grant View + permission to the reading process whether or not it possesses them. + Note that LSM security checks are still performed, and may further + filter out keys that the current process is not authorised to view. + + Only key attributes are listed here; key payloads are not included in + the resulting table. + + If you are unsure as to whether this is required, answer N. config SECURITY bool "Enable different security models" diff --git a/security/capability.c b/security/capability.c index f9b35cc..b868e7e 100644 --- a/security/capability.c +++ b/security/capability.c @@ -8,7 +8,6 @@ * */ -#include #include #include #include diff --git a/security/commoncap.c b/security/commoncap.c index 841eb4e..f50fc29 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include @@ -33,9 +32,9 @@ int cap_netlink_send(struct sock *sk, st EXPORT_SYMBOL(cap_netlink_send); -int cap_netlink_recv(struct sk_buff *skb) +int cap_netlink_recv(struct sk_buff *skb, int cap) { - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) + if (!cap_raised(NETLINK_CB(skb).eff_cap, cap)) return -EPERM; return 0; } diff --git a/security/dummy.c b/security/dummy.c index 8cccccc..bbbfda7 100644 --- a/security/dummy.c +++ b/security/dummy.c @@ -15,7 +15,6 @@ #undef DEBUG #include -#include #include #include #include @@ -191,7 +190,7 @@ static int dummy_sb_kern_mount (struct s return 0; } -static int dummy_sb_statfs (struct super_block *sb) +static int dummy_sb_statfs (struct dentry *dentry) { return 0; } @@ -506,6 +505,9 @@ static int dummy_task_getsid (struct tas return 0; } +static void dummy_task_getsecid (struct task_struct *p, u32 *secid) +{ } + static int dummy_task_setgroups (struct group_info *group_info) { return 0; @@ -516,6 +518,16 @@ static int dummy_task_setnice (struct ta return 0; } +static int dummy_task_setioprio (struct task_struct *p, int ioprio) +{ + return 0; +} + +static int dummy_task_getioprio (struct task_struct *p) +{ + return 0; +} + static int dummy_task_setrlimit (unsigned int resource, struct rlimit *new_rlim) { return 0; @@ -532,13 +544,18 @@ static int dummy_task_getscheduler (stru return 0; } +static int dummy_task_movememory (struct task_struct *p) +{ + return 0; +} + static int dummy_task_wait (struct task_struct *p) { return 0; } static int dummy_task_kill (struct task_struct *p, struct siginfo *info, - int sig) + int sig, u32 secid) { return 0; } @@ -665,9 +682,9 @@ static int dummy_netlink_send (struct so return 0; } -static int dummy_netlink_recv (struct sk_buff *skb) +static int dummy_netlink_recv (struct sk_buff *skb, int cap) { - if (!cap_raised (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN)) + if (!cap_raised (NETLINK_CB (skb).eff_cap, cap)) return -EPERM; return 0; } @@ -810,6 +827,11 @@ static void dummy_xfrm_policy_free_secur { } +static int dummy_xfrm_policy_delete_security(struct xfrm_policy *xp) +{ + return 0; +} + static int dummy_xfrm_state_alloc_security(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx) { return 0; @@ -819,6 +841,11 @@ static void dummy_xfrm_state_free_securi { } +static int dummy_xfrm_state_delete_security(struct xfrm_state *x) +{ + return 0; +} + static int dummy_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir) { return 0; @@ -850,7 +877,8 @@ static int dummy_setprocattr(struct task } #ifdef CONFIG_KEYS -static inline int dummy_key_alloc(struct key *key) +static inline int dummy_key_alloc(struct key *key, struct task_struct *ctx, + unsigned long flags) { return 0; } @@ -960,11 +988,15 @@ void security_fixup_ops (struct security set_to_dummy_if_null(ops, task_setpgid); set_to_dummy_if_null(ops, task_getpgid); set_to_dummy_if_null(ops, task_getsid); + set_to_dummy_if_null(ops, task_getsecid); set_to_dummy_if_null(ops, task_setgroups); set_to_dummy_if_null(ops, task_setnice); + set_to_dummy_if_null(ops, task_setioprio); + set_to_dummy_if_null(ops, task_getioprio); set_to_dummy_if_null(ops, task_setrlimit); set_to_dummy_if_null(ops, task_setscheduler); set_to_dummy_if_null(ops, task_getscheduler); + set_to_dummy_if_null(ops, task_movememory); set_to_dummy_if_null(ops, task_wait); set_to_dummy_if_null(ops, task_kill); set_to_dummy_if_null(ops, task_prctl); @@ -1024,8 +1056,10 @@ #ifdef CONFIG_SECURITY_NETWORK_XFRM set_to_dummy_if_null(ops, xfrm_policy_alloc_security); set_to_dummy_if_null(ops, xfrm_policy_clone_security); set_to_dummy_if_null(ops, xfrm_policy_free_security); + set_to_dummy_if_null(ops, xfrm_policy_delete_security); set_to_dummy_if_null(ops, xfrm_state_alloc_security); set_to_dummy_if_null(ops, xfrm_state_free_security); + set_to_dummy_if_null(ops, xfrm_state_delete_security); set_to_dummy_if_null(ops, xfrm_policy_lookup); #endif /* CONFIG_SECURITY_NETWORK_XFRM */ #ifdef CONFIG_KEYS diff --git a/security/inode.c b/security/inode.c index 0f77b02..47eb634 100644 --- a/security/inode.c +++ b/security/inode.c @@ -13,7 +13,6 @@ */ /* #define DEBUG */ -#include #include #include #include @@ -135,11 +134,11 @@ static int fill_super(struct super_block return simple_fill_super(sb, SECURITYFS_MAGIC, files); } -static struct super_block *get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data, struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, fill_super); + return get_sb_single(fs_type, flags, data, fill_super, mnt); } static struct file_system_type fs_type = { @@ -224,7 +223,7 @@ struct dentry *securityfs_create_file(co pr_debug("securityfs: creating file '%s'\n",name); - error = simple_pin_fs("securityfs", &mount, &mount_count); + error = simple_pin_fs(&fs_type, &mount, &mount_count); if (error) { dentry = ERR_PTR(error); goto exit; diff --git a/security/keys/internal.h b/security/keys/internal.h index e066e60..1bb416f 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -99,7 +99,9 @@ extern int install_process_keyring(struc extern struct key *request_key_and_link(struct key_type *type, const char *description, const char *callout_info, - struct key *dest_keyring); + void *aux, + struct key *dest_keyring, + unsigned long flags); /* * request_key authorisation diff --git a/security/keys/key.c b/security/keys/key.c index b6061fa..80de8c3 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -11,15 +11,16 @@ #include #include +#include #include #include #include #include +#include #include #include "internal.h" static kmem_cache_t *key_jar; -static key_serial_t key_serial_next = 3; struct rb_root key_serial_tree; /* tree of keys indexed by serial */ DEFINE_SPINLOCK(key_serial_lock); @@ -169,22 +170,23 @@ static void __init __key_insert_serial(s /*****************************************************************************/ /* * assign a key the next unique serial number - * - we work through all the serial numbers between 2 and 2^31-1 in turn and - * then wrap + * - these are assigned randomly to avoid security issues through covert + * channel problems */ static inline void key_alloc_serial(struct key *key) { struct rb_node *parent, **p; struct key *xkey; - spin_lock(&key_serial_lock); - - /* propose a likely serial number and look for a hole for it in the + /* propose a random serial number and look for a hole for it in the * serial number tree */ - key->serial = key_serial_next; - if (key->serial < 3) - key->serial = 3; - key_serial_next = key->serial + 1; + do { + get_random_bytes(&key->serial, sizeof(key->serial)); + + key->serial >>= 1; /* negative numbers are not permitted */ + } while (key->serial < 3); + + spin_lock(&key_serial_lock); parent = NULL; p = &key_serial_tree.rb_node; @@ -204,19 +206,18 @@ static inline void key_alloc_serial(stru /* we found a key with the proposed serial number - walk the tree from * that point looking for the next unused serial number */ - serial_exists: +serial_exists: for (;;) { - key->serial = key_serial_next; + key->serial++; if (key->serial < 2) key->serial = 2; - key_serial_next = key->serial + 1; - if (!parent->rb_parent) + if (!rb_parent(parent)) p = &key_serial_tree.rb_node; - else if (parent->rb_parent->rb_left == parent) - p = &parent->rb_parent->rb_left; + else if (rb_parent(parent)->rb_left == parent) + p = &(rb_parent(parent)->rb_left); else - p = &parent->rb_parent->rb_right; + p = &(rb_parent(parent)->rb_right); parent = rb_next(parent); if (!parent) @@ -228,7 +229,7 @@ static inline void key_alloc_serial(stru } /* we've found a suitable hole - arrange for this key to occupy it */ - insert_here: +insert_here: rb_link_node(&key->serial_node, parent, p); rb_insert_color(&key->serial_node, &key_serial_tree); @@ -247,8 +248,8 @@ static inline void key_alloc_serial(stru * instantiate the key or discard it before returning */ struct key *key_alloc(struct key_type *type, const char *desc, - uid_t uid, gid_t gid, key_perm_t perm, - int not_in_quota) + uid_t uid, gid_t gid, struct task_struct *ctx, + key_perm_t perm, unsigned long flags) { struct key_user *user = NULL; struct key *key; @@ -269,12 +270,14 @@ struct key *key_alloc(struct key_type *t /* check that the user's quota permits allocation of another key and * its description */ - if (!not_in_quota) { + if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { spin_lock(&user->lock); - if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS || - user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES - ) - goto no_quota; + if (!(flags & KEY_ALLOC_QUOTA_OVERRUN)) { + if (user->qnkeys + 1 >= KEYQUOTA_MAX_KEYS || + user->qnbytes + quotalen >= KEYQUOTA_MAX_BYTES + ) + goto no_quota; + } user->qnkeys++; user->qnbytes += quotalen; @@ -308,7 +311,7 @@ struct key *key_alloc(struct key_type *t key->payload.data = NULL; key->security = NULL; - if (!not_in_quota) + if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) key->flags |= 1 << KEY_FLAG_IN_QUOTA; memset(&key->type_data, 0, sizeof(key->type_data)); @@ -318,7 +321,7 @@ #ifdef KEY_DEBUGGING #endif /* let the security module know about the key */ - ret = security_key_alloc(key); + ret = security_key_alloc(key, ctx, flags); if (ret < 0) goto security_error; @@ -332,7 +335,7 @@ error: security_error: kfree(key->description); kmem_cache_free(key_jar, key); - if (!not_in_quota) { + if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { spin_lock(&user->lock); user->qnkeys--; user->qnbytes -= quotalen; @@ -345,7 +348,7 @@ security_error: no_memory_3: kmem_cache_free(key_jar, key); no_memory_2: - if (!not_in_quota) { + if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { spin_lock(&user->lock); user->qnkeys--; user->qnbytes -= quotalen; @@ -761,7 +764,7 @@ key_ref_t key_create_or_update(key_ref_t const char *description, const void *payload, size_t plen, - int not_in_quota) + unsigned long flags) { struct key_type *ktype; struct key *keyring, *key = NULL; @@ -822,7 +825,7 @@ key_ref_t key_create_or_update(key_ref_t /* allocate a new key */ key = key_alloc(ktype, description, current->fsuid, current->fsgid, - perm, not_in_quota); + current, perm, flags); if (IS_ERR(key)) { key_ref = ERR_PTR(PTR_ERR(key)); goto error_3; @@ -907,6 +910,10 @@ void key_revoke(struct key *key) * it */ down_write(&key->sem); set_bit(KEY_FLAG_REVOKED, &key->flags); + + if (key->type->revoke) + key->type->revoke(key); + up_write(&key->sem); } /* end key_revoke() */ @@ -982,7 +989,7 @@ void unregister_key_type(struct key_type if (key->type == ktype) { if (ktype->destroy) ktype->destroy(key); - memset(&key->payload, 0xbd, sizeof(key->payload)); + memset(&key->payload, KEY_DESTROY, sizeof(key->payload)); } } diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ed71d86..d9ca15c 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -102,7 +102,7 @@ asmlinkage long sys_add_key(const char _ /* create or update the requested key and add it to the target * keyring */ key_ref = key_create_or_update(keyring_ref, type, description, - payload, plen, 0); + payload, plen, KEY_ALLOC_IN_QUOTA); if (!IS_ERR(key_ref)) { ret = key_ref_to_ptr(key_ref)->serial; key_ref_put(key_ref); @@ -183,8 +183,9 @@ asmlinkage long sys_request_key(const ch } /* do the search */ - key = request_key_and_link(ktype, description, callout_info, - key_ref_to_ptr(dest_ref)); + key = request_key_and_link(ktype, description, callout_info, NULL, + key_ref_to_ptr(dest_ref), + KEY_ALLOC_IN_QUOTA); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error5; @@ -672,6 +673,7 @@ long keyctl_read_key(key_serial_t keyid, */ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) { + struct key_user *newowner, *zapowner = NULL; struct key *key; key_ref_t key_ref; long ret; @@ -695,19 +697,50 @@ long keyctl_chown_key(key_serial_t id, u if (!capable(CAP_SYS_ADMIN)) { /* only the sysadmin can chown a key to some other UID */ if (uid != (uid_t) -1 && key->uid != uid) - goto no_access; + goto error_put; /* only the sysadmin can set the key's GID to a group other * than one of those that the current process subscribes to */ if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) - goto no_access; + goto error_put; } - /* change the UID (have to update the quotas) */ + /* change the UID */ if (uid != (uid_t) -1 && uid != key->uid) { - /* don't support UID changing yet */ - ret = -EOPNOTSUPP; - goto no_access; + ret = -ENOMEM; + newowner = key_user_lookup(uid); + if (!newowner) + goto error_put; + + /* transfer the quota burden to the new user */ + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { + spin_lock(&newowner->lock); + if (newowner->qnkeys + 1 >= KEYQUOTA_MAX_KEYS || + newowner->qnbytes + key->quotalen >= + KEYQUOTA_MAX_BYTES) + goto quota_overrun; + + newowner->qnkeys++; + newowner->qnbytes += key->quotalen; + spin_unlock(&newowner->lock); + + spin_lock(&key->user->lock); + key->user->qnkeys--; + key->user->qnbytes -= key->quotalen; + spin_unlock(&key->user->lock); + } + + atomic_dec(&key->user->nkeys); + atomic_inc(&newowner->nkeys); + + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { + atomic_dec(&key->user->nikeys); + atomic_inc(&newowner->nikeys); + } + + zapowner = key->user; + key->user = newowner; + key->uid = uid; } /* change the GID */ @@ -716,12 +749,20 @@ long keyctl_chown_key(key_serial_t id, u ret = 0; - no_access: +error_put: up_write(&key->sem); key_put(key); - error: + if (zapowner) + key_user_put(zapowner); +error: return ret; +quota_overrun: + spin_unlock(&newowner->lock); + zapowner = newowner; + ret = -EDQUOT; + goto error_put; + } /* end keyctl_chown_key() */ /*****************************************************************************/ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index bffa924..e8d02ac 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -49,6 +49,7 @@ static inline unsigned keyring_hash(cons static int keyring_instantiate(struct key *keyring, const void *data, size_t datalen); static int keyring_match(const struct key *keyring, const void *criterion); +static void keyring_revoke(struct key *keyring); static void keyring_destroy(struct key *keyring); static void keyring_describe(const struct key *keyring, struct seq_file *m); static long keyring_read(const struct key *keyring, @@ -59,6 +60,7 @@ struct key_type key_type_keyring = { .def_datalen = sizeof(struct keyring_list), .instantiate = keyring_instantiate, .match = keyring_match, + .revoke = keyring_revoke, .destroy = keyring_destroy, .describe = keyring_describe, .read = keyring_read, @@ -240,15 +242,16 @@ static long keyring_read(const struct ke * allocate a keyring and link into the destination keyring */ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, - int not_in_quota, struct key *dest) + struct task_struct *ctx, unsigned long flags, + struct key *dest) { struct key *keyring; int ret; keyring = key_alloc(&key_type_keyring, description, - uid, gid, + uid, gid, ctx, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL, - not_in_quota); + flags); if (!IS_ERR(keyring)) { ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL); @@ -952,3 +955,22 @@ int keyring_clear(struct key *keyring) } /* end keyring_clear() */ EXPORT_SYMBOL(keyring_clear); + +/*****************************************************************************/ +/* + * dispose of the links from a revoked keyring + * - called with the key sem write-locked + */ +static void keyring_revoke(struct key *keyring) +{ + struct keyring_list *klist = keyring->payload.subscriptions; + + /* adjust the quota */ + key_payload_reserve(keyring, 0); + + if (klist) { + rcu_assign_pointer(keyring->payload.subscriptions, NULL); + call_rcu(&klist->rcu, keyring_clear_rcu_disposal); + } + +} /* end keyring_revoke() */ diff --git a/security/keys/proc.c b/security/keys/proc.c index 12b750e..686a9ee 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -137,6 +137,13 @@ static int proc_keys_show(struct seq_fil struct timespec now; unsigned long timo; char xbuf[12]; + int rc; + + /* check whether the current task is allowed to view the key (assuming + * non-possession) */ + rc = key_task_permission(make_key_ref(key, 0), current, KEY_VIEW); + if (rc < 0) + return 0; now = current_kernel_time(); diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 217a0be..32150cf 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -67,7 +67,8 @@ #endif /* * allocate the keyrings to be associated with a UID */ -int alloc_uid_keyring(struct user_struct *user) +int alloc_uid_keyring(struct user_struct *user, + struct task_struct *ctx) { struct key *uid_keyring, *session_keyring; char buf[20]; @@ -76,7 +77,8 @@ int alloc_uid_keyring(struct user_struct /* concoct a default session keyring */ sprintf(buf, "_uid_ses.%u", user->uid); - session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL); + session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, + KEY_ALLOC_IN_QUOTA, NULL); if (IS_ERR(session_keyring)) { ret = PTR_ERR(session_keyring); goto error; @@ -86,8 +88,8 @@ int alloc_uid_keyring(struct user_struct * keyring */ sprintf(buf, "_uid.%u", user->uid); - uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, - session_keyring); + uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, ctx, + KEY_ALLOC_IN_QUOTA, session_keyring); if (IS_ERR(uid_keyring)) { key_put(session_keyring); ret = PTR_ERR(uid_keyring); @@ -143,7 +145,8 @@ int install_thread_keyring(struct task_s sprintf(buf, "_tid.%u", tsk->pid); - keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, + KEY_ALLOC_QUOTA_OVERRUN, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; @@ -177,7 +180,8 @@ int install_process_keyring(struct task_ if (!tsk->signal->process_keyring) { sprintf(buf, "_pid.%u", tsk->tgid); - keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, + KEY_ALLOC_QUOTA_OVERRUN, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error; @@ -208,6 +212,7 @@ error: static int install_session_keyring(struct task_struct *tsk, struct key *keyring) { + unsigned long flags; struct key *old; char buf[20]; @@ -217,7 +222,12 @@ static int install_session_keyring(struc if (!keyring) { sprintf(buf, "_ses.%u", tsk->tgid); - keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL); + flags = KEY_ALLOC_QUOTA_OVERRUN; + if (tsk->signal->session_keyring) + flags = KEY_ALLOC_IN_QUOTA; + + keyring = keyring_alloc(buf, tsk->uid, tsk->gid, tsk, + flags, NULL); if (IS_ERR(keyring)) return PTR_ERR(keyring); } @@ -390,6 +400,8 @@ key_ref_t search_process_keyrings(struct struct request_key_auth *rka; key_ref_t key_ref, ret, err; + might_sleep(); + /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; * otherwise we want to return a sample error (probably -EACCES) if @@ -495,27 +507,35 @@ key_ref_t search_process_keyrings(struct */ if (context->request_key_auth && context == current && - type != &key_type_request_key_auth && - key_validate(context->request_key_auth) == 0 + type != &key_type_request_key_auth ) { - rka = context->request_key_auth->payload.data; + /* defend against the auth key being revoked */ + down_read(&context->request_key_auth->sem); - key_ref = search_process_keyrings(type, description, match, - rka->context); + if (key_validate(context->request_key_auth) == 0) { + rka = context->request_key_auth->payload.data; - if (!IS_ERR(key_ref)) - goto found; + key_ref = search_process_keyrings(type, description, + match, rka->context); - switch (PTR_ERR(key_ref)) { - case -EAGAIN: /* no key */ - if (ret) + up_read(&context->request_key_auth->sem); + + if (!IS_ERR(key_ref)) + goto found; + + switch (PTR_ERR(key_ref)) { + case -EAGAIN: /* no key */ + if (ret) + break; + case -ENOKEY: /* negative key */ + ret = key_ref; break; - case -ENOKEY: /* negative key */ - ret = key_ref; - break; - default: - err = key_ref; - break; + default: + err = key_ref; + break; + } + } else { + up_read(&context->request_key_auth->sem); } } @@ -717,7 +737,8 @@ long join_session_keyring(const char *na keyring = find_keyring_by_name(name, 0); if (PTR_ERR(keyring) == -ENOKEY) { /* not found - try and create a new one */ - keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL); + keyring = keyring_alloc(name, tsk->uid, tsk->gid, tsk, + KEY_ALLOC_IN_QUOTA, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error2; diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f030a0c..f573ac1 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -1,6 +1,6 @@ /* request_key.c: request a key from userspace * - * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-6 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or @@ -33,7 +33,8 @@ DECLARE_WAIT_QUEUE_HEAD(request_key_cons */ static int call_sbin_request_key(struct key *key, struct key *authkey, - const char *op) + const char *op, + void *aux) { struct task_struct *tsk = current; key_serial_t prkey, sskey; @@ -48,7 +49,8 @@ static int call_sbin_request_key(struct /* allocate a new session keyring */ sprintf(desc, "_req.%u", key->serial); - keyring = keyring_alloc(desc, current->fsuid, current->fsgid, 1, NULL); + keyring = keyring_alloc(desc, current->fsuid, current->fsgid, current, + KEY_ALLOC_QUOTA_OVERRUN, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto error_alloc; @@ -125,7 +127,9 @@ error_alloc: */ static struct key *__request_key_construction(struct key_type *type, const char *description, - const char *callout_info) + const char *callout_info, + void *aux, + unsigned long flags) { request_key_actor_t actor; struct key_construction cons; @@ -133,11 +137,12 @@ static struct key *__request_key_constru struct key *key, *authkey; int ret, negated; - kenter("%s,%s,%s", type->name, description, callout_info); + kenter("%s,%s,%s,%lx", type->name, description, callout_info, flags); /* create a key and add it to the queue */ key = key_alloc(type, description, - current->fsuid, current->fsgid, KEY_POS_ALL, 0); + current->fsuid, current->fsgid, current, KEY_POS_ALL, + flags); if (IS_ERR(key)) goto alloc_failed; @@ -161,7 +166,7 @@ static struct key *__request_key_constru actor = call_sbin_request_key; if (type->request_key) actor = type->request_key; - ret = actor(key, authkey, "create"); + ret = actor(key, authkey, "create", aux); if (ret < 0) goto request_failed; @@ -255,16 +260,18 @@ alloc_failed: */ static struct key *request_key_construction(struct key_type *type, const char *description, + const char *callout_info, + void *aux, struct key_user *user, - const char *callout_info) + unsigned long flags) { struct key_construction *pcons; struct key *key, *ckey; DECLARE_WAITQUEUE(myself, current); - kenter("%s,%s,{%d},%s", - type->name, description, user->uid, callout_info); + kenter("%s,%s,{%d},%s,%lx", + type->name, description, user->uid, callout_info, flags); /* see if there's such a key under construction already */ down_write(&key_construction_sem); @@ -280,7 +287,8 @@ static struct key *request_key_construct } /* see about getting userspace to construct the key */ - key = __request_key_construction(type, description, callout_info); + key = __request_key_construction(type, description, callout_info, aux, + flags); error: kleave(" = %p", key); return key; @@ -387,14 +395,17 @@ static void request_key_link(struct key struct key *request_key_and_link(struct key_type *type, const char *description, const char *callout_info, - struct key *dest_keyring) + void *aux, + struct key *dest_keyring, + unsigned long flags) { struct key_user *user; struct key *key; key_ref_t key_ref; - kenter("%s,%s,%s,%p", - type->name, description, callout_info, dest_keyring); + kenter("%s,%s,%s,%p,%p,%lx", + type->name, description, callout_info, aux, + dest_keyring, flags); /* search all the process keyrings for a key */ key_ref = search_process_keyrings(type, description, type->match, @@ -427,7 +438,8 @@ struct key *request_key_and_link(struct /* ask userspace (returns NULL if it waited on a key * being constructed) */ key = request_key_construction(type, description, - user, callout_info); + callout_info, aux, + user, flags); if (key) break; @@ -483,8 +495,28 @@ struct key *request_key(struct key_type const char *description, const char *callout_info) { - return request_key_and_link(type, description, callout_info, NULL); + return request_key_and_link(type, description, callout_info, NULL, + NULL, KEY_ALLOC_IN_QUOTA); } /* end request_key() */ EXPORT_SYMBOL(request_key); + +/*****************************************************************************/ +/* + * request a key with auxiliary data for the upcaller + * - search the process's keyrings + * - check the list of keys being created or updated + * - call out to userspace for a key if supplementary info was provided + */ +struct key *request_key_with_auxdata(struct key_type *type, + const char *description, + const char *callout_info, + void *aux) +{ + return request_key_and_link(type, description, callout_info, aux, + NULL, KEY_ALLOC_IN_QUOTA); + +} /* end request_key_with_auxdata() */ + +EXPORT_SYMBOL(request_key_with_auxdata); diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index cce6ba6..cbf58a9 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -20,6 +20,7 @@ #include "internal.h" static int request_key_auth_instantiate(struct key *, const void *, size_t); static void request_key_auth_describe(const struct key *, struct seq_file *); +static void request_key_auth_revoke(struct key *); static void request_key_auth_destroy(struct key *); static long request_key_auth_read(const struct key *, char __user *, size_t); @@ -31,6 +32,7 @@ struct key_type key_type_request_key_aut .def_datalen = sizeof(struct request_key_auth), .instantiate = request_key_auth_instantiate, .describe = request_key_auth_describe, + .revoke = request_key_auth_revoke, .destroy = request_key_auth_destroy, .read = request_key_auth_read, }; @@ -93,6 +95,24 @@ static long request_key_auth_read(const /*****************************************************************************/ /* + * handle revocation of an authorisation token key + * - called with the key sem write-locked + */ +static void request_key_auth_revoke(struct key *key) +{ + struct request_key_auth *rka = key->payload.data; + + kenter("{%d}", key->serial); + + if (rka->context) { + put_task_struct(rka->context); + rka->context = NULL; + } + +} /* end request_key_auth_revoke() */ + +/*****************************************************************************/ +/* * destroy an instantiation authorisation token key */ static void request_key_auth_destroy(struct key *key) @@ -101,6 +121,11 @@ static void request_key_auth_destroy(str kenter("{%d}", key->serial); + if (rka->context) { + put_task_struct(rka->context); + rka->context = NULL; + } + key_put(rka->target_key); kfree(rka); @@ -131,14 +156,26 @@ struct key *request_key_auth_new(struct * another process */ if (current->request_key_auth) { /* it is - use that instantiation context here too */ + down_read(¤t->request_key_auth->sem); + + /* if the auth key has been revoked, then the key we're + * servicing is already instantiated */ + if (test_bit(KEY_FLAG_REVOKED, + ¤t->request_key_auth->flags)) + goto auth_key_revoked; + irka = current->request_key_auth->payload.data; rka->context = irka->context; rka->pid = irka->pid; + get_task_struct(rka->context); + + up_read(¤t->request_key_auth->sem); } else { /* it isn't - use this process as the context */ rka->context = current; rka->pid = current->pid; + get_task_struct(rka->context); } rka->target_key = key_get(target); @@ -148,9 +185,9 @@ struct key *request_key_auth_new(struct sprintf(desc, "%x", target->serial); authkey = key_alloc(&key_type_request_key_auth, desc, - current->fsuid, current->fsgid, + current->fsuid, current->fsgid, current, KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | - KEY_USR_VIEW, 1); + KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA); if (IS_ERR(authkey)) { ret = PTR_ERR(authkey); goto error_alloc; @@ -161,9 +198,15 @@ struct key *request_key_auth_new(struct if (ret < 0) goto error_inst; - kleave(" = {%d})", authkey->serial); + kleave(" = {%d}", authkey->serial); return authkey; +auth_key_revoked: + up_read(¤t->request_key_auth->sem); + kfree(rka); + kleave("= -EKEYREVOKED"); + return ERR_PTR(-EKEYREVOKED); + error_inst: key_revoke(authkey); key_put(authkey); diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 8e71895..5bbfdeb 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -28,6 +28,7 @@ struct key_type key_type_user = { .instantiate = user_instantiate, .update = user_update, .match = user_match, + .revoke = user_revoke, .destroy = user_destroy, .describe = user_describe, .read = user_read, @@ -67,6 +68,7 @@ error: return ret; } /* end user_instantiate() */ + EXPORT_SYMBOL_GPL(user_instantiate); /*****************************************************************************/ @@ -141,7 +143,28 @@ EXPORT_SYMBOL_GPL(user_match); /*****************************************************************************/ /* - * dispose of the data dangling from the corpse of a user + * dispose of the links from a revoked keyring + * - called with the key sem write-locked + */ +void user_revoke(struct key *key) +{ + struct user_key_payload *upayload = key->payload.data; + + /* clear the quota */ + key_payload_reserve(key, 0); + + if (upayload) { + rcu_assign_pointer(key->payload.data, NULL); + call_rcu(&upayload->rcu, user_update_rcu_disposal); + } + +} /* end user_revoke() */ + +EXPORT_SYMBOL(user_revoke); + +/*****************************************************************************/ +/* + * dispose of the data dangling from the corpse of a user key */ void user_destroy(struct key *key) { diff --git a/security/root_plug.c b/security/root_plug.c index 07651de..38dd4f3 100644 --- a/security/root_plug.c +++ b/security/root_plug.c @@ -22,7 +22,6 @@ * License. */ -#include #include #include #include diff --git a/security/seclvl.c b/security/seclvl.c index 441beaf..c26dd7d 100644 --- a/security/seclvl.c +++ b/security/seclvl.c @@ -16,7 +16,6 @@ * (at your option) any later version. */ -#include #include #include #include diff --git a/security/security.c b/security/security.c index 51ef509..ee4e070 100644 --- a/security/security.c +++ b/security/security.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index f636f53..814ddc4 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -1,6 +1,7 @@ config SECURITY_SELINUX bool "NSA SELinux Support" depends on SECURITY_NETWORK && AUDIT && NET && INET + select NETWORK_SECMARK default n help This selects NSA Security-Enhanced Linux (SELinux). @@ -95,3 +96,31 @@ config SECURITY_SELINUX_CHECKREQPROT_VAL via /selinux/checkreqprot if authorized by policy. If you are unsure how to answer this question, answer 1. + +config SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT + bool "NSA SELinux enable new secmark network controls by default" + depends on SECURITY_SELINUX + default n + help + This option determines whether the new secmark-based network + controls will be enabled by default. If not, the old internal + per-packet controls will be enabled by default, preserving + old behavior. + + If you enable the new controls, you will need updated + SELinux userspace libraries, tools and policy. Typically, + your distribution will provide these and enable the new controls + in the kernel they also distribute. + + Note that this option can be overriden at boot with the + selinux_compat_net parameter, and after boot via + /selinux/compat_net. See Documentation/kernel-parameters.txt + for details on this parameter. + + If you enable the new network controls, you will likely + also require the SECMARK and CONNSECMARK targets, as + well as any conntrack helpers for protocols which you + wish to control. + + If you are unsure what do do here, select N. + diff --git a/security/selinux/exports.c b/security/selinux/exports.c index ae4c73e..9d7737d 100644 --- a/security/selinux/exports.c +++ b/security/selinux/exports.c @@ -72,3 +72,25 @@ void selinux_get_task_sid(struct task_st *sid = 0; } +int selinux_string_to_sid(char *str, u32 *sid) +{ + if (selinux_enabled) + return security_context_to_sid(str, strlen(str), sid); + else { + *sid = 0; + return 0; + } +} +EXPORT_SYMBOL_GPL(selinux_string_to_sid); + +int selinux_relabel_packet_permission(u32 sid) +{ + if (selinux_enabled) { + struct task_security_struct *tsec = current->security; + + return avc_has_perm(tsec->sid, sid, SECCLASS_PACKET, + PACKET__RELABELTO, NULL); + } + return 0; +} +EXPORT_SYMBOL_GPL(selinux_relabel_packet_permission); diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 90b4cdc..24caaee 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -18,7 +18,6 @@ * as published by the Free Software Foundation. */ -#include #include #include #include @@ -69,6 +68,7 @@ #include #include #include #include +#include #include "avc.h" #include "objsec.h" @@ -80,6 +80,7 @@ #define XATTR_NAME_SELINUX XATTR_SECURIT extern unsigned int policydb_loaded_version; extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); +extern int selinux_compat_net; #ifdef CONFIG_SECURITY_SELINUX_DEVELOP int selinux_enforcing = 0; @@ -696,6 +697,8 @@ static inline u16 socket_type_to_securit return SECCLASS_PACKET_SOCKET; case PF_KEY: return SECCLASS_KEY_SOCKET; + case PF_APPLETALK: + return SECCLASS_APPLETALK_SOCKET; } return SECCLASS_SOCKET; @@ -1096,6 +1099,17 @@ static int may_create(struct inode *dir, FILESYSTEM__ASSOCIATE, &ad); } +/* Check whether a task can create a key. */ +static int may_create_key(u32 ksid, + struct task_struct *ctx) +{ + struct task_security_struct *tsec; + + tsec = ctx->security; + + return avc_has_perm(tsec->sid, ksid, SECCLASS_KEY, KEY__CREATE, NULL); +} + #define MAY_LINK 0 #define MAY_UNLINK 1 #define MAY_RMDIR 2 @@ -1518,8 +1532,10 @@ static int selinux_bprm_set_security(str /* Default to the current task SID. */ bsec->sid = tsec->sid; - /* Reset create SID on execve. */ + /* Reset fs, key, and sock SIDs on execve. */ tsec->create_sid = 0; + tsec->keycreate_sid = 0; + tsec->sockcreate_sid = 0; if (tsec->exec_sid) { newsid = tsec->exec_sid; @@ -1900,13 +1916,13 @@ static int selinux_sb_kern_mount(struct return superblock_has_perm(current, sb, FILESYSTEM__MOUNT, &ad); } -static int selinux_sb_statfs(struct super_block *sb) +static int selinux_sb_statfs(struct dentry *dentry) { struct avc_audit_data ad; AVC_AUDIT_DATA_INIT(&ad,FS); - ad.u.fs.dentry = sb->s_root; - return superblock_has_perm(current, sb, FILESYSTEM__GETATTR, &ad); + ad.u.fs.dentry = dentry->d_sb->s_root; + return superblock_has_perm(current, dentry->d_sb, FILESYSTEM__GETATTR, &ad); } static int selinux_mount(char * dev_name, @@ -2571,9 +2587,11 @@ static int selinux_task_alloc_security(s tsec2->osid = tsec1->osid; tsec2->sid = tsec1->sid; - /* Retain the exec and create SIDs across fork */ + /* Retain the exec, fs, key, and sock SIDs across fork */ tsec2->exec_sid = tsec1->exec_sid; tsec2->create_sid = tsec1->create_sid; + tsec2->keycreate_sid = tsec1->keycreate_sid; + tsec2->sockcreate_sid = tsec1->sockcreate_sid; /* Retain ptracer SID across fork, if any. This will be reset by the ptrace hook upon any @@ -2625,6 +2643,11 @@ static int selinux_task_getsid(struct ta return task_has_perm(current, p, PROCESS__GETSESSION); } +static void selinux_task_getsecid(struct task_struct *p, u32 *secid) +{ + selinux_get_task_sid(p, secid); +} + static int selinux_task_setgroups(struct group_info *group_info) { /* See the comment for setuid above. */ @@ -2642,6 +2665,16 @@ static int selinux_task_setnice(struct t return task_has_perm(current,p, PROCESS__SETSCHED); } +static int selinux_task_setioprio(struct task_struct *p, int ioprio) +{ + return task_has_perm(current, p, PROCESS__SETSCHED); +} + +static int selinux_task_getioprio(struct task_struct *p) +{ + return task_has_perm(current, p, PROCESS__GETSCHED); +} + static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim) { struct rlimit *old_rlim = current->signal->rlim + resource; @@ -2671,12 +2704,19 @@ static int selinux_task_getscheduler(str return task_has_perm(current, p, PROCESS__GETSCHED); } -static int selinux_task_kill(struct task_struct *p, struct siginfo *info, int sig) +static int selinux_task_movememory(struct task_struct *p) +{ + return task_has_perm(current, p, PROCESS__SETSCHED); +} + +static int selinux_task_kill(struct task_struct *p, struct siginfo *info, + int sig, u32 secid) { u32 perm; int rc; + struct task_security_struct *tsec; - rc = secondary_ops->task_kill(p, info, sig); + rc = secondary_ops->task_kill(p, info, sig, secid); if (rc) return rc; @@ -2687,8 +2727,12 @@ static int selinux_task_kill(struct task perm = PROCESS__SIGNULL; /* null signal; existence test */ else perm = signal_to_av(sig); - - return task_has_perm(current, p, perm); + tsec = p->security; + if (secid) + rc = avc_has_perm(secid, tsec->sid, SECCLASS_PROCESS, perm, NULL); + else + rc = task_has_perm(current, p, perm); + return rc; } static int selinux_task_prctl(int option, @@ -2913,12 +2957,14 @@ static int selinux_socket_create(int fam { int err = 0; struct task_security_struct *tsec; + u32 newsid; if (kern) goto out; tsec = current->security; - err = avc_has_perm(tsec->sid, tsec->sid, + newsid = tsec->sockcreate_sid ? : tsec->sid; + err = avc_has_perm(tsec->sid, newsid, socket_type_to_security_class(family, type, protocol), SOCKET__CREATE, NULL); @@ -2931,12 +2977,14 @@ static void selinux_socket_post_create(s { struct inode_security_struct *isec; struct task_security_struct *tsec; + u32 newsid; isec = SOCK_INODE(sock)->i_security; tsec = current->security; + newsid = tsec->sockcreate_sid ? : tsec->sid; isec->sclass = socket_type_to_security_class(family, type, protocol); - isec->sid = kern ? SECINITSID_KERNEL : tsec->sid; + isec->sid = kern ? SECINITSID_KERNEL : newsid; isec->initialized = 1; return; @@ -3214,47 +3262,17 @@ static int selinux_socket_unix_may_send( return 0; } -static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, + struct avc_audit_data *ad, u32 sock_sid, u16 sock_class, + u16 family, char *addrp, int len) { - u16 family; - char *addrp; - int len, err = 0; + int err = 0; u32 netif_perm, node_perm, node_sid, if_sid, recv_perm = 0; - u32 sock_sid = 0; - u16 sock_class = 0; - struct socket *sock; - struct net_device *dev; - struct avc_audit_data ad; - - family = sk->sk_family; - if (family != PF_INET && family != PF_INET6) - goto out; - - /* Handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) - family = PF_INET; - - read_lock_bh(&sk->sk_callback_lock); - sock = sk->sk_socket; - if (sock) { - struct inode *inode; - inode = SOCK_INODE(sock); - if (inode) { - struct inode_security_struct *isec; - isec = inode->i_security; - sock_sid = isec->sid; - sock_class = isec->sclass; - } - } - read_unlock_bh(&sk->sk_callback_lock); - if (!sock_sid) - goto out; - dev = skb->dev; - if (!dev) + if (!skb->dev) goto out; - err = sel_netif_sids(dev, &if_sid, NULL); + err = sel_netif_sids(skb->dev, &if_sid, NULL); if (err) goto out; @@ -3277,44 +3295,88 @@ static int selinux_socket_sock_rcv_skb(s break; } - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); - if (err) - goto out; - - err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, &ad); + err = avc_has_perm(sock_sid, if_sid, SECCLASS_NETIF, netif_perm, ad); if (err) goto out; - /* Fixme: this lookup is inefficient */ err = security_node_sid(family, addrp, len, &node_sid); if (err) goto out; - err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, &ad); + err = avc_has_perm(sock_sid, node_sid, SECCLASS_NODE, node_perm, ad); if (err) goto out; if (recv_perm) { u32 port_sid; - /* Fixme: make this more efficient */ err = security_port_sid(sk->sk_family, sk->sk_type, - sk->sk_protocol, ntohs(ad.u.net.sport), + sk->sk_protocol, ntohs(ad->u.net.sport), &port_sid); if (err) goto out; err = avc_has_perm(sock_sid, port_sid, - sock_class, recv_perm, &ad); + sock_class, recv_perm, ad); } - if (!err) - err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); +out: + return err; +} + +static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + u16 family; + u16 sock_class = 0; + char *addrp; + int len, err = 0; + u32 sock_sid = 0; + struct socket *sock; + struct avc_audit_data ad; + + family = sk->sk_family; + if (family != PF_INET && family != PF_INET6) + goto out; + + /* Handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == ntohs(ETH_P_IP)) + family = PF_INET; + + read_lock_bh(&sk->sk_callback_lock); + sock = sk->sk_socket; + if (sock) { + struct inode *inode; + inode = SOCK_INODE(sock); + if (inode) { + struct inode_security_struct *isec; + isec = inode->i_security; + sock_sid = isec->sid; + sock_class = isec->sclass; + } + } + read_unlock_bh(&sk->sk_callback_lock); + if (!sock_sid) + goto out; + + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = skb->dev ? skb->dev->name : "[unknown]"; + ad.u.net.family = family; + + err = selinux_parse_skb(skb, &ad, &addrp, &len, 1); + if (err) + goto out; + + if (selinux_compat_net) + err = selinux_sock_rcv_skb_compat(sk, skb, &ad, sock_sid, + sock_class, family, + addrp, len); + else + err = avc_has_perm(sock_sid, skb->secmark, SECCLASS_PACKET, + PACKET__RECV, &ad); + if (err) + goto out; + err = selinux_xfrm_sock_rcv_skb(sock_sid, skb); out: return err; } @@ -3374,7 +3436,13 @@ out: static int selinux_socket_getpeersec_dgram(struct sk_buff *skb, char **secdata, u32 *seclen) { int err = 0; - u32 peer_sid = selinux_socket_getpeer_dgram(skb); + u32 peer_sid; + + if (skb->sk->sk_family == PF_UNIX) + selinux_get_inode_sid(SOCK_INODE(skb->sk->sk_socket), + &peer_sid); + else + peer_sid = selinux_socket_getpeer_dgram(skb); if (peer_sid == SECSID_NULL) return -EINVAL; @@ -3386,8 +3454,6 @@ static int selinux_socket_getpeersec_dgr return 0; } - - static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) { return sk_alloc_security(sk, family, priority); @@ -3454,42 +3520,18 @@ out: #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_postroute_last(unsigned int hooknum, - struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *), - u16 family) +static int selinux_ip_postroute_last_compat(struct sock *sk, struct net_device *dev, + struct inode_security_struct *isec, + struct avc_audit_data *ad, + u16 family, char *addrp, int len) { - char *addrp; - int len, err = NF_ACCEPT; + int err; u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0; - struct sock *sk; - struct socket *sock; - struct inode *inode; - struct sk_buff *skb = *pskb; - struct inode_security_struct *isec; - struct avc_audit_data ad; - struct net_device *dev = (struct net_device *)out; - sk = skb->sk; - if (!sk) - goto out; - - sock = sk->sk_socket; - if (!sock) - goto out; - - inode = SOCK_INODE(sock); - if (!inode) - goto out; - err = sel_netif_sids(dev, &if_sid, NULL); if (err) goto out; - isec = inode->i_security; - switch (isec->sclass) { case SECCLASS_UDP_SOCKET: netif_perm = NETIF__UDP_SEND; @@ -3509,55 +3551,88 @@ static unsigned int selinux_ip_postroute break; } - - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = dev->name; - ad.u.net.family = family; - - err = selinux_parse_skb(skb, &ad, &addrp, - &len, 0) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) - goto out; - - err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, - netif_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad); + if (err) goto out; - /* Fixme: this lookup is inefficient */ - err = security_node_sid(family, addrp, len, - &node_sid) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = security_node_sid(family, addrp, len, &node_sid); + if (err) goto out; - err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, - node_perm, &ad) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad); + if (err) goto out; if (send_perm) { u32 port_sid; - /* Fixme: make this more efficient */ err = security_port_sid(sk->sk_family, sk->sk_type, sk->sk_protocol, - ntohs(ad.u.net.dport), - &port_sid) ? NF_DROP : NF_ACCEPT; - if (err != NF_ACCEPT) + ntohs(ad->u.net.dport), + &port_sid); + if (err) goto out; err = avc_has_perm(isec->sid, port_sid, isec->sclass, - send_perm, &ad) ? NF_DROP : NF_ACCEPT; + send_perm, ad); } +out: + return err; +} + +static unsigned int selinux_ip_postroute_last(unsigned int hooknum, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *), + u16 family) +{ + char *addrp; + int len, err = 0; + struct sock *sk; + struct socket *sock; + struct inode *inode; + struct sk_buff *skb = *pskb; + struct inode_security_struct *isec; + struct avc_audit_data ad; + struct net_device *dev = (struct net_device *)out; - if (err != NF_ACCEPT) + sk = skb->sk; + if (!sk) goto out; - err = selinux_xfrm_postroute_last(isec->sid, skb); + sock = sk->sk_socket; + if (!sock) + goto out; + inode = SOCK_INODE(sock); + if (!inode) + goto out; + + isec = inode->i_security; + + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = dev->name; + ad.u.net.family = family; + + err = selinux_parse_skb(skb, &ad, &addrp, &len, 0); + if (err) + goto out; + + if (selinux_compat_net) + err = selinux_ip_postroute_last_compat(sk, dev, isec, &ad, + family, addrp, len); + else + err = avc_has_perm(isec->sid, skb->secmark, SECCLASS_PACKET, + PACKET__SEND, &ad); + + if (err) + goto out; + + err = selinux_xfrm_postroute_last(isec->sid, skb); out: - return err; + return err ? NF_DROP : NF_ACCEPT; } static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum, @@ -3586,32 +3661,32 @@ #endif /* CONFIG_NETFILTER */ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) { - struct task_security_struct *tsec; - struct av_decision avd; int err; err = secondary_ops->netlink_send(sk, skb); if (err) return err; - tsec = current->security; - - avd.allowed = 0; - avc_has_perm_noaudit(tsec->sid, tsec->sid, - SECCLASS_CAPABILITY, ~0, &avd); - cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed); - if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS) err = selinux_nlmsg_perm(sk, skb); return err; } -static int selinux_netlink_recv(struct sk_buff *skb) +static int selinux_netlink_recv(struct sk_buff *skb, int capability) { - if (!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) - return -EPERM; - return 0; + int err; + struct avc_audit_data ad; + + err = secondary_ops->netlink_recv(skb, capability); + if (err) + return err; + + AVC_AUDIT_DATA_INIT(&ad, CAP); + ad.u.cap = capability; + + return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid, + SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad); } static int ipc_alloc_security(struct task_struct *task, @@ -4114,6 +4189,10 @@ static int selinux_getprocattr(struct ta sid = tsec->exec_sid; else if (!strcmp(name, "fscreate")) sid = tsec->create_sid; + else if (!strcmp(name, "keycreate")) + sid = tsec->keycreate_sid; + else if (!strcmp(name, "sockcreate")) + sid = tsec->sockcreate_sid; else return -EINVAL; @@ -4146,6 +4225,10 @@ static int selinux_setprocattr(struct ta error = task_has_perm(current, p, PROCESS__SETEXEC); else if (!strcmp(name, "fscreate")) error = task_has_perm(current, p, PROCESS__SETFSCREATE); + else if (!strcmp(name, "keycreate")) + error = task_has_perm(current, p, PROCESS__SETKEYCREATE); + else if (!strcmp(name, "sockcreate")) + error = task_has_perm(current, p, PROCESS__SETSOCKCREATE); else if (!strcmp(name, "current")) error = task_has_perm(current, p, PROCESS__SETCURRENT); else @@ -4175,6 +4258,13 @@ static int selinux_setprocattr(struct ta tsec->exec_sid = sid; else if (!strcmp(name, "fscreate")) tsec->create_sid = sid; + else if (!strcmp(name, "keycreate")) { + error = may_create_key(sid, p); + if (error) + return error; + tsec->keycreate_sid = sid; + } else if (!strcmp(name, "sockcreate")) + tsec->sockcreate_sid = sid; else if (!strcmp(name, "current")) { struct av_decision avd; @@ -4226,6 +4316,61 @@ static int selinux_setprocattr(struct ta return size; } +#ifdef CONFIG_KEYS + +static int selinux_key_alloc(struct key *k, struct task_struct *tsk, + unsigned long flags) +{ + struct task_security_struct *tsec = tsk->security; + struct key_security_struct *ksec; + + ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL); + if (!ksec) + return -ENOMEM; + + ksec->obj = k; + if (tsec->keycreate_sid) + ksec->sid = tsec->keycreate_sid; + else + ksec->sid = tsec->sid; + k->security = ksec; + + return 0; +} + +static void selinux_key_free(struct key *k) +{ + struct key_security_struct *ksec = k->security; + + k->security = NULL; + kfree(ksec); +} + +static int selinux_key_permission(key_ref_t key_ref, + struct task_struct *ctx, + key_perm_t perm) +{ + struct key *key; + struct task_security_struct *tsec; + struct key_security_struct *ksec; + + key = key_ref_to_ptr(key_ref); + + tsec = ctx->security; + ksec = key->security; + + /* if no specific permissions are requested, we skip the + permission check. No serious, additional covert channels + appear to be created. */ + if (perm == 0) + return 0; + + return avc_has_perm(tsec->sid, ksec->sid, + SECCLASS_KEY, perm, NULL); +} + +#endif + static struct security_operations selinux_ops = { .ptrace = selinux_ptrace, .capget = selinux_capget, @@ -4304,11 +4449,15 @@ static struct security_operations selinu .task_setpgid = selinux_task_setpgid, .task_getpgid = selinux_task_getpgid, .task_getsid = selinux_task_getsid, + .task_getsecid = selinux_task_getsecid, .task_setgroups = selinux_task_setgroups, .task_setnice = selinux_task_setnice, + .task_setioprio = selinux_task_setioprio, + .task_getioprio = selinux_task_getioprio, .task_setrlimit = selinux_task_setrlimit, .task_setscheduler = selinux_task_setscheduler, .task_getscheduler = selinux_task_getscheduler, + .task_movememory = selinux_task_movememory, .task_kill = selinux_task_kill, .task_wait = selinux_task_wait, .task_prctl = selinux_task_prctl, @@ -4374,10 +4523,18 @@ #ifdef CONFIG_SECURITY_NETWORK_XFRM .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc, .xfrm_policy_clone_security = selinux_xfrm_policy_clone, .xfrm_policy_free_security = selinux_xfrm_policy_free, + .xfrm_policy_delete_security = selinux_xfrm_policy_delete, .xfrm_state_alloc_security = selinux_xfrm_state_alloc, .xfrm_state_free_security = selinux_xfrm_state_free, + .xfrm_state_delete_security = selinux_xfrm_state_delete, .xfrm_policy_lookup = selinux_xfrm_policy_lookup, #endif + +#ifdef CONFIG_KEYS + .key_alloc = selinux_key_alloc, + .key_free = selinux_key_free, + .key_permission = selinux_key_permission, +#endif }; static __init int selinux_init(void) @@ -4413,6 +4570,15 @@ static __init int selinux_init(void) } else { printk(KERN_INFO "SELinux: Starting in permissive mode\n"); } + +#ifdef CONFIG_KEYS + /* Add security information to initial keyrings */ + selinux_key_alloc(&root_user_keyring, current, + KEY_ALLOC_NOT_IN_QUOTA); + selinux_key_alloc(&root_session_keyring, current, + KEY_ALLOC_NOT_IN_QUOTA); +#endif + return 0; } diff --git a/security/selinux/include/av_inherit.h b/security/selinux/include/av_inherit.h index b0e6b12..a68fdd5 100644 --- a/security/selinux/include/av_inherit.h +++ b/security/selinux/include/av_inherit.h @@ -29,3 +29,4 @@ S_(SECCLASS_NETLINK_IP6FW_SOCKET, socket, 0x00400000UL) S_(SECCLASS_NETLINK_DNRT_SOCKET, socket, 0x00400000UL) S_(SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET, socket, 0x00400000UL) + S_(SECCLASS_APPLETALK_SOCKET, socket, 0x00400000UL) diff --git a/security/selinux/include/av_perm_to_string.h b/security/selinux/include/av_perm_to_string.h index 591e98d..7c9b583 100644 --- a/security/selinux/include/av_perm_to_string.h +++ b/security/selinux/include/av_perm_to_string.h @@ -72,6 +72,8 @@ S_(SECCLASS_PROCESS, PROCESS__EXECMEM, "execmem") S_(SECCLASS_PROCESS, PROCESS__EXECSTACK, "execstack") S_(SECCLASS_PROCESS, PROCESS__EXECHEAP, "execheap") + S_(SECCLASS_PROCESS, PROCESS__SETKEYCREATE, "setkeycreate") + S_(SECCLASS_PROCESS, PROCESS__SETSOCKCREATE, "setsockcreate") S_(SECCLASS_MSGQ, MSGQ__ENQUEUE, "enqueue") S_(SECCLASS_MSG, MSG__SEND, "send") S_(SECCLASS_MSG, MSG__RECEIVE, "receive") @@ -239,3 +241,13 @@ S_(SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, "sendto") S_(SECCLASS_ASSOCIATION, ASSOCIATION__RECVFROM, "recvfrom") S_(SECCLASS_ASSOCIATION, ASSOCIATION__SETCONTEXT, "setcontext") + S_(SECCLASS_PACKET, PACKET__SEND, "send") + S_(SECCLASS_PACKET, PACKET__RECV, "recv") + S_(SECCLASS_PACKET, PACKET__RELABELTO, "relabelto") + S_(SECCLASS_KEY, KEY__VIEW, "view") + S_(SECCLASS_KEY, KEY__READ, "read") + S_(SECCLASS_KEY, KEY__WRITE, "write") + S_(SECCLASS_KEY, KEY__SEARCH, "search") + S_(SECCLASS_KEY, KEY__LINK, "link") + S_(SECCLASS_KEY, KEY__SETATTR, "setattr") + S_(SECCLASS_KEY, KEY__CREATE, "create") diff --git a/security/selinux/include/av_permissions.h b/security/selinux/include/av_permissions.h index d7f02ed..69fd4b4 100644 --- a/security/selinux/include/av_permissions.h +++ b/security/selinux/include/av_permissions.h @@ -467,6 +467,8 @@ #define PROCESS__SETCURRENT #define PROCESS__EXECMEM 0x02000000UL #define PROCESS__EXECSTACK 0x04000000UL #define PROCESS__EXECHEAP 0x08000000UL +#define PROCESS__SETKEYCREATE 0x10000000UL +#define PROCESS__SETSOCKCREATE 0x20000000UL #define IPC__CREATE 0x00000001UL #define IPC__DESTROY 0x00000002UL @@ -933,3 +935,37 @@ #define NETLINK_KOBJECT_UEVENT_SOCKET__R #define NETLINK_KOBJECT_UEVENT_SOCKET__SEND_MSG 0x00100000UL #define NETLINK_KOBJECT_UEVENT_SOCKET__NAME_BIND 0x00200000UL +#define APPLETALK_SOCKET__IOCTL 0x00000001UL +#define APPLETALK_SOCKET__READ 0x00000002UL +#define APPLETALK_SOCKET__WRITE 0x00000004UL +#define APPLETALK_SOCKET__CREATE 0x00000008UL +#define APPLETALK_SOCKET__GETATTR 0x00000010UL +#define APPLETALK_SOCKET__SETATTR 0x00000020UL +#define APPLETALK_SOCKET__LOCK 0x00000040UL +#define APPLETALK_SOCKET__RELABELFROM 0x00000080UL +#define APPLETALK_SOCKET__RELABELTO 0x00000100UL +#define APPLETALK_SOCKET__APPEND 0x00000200UL +#define APPLETALK_SOCKET__BIND 0x00000400UL +#define APPLETALK_SOCKET__CONNECT 0x00000800UL +#define APPLETALK_SOCKET__LISTEN 0x00001000UL +#define APPLETALK_SOCKET__ACCEPT 0x00002000UL +#define APPLETALK_SOCKET__GETOPT 0x00004000UL +#define APPLETALK_SOCKET__SETOPT 0x00008000UL +#define APPLETALK_SOCKET__SHUTDOWN 0x00010000UL +#define APPLETALK_SOCKET__RECVFROM 0x00020000UL +#define APPLETALK_SOCKET__SENDTO 0x00040000UL +#define APPLETALK_SOCKET__RECV_MSG 0x00080000UL +#define APPLETALK_SOCKET__SEND_MSG 0x00100000UL +#define APPLETALK_SOCKET__NAME_BIND 0x00200000UL + +#define PACKET__SEND 0x00000001UL +#define PACKET__RECV 0x00000002UL +#define PACKET__RELABELTO 0x00000004UL + +#define KEY__VIEW 0x00000001UL +#define KEY__READ 0x00000002UL +#define KEY__WRITE 0x00000004UL +#define KEY__SEARCH 0x00000008UL +#define KEY__LINK 0x00000010UL +#define KEY__SETATTR 0x00000020UL +#define KEY__CREATE 0x00000040UL diff --git a/security/selinux/include/class_to_string.h b/security/selinux/include/class_to_string.h index 77b2c59..24303b6 100644 --- a/security/selinux/include/class_to_string.h +++ b/security/selinux/include/class_to_string.h @@ -58,3 +58,6 @@ S_("nscd") S_("association") S_("netlink_kobject_uevent_socket") + S_("appletalk_socket") + S_("packet") + S_("key") diff --git a/security/selinux/include/flask.h b/security/selinux/include/flask.h index eb9f508..95887ae 100644 --- a/security/selinux/include/flask.h +++ b/security/selinux/include/flask.h @@ -60,6 +60,9 @@ #define SECCLASS_DBUS #define SECCLASS_NSCD 53 #define SECCLASS_ASSOCIATION 54 #define SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET 55 +#define SECCLASS_APPLETALK_SOCKET 56 +#define SECCLASS_PACKET 57 +#define SECCLASS_KEY 58 /* * Security identifier indices for initial entities diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 54c0307..cf54a30 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -32,6 +32,8 @@ struct task_security_struct { u32 sid; /* current SID */ u32 exec_sid; /* exec SID */ u32 create_sid; /* fscreate SID */ + u32 keycreate_sid; /* keycreate SID */ + u32 sockcreate_sid; /* fscreate SID */ u32 ptrace_sid; /* SID of ptrace parent */ }; @@ -99,6 +101,11 @@ struct sk_security_struct { u32 peer_sid; /* SID of peer */ }; +struct key_security_struct { + struct key *obj; /* back pointer */ + u32 sid; /* SID of key */ +}; + extern unsigned int selinux_checkreqprot; #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index c10f1fc..c96498a 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -9,8 +9,10 @@ #define _SELINUX_XFRM_H_ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *sec_ctx); int selinux_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new); void selinux_xfrm_policy_free(struct xfrm_policy *xp); +int selinux_xfrm_policy_delete(struct xfrm_policy *xp); int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *sec_ctx); void selinux_xfrm_state_free(struct xfrm_state *x); +int selinux_xfrm_state_delete(struct xfrm_state *x); int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir); /* @@ -49,7 +51,7 @@ static inline int selinux_xfrm_sock_rcv_ static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb) { - return NF_ACCEPT; + return 0; } static inline int selinux_socket_getpeer_stream(struct sock *sk) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index a4efc96..00534c3 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -9,7 +9,6 @@ * the Free Software Foundation, version 2. */ -#include #include #include #include @@ -38,6 +37,14 @@ #include "conditional.h" unsigned int selinux_checkreqprot = CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE; +#ifdef CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT +#define SELINUX_COMPAT_NET_VALUE 0 +#else +#define SELINUX_COMPAT_NET_VALUE 1 +#endif + +int selinux_compat_net = SELINUX_COMPAT_NET_VALUE; + static int __init checkreqprot_setup(char *str) { selinux_checkreqprot = simple_strtoul(str,NULL,0) ? 1 : 0; @@ -45,6 +52,13 @@ static int __init checkreqprot_setup(cha } __setup("checkreqprot=", checkreqprot_setup); +static int __init selinux_compat_net_setup(char *str) +{ + selinux_compat_net = simple_strtoul(str,NULL,0) ? 1 : 0; + return 1; +} +__setup("selinux_compat_net=", selinux_compat_net_setup); + static DEFINE_MUTEX(sel_mutex); @@ -85,6 +99,7 @@ enum sel_inos { SEL_AVC, /* AVC management directory */ SEL_MEMBER, /* compute polyinstantiation membership decision */ SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ + SEL_COMPAT_NET, /* whether to use old compat network packet controls */ }; #define TMPBUFLEN 12 @@ -364,6 +379,55 @@ static struct file_operations sel_checkr .write = sel_write_checkreqprot, }; +static ssize_t sel_read_compat_net(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char tmpbuf[TMPBUFLEN]; + ssize_t length; + + length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_compat_net); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); +} + +static ssize_t sel_write_compat_net(struct file * file, const char __user * buf, + size_t count, loff_t *ppos) +{ + char *page; + ssize_t length; + int new_value; + + length = task_has_security(current, SECURITY__LOAD_POLICY); + if (length) + return length; + + if (count >= PAGE_SIZE) + return -ENOMEM; + if (*ppos != 0) { + /* No partial writes. */ + return -EINVAL; + } + page = (char*)get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + length = -EFAULT; + if (copy_from_user(page, buf, count)) + goto out; + + length = -EINVAL; + if (sscanf(page, "%d", &new_value) != 1) + goto out; + + selinux_compat_net = new_value ? 1 : 0; + length = count; +out: + free_page((unsigned long) page); + return length; +} +static struct file_operations sel_compat_net_ops = { + .read = sel_read_compat_net, + .write = sel_write_compat_net, +}; + /* * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c */ @@ -1219,6 +1283,7 @@ static int sel_fill_super(struct super_b [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, + [SEL_COMPAT_NET] = {"compat_net", &sel_compat_net_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); @@ -1279,10 +1344,11 @@ err: goto out; } -static struct super_block *sel_get_sb(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int sel_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + struct vfsmount *mnt) { - return get_sb_single(fs_type, flags, data, sel_fill_super); + return get_sb_single(fs_type, flags, data, sel_fill_super, mnt); } static struct file_system_type sel_fs_type = { diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index c284dbb..d2e80e6 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1845,15 +1845,20 @@ int selinux_audit_rule_init(u32 field, u return -ENOTSUPP; switch (field) { - case AUDIT_SE_USER: - case AUDIT_SE_ROLE: - case AUDIT_SE_TYPE: + case AUDIT_SUBJ_USER: + case AUDIT_SUBJ_ROLE: + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_USER: + case AUDIT_OBJ_ROLE: + case AUDIT_OBJ_TYPE: /* only 'equals' and 'not equals' fit user, role, and type */ if (op != AUDIT_EQUAL && op != AUDIT_NOT_EQUAL) return -EINVAL; break; - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: /* we do not allow a range, indicated by the presense of '-' */ if (strchr(rulestr, '-')) return -EINVAL; @@ -1874,29 +1879,34 @@ int selinux_audit_rule_init(u32 field, u tmprule->au_seqno = latest_granting; switch (field) { - case AUDIT_SE_USER: + case AUDIT_SUBJ_USER: + case AUDIT_OBJ_USER: userdatum = hashtab_search(policydb.p_users.table, rulestr); if (!userdatum) rc = -EINVAL; else tmprule->au_ctxt.user = userdatum->value; break; - case AUDIT_SE_ROLE: + case AUDIT_SUBJ_ROLE: + case AUDIT_OBJ_ROLE: roledatum = hashtab_search(policydb.p_roles.table, rulestr); if (!roledatum) rc = -EINVAL; else tmprule->au_ctxt.role = roledatum->value; break; - case AUDIT_SE_TYPE: + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_TYPE: typedatum = hashtab_search(policydb.p_types.table, rulestr); if (!typedatum) rc = -EINVAL; else tmprule->au_ctxt.type = typedatum->value; break; - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: rc = mls_from_string(rulestr, &tmprule->au_ctxt, GFP_ATOMIC); break; } @@ -1948,7 +1958,8 @@ int selinux_audit_rule_match(u32 ctxid, /* a field/op pair that is not caught here will simply fall through without a match */ switch (field) { - case AUDIT_SE_USER: + case AUDIT_SUBJ_USER: + case AUDIT_OBJ_USER: switch (op) { case AUDIT_EQUAL: match = (ctxt->user == rule->au_ctxt.user); @@ -1958,7 +1969,8 @@ int selinux_audit_rule_match(u32 ctxid, break; } break; - case AUDIT_SE_ROLE: + case AUDIT_SUBJ_ROLE: + case AUDIT_OBJ_ROLE: switch (op) { case AUDIT_EQUAL: match = (ctxt->role == rule->au_ctxt.role); @@ -1968,7 +1980,8 @@ int selinux_audit_rule_match(u32 ctxid, break; } break; - case AUDIT_SE_TYPE: + case AUDIT_SUBJ_TYPE: + case AUDIT_OBJ_TYPE: switch (op) { case AUDIT_EQUAL: match = (ctxt->type == rule->au_ctxt.type); @@ -1978,9 +1991,12 @@ int selinux_audit_rule_match(u32 ctxid, break; } break; - case AUDIT_SE_SEN: - case AUDIT_SE_CLR: - level = (op == AUDIT_SE_SEN ? + case AUDIT_SUBJ_SEN: + case AUDIT_SUBJ_CLR: + case AUDIT_OBJ_LEV_LOW: + case AUDIT_OBJ_LEV_HIGH: + level = ((field == AUDIT_SUBJ_SEN || + field == AUDIT_OBJ_LEV_LOW) ? &ctxt->range.level[0] : &ctxt->range.level[1]); switch (op) { case AUDIT_EQUAL: diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index abe99d8..6c985ce 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -26,7 +26,6 @@ * 2. Emulating a reasonable SO_PEERSEC across machines * 3. Testing addition of sk_policy's with security context via setsockopt */ -#include #include #include #include @@ -132,10 +131,7 @@ static int selinux_xfrm_sec_ctx_alloc(st goto out; /* - * Does the subject have permission to set security or permission to - * do the relabel? - * Must be permitted to relabel from default socket type (process type) - * to specified context + * Does the subject have permission to set security context? */ rc = avc_has_perm(tsec->sid, ctx->ctx_sid, SECCLASS_ASSOCIATION, @@ -201,6 +197,23 @@ void selinux_xfrm_policy_free(struct xfr } /* + * LSM hook implementation that authorizes deletion of labeled policies. + */ +int selinux_xfrm_policy_delete(struct xfrm_policy *xp) +{ + struct task_security_struct *tsec = current->security; + struct xfrm_sec_ctx *ctx = xp->security; + int rc = 0; + + if (ctx) + rc = avc_has_perm(tsec->sid, ctx->ctx_sid, + SECCLASS_ASSOCIATION, + ASSOCIATION__SETCONTEXT, NULL); + + return rc; +} + +/* * LSM hook implementation that allocs and transfers sec_ctx spec to * xfrm_state. */ @@ -292,6 +305,23 @@ u32 selinux_socket_getpeer_dgram(struct return SECSID_NULL; } + /* + * LSM hook implementation that authorizes deletion of labeled SAs. + */ +int selinux_xfrm_state_delete(struct xfrm_state *x) +{ + struct task_security_struct *tsec = current->security; + struct xfrm_sec_ctx *ctx = x->security; + int rc = 0; + + if (ctx) + rc = avc_has_perm(tsec->sid, ctx->ctx_sid, + SECCLASS_ASSOCIATION, + ASSOCIATION__SETCONTEXT, NULL); + + return rc; +} + /* * LSM hook that controls access to unlabelled packets. If * a xfrm_state is authorizable (defined by macro) then it was @@ -356,18 +386,12 @@ int selinux_xfrm_postroute_last(u32 isec struct xfrm_state *x = dst_test->xfrm; if (x && selinux_authorizable_xfrm(x)) - goto accept; + goto out; } } rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION, ASSOCIATION__SENDTO, NULL); - if (rc) - goto drop; - -accept: - return NF_ACCEPT; - -drop: - return NF_DROP; +out: + return rc; } diff --git a/sound/Kconfig b/sound/Kconfig index b65ee47..e0d791a 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -58,6 +58,8 @@ source "sound/pci/Kconfig" source "sound/ppc/Kconfig" +source "sound/aoa/Kconfig" + source "sound/arm/Kconfig" source "sound/mips/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index f352bb2..1f60797 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ +obj-$(CONFIG_SND_AOA) += aoa/ ifeq ($(CONFIG_SND),y) obj-y += last.o diff --git a/sound/aoa/Kconfig b/sound/aoa/Kconfig new file mode 100644 index 0000000..2f4334d --- /dev/null +++ b/sound/aoa/Kconfig @@ -0,0 +1,18 @@ +menu "Apple Onboard Audio driver" + depends on SND!=n && PPC + +config SND_AOA + tristate "Apple Onboard Audio driver" + depends on SND + select SND_PCM + ---help--- + This option enables the new driver for the various + Apple Onboard Audio components. + +source "sound/aoa/fabrics/Kconfig" + +source "sound/aoa/codecs/Kconfig" + +source "sound/aoa/soundbus/Kconfig" + +endmenu diff --git a/sound/aoa/Makefile b/sound/aoa/Makefile new file mode 100644 index 0000000..d8de3e7 --- /dev/null +++ b/sound/aoa/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_SND_AOA) += core/ +obj-$(CONFIG_SND_AOA) += codecs/ +obj-$(CONFIG_SND_AOA) += fabrics/ +obj-$(CONFIG_SND_AOA_SOUNDBUS) += soundbus/ diff --git a/sound/aoa/aoa-gpio.h b/sound/aoa/aoa-gpio.h new file mode 100644 index 0000000..3a61f31 --- /dev/null +++ b/sound/aoa/aoa-gpio.h @@ -0,0 +1,81 @@ +/* + * Apple Onboard Audio GPIO definitions + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#ifndef __AOA_GPIO_H +#define __AOA_GPIO_H +#include +#include +#include + +typedef void (*notify_func_t)(void *data); + +enum notify_type { + AOA_NOTIFY_HEADPHONE, + AOA_NOTIFY_LINE_IN, + AOA_NOTIFY_LINE_OUT, +}; + +struct gpio_runtime; +struct gpio_methods { + /* for initialisation/de-initialisation of the GPIO layer */ + void (*init)(struct gpio_runtime *rt); + void (*exit)(struct gpio_runtime *rt); + + /* turn off headphone, speakers, lineout */ + void (*all_amps_off)(struct gpio_runtime *rt); + /* turn headphone, speakers, lineout back to previous setting */ + void (*all_amps_restore)(struct gpio_runtime *rt); + + void (*set_headphone)(struct gpio_runtime *rt, int on); + void (*set_speakers)(struct gpio_runtime *rt, int on); + void (*set_lineout)(struct gpio_runtime *rt, int on); + + int (*get_headphone)(struct gpio_runtime *rt); + int (*get_speakers)(struct gpio_runtime *rt); + int (*get_lineout)(struct gpio_runtime *rt); + + void (*set_hw_reset)(struct gpio_runtime *rt, int on); + + /* use this to be notified of any events. The notification + * function is passed the data, and is called in process + * context by the use of schedule_work. + * The interface for it is that setting a function to NULL + * removes it, and they return 0 if the operation succeeded, + * and -EBUSY if the notification is already assigned by + * someone else. */ + int (*set_notify)(struct gpio_runtime *rt, + enum notify_type type, + notify_func_t notify, + void *data); + /* returns 0 if not plugged in, 1 if plugged in + * or a negative error code */ + int (*get_detect)(struct gpio_runtime *rt, + enum notify_type type); +}; + +struct gpio_notification { + notify_func_t notify; + void *data; + void *gpio_private; + struct work_struct work; + struct mutex mutex; +}; + +struct gpio_runtime { + /* to be assigned by fabric */ + struct device_node *node; + /* since everyone needs this pointer anyway... */ + struct gpio_methods *methods; + /* to be used by the gpio implementation */ + int implementation_private; + struct gpio_notification headphone_notify; + struct gpio_notification line_in_notify; + struct gpio_notification line_out_notify; +}; + +#endif /* __AOA_GPIO_H */ diff --git a/sound/aoa/aoa.h b/sound/aoa/aoa.h new file mode 100644 index 0000000..378ef1e --- /dev/null +++ b/sound/aoa/aoa.h @@ -0,0 +1,131 @@ +/* + * Apple Onboard Audio definitions + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#ifndef __AOA_H +#define __AOA_H +#include +#include +/* So apparently there's a reason for requiring driver.h to be included first! */ +#include +#include +#include +#include +#include "aoa-gpio.h" +#include "soundbus/soundbus.h" + +#define MAX_CODEC_NAME_LEN 32 + +struct aoa_codec { + char name[MAX_CODEC_NAME_LEN]; + + struct module *owner; + + /* called when the fabric wants to init this codec. + * Do alsa card manipulations from here. */ + int (*init)(struct aoa_codec *codec); + + /* called when the fabric is done with the codec. + * The alsa card will be cleaned up so don't bother. */ + void (*exit)(struct aoa_codec *codec); + + /* May be NULL, but can be used by the fabric. + * Refcounting is the codec driver's responsibility */ + struct device_node *node; + + /* assigned by fabric before init() is called, points + * to the soundbus device. Cannot be NULL. */ + struct soundbus_dev *soundbus_dev; + + /* assigned by the fabric before init() is called, points + * to the fabric's gpio runtime record for the relevant + * device. */ + struct gpio_runtime *gpio; + + /* assigned by the fabric before init() is called, contains + * a codec specific bitmask of what outputs and inputs are + * actually connected */ + u32 connected; + + /* data the fabric can associate with this structure */ + void *fabric_data; + + /* private! */ + struct list_head list; + struct aoa_fabric *fabric; +}; + +/* return 0 on success */ +extern int +aoa_codec_register(struct aoa_codec *codec); +extern void +aoa_codec_unregister(struct aoa_codec *codec); + +#define MAX_LAYOUT_NAME_LEN 32 + +struct aoa_fabric { + char name[MAX_LAYOUT_NAME_LEN]; + + struct module *owner; + + /* once codecs register, they are passed here after. + * They are of course not initialised, since the + * fabric is responsible for initialising some fields + * in the codec structure! */ + int (*found_codec)(struct aoa_codec *codec); + /* called for each codec when it is removed, + * also in the case that aoa_fabric_unregister + * is called and all codecs are removed + * from this fabric. + * Also called if found_codec returned 0 but + * the codec couldn't initialise. */ + void (*remove_codec)(struct aoa_codec *codec); + /* If found_codec returned 0, and the codec + * could be initialised, this is called. */ + void (*attached_codec)(struct aoa_codec *codec); +}; + +/* return 0 on success, -EEXIST if another fabric is + * registered, -EALREADY if the same fabric is registered. + * Passing NULL can be used to test for the presence + * of another fabric, if -EALREADY is returned there is + * no other fabric present. + * In the case that the function returns -EALREADY + * and the fabric passed is not NULL, all codecs + * that are not assigned yet are passed to the fabric + * again for reconsideration. */ +extern int +aoa_fabric_register(struct aoa_fabric *fabric); + +/* it is vital to call this when the fabric exits! + * When calling, the remove_codec will be called + * for all codecs, unless it is NULL. */ +extern void +aoa_fabric_unregister(struct aoa_fabric *fabric); + +/* if for some reason you want to get rid of a codec + * before the fabric is removed, use this. + * Note that remove_codec is called for it! */ +extern void +aoa_fabric_unlink_codec(struct aoa_codec *codec); + +/* alsa help methods */ +struct aoa_card { + struct snd_card *alsa_card; +}; + +extern int aoa_snd_device_new(snd_device_type_t type, + void * device_data, struct snd_device_ops * ops); +extern struct snd_card *aoa_get_card(void); +extern int aoa_snd_ctl_add(struct snd_kcontrol* control); + +/* GPIO stuff */ +extern struct gpio_methods *pmf_gpio_methods; +extern struct gpio_methods *ftr_gpio_methods; +/* extern struct gpio_methods *map_gpio_methods; */ + +#endif /* __AOA_H */ diff --git a/sound/aoa/codecs/Kconfig b/sound/aoa/codecs/Kconfig new file mode 100644 index 0000000..90cf58f --- /dev/null +++ b/sound/aoa/codecs/Kconfig @@ -0,0 +1,32 @@ +config SND_AOA_ONYX + tristate "support Onyx chip" + depends on SND_AOA + ---help--- + This option enables support for the Onyx (pcm3052) + codec chip found in the latest Apple machines + (most of those with digital audio output). + +#config SND_AOA_TOPAZ +# tristate "support Topaz chips" +# depends on SND_AOA +# ---help--- +# This option enables support for the Topaz (CS84xx) +# codec chips found in the latest Apple machines, +# these chips do the digital input and output on +# some PowerMacs. + +config SND_AOA_TAS + tristate "support TAS chips" + depends on SND_AOA + ---help--- + This option enables support for the tas chips + found in a lot of Apple Machines, especially + iBooks and PowerBooks without digital. + +config SND_AOA_TOONIE + tristate "support Toonie chip" + depends on SND_AOA + ---help--- + This option enables support for the toonie codec + found in the Mac Mini. If you have a Mac Mini and + want to hear sound, select this option. diff --git a/sound/aoa/codecs/Makefile b/sound/aoa/codecs/Makefile new file mode 100644 index 0000000..31cbe68 --- /dev/null +++ b/sound/aoa/codecs/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SND_AOA_ONYX) += snd-aoa-codec-onyx.o +obj-$(CONFIG_SND_AOA_TAS) += snd-aoa-codec-tas.o +obj-$(CONFIG_SND_AOA_TOONIE) += snd-aoa-codec-toonie.o diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.c b/sound/aoa/codecs/snd-aoa-codec-onyx.c new file mode 100644 index 0000000..0b76507 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.c @@ -0,0 +1,1113 @@ +/* + * Apple Onboard Audio driver for Onyx codec + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + * + * + * This is a driver for the pcm3052 codec chip (codenamed Onyx) + * that is present in newer Apple hardware (with digital output). + * + * The Onyx codec has the following connections (listed by the bit + * to be used in aoa_codec.connected): + * 0: analog output + * 1: digital output + * 2: line input + * 3: microphone input + * Note that even though I know of no machine that has for example + * the digital output connected but not the analog, I have handled + * all the different cases in the code so that this driver may serve + * as a good example of what to do. + * + * NOTE: This driver assumes that there's at most one chip to be + * used with one alsa card, in form of creating all kinds + * of mixer elements without regard for their existence. + * But snd-aoa assumes that there's at most one card, so + * this means you can only have one onyx on a system. This + * should probably be fixed by changing the assumption of + * having just a single card on a system, and making the + * 'card' pointer accessible to anyone who needs it instead + * of hiding it in the aoa_snd_* functions... + * + */ +#include +#include +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("pcm3052 (onyx) codec driver for snd-aoa"); + +#include "snd-aoa-codec-onyx.h" +#include "../aoa.h" +#include "../soundbus/soundbus.h" + + +#define PFX "snd-aoa-codec-onyx: " + +struct onyx { + /* cache registers 65 to 80, they are write-only! */ + u8 cache[16]; + struct i2c_client i2c; + struct aoa_codec codec; + u32 initialised:1, + spdif_locked:1, + analog_locked:1, + original_mute:2; + int open_count; + struct codec_info *codec_info; + + /* mutex serializes concurrent access to the device + * and this structure. + */ + struct mutex mutex; +}; +#define codec_to_onyx(c) container_of(c, struct onyx, codec) + +/* both return 0 if all ok, else on error */ +static int onyx_read_register(struct onyx *onyx, u8 reg, u8 *value) +{ + s32 v; + + if (reg != ONYX_REG_CONTROL) { + *value = onyx->cache[reg-FIRSTREGISTER]; + return 0; + } + v = i2c_smbus_read_byte_data(&onyx->i2c, reg); + if (v < 0) + return -1; + *value = (u8)v; + onyx->cache[ONYX_REG_CONTROL-FIRSTREGISTER] = *value; + return 0; +} + +static int onyx_write_register(struct onyx *onyx, u8 reg, u8 value) +{ + int result; + + result = i2c_smbus_write_byte_data(&onyx->i2c, reg, value); + if (!result) + onyx->cache[reg-FIRSTREGISTER] = value; + return result; +} + +/* alsa stuff */ + +static int onyx_dev_register(struct snd_device *dev) +{ + return 0; +} + +static struct snd_device_ops ops = { + .dev_register = onyx_dev_register, +}; + +/* this is necessary because most alsa mixer programs + * can't properly handle the negative range */ +#define VOLUME_RANGE_SHIFT 128 + +static int onyx_snd_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = -128 + VOLUME_RANGE_SHIFT; + uinfo->value.integer.max = -1 + VOLUME_RANGE_SHIFT; + return 0; +} + +static int onyx_snd_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + s8 l, r; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); + mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = l + VOLUME_RANGE_SHIFT; + ucontrol->value.integer.value[1] = r + VOLUME_RANGE_SHIFT; + + return 0; +} + +static int onyx_snd_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + s8 l, r; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, &l); + onyx_read_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, &r); + + if (l + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[0] && + r + VOLUME_RANGE_SHIFT == ucontrol->value.integer.value[1]) { + mutex_unlock(&onyx->mutex); + return 0; + } + + onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_LEFT, + ucontrol->value.integer.value[0] + - VOLUME_RANGE_SHIFT); + onyx_write_register(onyx, ONYX_REG_DAC_ATTEN_RIGHT, + ucontrol->value.integer.value[1] + - VOLUME_RANGE_SHIFT); + mutex_unlock(&onyx->mutex); + + return 1; +} + +static struct snd_kcontrol_new volume_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = onyx_snd_vol_info, + .get = onyx_snd_vol_get, + .put = onyx_snd_vol_put, +}; + +/* like above, this is necessary because a lot + * of alsa mixer programs don't handle ranges + * that don't start at 0 properly. + * even alsamixer is one of them... */ +#define INPUTGAIN_RANGE_SHIFT (-3) + +static int onyx_snd_inputgain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 3 + INPUTGAIN_RANGE_SHIFT; + uinfo->value.integer.max = 28 + INPUTGAIN_RANGE_SHIFT; + return 0; +} + +static int onyx_snd_inputgain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 ig; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &ig); + mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = + (ig & ONYX_ADC_PGA_GAIN_MASK) + INPUTGAIN_RANGE_SHIFT; + + return 0; +} + +static int onyx_snd_inputgain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v, n; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); + n = v; + n &= ~ONYX_ADC_PGA_GAIN_MASK; + n |= (ucontrol->value.integer.value[0] - INPUTGAIN_RANGE_SHIFT) + & ONYX_ADC_PGA_GAIN_MASK; + onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, n); + mutex_unlock(&onyx->mutex); + + return n != v; +} + +static struct snd_kcontrol_new inputgain_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = onyx_snd_inputgain_info, + .get = onyx_snd_inputgain_get, + .put = onyx_snd_inputgain_put, +}; + +static int onyx_snd_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "Line-In", "Microphone" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int onyx_snd_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + s8 v; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); + mutex_unlock(&onyx->mutex); + + ucontrol->value.enumerated.item[0] = !!(v&ONYX_ADC_INPUT_MIC); + + return 0; +} + +static void onyx_set_capture_source(struct onyx *onyx, int mic) +{ + s8 v; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_ADC_CONTROL, &v); + v &= ~ONYX_ADC_INPUT_MIC; + if (mic) + v |= ONYX_ADC_INPUT_MIC; + onyx_write_register(onyx, ONYX_REG_ADC_CONTROL, v); + mutex_unlock(&onyx->mutex); +} + +static int onyx_snd_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + onyx_set_capture_source(snd_kcontrol_chip(kcontrol), + ucontrol->value.enumerated.item[0]); + return 1; +} + +static struct snd_kcontrol_new capture_source_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* If we name this 'Input Source', it properly shows up in + * alsamixer as a selection, * but it's shown under the + * 'Playback' category. + * If I name it 'Capture Source', it shows up in strange + * ways (two bools of which one can be selected at a + * time) but at least it's shown in the 'Capture' + * category. + * I was told that this was due to backward compatibility, + * but I don't understand then why the mangling is *not* + * done when I name it "Input Source"..... + */ + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = onyx_snd_capture_source_info, + .get = onyx_snd_capture_source_get, + .put = onyx_snd_capture_source_put, +}; + +static int onyx_snd_mute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int onyx_snd_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 c; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &c); + mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = !(c & ONYX_MUTE_LEFT); + ucontrol->value.integer.value[1] = !(c & ONYX_MUTE_RIGHT); + + return 0; +} + +static int onyx_snd_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v = 0, c = 0; + int err = -EBUSY; + + mutex_lock(&onyx->mutex); + if (onyx->analog_locked) + goto out_unlock; + + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); + c = v; + c &= ~(ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT); + if (!ucontrol->value.integer.value[0]) + c |= ONYX_MUTE_LEFT; + if (!ucontrol->value.integer.value[1]) + c |= ONYX_MUTE_RIGHT; + err = onyx_write_register(onyx, ONYX_REG_DAC_CONTROL, c); + + out_unlock: + mutex_unlock(&onyx->mutex); + + return !err ? (v != c) : err; +} + +static struct snd_kcontrol_new mute_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = onyx_snd_mute_info, + .get = onyx_snd_mute_get, + .put = onyx_snd_mute_put, +}; + + +static int onyx_snd_single_bit_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +#define FLAG_POLARITY_INVERT 1 +#define FLAG_SPDIFLOCK 2 + +static int onyx_snd_single_bit_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 c; + long int pv = kcontrol->private_value; + u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; + u8 address = (pv >> 8) & 0xff; + u8 mask = pv & 0xff; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, address, &c); + mutex_unlock(&onyx->mutex); + + ucontrol->value.integer.value[0] = !!(c & mask) ^ polarity; + + return 0; +} + +static int onyx_snd_single_bit_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v = 0, c = 0; + int err; + long int pv = kcontrol->private_value; + u8 polarity = (pv >> 16) & FLAG_POLARITY_INVERT; + u8 spdiflock = (pv >> 16) & FLAG_SPDIFLOCK; + u8 address = (pv >> 8) & 0xff; + u8 mask = pv & 0xff; + + mutex_lock(&onyx->mutex); + if (spdiflock && onyx->spdif_locked) { + /* even if alsamixer doesn't care.. */ + err = -EBUSY; + goto out_unlock; + } + onyx_read_register(onyx, address, &v); + c = v; + c &= ~(mask); + if (!!ucontrol->value.integer.value[0] ^ polarity) + c |= mask; + err = onyx_write_register(onyx, address, c); + + out_unlock: + mutex_unlock(&onyx->mutex); + + return !err ? (v != c) : err; +} + +#define SINGLE_BIT(n, type, description, address, mask, flags) \ +static struct snd_kcontrol_new n##_control = { \ + .iface = SNDRV_CTL_ELEM_IFACE_##type, \ + .name = description, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = onyx_snd_single_bit_info, \ + .get = onyx_snd_single_bit_get, \ + .put = onyx_snd_single_bit_put, \ + .private_value = (flags << 16) | (address << 8) | mask \ +} + +SINGLE_BIT(spdif, + MIXER, + SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH), + ONYX_REG_DIG_INFO4, + ONYX_SPDIF_ENABLE, + FLAG_SPDIFLOCK); +SINGLE_BIT(ovr1, + MIXER, + "Oversampling Rate", + ONYX_REG_DAC_CONTROL, + ONYX_OVR1, + 0); +SINGLE_BIT(flt0, + MIXER, + "Fast Digital Filter Rolloff", + ONYX_REG_DAC_FILTER, + ONYX_ROLLOFF_FAST, + FLAG_POLARITY_INVERT); +SINGLE_BIT(hpf, + MIXER, + "Highpass Filter", + ONYX_REG_ADC_HPF_BYPASS, + ONYX_HPF_DISABLE, + FLAG_POLARITY_INVERT); +SINGLE_BIT(dm12, + MIXER, + "Digital De-Emphasis", + ONYX_REG_DAC_DEEMPH, + ONYX_DIGDEEMPH_CTRL, + 0); + +static int onyx_spdif_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + return 0; +} + +static int onyx_spdif_mask_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* datasheet page 30, all others are 0 */ + ucontrol->value.iec958.status[0] = 0x3e; + ucontrol->value.iec958.status[1] = 0xff; + + ucontrol->value.iec958.status[3] = 0x3f; + ucontrol->value.iec958.status[4] = 0x0f; + + return 0; +} + +static struct snd_kcontrol_new onyx_spdif_mask = { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK), + .info = onyx_spdif_info, + .get = onyx_spdif_mask_get, +}; + +static int onyx_spdif_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); + ucontrol->value.iec958.status[0] = v & 0x3e; + + onyx_read_register(onyx, ONYX_REG_DIG_INFO2, &v); + ucontrol->value.iec958.status[1] = v; + + onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); + ucontrol->value.iec958.status[3] = v & 0x3f; + + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + ucontrol->value.iec958.status[4] = v & 0x0f; + mutex_unlock(&onyx->mutex); + + return 0; +} + +static int onyx_spdif_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct onyx *onyx = snd_kcontrol_chip(kcontrol); + u8 v; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DIG_INFO1, &v); + v = (v & ~0x3e) | (ucontrol->value.iec958.status[0] & 0x3e); + onyx_write_register(onyx, ONYX_REG_DIG_INFO1, v); + + v = ucontrol->value.iec958.status[1]; + onyx_write_register(onyx, ONYX_REG_DIG_INFO2, v); + + onyx_read_register(onyx, ONYX_REG_DIG_INFO3, &v); + v = (v & ~0x3f) | (ucontrol->value.iec958.status[3] & 0x3f); + onyx_write_register(onyx, ONYX_REG_DIG_INFO3, v); + + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + v = (v & ~0x0f) | (ucontrol->value.iec958.status[4] & 0x0f); + onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); + mutex_unlock(&onyx->mutex); + + return 1; +} + +static struct snd_kcontrol_new onyx_spdif_ctrl = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), + .info = onyx_spdif_info, + .get = onyx_spdif_get, + .put = onyx_spdif_put, +}; + +/* our registers */ + +static u8 register_map[] = { + ONYX_REG_DAC_ATTEN_LEFT, + ONYX_REG_DAC_ATTEN_RIGHT, + ONYX_REG_CONTROL, + ONYX_REG_DAC_CONTROL, + ONYX_REG_DAC_DEEMPH, + ONYX_REG_DAC_FILTER, + ONYX_REG_DAC_OUTPHASE, + ONYX_REG_ADC_CONTROL, + ONYX_REG_ADC_HPF_BYPASS, + ONYX_REG_DIG_INFO1, + ONYX_REG_DIG_INFO2, + ONYX_REG_DIG_INFO3, + ONYX_REG_DIG_INFO4 +}; + +static u8 initial_values[ARRAY_SIZE(register_map)] = { + 0x80, 0x80, /* muted */ + ONYX_MRST | ONYX_SRST, /* but handled specially! */ + ONYX_MUTE_LEFT | ONYX_MUTE_RIGHT, + 0, /* no deemphasis */ + ONYX_DAC_FILTER_ALWAYS, + ONYX_OUTPHASE_INVERTED, + (-1 /*dB*/ + 8) & 0xF, /* line in selected, -1 dB gain*/ + ONYX_ADC_HPF_ALWAYS, + (1<<2), /* pcm audio */ + 2, /* category: pcm coder */ + 0, /* sampling frequency 44.1 kHz, clock accuracy level II */ + 1 /* 24 bit depth */ +}; + +/* reset registers of chip, either to initial or to previous values */ +static int onyx_register_init(struct onyx *onyx) +{ + int i; + u8 val; + u8 regs[sizeof(initial_values)]; + + if (!onyx->initialised) { + memcpy(regs, initial_values, sizeof(initial_values)); + if (onyx_read_register(onyx, ONYX_REG_CONTROL, &val)) + return -1; + val &= ~ONYX_SILICONVERSION; + val |= initial_values[3]; + regs[3] = val; + } else { + for (i=0; icache[register_map[i]-FIRSTREGISTER]; + } + + for (i=0; iinitialised = 1; + return 0; +} + +static struct transfer_info onyx_transfers[] = { + /* this is first so we can skip it if no input is present... + * No hardware exists with that, but it's here as an example + * of what to do :) */ + { + /* analog input */ + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_8000_96000, + .transfer_in = 1, + .must_be_clock_source = 0, + .tag = 0, + }, + { + /* if analog and digital are currently off, anything should go, + * so this entry describes everything we can do... */ + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE +#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE + | SNDRV_PCM_FMTBIT_COMPRESSED_16BE +#endif + , + .rates = SNDRV_PCM_RATE_8000_96000, + .tag = 0, + }, + { + /* analog output */ + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_8000_96000, + .transfer_in = 0, + .must_be_clock_source = 0, + .tag = 1, + }, + { + /* digital pcm output, also possible for analog out */ + .formats = SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .transfer_in = 0, + .must_be_clock_source = 0, + .tag = 2, + }, +#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE +Once alsa gets supports for this kind of thing we can add it... + { + /* digital compressed output */ + .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .tag = 2, + }, +#endif + {} +}; + +static int onyx_usable(struct codec_info_item *cii, + struct transfer_info *ti, + struct transfer_info *out) +{ + u8 v; + struct onyx *onyx = cii->codec_data; + int spdif_enabled, analog_enabled; + + mutex_lock(&onyx->mutex); + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + spdif_enabled = !!(v & ONYX_SPDIF_ENABLE); + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); + analog_enabled = + (v & (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT)) + != (ONYX_MUTE_RIGHT|ONYX_MUTE_LEFT); + mutex_unlock(&onyx->mutex); + + switch (ti->tag) { + case 0: return 1; + case 1: return analog_enabled; + case 2: return spdif_enabled; + } + return 1; +} + +static int onyx_prepare(struct codec_info_item *cii, + struct bus_info *bi, + struct snd_pcm_substream *substream) +{ + u8 v; + struct onyx *onyx = cii->codec_data; + int err = -EBUSY; + + mutex_lock(&onyx->mutex); + +#ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE + if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) { + /* mute and lock analog output */ + onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v); + if (onyx_write_register(onyx + ONYX_REG_DAC_CONTROL, + v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT)) + goto out_unlock; + onyx->analog_locked = 1; + err = 0; + goto out_unlock; + } +#endif + switch (substream->runtime->rate) { + case 32000: + case 44100: + case 48000: + /* these rates are ok for all outputs */ + /* FIXME: program spdif channel control bits here so that + * userspace doesn't have to if it only plays pcm! */ + err = 0; + goto out_unlock; + default: + /* got some rate that the digital output can't do, + * so disable and lock it */ + onyx_read_register(cii->codec_data, ONYX_REG_DIG_INFO4, &v); + if (onyx_write_register(onyx, + ONYX_REG_DIG_INFO4, + v & ~ONYX_SPDIF_ENABLE)) + goto out_unlock; + onyx->spdif_locked = 1; + err = 0; + goto out_unlock; + } + + out_unlock: + mutex_unlock(&onyx->mutex); + + return err; +} + +static int onyx_open(struct codec_info_item *cii, + struct snd_pcm_substream *substream) +{ + struct onyx *onyx = cii->codec_data; + + mutex_lock(&onyx->mutex); + onyx->open_count++; + mutex_unlock(&onyx->mutex); + + return 0; +} + +static int onyx_close(struct codec_info_item *cii, + struct snd_pcm_substream *substream) +{ + struct onyx *onyx = cii->codec_data; + + mutex_lock(&onyx->mutex); + onyx->open_count--; + if (!onyx->open_count) + onyx->spdif_locked = onyx->analog_locked = 0; + mutex_unlock(&onyx->mutex); + + return 0; +} + +static int onyx_switch_clock(struct codec_info_item *cii, + enum clock_switch what) +{ + struct onyx *onyx = cii->codec_data; + + mutex_lock(&onyx->mutex); + /* this *MUST* be more elaborate later... */ + switch (what) { + case CLOCK_SWITCH_PREPARE_SLAVE: + onyx->codec.gpio->methods->all_amps_off(onyx->codec.gpio); + break; + case CLOCK_SWITCH_SLAVE: + onyx->codec.gpio->methods->all_amps_restore(onyx->codec.gpio); + break; + default: /* silence warning */ + break; + } + mutex_unlock(&onyx->mutex); + + return 0; +} + +#ifdef CONFIG_PM + +static int onyx_suspend(struct codec_info_item *cii, pm_message_t state) +{ + struct onyx *onyx = cii->codec_data; + u8 v; + int err = -ENXIO; + + mutex_lock(&onyx->mutex); + if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) + goto out_unlock; + onyx_write_register(onyx, ONYX_REG_CONTROL, v | ONYX_ADPSV | ONYX_DAPSV); + /* Apple does a sleep here but the datasheet says to do it on resume */ + err = 0; + out_unlock: + mutex_unlock(&onyx->mutex); + + return err; +} + +static int onyx_resume(struct codec_info_item *cii) +{ + struct onyx *onyx = cii->codec_data; + u8 v; + int err = -ENXIO; + + mutex_lock(&onyx->mutex); + /* take codec out of suspend */ + if (onyx_read_register(onyx, ONYX_REG_CONTROL, &v)) + goto out_unlock; + onyx_write_register(onyx, ONYX_REG_CONTROL, v & ~(ONYX_ADPSV | ONYX_DAPSV)); + /* FIXME: should divide by sample rate, but 8k is the lowest we go */ + msleep(2205000/8000); + /* reset all values */ + onyx_register_init(onyx); + err = 0; + out_unlock: + mutex_unlock(&onyx->mutex); + + return err; +} + +#endif /* CONFIG_PM */ + +static struct codec_info onyx_codec_info = { + .transfers = onyx_transfers, + .sysclock_factor = 256, + .bus_factor = 64, + .owner = THIS_MODULE, + .usable = onyx_usable, + .prepare = onyx_prepare, + .open = onyx_open, + .close = onyx_close, + .switch_clock = onyx_switch_clock, +#ifdef CONFIG_PM + .suspend = onyx_suspend, + .resume = onyx_resume, +#endif +}; + +static int onyx_init_codec(struct aoa_codec *codec) +{ + struct onyx *onyx = codec_to_onyx(codec); + struct snd_kcontrol *ctl; + struct codec_info *ci = &onyx_codec_info; + u8 v; + int err; + + if (!onyx->codec.gpio || !onyx->codec.gpio->methods) { + printk(KERN_ERR PFX "gpios not assigned!!\n"); + return -EINVAL; + } + + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); + msleep(1); + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 1); + msleep(1); + onyx->codec.gpio->methods->set_hw_reset(onyx->codec.gpio, 0); + msleep(1); + + if (onyx_register_init(onyx)) { + printk(KERN_ERR PFX "failed to initialise onyx registers\n"); + return -ENODEV; + } + + if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, onyx, &ops)) { + printk(KERN_ERR PFX "failed to create onyx snd device!\n"); + return -ENODEV; + } + + /* nothing connected? what a joke! */ + if ((onyx->codec.connected & 0xF) == 0) + return -ENOTCONN; + + /* if no inputs are present... */ + if ((onyx->codec.connected & 0xC) == 0) { + if (!onyx->codec_info) + onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); + if (!onyx->codec_info) + return -ENOMEM; + ci = onyx->codec_info; + *ci = onyx_codec_info; + ci->transfers++; + } + + /* if no outputs are present... */ + if ((onyx->codec.connected & 3) == 0) { + if (!onyx->codec_info) + onyx->codec_info = kmalloc(sizeof(struct codec_info), GFP_KERNEL); + if (!onyx->codec_info) + return -ENOMEM; + ci = onyx->codec_info; + /* this is fine as there have to be inputs + * if we end up in this part of the code */ + *ci = onyx_codec_info; + ci->transfers[1].formats = 0; + } + + if (onyx->codec.soundbus_dev->attach_codec(onyx->codec.soundbus_dev, + aoa_get_card(), + ci, onyx)) { + printk(KERN_ERR PFX "error creating onyx pcm\n"); + return -ENODEV; + } +#define ADDCTL(n) \ + do { \ + ctl = snd_ctl_new1(&n, onyx); \ + if (ctl) { \ + ctl->id.device = \ + onyx->codec.soundbus_dev->pcm->device; \ + err = aoa_snd_ctl_add(ctl); \ + if (err) \ + goto error; \ + } \ + } while (0) + + if (onyx->codec.soundbus_dev->pcm) { + /* give the user appropriate controls + * depending on what inputs are connected */ + if ((onyx->codec.connected & 0xC) == 0xC) + ADDCTL(capture_source_control); + else if (onyx->codec.connected & 4) + onyx_set_capture_source(onyx, 0); + else + onyx_set_capture_source(onyx, 1); + if (onyx->codec.connected & 0xC) + ADDCTL(inputgain_control); + + /* depending on what output is connected, + * give the user appropriate controls */ + if (onyx->codec.connected & 1) { + ADDCTL(volume_control); + ADDCTL(mute_control); + ADDCTL(ovr1_control); + ADDCTL(flt0_control); + ADDCTL(hpf_control); + ADDCTL(dm12_control); + /* spdif control defaults to off */ + } + if (onyx->codec.connected & 2) { + ADDCTL(onyx_spdif_mask); + ADDCTL(onyx_spdif_ctrl); + } + if ((onyx->codec.connected & 3) == 3) + ADDCTL(spdif_control); + /* if only S/PDIF is connected, enable it unconditionally */ + if ((onyx->codec.connected & 3) == 2) { + onyx_read_register(onyx, ONYX_REG_DIG_INFO4, &v); + v |= ONYX_SPDIF_ENABLE; + onyx_write_register(onyx, ONYX_REG_DIG_INFO4, v); + } + } +#undef ADDCTL + printk(KERN_INFO PFX "attached to onyx codec via i2c\n"); + + return 0; + error: + onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); + snd_device_free(aoa_get_card(), onyx); + return err; +} + +static void onyx_exit_codec(struct aoa_codec *codec) +{ + struct onyx *onyx = codec_to_onyx(codec); + + if (!onyx->codec.soundbus_dev) { + printk(KERN_ERR PFX "onyx_exit_codec called without soundbus_dev!\n"); + return; + } + onyx->codec.soundbus_dev->detach_codec(onyx->codec.soundbus_dev, onyx); +} + +static struct i2c_driver onyx_driver; + +static int onyx_create(struct i2c_adapter *adapter, + struct device_node *node, + int addr) +{ + struct onyx *onyx; + u8 dummy; + + onyx = kzalloc(sizeof(struct onyx), GFP_KERNEL); + + if (!onyx) + return -ENOMEM; + + mutex_init(&onyx->mutex); + onyx->i2c.driver = &onyx_driver; + onyx->i2c.adapter = adapter; + onyx->i2c.addr = addr & 0x7f; + strlcpy(onyx->i2c.name, "onyx audio codec", I2C_NAME_SIZE-1); + + if (i2c_attach_client(&onyx->i2c)) { + printk(KERN_ERR PFX "failed to attach to i2c\n"); + goto fail; + } + + /* we try to read from register ONYX_REG_CONTROL + * to check if the codec is present */ + if (onyx_read_register(onyx, ONYX_REG_CONTROL, &dummy) != 0) { + i2c_detach_client(&onyx->i2c); + printk(KERN_ERR PFX "failed to read control register\n"); + goto fail; + } + + strlcpy(onyx->codec.name, "onyx", MAX_CODEC_NAME_LEN-1); + onyx->codec.owner = THIS_MODULE; + onyx->codec.init = onyx_init_codec; + onyx->codec.exit = onyx_exit_codec; + onyx->codec.node = of_node_get(node); + + if (aoa_codec_register(&onyx->codec)) { + i2c_detach_client(&onyx->i2c); + goto fail; + } + printk(KERN_DEBUG PFX "created and attached onyx instance\n"); + return 0; + fail: + kfree(onyx); + return -EINVAL; +} + +static int onyx_i2c_attach(struct i2c_adapter *adapter) +{ + struct device_node *busnode, *dev = NULL; + struct pmac_i2c_bus *bus; + + bus = pmac_i2c_adapter_to_bus(adapter); + if (bus == NULL) + return -ENODEV; + busnode = pmac_i2c_get_bus_node(bus); + + while ((dev = of_get_next_child(busnode, dev)) != NULL) { + if (device_is_compatible(dev, "pcm3052")) { + u32 *addr; + printk(KERN_DEBUG PFX "found pcm3052\n"); + addr = (u32 *) get_property(dev, "reg", NULL); + if (!addr) + return -ENODEV; + return onyx_create(adapter, dev, (*addr)>>1); + } + } + + /* if that didn't work, try desperate mode for older + * machines that have stuff missing from the device tree */ + + if (!device_is_compatible(busnode, "k2-i2c")) + return -ENODEV; + + printk(KERN_DEBUG PFX "found k2-i2c, checking if onyx chip is on it\n"); + /* probe both possible addresses for the onyx chip */ + if (onyx_create(adapter, NULL, 0x46) == 0) + return 0; + return onyx_create(adapter, NULL, 0x47); +} + +static int onyx_i2c_detach(struct i2c_client *client) +{ + struct onyx *onyx = container_of(client, struct onyx, i2c); + int err; + + if ((err = i2c_detach_client(client))) + return err; + aoa_codec_unregister(&onyx->codec); + of_node_put(onyx->codec.node); + if (onyx->codec_info) + kfree(onyx->codec_info); + kfree(onyx); + return 0; +} + +static struct i2c_driver onyx_driver = { + .driver = { + .name = "aoa_codec_onyx", + .owner = THIS_MODULE, + }, + .attach_adapter = onyx_i2c_attach, + .detach_client = onyx_i2c_detach, +}; + +static int __init onyx_init(void) +{ + return i2c_add_driver(&onyx_driver); +} + +static void __exit onyx_exit(void) +{ + i2c_del_driver(&onyx_driver); +} + +module_init(onyx_init); +module_exit(onyx_exit); diff --git a/sound/aoa/codecs/snd-aoa-codec-onyx.h b/sound/aoa/codecs/snd-aoa-codec-onyx.h new file mode 100644 index 0000000..aeedda7 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-onyx.h @@ -0,0 +1,76 @@ +/* + * Apple Onboard Audio driver for Onyx codec (header) + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#ifndef __SND_AOA_CODEC_ONYX_H +#define __SND_AOA_CODEC_ONYX_H +#include +#include +#include +#include +#include + +/* PCM3052 register definitions */ + +/* the attenuation registers take values from + * -1 (0dB) to -127 (-63.0 dB) or others (muted) */ +#define ONYX_REG_DAC_ATTEN_LEFT 65 +#define FIRSTREGISTER ONYX_REG_DAC_ATTEN_LEFT +#define ONYX_REG_DAC_ATTEN_RIGHT 66 + +#define ONYX_REG_CONTROL 67 +# define ONYX_MRST (1<<7) +# define ONYX_SRST (1<<6) +# define ONYX_ADPSV (1<<5) +# define ONYX_DAPSV (1<<4) +# define ONYX_SILICONVERSION (1<<0) +/* all others reserved */ + +#define ONYX_REG_DAC_CONTROL 68 +# define ONYX_OVR1 (1<<6) +# define ONYX_MUTE_RIGHT (1<<1) +# define ONYX_MUTE_LEFT (1<<0) + +#define ONYX_REG_DAC_DEEMPH 69 +# define ONYX_DIGDEEMPH_SHIFT 5 +# define ONYX_DIGDEEMPH_MASK (3< +#include +int main() { + int dB2; + printf("/" "* This file is only included exactly once!\n"); + printf(" *\n"); + printf(" * If they'd only tell us that generating this table was\n"); + printf(" * as easy as calculating\n"); + printf(" * hwvalue = 1048576.0*exp(0.057564628*dB*2)\n"); + printf(" * :) *" "/\n"); + printf("static int tas_gaintable[] = {\n"); + printf(" 0x000000, /" "* -infinity dB *" "/\n"); + for (dB2=-140;dB2<=36;dB2++) + printf(" 0x%.6x, /" "* %-02.1f dB *" "/\n", (int)(1048576.0*exp(0.057564628*dB2)), dB2/2.0); + printf("};\n\n"); +} + +*/ + +/* This file is only included exactly once! + * + * If they'd only tell us that generating this table was + * as easy as calculating + * hwvalue = 1048576.0*exp(0.057564628*dB*2) + * :) */ +static int tas_gaintable[] = { + 0x000000, /* -infinity dB */ + 0x00014b, /* -70.0 dB */ + 0x00015f, /* -69.5 dB */ + 0x000174, /* -69.0 dB */ + 0x00018a, /* -68.5 dB */ + 0x0001a1, /* -68.0 dB */ + 0x0001ba, /* -67.5 dB */ + 0x0001d4, /* -67.0 dB */ + 0x0001f0, /* -66.5 dB */ + 0x00020d, /* -66.0 dB */ + 0x00022c, /* -65.5 dB */ + 0x00024d, /* -65.0 dB */ + 0x000270, /* -64.5 dB */ + 0x000295, /* -64.0 dB */ + 0x0002bc, /* -63.5 dB */ + 0x0002e6, /* -63.0 dB */ + 0x000312, /* -62.5 dB */ + 0x000340, /* -62.0 dB */ + 0x000372, /* -61.5 dB */ + 0x0003a6, /* -61.0 dB */ + 0x0003dd, /* -60.5 dB */ + 0x000418, /* -60.0 dB */ + 0x000456, /* -59.5 dB */ + 0x000498, /* -59.0 dB */ + 0x0004de, /* -58.5 dB */ + 0x000528, /* -58.0 dB */ + 0x000576, /* -57.5 dB */ + 0x0005c9, /* -57.0 dB */ + 0x000620, /* -56.5 dB */ + 0x00067d, /* -56.0 dB */ + 0x0006e0, /* -55.5 dB */ + 0x000748, /* -55.0 dB */ + 0x0007b7, /* -54.5 dB */ + 0x00082c, /* -54.0 dB */ + 0x0008a8, /* -53.5 dB */ + 0x00092b, /* -53.0 dB */ + 0x0009b6, /* -52.5 dB */ + 0x000a49, /* -52.0 dB */ + 0x000ae5, /* -51.5 dB */ + 0x000b8b, /* -51.0 dB */ + 0x000c3a, /* -50.5 dB */ + 0x000cf3, /* -50.0 dB */ + 0x000db8, /* -49.5 dB */ + 0x000e88, /* -49.0 dB */ + 0x000f64, /* -48.5 dB */ + 0x00104e, /* -48.0 dB */ + 0x001145, /* -47.5 dB */ + 0x00124b, /* -47.0 dB */ + 0x001361, /* -46.5 dB */ + 0x001487, /* -46.0 dB */ + 0x0015be, /* -45.5 dB */ + 0x001708, /* -45.0 dB */ + 0x001865, /* -44.5 dB */ + 0x0019d8, /* -44.0 dB */ + 0x001b60, /* -43.5 dB */ + 0x001cff, /* -43.0 dB */ + 0x001eb7, /* -42.5 dB */ + 0x002089, /* -42.0 dB */ + 0x002276, /* -41.5 dB */ + 0x002481, /* -41.0 dB */ + 0x0026ab, /* -40.5 dB */ + 0x0028f5, /* -40.0 dB */ + 0x002b63, /* -39.5 dB */ + 0x002df5, /* -39.0 dB */ + 0x0030ae, /* -38.5 dB */ + 0x003390, /* -38.0 dB */ + 0x00369e, /* -37.5 dB */ + 0x0039db, /* -37.0 dB */ + 0x003d49, /* -36.5 dB */ + 0x0040ea, /* -36.0 dB */ + 0x0044c3, /* -35.5 dB */ + 0x0048d6, /* -35.0 dB */ + 0x004d27, /* -34.5 dB */ + 0x0051b9, /* -34.0 dB */ + 0x005691, /* -33.5 dB */ + 0x005bb2, /* -33.0 dB */ + 0x006121, /* -32.5 dB */ + 0x0066e3, /* -32.0 dB */ + 0x006cfb, /* -31.5 dB */ + 0x007370, /* -31.0 dB */ + 0x007a48, /* -30.5 dB */ + 0x008186, /* -30.0 dB */ + 0x008933, /* -29.5 dB */ + 0x009154, /* -29.0 dB */ + 0x0099f1, /* -28.5 dB */ + 0x00a310, /* -28.0 dB */ + 0x00acba, /* -27.5 dB */ + 0x00b6f6, /* -27.0 dB */ + 0x00c1cd, /* -26.5 dB */ + 0x00cd49, /* -26.0 dB */ + 0x00d973, /* -25.5 dB */ + 0x00e655, /* -25.0 dB */ + 0x00f3fb, /* -24.5 dB */ + 0x010270, /* -24.0 dB */ + 0x0111c0, /* -23.5 dB */ + 0x0121f9, /* -23.0 dB */ + 0x013328, /* -22.5 dB */ + 0x01455b, /* -22.0 dB */ + 0x0158a2, /* -21.5 dB */ + 0x016d0e, /* -21.0 dB */ + 0x0182af, /* -20.5 dB */ + 0x019999, /* -20.0 dB */ + 0x01b1de, /* -19.5 dB */ + 0x01cb94, /* -19.0 dB */ + 0x01e6cf, /* -18.5 dB */ + 0x0203a7, /* -18.0 dB */ + 0x022235, /* -17.5 dB */ + 0x024293, /* -17.0 dB */ + 0x0264db, /* -16.5 dB */ + 0x02892c, /* -16.0 dB */ + 0x02afa3, /* -15.5 dB */ + 0x02d862, /* -15.0 dB */ + 0x03038a, /* -14.5 dB */ + 0x033142, /* -14.0 dB */ + 0x0361af, /* -13.5 dB */ + 0x0394fa, /* -13.0 dB */ + 0x03cb50, /* -12.5 dB */ + 0x0404de, /* -12.0 dB */ + 0x0441d5, /* -11.5 dB */ + 0x048268, /* -11.0 dB */ + 0x04c6d0, /* -10.5 dB */ + 0x050f44, /* -10.0 dB */ + 0x055c04, /* -9.5 dB */ + 0x05ad50, /* -9.0 dB */ + 0x06036e, /* -8.5 dB */ + 0x065ea5, /* -8.0 dB */ + 0x06bf44, /* -7.5 dB */ + 0x07259d, /* -7.0 dB */ + 0x079207, /* -6.5 dB */ + 0x0804dc, /* -6.0 dB */ + 0x087e80, /* -5.5 dB */ + 0x08ff59, /* -5.0 dB */ + 0x0987d5, /* -4.5 dB */ + 0x0a1866, /* -4.0 dB */ + 0x0ab189, /* -3.5 dB */ + 0x0b53be, /* -3.0 dB */ + 0x0bff91, /* -2.5 dB */ + 0x0cb591, /* -2.0 dB */ + 0x0d765a, /* -1.5 dB */ + 0x0e4290, /* -1.0 dB */ + 0x0f1adf, /* -0.5 dB */ + 0x100000, /* 0.0 dB */ + 0x10f2b4, /* 0.5 dB */ + 0x11f3c9, /* 1.0 dB */ + 0x13041a, /* 1.5 dB */ + 0x14248e, /* 2.0 dB */ + 0x15561a, /* 2.5 dB */ + 0x1699c0, /* 3.0 dB */ + 0x17f094, /* 3.5 dB */ + 0x195bb8, /* 4.0 dB */ + 0x1adc61, /* 4.5 dB */ + 0x1c73d5, /* 5.0 dB */ + 0x1e236d, /* 5.5 dB */ + 0x1fec98, /* 6.0 dB */ + 0x21d0d9, /* 6.5 dB */ + 0x23d1cd, /* 7.0 dB */ + 0x25f125, /* 7.5 dB */ + 0x2830af, /* 8.0 dB */ + 0x2a9254, /* 8.5 dB */ + 0x2d1818, /* 9.0 dB */ + 0x2fc420, /* 9.5 dB */ + 0x3298b0, /* 10.0 dB */ + 0x35982f, /* 10.5 dB */ + 0x38c528, /* 11.0 dB */ + 0x3c224c, /* 11.5 dB */ + 0x3fb278, /* 12.0 dB */ + 0x4378b0, /* 12.5 dB */ + 0x477829, /* 13.0 dB */ + 0x4bb446, /* 13.5 dB */ + 0x5030a1, /* 14.0 dB */ + 0x54f106, /* 14.5 dB */ + 0x59f980, /* 15.0 dB */ + 0x5f4e52, /* 15.5 dB */ + 0x64f403, /* 16.0 dB */ + 0x6aef5e, /* 16.5 dB */ + 0x714575, /* 17.0 dB */ + 0x77fbaa, /* 17.5 dB */ + 0x7f17af, /* 18.0 dB */ +}; + diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.c b/sound/aoa/codecs/snd-aoa-codec-tas.c new file mode 100644 index 0000000..2e39ff6 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-tas.c @@ -0,0 +1,654 @@ +/* + * Apple Onboard Audio driver for tas codec + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + * + * Open questions: + * - How to distinguish between 3004 and versions? + * + * FIXMEs: + * - This codec driver doesn't honour the 'connected' + * property of the aoa_codec struct, hence if + * it is used in machines where not everything is + * connected it will display wrong mixer elements. + * - Driver assumes that the microphone is always + * monaureal and connected to the right channel of + * the input. This should also be a codec-dependent + * flag, maybe the codec should have 3 different + * bits for the three different possibilities how + * it can be hooked up... + * But as long as I don't see any hardware hooked + * up that way... + * - As Apple notes in their code, the tas3004 seems + * to delay the right channel by one sample. You can + * see this when for example recording stereo in + * audacity, or recording the tas output via cable + * on another machine (use a sinus generator or so). + * I tried programming the BiQuads but couldn't + * make the delay work, maybe someone can read the + * datasheet and fix it. The relevant Apple comment + * is in AppleTAS3004Audio.cpp lines 1637 ff. Note + * that their comment describing how they program + * the filters sucks... + * + * Other things: + * - this should actually register *two* aoa_codec + * structs since it has two inputs. Then it must + * use the prepare callback to forbid running the + * secondary output on a different clock. + * Also, whatever bus knows how to do this must + * provide two soundbus_dev devices and the fabric + * must be able to link them correctly. + * + * I don't even know if Apple ever uses the second + * port on the tas3004 though, I don't think their + * i2s controllers can even do it. OTOH, they all + * derive the clocks from common clocks, so it + * might just be possible. The framework allows the + * codec to refine the transfer_info items in the + * usable callback, so we can simply remove the + * rates the second instance is not using when it + * actually is in use. + * Maybe we'll need to make the sound busses have + * a 'clock group id' value so the codec can + * determine if the two outputs can be driven at + * the same time. But that is likely overkill, up + * to the fabric to not link them up incorrectly, + * and up to the hardware designer to not wire + * them up in some weird unusable way. + */ +#include +#include +#include +#include +#include +#include +#include +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("tas codec driver for snd-aoa"); + +#include "snd-aoa-codec-tas.h" +#include "snd-aoa-codec-tas-gain-table.h" +#include "../aoa.h" +#include "../soundbus/soundbus.h" + + +#define PFX "snd-aoa-codec-tas: " + +struct tas { + struct aoa_codec codec; + struct i2c_client i2c; + u32 muted_l:1, muted_r:1, + controls_created:1; + u8 cached_volume_l, cached_volume_r; + u8 mixer_l[3], mixer_r[3]; + u8 acr; +}; + +static struct tas *codec_to_tas(struct aoa_codec *codec) +{ + return container_of(codec, struct tas, codec); +} + +static inline int tas_write_reg(struct tas *tas, u8 reg, u8 len, u8 *data) +{ + if (len == 1) + return i2c_smbus_write_byte_data(&tas->i2c, reg, *data); + else + return i2c_smbus_write_i2c_block_data(&tas->i2c, reg, len, data); +} + +static void tas_set_volume(struct tas *tas) +{ + u8 block[6]; + int tmp; + u8 left, right; + + left = tas->cached_volume_l; + right = tas->cached_volume_r; + + if (left > 177) left = 177; + if (right > 177) right = 177; + + if (tas->muted_l) left = 0; + if (tas->muted_r) right = 0; + + /* analysing the volume and mixer tables shows + * that they are similar enough when we shift + * the mixer table down by 4 bits. The error + * is miniscule, in just one item the error + * is 1, at a value of 0x07f17b (mixer table + * value is 0x07f17a) */ + tmp = tas_gaintable[left]; + block[0] = tmp>>20; + block[1] = tmp>>12; + block[2] = tmp>>4; + tmp = tas_gaintable[right]; + block[3] = tmp>>20; + block[4] = tmp>>12; + block[5] = tmp>>4; + tas_write_reg(tas, TAS_REG_VOL, 6, block); +} + +static void tas_set_mixer(struct tas *tas) +{ + u8 block[9]; + int tmp, i; + u8 val; + + for (i=0;i<3;i++) { + val = tas->mixer_l[i]; + if (val > 177) val = 177; + tmp = tas_gaintable[val]; + block[3*i+0] = tmp>>16; + block[3*i+1] = tmp>>8; + block[3*i+2] = tmp; + } + tas_write_reg(tas, TAS_REG_LMIX, 9, block); + + for (i=0;i<3;i++) { + val = tas->mixer_r[i]; + if (val > 177) val = 177; + tmp = tas_gaintable[val]; + block[3*i+0] = tmp>>16; + block[3*i+1] = tmp>>8; + block[3*i+2] = tmp; + } + tas_write_reg(tas, TAS_REG_RMIX, 9, block); +} + +/* alsa stuff */ + +static int tas_dev_register(struct snd_device *dev) +{ + return 0; +} + +static struct snd_device_ops ops = { + .dev_register = tas_dev_register, +}; + +static int tas_snd_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 177; + return 0; +} + +static int tas_snd_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = tas->cached_volume_l; + ucontrol->value.integer.value[1] = tas->cached_volume_r; + return 0; +} + +static int tas_snd_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + if (tas->cached_volume_l == ucontrol->value.integer.value[0] + && tas->cached_volume_r == ucontrol->value.integer.value[1]) + return 0; + + tas->cached_volume_l = ucontrol->value.integer.value[0]; + tas->cached_volume_r = ucontrol->value.integer.value[1]; + tas_set_volume(tas); + return 1; +} + +static struct snd_kcontrol_new volume_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_vol_info, + .get = tas_snd_vol_get, + .put = tas_snd_vol_put, +}; + +static int tas_snd_mute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int tas_snd_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = !tas->muted_l; + ucontrol->value.integer.value[1] = !tas->muted_r; + return 0; +} + +static int tas_snd_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + if (tas->muted_l == !ucontrol->value.integer.value[0] + && tas->muted_r == !ucontrol->value.integer.value[1]) + return 0; + + tas->muted_l = !ucontrol->value.integer.value[0]; + tas->muted_r = !ucontrol->value.integer.value[1]; + tas_set_volume(tas); + return 1; +} + +static struct snd_kcontrol_new mute_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_mute_info, + .get = tas_snd_mute_get, + .put = tas_snd_mute_put, +}; + +static int tas_snd_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 177; + return 0; +} + +static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + + ucontrol->value.integer.value[0] = tas->mixer_l[idx]; + ucontrol->value.integer.value[1] = tas->mixer_r[idx]; + + return 0; +} + +static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + int idx = kcontrol->private_value; + + if (tas->mixer_l[idx] == ucontrol->value.integer.value[0] + && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) + return 0; + + tas->mixer_l[idx] = ucontrol->value.integer.value[0]; + tas->mixer_r[idx] = ucontrol->value.integer.value[1]; + + tas_set_mixer(tas); + return 1; +} + +#define MIXER_CONTROL(n,descr,idx) \ +static struct snd_kcontrol_new n##_control = { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = descr " Playback Volume", \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = tas_snd_mixer_info, \ + .get = tas_snd_mixer_get, \ + .put = tas_snd_mixer_put, \ + .private_value = idx, \ +} + +MIXER_CONTROL(pcm1, "PCM1", 0); +MIXER_CONTROL(monitor, "Monitor", 2); + +static int tas_snd_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[] = { "Line-In", "Microphone" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B); + return 0; +} + +static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tas *tas = snd_kcontrol_chip(kcontrol); + int oldacr = tas->acr; + + tas->acr &= ~TAS_ACR_INPUT_B; + if (ucontrol->value.enumerated.item[0]) + tas->acr |= TAS_ACR_INPUT_B; + if (oldacr == tas->acr) + return 0; + tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); + return 1; +} + +static struct snd_kcontrol_new capture_source_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* If we name this 'Input Source', it properly shows up in + * alsamixer as a selection, * but it's shown under the + * 'Playback' category. + * If I name it 'Capture Source', it shows up in strange + * ways (two bools of which one can be selected at a + * time) but at least it's shown in the 'Capture' + * category. + * I was told that this was due to backward compatibility, + * but I don't understand then why the mangling is *not* + * done when I name it "Input Source"..... + */ + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = tas_snd_capture_source_info, + .get = tas_snd_capture_source_get, + .put = tas_snd_capture_source_put, +}; + + +static struct transfer_info tas_transfers[] = { + { + /* input */ + .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .transfer_in = 1, + }, + { + /* output */ + .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .transfer_in = 0, + }, + {} +}; + +static int tas_usable(struct codec_info_item *cii, + struct transfer_info *ti, + struct transfer_info *out) +{ + return 1; +} + +static int tas_reset_init(struct tas *tas) +{ + u8 tmp; + tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); + msleep(1); + tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 1); + msleep(1); + tas->codec.gpio->methods->set_hw_reset(tas->codec.gpio, 0); + msleep(1); + + tas->acr &= ~TAS_ACR_ANALOG_PDOWN; + tas->acr |= TAS_ACR_B_MONAUREAL | TAS_ACR_B_MON_SEL_RIGHT; + if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr)) + return -ENODEV; + + tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT; + if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp)) + return -ENODEV; + + tmp = 0; + if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp)) + return -ENODEV; + + return 0; +} + +/* we are controlled via i2c and assume that is always up + * If that wasn't the case, we'd have to suspend once + * our i2c device is suspended, and then take note of that! */ +static int tas_suspend(struct tas *tas) +{ + tas->acr |= TAS_ACR_ANALOG_PDOWN; + tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr); + return 0; +} + +static int tas_resume(struct tas *tas) +{ + /* reset codec */ + tas_reset_init(tas); + tas_set_volume(tas); + tas_set_mixer(tas); + return 0; +} + +#ifdef CONFIG_PM +static int _tas_suspend(struct codec_info_item *cii, pm_message_t state) +{ + return tas_suspend(cii->codec_data); +} + +static int _tas_resume(struct codec_info_item *cii) +{ + return tas_resume(cii->codec_data); +} +#endif + +static struct codec_info tas_codec_info = { + .transfers = tas_transfers, + /* in theory, we can drive it at 512 too... + * but so far the framework doesn't allow + * for that and I don't see much point in it. */ + .sysclock_factor = 256, + /* same here, could be 32 for just one 16 bit format */ + .bus_factor = 64, + .owner = THIS_MODULE, + .usable = tas_usable, +#ifdef CONFIG_PM + .suspend = _tas_suspend, + .resume = _tas_resume, +#endif +}; + +static int tas_init_codec(struct aoa_codec *codec) +{ + struct tas *tas = codec_to_tas(codec); + int err; + + if (!tas->codec.gpio || !tas->codec.gpio->methods) { + printk(KERN_ERR PFX "gpios not assigned!!\n"); + return -EINVAL; + } + + if (tas_reset_init(tas)) { + printk(KERN_ERR PFX "tas failed to initialise\n"); + return -ENXIO; + } + + if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev, + aoa_get_card(), + &tas_codec_info, tas)) { + printk(KERN_ERR PFX "error attaching tas to soundbus\n"); + return -ENODEV; + } + + if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, tas, &ops)) { + printk(KERN_ERR PFX "failed to create tas snd device!\n"); + return -ENODEV; + } + err = aoa_snd_ctl_add(snd_ctl_new1(&volume_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&mute_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&pcm1_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&monitor_control, tas)); + if (err) + goto error; + + err = aoa_snd_ctl_add(snd_ctl_new1(&capture_source_control, tas)); + if (err) + goto error; + + return 0; + error: + tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas); + snd_device_free(aoa_get_card(), tas); + return err; +} + +static void tas_exit_codec(struct aoa_codec *codec) +{ + struct tas *tas = codec_to_tas(codec); + + if (!tas->codec.soundbus_dev) + return; + tas->codec.soundbus_dev->detach_codec(tas->codec.soundbus_dev, tas); +} + + +static struct i2c_driver tas_driver; + +static int tas_create(struct i2c_adapter *adapter, + struct device_node *node, + int addr) +{ + struct tas *tas; + + tas = kzalloc(sizeof(struct tas), GFP_KERNEL); + + if (!tas) + return -ENOMEM; + + tas->i2c.driver = &tas_driver; + tas->i2c.adapter = adapter; + tas->i2c.addr = addr; + strlcpy(tas->i2c.name, "tas audio codec", I2C_NAME_SIZE-1); + + if (i2c_attach_client(&tas->i2c)) { + printk(KERN_ERR PFX "failed to attach to i2c\n"); + goto fail; + } + + strlcpy(tas->codec.name, "tas", MAX_CODEC_NAME_LEN-1); + tas->codec.owner = THIS_MODULE; + tas->codec.init = tas_init_codec; + tas->codec.exit = tas_exit_codec; + tas->codec.node = of_node_get(node); + + if (aoa_codec_register(&tas->codec)) { + goto detach; + } + printk(KERN_DEBUG "snd-aoa-codec-tas: created and attached tas instance\n"); + return 0; + detach: + i2c_detach_client(&tas->i2c); + fail: + kfree(tas); + return -EINVAL; +} + +static int tas_i2c_attach(struct i2c_adapter *adapter) +{ + struct device_node *busnode, *dev = NULL; + struct pmac_i2c_bus *bus; + + bus = pmac_i2c_adapter_to_bus(adapter); + if (bus == NULL) + return -ENODEV; + busnode = pmac_i2c_get_bus_node(bus); + + while ((dev = of_get_next_child(busnode, dev)) != NULL) { + if (device_is_compatible(dev, "tas3004")) { + u32 *addr; + printk(KERN_DEBUG PFX "found tas3004\n"); + addr = (u32 *) get_property(dev, "reg", NULL); + if (!addr) + continue; + return tas_create(adapter, dev, ((*addr) >> 1) & 0x7f); + } + /* older machines have no 'codec' node with a 'compatible' + * property that says 'tas3004', they just have a 'deq' + * node without any such property... */ + if (strcmp(dev->name, "deq") == 0) { + u32 *_addr, addr; + printk(KERN_DEBUG PFX "found 'deq' node\n"); + _addr = (u32 *) get_property(dev, "i2c-address", NULL); + if (!_addr) + continue; + addr = ((*_addr) >> 1) & 0x7f; + /* now, if the address doesn't match any of the two + * that a tas3004 can have, we cannot handle this. + * I doubt it ever happens but hey. */ + if (addr != 0x34 && addr != 0x35) + continue; + return tas_create(adapter, dev, addr); + } + } + return -ENODEV; +} + +static int tas_i2c_detach(struct i2c_client *client) +{ + struct tas *tas = container_of(client, struct tas, i2c); + int err; + u8 tmp = TAS_ACR_ANALOG_PDOWN; + + if ((err = i2c_detach_client(client))) + return err; + aoa_codec_unregister(&tas->codec); + of_node_put(tas->codec.node); + + /* power down codec chip */ + tas_write_reg(tas, TAS_REG_ACR, 1, &tmp); + + kfree(tas); + return 0; +} + +static struct i2c_driver tas_driver = { + .driver = { + .name = "aoa_codec_tas", + .owner = THIS_MODULE, + }, + .attach_adapter = tas_i2c_attach, + .detach_client = tas_i2c_detach, +}; + +static int __init tas_init(void) +{ + return i2c_add_driver(&tas_driver); +} + +static void __exit tas_exit(void) +{ + i2c_del_driver(&tas_driver); +} + +module_init(tas_init); +module_exit(tas_exit); diff --git a/sound/aoa/codecs/snd-aoa-codec-tas.h b/sound/aoa/codecs/snd-aoa-codec-tas.h new file mode 100644 index 0000000..daf81f4 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-tas.h @@ -0,0 +1,47 @@ +/* + * Apple Onboard Audio driver for tas codec (header) + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#ifndef __SND_AOA_CODECTASH +#define __SND_AOA_CODECTASH + +#define TAS_REG_MCS 0x01 /* main control */ +# define TAS_MCS_FASTLOAD (1<<7) +# define TAS_MCS_SCLK64 (1<<6) +# define TAS_MCS_SPORT_MODE_MASK (3<<4) +# define TAS_MCS_SPORT_MODE_I2S (2<<4) +# define TAS_MCS_SPORT_MODE_RJ (1<<4) +# define TAS_MCS_SPORT_MODE_LJ (0<<4) +# define TAS_MCS_SPORT_WL_MASK (3<<0) +# define TAS_MCS_SPORT_WL_16BIT (0<<0) +# define TAS_MCS_SPORT_WL_18BIT (1<<0) +# define TAS_MCS_SPORT_WL_20BIT (2<<0) +# define TAS_MCS_SPORT_WL_24BIT (3<<0) + +#define TAS_REG_DRC 0x02 +#define TAS_REG_VOL 0x04 +#define TAS_REG_TREBLE 0x05 +#define TAS_REG_BASS 0x06 +#define TAS_REG_LMIX 0x07 +#define TAS_REG_RMIX 0x08 + +#define TAS_REG_ACR 0x40 /* analog control */ +# define TAS_ACR_B_MONAUREAL (1<<7) +# define TAS_ACR_B_MON_SEL_RIGHT (1<<6) +# define TAS_ACR_DEEMPH_MASK (3<<2) +# define TAS_ACR_DEEMPH_OFF (0<<2) +# define TAS_ACR_DEEMPH_48KHz (1<<2) +# define TAS_ACR_DEEMPH_44KHz (2<<2) +# define TAS_ACR_INPUT_B (1<<1) +# define TAS_ACR_ANALOG_PDOWN (1<<0) + +#define TAS_REG_MCS2 0x43 /* main control 2 */ +# define TAS_MCS2_ALLPASS (1<<1) + +#define TAS_REG_LEFT_BIQUAD6 0x10 +#define TAS_REG_RIGHT_BIQUAD6 0x19 + +#endif /* __SND_AOA_CODECTASH */ diff --git a/sound/aoa/codecs/snd-aoa-codec-toonie.c b/sound/aoa/codecs/snd-aoa-codec-toonie.c new file mode 100644 index 0000000..bcc5556 --- /dev/null +++ b/sound/aoa/codecs/snd-aoa-codec-toonie.c @@ -0,0 +1,141 @@ +/* + * Apple Onboard Audio driver for Toonie codec + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + * + * + * This is a driver for the toonie codec chip. This chip is present + * on the Mac Mini and is nothing but a DAC. + */ +#include +#include +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("toonie codec driver for snd-aoa"); + +#include "../aoa.h" +#include "../soundbus/soundbus.h" + + +#define PFX "snd-aoa-codec-toonie: " + +struct toonie { + struct aoa_codec codec; +}; +#define codec_to_toonie(c) container_of(c, struct toonie, codec) + +static int toonie_dev_register(struct snd_device *dev) +{ + return 0; +} + +static struct snd_device_ops ops = { + .dev_register = toonie_dev_register, +}; + +static struct transfer_info toonie_transfers[] = { + /* This thing *only* has analog output, + * the rates are taken from Info.plist + * from Darwin. */ + { + .formats = SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_S24_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + }, + {} +}; + +#ifdef CONFIG_PM +static int toonie_suspend(struct codec_info_item *cii, pm_message_t state) +{ + /* can we turn it off somehow? */ + return 0; +} + +static int toonie_resume(struct codec_info_item *cii) +{ + return 0; +} +#endif /* CONFIG_PM */ + +static struct codec_info toonie_codec_info = { + .transfers = toonie_transfers, + .sysclock_factor = 256, + .bus_factor = 64, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .suspend = toonie_suspend, + .resume = toonie_resume, +#endif +}; + +static int toonie_init_codec(struct aoa_codec *codec) +{ + struct toonie *toonie = codec_to_toonie(codec); + + if (aoa_snd_device_new(SNDRV_DEV_LOWLEVEL, toonie, &ops)) { + printk(KERN_ERR PFX "failed to create toonie snd device!\n"); + return -ENODEV; + } + + /* nothing connected? what a joke! */ + if (toonie->codec.connected != 1) + return -ENOTCONN; + + if (toonie->codec.soundbus_dev->attach_codec(toonie->codec.soundbus_dev, + aoa_get_card(), + &toonie_codec_info, toonie)) { + printk(KERN_ERR PFX "error creating toonie pcm\n"); + return -ENODEV; + } + + return 0; +} + +static void toonie_exit_codec(struct aoa_codec *codec) +{ + struct toonie *toonie = codec_to_toonie(codec); + + if (!toonie->codec.soundbus_dev) { + printk(KERN_ERR PFX "toonie_exit_codec called without soundbus_dev!\n"); + return; + } + toonie->codec.soundbus_dev->detach_codec(toonie->codec.soundbus_dev, toonie); +} + +static struct toonie *toonie; + +static int __init toonie_init(void) +{ + toonie = kzalloc(sizeof(struct toonie), GFP_KERNEL); + + if (!toonie) + return -ENOMEM; + + strlcpy(toonie->codec.name, "toonie", sizeof(toonie->codec.name)); + toonie->codec.owner = THIS_MODULE; + toonie->codec.init = toonie_init_codec; + toonie->codec.exit = toonie_exit_codec; + + if (aoa_codec_register(&toonie->codec)) { + kfree(toonie); + return -EINVAL; + } + + return 0; +} + +static void __exit toonie_exit(void) +{ + aoa_codec_unregister(&toonie->codec); + kfree(toonie); +} + +module_init(toonie_init); +module_exit(toonie_exit); diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile new file mode 100644 index 0000000..62dc728 --- /dev/null +++ b/sound/aoa/core/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_SND_AOA) += snd-aoa.o +snd-aoa-objs := snd-aoa-core.o \ + snd-aoa-alsa.o \ + snd-aoa-gpio-pmf.o \ + snd-aoa-gpio-feature.o diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c new file mode 100644 index 0000000..b42fdea --- /dev/null +++ b/sound/aoa/core/snd-aoa-alsa.c @@ -0,0 +1,98 @@ +/* + * Apple Onboard Audio Alsa helpers + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#include +#include "snd-aoa-alsa.h" + +static int index = -1; +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "index for AOA sound card."); + +static struct aoa_card *aoa_card; + +int aoa_alsa_init(char *name, struct module *mod) +{ + struct snd_card *alsa_card; + int err; + + if (aoa_card) + /* cannot be EEXIST due to usage in aoa_fabric_register */ + return -EBUSY; + + alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card)); + if (!alsa_card) + return -ENOMEM; + aoa_card = alsa_card->private_data; + aoa_card->alsa_card = alsa_card; + strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver)); + strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname)); + strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname)); + strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername)); + err = snd_card_register(aoa_card->alsa_card); + if (err < 0) { + printk(KERN_ERR "snd-aoa: couldn't register alsa card\n"); + snd_card_free(aoa_card->alsa_card); + aoa_card = NULL; + return err; + } + return 0; +} + +struct snd_card *aoa_get_card(void) +{ + if (aoa_card) + return aoa_card->alsa_card; + return NULL; +} +EXPORT_SYMBOL_GPL(aoa_get_card); + +void aoa_alsa_cleanup(void) +{ + if (aoa_card) { + snd_card_free(aoa_card->alsa_card); + aoa_card = NULL; + } +} + +int aoa_snd_device_new(snd_device_type_t type, + void * device_data, struct snd_device_ops * ops) +{ + struct snd_card *card = aoa_get_card(); + int err; + + if (!card) return -ENOMEM; + + err = snd_device_new(card, type, device_data, ops); + if (err) { + printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err); + return err; + } + err = snd_device_register(card, device_data); + if (err) { + printk(KERN_ERR "snd-aoa: failed to register " + "snd device (%d)\n", err); + printk(KERN_ERR "snd-aoa: have you forgotten the " + "dev_register callback?\n"); + snd_device_free(card, device_data); + } + return err; +} +EXPORT_SYMBOL_GPL(aoa_snd_device_new); + +int aoa_snd_ctl_add(struct snd_kcontrol* control) +{ + int err; + + if (!aoa_card) return -ENODEV; + + err = snd_ctl_add(aoa_card->alsa_card, control); + if (err) + printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n", + err); + return err; +} +EXPORT_SYMBOL_GPL(aoa_snd_ctl_add); diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/snd-aoa-alsa.h new file mode 100644 index 0000000..660d2f1 --- /dev/null +++ b/sound/aoa/core/snd-aoa-alsa.h @@ -0,0 +1,16 @@ +/* + * Apple Onboard Audio Alsa private helpers + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#ifndef __SND_AOA_ALSA_H +#define __SND_AOA_ALSA_H +#include "../aoa.h" + +extern int aoa_alsa_init(char *name, struct module *mod); +extern void aoa_alsa_cleanup(void); + +#endif /* __SND_AOA_ALSA_H */ diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/snd-aoa-core.c new file mode 100644 index 0000000..ecd2d82 --- /dev/null +++ b/sound/aoa/core/snd-aoa-core.c @@ -0,0 +1,162 @@ +/* + * Apple Onboard Audio driver core + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#include +#include +#include +#include "../aoa.h" +#include "snd-aoa-alsa.h" + +MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver"); +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); + +/* We allow only one fabric. This simplifies things, + * and more don't really make that much sense */ +static struct aoa_fabric *fabric; +static LIST_HEAD(codec_list); + +static int attach_codec_to_fabric(struct aoa_codec *c) +{ + int err; + + if (!try_module_get(c->owner)) + return -EBUSY; + /* found_codec has to be assigned */ + err = -ENOENT; + if (fabric->found_codec) + err = fabric->found_codec(c); + if (err) { + module_put(c->owner); + printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n", + c->name); + return err; + } + c->fabric = fabric; + + err = 0; + if (c->init) + err = c->init(c); + if (err) { + printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name); + c->fabric = NULL; + if (fabric->remove_codec) + fabric->remove_codec(c); + module_put(c->owner); + return err; + } + if (fabric->attached_codec) + fabric->attached_codec(c); + return 0; +} + +int aoa_codec_register(struct aoa_codec *codec) +{ + int err = 0; + + /* if there's a fabric already, we can tell if we + * will want to have this codec, so propagate error + * through. Otherwise, this will happen later... */ + if (fabric) + err = attach_codec_to_fabric(codec); + if (!err) + list_add(&codec->list, &codec_list); + return err; +} +EXPORT_SYMBOL_GPL(aoa_codec_register); + +void aoa_codec_unregister(struct aoa_codec *codec) +{ + list_del(&codec->list); + if (codec->fabric && codec->exit) + codec->exit(codec); + if (fabric && fabric->remove_codec) + fabric->remove_codec(codec); + codec->fabric = NULL; + module_put(codec->owner); +} +EXPORT_SYMBOL_GPL(aoa_codec_unregister); + +int aoa_fabric_register(struct aoa_fabric *new_fabric) +{ + struct aoa_codec *c; + int err; + + /* allow querying for presence of fabric + * (i.e. do this test first!) */ + if (new_fabric == fabric) { + err = -EALREADY; + goto attach; + } + if (fabric) + return -EEXIST; + if (!new_fabric) + return -EINVAL; + + err = aoa_alsa_init(new_fabric->name, new_fabric->owner); + if (err) + return err; + + fabric = new_fabric; + + attach: + list_for_each_entry(c, &codec_list, list) { + if (c->fabric != fabric) + attach_codec_to_fabric(c); + } + return err; +} +EXPORT_SYMBOL_GPL(aoa_fabric_register); + +void aoa_fabric_unregister(struct aoa_fabric *old_fabric) +{ + struct aoa_codec *c; + + if (fabric != old_fabric) + return; + + list_for_each_entry(c, &codec_list, list) { + if (c->fabric) + aoa_fabric_unlink_codec(c); + } + + aoa_alsa_cleanup(); + + fabric = NULL; +} +EXPORT_SYMBOL_GPL(aoa_fabric_unregister); + +void aoa_fabric_unlink_codec(struct aoa_codec *codec) +{ + if (!codec->fabric) { + printk(KERN_ERR "snd-aoa: fabric unassigned " + "in aoa_fabric_unlink_codec\n"); + dump_stack(); + return; + } + if (codec->exit) + codec->exit(codec); + if (codec->fabric->remove_codec) + codec->fabric->remove_codec(codec); + codec->fabric = NULL; + module_put(codec->owner); +} +EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec); + +static int __init aoa_init(void) +{ + return 0; +} + +static void __exit aoa_exit(void) +{ + aoa_alsa_cleanup(); +} + +module_init(aoa_init); +module_exit(aoa_exit); diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c new file mode 100644 index 0000000..7ae0c0b --- /dev/null +++ b/sound/aoa/core/snd-aoa-gpio-feature.c @@ -0,0 +1,409 @@ +/* + * Apple Onboard Audio feature call GPIO control + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + * + * This file contains the GPIO control routines for + * direct (through feature calls) access to the GPIO + * registers. + */ + +#include +#include +#include "../aoa.h" + +/* TODO: these are 20 global variables + * that aren't used on most machines... + * Move them into a dynamically allocated + * structure and use that. + */ + +/* these are the GPIO numbers (register addresses as offsets into + * the GPIO space) */ +static int headphone_mute_gpio; +static int amp_mute_gpio; +static int lineout_mute_gpio; +static int hw_reset_gpio; +static int lineout_detect_gpio; +static int headphone_detect_gpio; +static int linein_detect_gpio; + +/* see the SWITCH_GPIO macro */ +static int headphone_mute_gpio_activestate; +static int amp_mute_gpio_activestate; +static int lineout_mute_gpio_activestate; +static int hw_reset_gpio_activestate; +static int lineout_detect_gpio_activestate; +static int headphone_detect_gpio_activestate; +static int linein_detect_gpio_activestate; + +/* node pointers that we save when getting the GPIO number + * to get the interrupt later */ +static struct device_node *lineout_detect_node; +static struct device_node *linein_detect_node; +static struct device_node *headphone_detect_node; + +static int lineout_detect_irq; +static int linein_detect_irq; +static int headphone_detect_irq; + +static struct device_node *get_gpio(char *name, + char *altname, + int *gpioptr, + int *gpioactiveptr) +{ + struct device_node *np, *gpio; + u32 *reg; + char *audio_gpio; + + *gpioptr = -1; + + /* check if we can get it the easy way ... */ + np = of_find_node_by_name(NULL, name); + if (!np) { + /* some machines have only gpioX/extint-gpioX nodes, + * and an audio-gpio property saying what it is ... + * So what we have to do is enumerate all children + * of the gpio node and check them all. */ + gpio = of_find_node_by_name(NULL, "gpio"); + if (!gpio) + return NULL; + while ((np = of_get_next_child(gpio, np))) { + audio_gpio = get_property(np, "audio-gpio", NULL); + if (!audio_gpio) + continue; + if (strcmp(audio_gpio, name) == 0) + break; + if (altname && (strcmp(audio_gpio, altname) == 0)) + break; + } + /* still not found, assume not there */ + if (!np) + return NULL; + } + + reg = (u32 *)get_property(np, "reg", NULL); + if (!reg) + return NULL; + + *gpioptr = *reg; + + /* this is a hack, usually the GPIOs 'reg' property + * should have the offset based from the GPIO space + * which is at 0x50, but apparently not always... */ + if (*gpioptr < 0x50) + *gpioptr += 0x50; + + reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL); + if (!reg) + /* Apple seems to default to 1, but + * that doesn't seem right at least on most + * machines. So until proven that the opposite + * is necessary, we default to 0 + * (which, incidentally, snd-powermac also does...) */ + *gpioactiveptr = 0; + else + *gpioactiveptr = *reg; + + return np; +} + +static void get_irq(struct device_node * np, int *irqptr) +{ + *irqptr = irq_of_parse_and_map(np, 0); +} + +/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */ +#define SWITCH_GPIO(name, v, on) \ + (((v)&~1) | ((on)? \ + (name##_gpio_activestate==0?4:5): \ + (name##_gpio_activestate==0?5:4))) + +#define FTR_GPIO(name, bit) \ +static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\ +{ \ + int v; \ + \ + if (unlikely(!rt)) return; \ + \ + if (name##_mute_gpio < 0) \ + return; \ + \ + v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \ + name##_mute_gpio, \ + 0); \ + \ + /* muted = !on... */ \ + v = SWITCH_GPIO(name##_mute, v, !on); \ + \ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \ + name##_mute_gpio, v); \ + \ + rt->implementation_private &= ~(1<implementation_private |= (!!on << bit); \ +} \ +static int ftr_gpio_get_##name(struct gpio_runtime *rt) \ +{ \ + if (unlikely(!rt)) return 0; \ + return (rt->implementation_private>>bit)&1; \ +} + +FTR_GPIO(headphone, 0); +FTR_GPIO(amp, 1); +FTR_GPIO(lineout, 2); + +static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on) +{ + int v; + + if (unlikely(!rt)) return; + if (hw_reset_gpio < 0) + return; + + v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, + hw_reset_gpio, 0); + v = SWITCH_GPIO(hw_reset, v, on); + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, + hw_reset_gpio, v); +} + +static void ftr_gpio_all_amps_off(struct gpio_runtime *rt) +{ + int saved; + + if (unlikely(!rt)) return; + saved = rt->implementation_private; + ftr_gpio_set_headphone(rt, 0); + ftr_gpio_set_amp(rt, 0); + ftr_gpio_set_lineout(rt, 0); + rt->implementation_private = saved; +} + +static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt) +{ + int s; + + if (unlikely(!rt)) return; + s = rt->implementation_private; + ftr_gpio_set_headphone(rt, (s>>0)&1); + ftr_gpio_set_amp(rt, (s>>1)&1); + ftr_gpio_set_lineout(rt, (s>>2)&1); +} + +static void ftr_handle_notify(void *data) +{ + struct gpio_notification *notif = data; + + mutex_lock(¬if->mutex); + if (notif->notify) + notif->notify(notif->data); + mutex_unlock(¬if->mutex); +} + +static void gpio_enable_dual_edge(int gpio) +{ + int v; + + if (gpio == -1) + return; + v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0); + v |= 0x80; /* enable dual edge */ + pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio, v); +} + +static void ftr_gpio_init(struct gpio_runtime *rt) +{ + get_gpio("headphone-mute", NULL, + &headphone_mute_gpio, + &headphone_mute_gpio_activestate); + get_gpio("amp-mute", NULL, + &_mute_gpio, + &_mute_gpio_activestate); + get_gpio("lineout-mute", NULL, + &lineout_mute_gpio, + &lineout_mute_gpio_activestate); + get_gpio("hw-reset", "audio-hw-reset", + &hw_reset_gpio, + &hw_reset_gpio_activestate); + + headphone_detect_node = get_gpio("headphone-detect", NULL, + &headphone_detect_gpio, + &headphone_detect_gpio_activestate); + /* go Apple, and thanks for giving these different names + * across the board... */ + lineout_detect_node = get_gpio("lineout-detect", "line-output-detect", + &lineout_detect_gpio, + &lineout_detect_gpio_activestate); + linein_detect_node = get_gpio("linein-detect", "line-input-detect", + &linein_detect_gpio, + &linein_detect_gpio_activestate); + + gpio_enable_dual_edge(headphone_detect_gpio); + gpio_enable_dual_edge(lineout_detect_gpio); + gpio_enable_dual_edge(linein_detect_gpio); + + get_irq(headphone_detect_node, &headphone_detect_irq); + get_irq(lineout_detect_node, &lineout_detect_irq); + get_irq(linein_detect_node, &linein_detect_irq); + + ftr_gpio_all_amps_off(rt); + rt->implementation_private = 0; + INIT_WORK(&rt->headphone_notify.work, ftr_handle_notify, + &rt->headphone_notify); + INIT_WORK(&rt->line_in_notify.work, ftr_handle_notify, + &rt->line_in_notify); + INIT_WORK(&rt->line_out_notify.work, ftr_handle_notify, + &rt->line_out_notify); + mutex_init(&rt->headphone_notify.mutex); + mutex_init(&rt->line_in_notify.mutex); + mutex_init(&rt->line_out_notify.mutex); +} + +static void ftr_gpio_exit(struct gpio_runtime *rt) +{ + ftr_gpio_all_amps_off(rt); + rt->implementation_private = 0; + if (rt->headphone_notify.notify) + free_irq(headphone_detect_irq, &rt->headphone_notify); + if (rt->line_in_notify.gpio_private) + free_irq(linein_detect_irq, &rt->line_in_notify); + if (rt->line_out_notify.gpio_private) + free_irq(lineout_detect_irq, &rt->line_out_notify); + cancel_delayed_work(&rt->headphone_notify.work); + cancel_delayed_work(&rt->line_in_notify.work); + cancel_delayed_work(&rt->line_out_notify.work); + flush_scheduled_work(); + mutex_destroy(&rt->headphone_notify.mutex); + mutex_destroy(&rt->line_in_notify.mutex); + mutex_destroy(&rt->line_out_notify.mutex); +} + +static irqreturn_t ftr_handle_notify_irq(int xx, + void *data, + struct pt_regs *regs) +{ + struct gpio_notification *notif = data; + + schedule_work(¬if->work); + + return IRQ_HANDLED; +} + +static int ftr_set_notify(struct gpio_runtime *rt, + enum notify_type type, + notify_func_t notify, + void *data) +{ + struct gpio_notification *notif; + notify_func_t old; + int irq; + char *name; + int err = -EBUSY; + + switch (type) { + case AOA_NOTIFY_HEADPHONE: + notif = &rt->headphone_notify; + name = "headphone-detect"; + irq = headphone_detect_irq; + break; + case AOA_NOTIFY_LINE_IN: + notif = &rt->line_in_notify; + name = "linein-detect"; + irq = linein_detect_irq; + break; + case AOA_NOTIFY_LINE_OUT: + notif = &rt->line_out_notify; + name = "lineout-detect"; + irq = lineout_detect_irq; + break; + default: + return -EINVAL; + } + + if (irq == -1) + return -ENODEV; + + mutex_lock(¬if->mutex); + + old = notif->notify; + + if (!old && !notify) { + err = 0; + goto out_unlock; + } + + if (old && notify) { + if (old == notify && notif->data == data) + err = 0; + goto out_unlock; + } + + if (old && !notify) + free_irq(irq, notif); + + if (!old && notify) { + err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif); + if (err) + goto out_unlock; + } + + notif->notify = notify; + notif->data = data; + + err = 0; + out_unlock: + mutex_unlock(¬if->mutex); + return err; +} + +static int ftr_get_detect(struct gpio_runtime *rt, + enum notify_type type) +{ + int gpio, ret, active; + + switch (type) { + case AOA_NOTIFY_HEADPHONE: + gpio = headphone_detect_gpio; + active = headphone_detect_gpio_activestate; + break; + case AOA_NOTIFY_LINE_IN: + gpio = linein_detect_gpio; + active = linein_detect_gpio_activestate; + break; + case AOA_NOTIFY_LINE_OUT: + gpio = lineout_detect_gpio; + active = lineout_detect_gpio_activestate; + break; + default: + return -EINVAL; + } + + if (gpio == -1) + return -ENODEV; + + ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0); + if (ret < 0) + return ret; + return ((ret >> 1) & 1) == active; +} + +static struct gpio_methods methods = { + .init = ftr_gpio_init, + .exit = ftr_gpio_exit, + .all_amps_off = ftr_gpio_all_amps_off, + .all_amps_restore = ftr_gpio_all_amps_restore, + .set_headphone = ftr_gpio_set_headphone, + .set_speakers = ftr_gpio_set_amp, + .set_lineout = ftr_gpio_set_lineout, + .set_hw_reset = ftr_gpio_set_hw_reset, + .get_headphone = ftr_gpio_get_headphone, + .get_speakers = ftr_gpio_get_amp, + .get_lineout = ftr_gpio_get_lineout, + .set_notify = ftr_set_notify, + .get_detect = ftr_get_detect, +}; + +struct gpio_methods *ftr_gpio_methods = &methods; +EXPORT_SYMBOL_GPL(ftr_gpio_methods); diff --git a/sound/aoa/core/snd-aoa-gpio-pmf.c b/sound/aoa/core/snd-aoa-gpio-pmf.c new file mode 100644 index 0000000..0e9b9bb --- /dev/null +++ b/sound/aoa/core/snd-aoa-gpio-pmf.c @@ -0,0 +1,246 @@ +/* + * Apple Onboard Audio pmf GPIOs + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#include +#include +#include "../aoa.h" + +#define PMF_GPIO(name, bit) \ +static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\ +{ \ + struct pmf_args args = { .count = 1, .u[0].v = !on }; \ + \ + if (unlikely(!rt)) return; \ + pmf_call_function(rt->node, #name "-mute", &args); \ + rt->implementation_private &= ~(1<implementation_private |= (!!on << bit); \ +} \ +static int pmf_gpio_get_##name(struct gpio_runtime *rt) \ +{ \ + if (unlikely(!rt)) return 0; \ + return (rt->implementation_private>>bit)&1; \ +} + +PMF_GPIO(headphone, 0); +PMF_GPIO(amp, 1); +PMF_GPIO(lineout, 2); + +static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on) +{ + struct pmf_args args = { .count = 1, .u[0].v = !!on }; + + if (unlikely(!rt)) return; + pmf_call_function(rt->node, "hw-reset", &args); +} + +static void pmf_gpio_all_amps_off(struct gpio_runtime *rt) +{ + int saved; + + if (unlikely(!rt)) return; + saved = rt->implementation_private; + pmf_gpio_set_headphone(rt, 0); + pmf_gpio_set_amp(rt, 0); + pmf_gpio_set_lineout(rt, 0); + rt->implementation_private = saved; +} + +static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt) +{ + int s; + + if (unlikely(!rt)) return; + s = rt->implementation_private; + pmf_gpio_set_headphone(rt, (s>>0)&1); + pmf_gpio_set_amp(rt, (s>>1)&1); + pmf_gpio_set_lineout(rt, (s>>2)&1); +} + +static void pmf_handle_notify(void *data) +{ + struct gpio_notification *notif = data; + + mutex_lock(¬if->mutex); + if (notif->notify) + notif->notify(notif->data); + mutex_unlock(¬if->mutex); +} + +static void pmf_gpio_init(struct gpio_runtime *rt) +{ + pmf_gpio_all_amps_off(rt); + rt->implementation_private = 0; + INIT_WORK(&rt->headphone_notify.work, pmf_handle_notify, + &rt->headphone_notify); + INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify, + &rt->line_in_notify); + INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify, + &rt->line_out_notify); + mutex_init(&rt->headphone_notify.mutex); + mutex_init(&rt->line_in_notify.mutex); + mutex_init(&rt->line_out_notify.mutex); +} + +static void pmf_gpio_exit(struct gpio_runtime *rt) +{ + pmf_gpio_all_amps_off(rt); + rt->implementation_private = 0; + + if (rt->headphone_notify.gpio_private) + pmf_unregister_irq_client(rt->headphone_notify.gpio_private); + if (rt->line_in_notify.gpio_private) + pmf_unregister_irq_client(rt->line_in_notify.gpio_private); + if (rt->line_out_notify.gpio_private) + pmf_unregister_irq_client(rt->line_out_notify.gpio_private); + + /* make sure no work is pending before freeing + * all things */ + cancel_delayed_work(&rt->headphone_notify.work); + cancel_delayed_work(&rt->line_in_notify.work); + cancel_delayed_work(&rt->line_out_notify.work); + flush_scheduled_work(); + + mutex_destroy(&rt->headphone_notify.mutex); + mutex_destroy(&rt->line_in_notify.mutex); + mutex_destroy(&rt->line_out_notify.mutex); + + if (rt->headphone_notify.gpio_private) + kfree(rt->headphone_notify.gpio_private); + if (rt->line_in_notify.gpio_private) + kfree(rt->line_in_notify.gpio_private); + if (rt->line_out_notify.gpio_private) + kfree(rt->line_out_notify.gpio_private); +} + +static void pmf_handle_notify_irq(void *data) +{ + struct gpio_notification *notif = data; + + schedule_work(¬if->work); +} + +static int pmf_set_notify(struct gpio_runtime *rt, + enum notify_type type, + notify_func_t notify, + void *data) +{ + struct gpio_notification *notif; + notify_func_t old; + struct pmf_irq_client *irq_client; + char *name; + int err = -EBUSY; + + switch (type) { + case AOA_NOTIFY_HEADPHONE: + notif = &rt->headphone_notify; + name = "headphone-detect"; + break; + case AOA_NOTIFY_LINE_IN: + notif = &rt->line_in_notify; + name = "linein-detect"; + break; + case AOA_NOTIFY_LINE_OUT: + notif = &rt->line_out_notify; + name = "lineout-detect"; + break; + default: + return -EINVAL; + } + + mutex_lock(¬if->mutex); + + old = notif->notify; + + if (!old && !notify) { + err = 0; + goto out_unlock; + } + + if (old && notify) { + if (old == notify && notif->data == data) + err = 0; + goto out_unlock; + } + + if (old && !notify) { + irq_client = notif->gpio_private; + pmf_unregister_irq_client(irq_client); + kfree(irq_client); + notif->gpio_private = NULL; + } + if (!old && notify) { + irq_client = kzalloc(sizeof(struct pmf_irq_client), + GFP_KERNEL); + irq_client->data = notif; + irq_client->handler = pmf_handle_notify_irq; + irq_client->owner = THIS_MODULE; + err = pmf_register_irq_client(rt->node, + name, + irq_client); + if (err) { + printk(KERN_ERR "snd-aoa: gpio layer failed to" + " register %s irq (%d)\n", name, err); + kfree(irq_client); + goto out_unlock; + } + notif->gpio_private = irq_client; + } + notif->notify = notify; + notif->data = data; + + err = 0; + out_unlock: + mutex_unlock(¬if->mutex); + return err; +} + +static int pmf_get_detect(struct gpio_runtime *rt, + enum notify_type type) +{ + char *name; + int err = -EBUSY, ret; + struct pmf_args args = { .count = 1, .u[0].p = &ret }; + + switch (type) { + case AOA_NOTIFY_HEADPHONE: + name = "headphone-detect"; + break; + case AOA_NOTIFY_LINE_IN: + name = "linein-detect"; + break; + case AOA_NOTIFY_LINE_OUT: + name = "lineout-detect"; + break; + default: + return -EINVAL; + } + + err = pmf_call_function(rt->node, name, &args); + if (err) + return err; + return ret; +} + +static struct gpio_methods methods = { + .init = pmf_gpio_init, + .exit = pmf_gpio_exit, + .all_amps_off = pmf_gpio_all_amps_off, + .all_amps_restore = pmf_gpio_all_amps_restore, + .set_headphone = pmf_gpio_set_headphone, + .set_speakers = pmf_gpio_set_amp, + .set_lineout = pmf_gpio_set_lineout, + .set_hw_reset = pmf_gpio_set_hw_reset, + .get_headphone = pmf_gpio_get_headphone, + .get_speakers = pmf_gpio_get_amp, + .get_lineout = pmf_gpio_get_lineout, + .set_notify = pmf_set_notify, + .get_detect = pmf_get_detect, +}; + +struct gpio_methods *pmf_gpio_methods = &methods; +EXPORT_SYMBOL_GPL(pmf_gpio_methods); diff --git a/sound/aoa/fabrics/Kconfig b/sound/aoa/fabrics/Kconfig new file mode 100644 index 0000000..c3bc770 --- /dev/null +++ b/sound/aoa/fabrics/Kconfig @@ -0,0 +1,12 @@ +config SND_AOA_FABRIC_LAYOUT + tristate "layout-id fabric" + depends SND_AOA + select SND_AOA_SOUNDBUS + select SND_AOA_SOUNDBUS_I2S + ---help--- + This enables the layout-id fabric for the Apple Onboard + Audio driver, the module holding it all together + based on the device-tree's layout-id property. + + If you are unsure and have a later Apple machine, + compile it as a module. diff --git a/sound/aoa/fabrics/Makefile b/sound/aoa/fabrics/Makefile new file mode 100644 index 0000000..55fc5e7 --- /dev/null +++ b/sound/aoa/fabrics/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SND_AOA_FABRIC_LAYOUT) += snd-aoa-fabric-layout.o diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c new file mode 100644 index 0000000..cbc8a3b --- /dev/null +++ b/sound/aoa/fabrics/snd-aoa-fabric-layout.c @@ -0,0 +1,1111 @@ +/* + * Apple Onboard Audio driver -- layout fabric + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + * + * + * This fabric module looks for sound codecs + * based on the layout-id property in the device tree. + * + */ + +#include +#include +#include +#include "../aoa.h" +#include "../soundbus/soundbus.h" + +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa"); + +#define MAX_CODECS_PER_BUS 2 + +/* These are the connections the layout fabric + * knows about. It doesn't really care about the + * input ones, but I thought I'd separate them + * to give them proper names. The thing is that + * Apple usually will distinguish the active output + * by GPIOs, while the active input is set directly + * on the codec. Hence we here tell the codec what + * we think is connected. This information is hard- + * coded below ... */ +#define CC_SPEAKERS (1<<0) +#define CC_HEADPHONE (1<<1) +#define CC_LINEOUT (1<<2) +#define CC_DIGITALOUT (1<<3) +#define CC_LINEIN (1<<4) +#define CC_MICROPHONE (1<<5) +#define CC_DIGITALIN (1<<6) +/* pretty bogus but users complain... + * This is a flag saying that the LINEOUT + * should be renamed to HEADPHONE. + * be careful with input detection! */ +#define CC_LINEOUT_LABELLED_HEADPHONE (1<<7) + +struct codec_connection { + /* CC_ flags from above */ + int connected; + /* codec dependent bit to be set in the aoa_codec.connected field. + * This intentionally doesn't have any generic flags because the + * fabric has to know the codec anyway and all codecs might have + * different connectors */ + int codec_bit; +}; + +struct codec_connect_info { + char *name; + struct codec_connection *connections; +}; + +#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0) + +struct layout { + unsigned int layout_id; + struct codec_connect_info codecs[MAX_CODECS_PER_BUS]; + int flags; + + /* if busname is not assigned, we use 'Master' below, + * so that our layout table doesn't need to be filled + * too much. + * We only assign these two if we expect to find more + * than one soundbus, i.e. on those machines with + * multiple layout-ids */ + char *busname; + int pcmid; +}; + +MODULE_ALIAS("sound-layout-41"); +MODULE_ALIAS("sound-layout-45"); +MODULE_ALIAS("sound-layout-51"); +MODULE_ALIAS("sound-layout-58"); +MODULE_ALIAS("sound-layout-60"); +MODULE_ALIAS("sound-layout-61"); +MODULE_ALIAS("sound-layout-64"); +MODULE_ALIAS("sound-layout-65"); +MODULE_ALIAS("sound-layout-68"); +MODULE_ALIAS("sound-layout-69"); +MODULE_ALIAS("sound-layout-70"); +MODULE_ALIAS("sound-layout-72"); +MODULE_ALIAS("sound-layout-80"); +MODULE_ALIAS("sound-layout-82"); +MODULE_ALIAS("sound-layout-84"); +MODULE_ALIAS("sound-layout-86"); +MODULE_ALIAS("sound-layout-92"); +MODULE_ALIAS("sound-layout-96"); + +/* onyx with all but microphone connected */ +static struct codec_connection onyx_connections_nomic[] = { + { + .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, + .codec_bit = 0, + }, + { + .connected = CC_DIGITALOUT, + .codec_bit = 1, + }, + { + .connected = CC_LINEIN, + .codec_bit = 2, + }, + {} /* terminate array by .connected == 0 */ +}; + +/* onyx on machines without headphone */ +static struct codec_connection onyx_connections_noheadphones[] = { + { + .connected = CC_SPEAKERS | CC_LINEOUT | + CC_LINEOUT_LABELLED_HEADPHONE, + .codec_bit = 0, + }, + { + .connected = CC_DIGITALOUT, + .codec_bit = 1, + }, + /* FIXME: are these correct? probably not for all the machines + * below ... If not this will need separating. */ + { + .connected = CC_LINEIN, + .codec_bit = 2, + }, + { + .connected = CC_MICROPHONE, + .codec_bit = 3, + }, + {} /* terminate array by .connected == 0 */ +}; + +/* onyx on machines with real line-out */ +static struct codec_connection onyx_connections_reallineout[] = { + { + .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE, + .codec_bit = 0, + }, + { + .connected = CC_DIGITALOUT, + .codec_bit = 1, + }, + { + .connected = CC_LINEIN, + .codec_bit = 2, + }, + {} /* terminate array by .connected == 0 */ +}; + +/* tas on machines without line out */ +static struct codec_connection tas_connections_nolineout[] = { + { + .connected = CC_SPEAKERS | CC_HEADPHONE, + .codec_bit = 0, + }, + { + .connected = CC_LINEIN, + .codec_bit = 2, + }, + { + .connected = CC_MICROPHONE, + .codec_bit = 3, + }, + {} /* terminate array by .connected == 0 */ +}; + +/* tas on machines with neither line out nor line in */ +static struct codec_connection tas_connections_noline[] = { + { + .connected = CC_SPEAKERS | CC_HEADPHONE, + .codec_bit = 0, + }, + { + .connected = CC_MICROPHONE, + .codec_bit = 3, + }, + {} /* terminate array by .connected == 0 */ +}; + +/* tas on machines without microphone */ +static struct codec_connection tas_connections_nomic[] = { + { + .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, + .codec_bit = 0, + }, + { + .connected = CC_LINEIN, + .codec_bit = 2, + }, + {} /* terminate array by .connected == 0 */ +}; + +/* tas on machines with everything connected */ +static struct codec_connection tas_connections_all[] = { + { + .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, + .codec_bit = 0, + }, + { + .connected = CC_LINEIN, + .codec_bit = 2, + }, + { + .connected = CC_MICROPHONE, + .codec_bit = 3, + }, + {} /* terminate array by .connected == 0 */ +}; + +static struct codec_connection toonie_connections[] = { + { + .connected = CC_SPEAKERS | CC_HEADPHONE, + .codec_bit = 0, + }, + {} /* terminate array by .connected == 0 */ +}; + +static struct codec_connection topaz_input[] = { + { + .connected = CC_DIGITALIN, + .codec_bit = 0, + }, + {} /* terminate array by .connected == 0 */ +}; + +static struct codec_connection topaz_output[] = { + { + .connected = CC_DIGITALOUT, + .codec_bit = 1, + }, + {} /* terminate array by .connected == 0 */ +}; + +static struct codec_connection topaz_inout[] = { + { + .connected = CC_DIGITALIN, + .codec_bit = 0, + }, + { + .connected = CC_DIGITALOUT, + .codec_bit = 1, + }, + {} /* terminate array by .connected == 0 */ +}; + +static struct layout layouts[] = { + /* last PowerBooks (15" Oct 2005) */ + { .layout_id = 82, + .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + /* PowerMac9,1 */ + { .layout_id = 60, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_reallineout, + }, + }, + /* PowerMac9,1 */ + { .layout_id = 61, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + /* PowerBook5,7 */ + { .layout_id = 64, + .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + }, + /* PowerBook5,7 */ + { .layout_id = 65, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + /* PowerBook5,9 [17" Oct 2005] */ + { .layout_id = 84, + .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + /* PowerMac8,1 */ + { .layout_id = 45, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + /* Quad PowerMac (analog in, analog/digital out) */ + { .layout_id = 68, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_nomic, + }, + }, + /* Quad PowerMac (digital in) */ + { .layout_id = 69, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + .busname = "digital in", .pcmid = 1 }, + /* Early 2005 PowerBook (PowerBook 5,6) */ + { .layout_id = 70, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nolineout, + }, + }, + /* PowerBook 5,4 */ + { .layout_id = 51, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nolineout, + }, + }, + /* PowerBook6,7 */ + { .layout_id = 80, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_noline, + }, + }, + /* PowerBook6,8 */ + { .layout_id = 72, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nolineout, + }, + }, + /* PowerMac8,2 */ + { .layout_id = 86, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_nomic, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + /* PowerBook6,7 */ + { .layout_id = 92, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nolineout, + }, + }, + /* PowerMac10,1 (Mac Mini) */ + { .layout_id = 58, + .codecs[0] = { + .name = "toonie", + .connections = toonie_connections, + }, + }, + { + .layout_id = 96, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + }, + /* unknown, untested, but this comes from Apple */ + { .layout_id = 41, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_all, + }, + }, + { .layout_id = 36, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nomic, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_inout, + }, + }, + { .layout_id = 47, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + }, + { .layout_id = 48, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + { .layout_id = 49, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_nomic, + }, + }, + { .layout_id = 50, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + { .layout_id = 56, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + }, + { .layout_id = 57, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + { .layout_id = 62, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_output, + }, + }, + { .layout_id = 66, + .codecs[0] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + }, + { .layout_id = 67, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + }, + { .layout_id = 76, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_nomic, + }, + .codecs[1] = { + .name = "topaz", + .connections = topaz_inout, + }, + }, + { .layout_id = 90, + .codecs[0] = { + .name = "tas", + .connections = tas_connections_noline, + }, + }, + { .layout_id = 94, + .codecs[0] = { + .name = "onyx", + /* but it has an external mic?? how to select? */ + .connections = onyx_connections_noheadphones, + }, + }, + { .layout_id = 98, + .codecs[0] = { + .name = "toonie", + .connections = toonie_connections, + }, + }, + { .layout_id = 100, + .codecs[0] = { + .name = "topaz", + .connections = topaz_input, + }, + .codecs[1] = { + .name = "onyx", + .connections = onyx_connections_noheadphones, + }, + }, + {} +}; + +static struct layout *find_layout_by_id(unsigned int id) +{ + struct layout *l; + + l = layouts; + while (l->layout_id) { + if (l->layout_id == id) + return l; + l++; + } + return NULL; +} + +static void use_layout(struct layout *l) +{ + int i; + + for (i=0; icodecs[i].name) { + request_module("snd-aoa-codec-%s", l->codecs[i].name); + } + } + /* now we wait for the codecs to call us back */ +} + +struct layout_dev; + +struct layout_dev_ptr { + struct layout_dev *ptr; +}; + +struct layout_dev { + struct list_head list; + struct soundbus_dev *sdev; + struct device_node *sound; + struct aoa_codec *codecs[MAX_CODECS_PER_BUS]; + struct layout *layout; + struct gpio_runtime gpio; + + /* we need these for headphone/lineout detection */ + struct snd_kcontrol *headphone_ctrl; + struct snd_kcontrol *lineout_ctrl; + struct snd_kcontrol *speaker_ctrl; + struct snd_kcontrol *headphone_detected_ctrl; + struct snd_kcontrol *lineout_detected_ctrl; + + struct layout_dev_ptr selfptr_headphone; + struct layout_dev_ptr selfptr_lineout; + + u32 have_lineout_detect:1, + have_headphone_detect:1, + switch_on_headphone:1, + switch_on_lineout:1; +}; + +static LIST_HEAD(layouts_list); +static int layouts_list_items; +/* this can go away but only if we allow multiple cards, + * make the fabric handle all the card stuff, etc... */ +static struct layout_dev *layout_device; + +static int control_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +#define AMP_CONTROL(n, description) \ +static int n##_control_get(struct snd_kcontrol *kcontrol, \ + struct snd_ctl_elem_value *ucontrol) \ +{ \ + struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ + if (gpio->methods && gpio->methods->get_##n) \ + ucontrol->value.integer.value[0] = \ + gpio->methods->get_##n(gpio); \ + return 0; \ +} \ +static int n##_control_put(struct snd_kcontrol *kcontrol, \ + struct snd_ctl_elem_value *ucontrol) \ +{ \ + struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ + if (gpio->methods && gpio->methods->get_##n) \ + gpio->methods->set_##n(gpio, \ + ucontrol->value.integer.value[0]); \ + return 1; \ +} \ +static struct snd_kcontrol_new n##_ctl = { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = description, \ + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = control_info, \ + .get = n##_control_get, \ + .put = n##_control_put, \ +} + +AMP_CONTROL(headphone, "Headphone Switch"); +AMP_CONTROL(speakers, "Speakers Switch"); +AMP_CONTROL(lineout, "Line-Out Switch"); + +static int detect_choice_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); + + switch (kcontrol->private_value) { + case 0: + ucontrol->value.integer.value[0] = ldev->switch_on_headphone; + break; + case 1: + ucontrol->value.integer.value[0] = ldev->switch_on_lineout; + break; + default: + return -ENODEV; + } + return 0; +} + +static int detect_choice_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); + + switch (kcontrol->private_value) { + case 0: + ldev->switch_on_headphone = !!ucontrol->value.integer.value[0]; + break; + case 1: + ldev->switch_on_lineout = !!ucontrol->value.integer.value[0]; + break; + default: + return -ENODEV; + } + return 1; +} + +static struct snd_kcontrol_new headphone_detect_choice = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Detect Autoswitch", + .info = control_info, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .get = detect_choice_get, + .put = detect_choice_put, + .private_value = 0, +}; + +static struct snd_kcontrol_new lineout_detect_choice = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line-Out Detect Autoswitch", + .info = control_info, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .get = detect_choice_get, + .put = detect_choice_put, + .private_value = 1, +}; + +static int detected_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); + int v; + + switch (kcontrol->private_value) { + case 0: + v = ldev->gpio.methods->get_detect(&ldev->gpio, + AOA_NOTIFY_HEADPHONE); + break; + case 1: + v = ldev->gpio.methods->get_detect(&ldev->gpio, + AOA_NOTIFY_LINE_OUT); + break; + default: + return -ENODEV; + } + ucontrol->value.integer.value[0] = v; + return 0; +} + +static struct snd_kcontrol_new headphone_detected = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Headphone Detected", + .info = control_info, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .get = detected_get, + .private_value = 0, +}; + +static struct snd_kcontrol_new lineout_detected = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line-Out Detected", + .info = control_info, + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .get = detected_get, + .private_value = 1, +}; + +static int check_codec(struct aoa_codec *codec, + struct layout_dev *ldev, + struct codec_connect_info *cci) +{ + u32 *ref; + char propname[32]; + struct codec_connection *cc; + + /* if the codec has a 'codec' node, we require a reference */ + if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { + snprintf(propname, sizeof(propname), + "platform-%s-codec-ref", codec->name); + ref = (u32*)get_property(ldev->sound, propname, NULL); + if (!ref) { + printk(KERN_INFO "snd-aoa-fabric-layout: " + "required property %s not present\n", propname); + return -ENODEV; + } + if (*ref != codec->node->linux_phandle) { + printk(KERN_INFO "snd-aoa-fabric-layout: " + "%s doesn't match!\n", propname); + return -ENODEV; + } + } else { + if (layouts_list_items != 1) { + printk(KERN_INFO "snd-aoa-fabric-layout: " + "more than one soundbus, but no references.\n"); + return -ENODEV; + } + } + codec->soundbus_dev = ldev->sdev; + codec->gpio = &ldev->gpio; + + cc = cci->connections; + if (!cc) + return -EINVAL; + + printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n"); + + codec->connected = 0; + codec->fabric_data = cc; + + while (cc->connected) { + codec->connected |= 1<codec_bit; + cc++; + } + + return 0; +} + +static int layout_found_codec(struct aoa_codec *codec) +{ + struct layout_dev *ldev; + int i; + + list_for_each_entry(ldev, &layouts_list, list) { + for (i=0; ilayout->codecs[i].name) + continue; + if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) { + if (check_codec(codec, + ldev, + &ldev->layout->codecs[i]) == 0) + return 0; + } + } + } + return -ENODEV; +} + +static void layout_remove_codec(struct aoa_codec *codec) +{ + int i; + /* here remove the codec from the layout dev's + * codec reference */ + + codec->soundbus_dev = NULL; + codec->gpio = NULL; + for (i=0; iptr; + if (data == &ldev->selfptr_headphone) { + v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE); + detected = ldev->headphone_detected_ctrl; + update = ldev->switch_on_headphone; + if (update) { + ldev->gpio.methods->set_speakers(&ldev->gpio, !v); + ldev->gpio.methods->set_headphone(&ldev->gpio, v); + ldev->gpio.methods->set_lineout(&ldev->gpio, 0); + } + } else if (data == &ldev->selfptr_lineout) { + v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT); + detected = ldev->lineout_detected_ctrl; + update = ldev->switch_on_lineout; + if (update) { + ldev->gpio.methods->set_speakers(&ldev->gpio, !v); + ldev->gpio.methods->set_headphone(&ldev->gpio, 0); + ldev->gpio.methods->set_lineout(&ldev->gpio, v); + } + } else + return; + + if (detected) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id); + if (update) { + c = ldev->headphone_ctrl; + if (c) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); + c = ldev->speaker_ctrl; + if (c) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); + c = ldev->lineout_ctrl; + if (c) + snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); + } +} + +static void layout_attached_codec(struct aoa_codec *codec) +{ + struct codec_connection *cc; + struct snd_kcontrol *ctl; + int headphones, lineout; + struct layout_dev *ldev = layout_device; + + /* need to add this codec to our codec array! */ + + cc = codec->fabric_data; + + headphones = codec->gpio->methods->get_detect(codec->gpio, + AOA_NOTIFY_HEADPHONE); + lineout = codec->gpio->methods->get_detect(codec->gpio, + AOA_NOTIFY_LINE_OUT); + + while (cc->connected) { + if (cc->connected & CC_SPEAKERS) { + if (headphones <= 0 && lineout <= 0) + ldev->gpio.methods->set_speakers(codec->gpio, 1); + ctl = snd_ctl_new1(&speakers_ctl, codec->gpio); + ldev->speaker_ctrl = ctl; + aoa_snd_ctl_add(ctl); + } + if (cc->connected & CC_HEADPHONE) { + if (headphones == 1) + ldev->gpio.methods->set_headphone(codec->gpio, 1); + ctl = snd_ctl_new1(&headphone_ctl, codec->gpio); + ldev->headphone_ctrl = ctl; + aoa_snd_ctl_add(ctl); + ldev->have_headphone_detect = + !ldev->gpio.methods + ->set_notify(&ldev->gpio, + AOA_NOTIFY_HEADPHONE, + layout_notify, + &ldev->selfptr_headphone); + if (ldev->have_headphone_detect) { + ctl = snd_ctl_new1(&headphone_detect_choice, + ldev); + aoa_snd_ctl_add(ctl); + ctl = snd_ctl_new1(&headphone_detected, + ldev); + ldev->headphone_detected_ctrl = ctl; + aoa_snd_ctl_add(ctl); + } + } + if (cc->connected & CC_LINEOUT) { + if (lineout == 1) + ldev->gpio.methods->set_lineout(codec->gpio, 1); + ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); + if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) + strlcpy(ctl->id.name, + "Headphone Switch", sizeof(ctl->id.name)); + ldev->lineout_ctrl = ctl; + aoa_snd_ctl_add(ctl); + ldev->have_lineout_detect = + !ldev->gpio.methods + ->set_notify(&ldev->gpio, + AOA_NOTIFY_LINE_OUT, + layout_notify, + &ldev->selfptr_lineout); + if (ldev->have_lineout_detect) { + ctl = snd_ctl_new1(&lineout_detect_choice, + ldev); + if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) + strlcpy(ctl->id.name, + "Headphone Detect Autoswitch", + sizeof(ctl->id.name)); + aoa_snd_ctl_add(ctl); + ctl = snd_ctl_new1(&lineout_detected, + ldev); + if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) + strlcpy(ctl->id.name, + "Headphone Detected", + sizeof(ctl->id.name)); + ldev->lineout_detected_ctrl = ctl; + aoa_snd_ctl_add(ctl); + } + } + cc++; + } + /* now update initial state */ + if (ldev->have_headphone_detect) + layout_notify(&ldev->selfptr_headphone); + if (ldev->have_lineout_detect) + layout_notify(&ldev->selfptr_lineout); +} + +static struct aoa_fabric layout_fabric = { + .name = "SoundByLayout", + .owner = THIS_MODULE, + .found_codec = layout_found_codec, + .remove_codec = layout_remove_codec, + .attached_codec = layout_attached_codec, +}; + +static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) +{ + struct device_node *sound = NULL; + unsigned int *layout_id; + struct layout *layout; + struct layout_dev *ldev = NULL; + int err; + + /* hm, currently we can only have one ... */ + if (layout_device) + return -ENODEV; + + /* by breaking out we keep a reference */ + while ((sound = of_get_next_child(sdev->ofdev.node, sound))) { + if (sound->type && strcasecmp(sound->type, "soundchip") == 0) + break; + } + if (!sound) return -ENODEV; + + layout_id = (unsigned int *) get_property(sound, "layout-id", NULL); + if (!layout_id) + goto outnodev; + printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d ", *layout_id); + + layout = find_layout_by_id(*layout_id); + if (!layout) { + printk("(no idea how to handle)\n"); + goto outnodev; + } + + ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL); + if (!ldev) + goto outnodev; + + layout_device = ldev; + ldev->sdev = sdev; + ldev->sound = sound; + ldev->layout = layout; + ldev->gpio.node = sound->parent; + switch (layout->layout_id) { + case 41: /* that unknown machine no one seems to have */ + case 51: /* PowerBook5,4 */ + case 58: /* Mac Mini */ + ldev->gpio.methods = ftr_gpio_methods; + break; + default: + ldev->gpio.methods = pmf_gpio_methods; + } + ldev->selfptr_headphone.ptr = ldev; + ldev->selfptr_lineout.ptr = ldev; + sdev->ofdev.dev.driver_data = ldev; + + printk("(using)\n"); + list_add(&ldev->list, &layouts_list); + layouts_list_items++; + + /* assign these before registering ourselves, so + * callbacks that are done during registration + * already have the values */ + sdev->pcmid = ldev->layout->pcmid; + if (ldev->layout->busname) { + sdev->pcmname = ldev->layout->busname; + } else { + sdev->pcmname = "Master"; + } + + ldev->gpio.methods->init(&ldev->gpio); + + err = aoa_fabric_register(&layout_fabric); + if (err && err != -EALREADY) { + printk(KERN_INFO "snd-aoa-fabric-layout: can't use," + " another fabric is active!\n"); + goto outlistdel; + } + + use_layout(layout); + ldev->switch_on_headphone = 1; + ldev->switch_on_lineout = 1; + return 0; + outlistdel: + /* we won't be using these then... */ + ldev->gpio.methods->exit(&ldev->gpio); + /* reset if we didn't use it */ + sdev->pcmname = NULL; + sdev->pcmid = -1; + list_del(&ldev->list); + layouts_list_items--; + outnodev: + if (sound) of_node_put(sound); + layout_device = NULL; + if (ldev) kfree(ldev); + return -ENODEV; +} + +static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) +{ + struct layout_dev *ldev = sdev->ofdev.dev.driver_data; + int i; + + for (i=0; icodecs[i]) { + aoa_fabric_unlink_codec(ldev->codecs[i]); + } + ldev->codecs[i] = NULL; + } + list_del(&ldev->list); + layouts_list_items--; + of_node_put(ldev->sound); + + ldev->gpio.methods->set_notify(&ldev->gpio, + AOA_NOTIFY_HEADPHONE, + NULL, + NULL); + ldev->gpio.methods->set_notify(&ldev->gpio, + AOA_NOTIFY_LINE_OUT, + NULL, + NULL); + + ldev->gpio.methods->exit(&ldev->gpio); + layout_device = NULL; + kfree(ldev); + sdev->pcmid = -1; + sdev->pcmname = NULL; + return 0; +} + +#ifdef CONFIG_PM +static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state) +{ + struct layout_dev *ldev = sdev->ofdev.dev.driver_data; + + printk("aoa_fabric_layout_suspend()\n"); + + if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) + ldev->gpio.methods->all_amps_off(&ldev->gpio); + + return 0; +} + +static int aoa_fabric_layout_resume(struct soundbus_dev *sdev) +{ + struct layout_dev *ldev = sdev->ofdev.dev.driver_data; + + printk("aoa_fabric_layout_resume()\n"); + + if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) + ldev->gpio.methods->all_amps_restore(&ldev->gpio); + + return 0; +} +#endif + +static struct soundbus_driver aoa_soundbus_driver = { + .name = "snd_aoa_soundbus_drv", + .owner = THIS_MODULE, + .probe = aoa_fabric_layout_probe, + .remove = aoa_fabric_layout_remove, +#ifdef CONFIG_PM + .suspend = aoa_fabric_layout_suspend, + .resume = aoa_fabric_layout_resume, +#endif +}; + +static int __init aoa_fabric_layout_init(void) +{ + int err; + + err = soundbus_register_driver(&aoa_soundbus_driver); + if (err) + return err; + return 0; +} + +static void __exit aoa_fabric_layout_exit(void) +{ + soundbus_unregister_driver(&aoa_soundbus_driver); + aoa_fabric_unregister(&layout_fabric); +} + +module_init(aoa_fabric_layout_init); +module_exit(aoa_fabric_layout_exit); diff --git a/sound/aoa/soundbus/Kconfig b/sound/aoa/soundbus/Kconfig new file mode 100644 index 0000000..7368b7d --- /dev/null +++ b/sound/aoa/soundbus/Kconfig @@ -0,0 +1,15 @@ +config SND_AOA_SOUNDBUS + tristate "Apple Soundbus support" + depends on SOUND + select SND_PCM + ---help--- + This option enables the generic driver for the soundbus + support on Apple machines. + + It is required for the sound bus implementations. + +config SND_AOA_SOUNDBUS_I2S + tristate "I2S bus support" + depends on SND_AOA_SOUNDBUS && PCI + ---help--- + This option enables support for Apple I2S busses. diff --git a/sound/aoa/soundbus/Makefile b/sound/aoa/soundbus/Makefile new file mode 100644 index 0000000..0e61f5a --- /dev/null +++ b/sound/aoa/soundbus/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SND_AOA_SOUNDBUS) += snd-aoa-soundbus.o +snd-aoa-soundbus-objs := core.o sysfs.o +obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += i2sbus/ diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c new file mode 100644 index 0000000..abe84a7 --- /dev/null +++ b/sound/aoa/soundbus/core.c @@ -0,0 +1,250 @@ +/* + * soundbus + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#include +#include "soundbus.h" + +MODULE_AUTHOR("Johannes Berg "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Apple Soundbus"); + +struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev) +{ + struct device *tmp; + + if (!dev) + return NULL; + tmp = get_device(&dev->ofdev.dev); + if (tmp) + return to_soundbus_device(tmp); + else + return NULL; +} +EXPORT_SYMBOL_GPL(soundbus_dev_get); + +void soundbus_dev_put(struct soundbus_dev *dev) +{ + if (dev) + put_device(&dev->ofdev.dev); +} +EXPORT_SYMBOL_GPL(soundbus_dev_put); + +static int soundbus_probe(struct device *dev) +{ + int error = -ENODEV; + struct soundbus_driver *drv; + struct soundbus_dev *soundbus_dev; + + drv = to_soundbus_driver(dev->driver); + soundbus_dev = to_soundbus_device(dev); + + if (!drv->probe) + return error; + + soundbus_dev_get(soundbus_dev); + + error = drv->probe(soundbus_dev); + if (error) + soundbus_dev_put(soundbus_dev); + + return error; +} + + +static int soundbus_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct soundbus_dev * soundbus_dev; + struct of_device * of; + char *scratch, *compat, *compat2; + int i = 0; + int length, cplen, cplen2, seen = 0; + + if (!dev) + return -ENODEV; + + soundbus_dev = to_soundbus_device(dev); + if (!soundbus_dev) + return -ENODEV; + + of = &soundbus_dev->ofdev; + + /* stuff we want to pass to /sbin/hotplug */ + envp[i++] = scratch = buffer; + length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + + /* Since the compatible field can contain pretty much anything + * it's not really legal to split it out with commas. We split it + * up using a number of environment variables instead. */ + + compat = (char *) get_property(of->node, "compatible", &cplen); + compat2 = compat; + cplen2= cplen; + while (compat && cplen > 0) { + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, + "OF_COMPATIBLE_%d=%s", seen, compat); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + length = strlen (compat) + 1; + compat += length; + cplen -= length; + seen++; + } + + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); + ++length; + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + scratch += length; + + envp[i++] = scratch; + length = scnprintf (scratch, buffer_size, "MODALIAS=%s", + soundbus_dev->modalias); + + buffer_size -= length; + if ((buffer_size <= 0) || (i >= num_envp)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +static int soundbus_device_remove(struct device *dev) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->remove) + drv->remove(soundbus_dev); + soundbus_dev_put(soundbus_dev); + + return 0; +} + +static void soundbus_device_shutdown(struct device *dev) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(soundbus_dev); +} + +#ifdef CONFIG_PM + +static int soundbus_device_suspend(struct device *dev, pm_message_t state) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->suspend) + return drv->suspend(soundbus_dev, state); + return 0; +} + +static int soundbus_device_resume(struct device * dev) +{ + struct soundbus_dev * soundbus_dev = to_soundbus_device(dev); + struct soundbus_driver * drv = to_soundbus_driver(dev->driver); + + if (dev->driver && drv->resume) + return drv->resume(soundbus_dev); + return 0; +} + +#endif /* CONFIG_PM */ + +extern struct device_attribute soundbus_dev_attrs[]; + +static struct bus_type soundbus_bus_type = { + .name = "aoa-soundbus", + .probe = soundbus_probe, + .uevent = soundbus_uevent, + .remove = soundbus_device_remove, + .shutdown = soundbus_device_shutdown, +#ifdef CONFIG_PM + .suspend = soundbus_device_suspend, + .resume = soundbus_device_resume, +#endif + .dev_attrs = soundbus_dev_attrs, +}; + +static int __init soundbus_init(void) +{ + return bus_register(&soundbus_bus_type); +} + +static void __exit soundbus_exit(void) +{ + bus_unregister(&soundbus_bus_type); +} + +int soundbus_add_one(struct soundbus_dev *dev) +{ + static int devcount; + + /* sanity checks */ + if (!dev->attach_codec || + !dev->ofdev.node || + dev->pcmname || + dev->pcmid != -1) { + printk(KERN_ERR "soundbus: adding device failed sanity check!\n"); + return -EINVAL; + } + + snprintf(dev->ofdev.dev.bus_id, BUS_ID_SIZE, "soundbus:%x", ++devcount); + dev->ofdev.dev.bus = &soundbus_bus_type; + return of_device_register(&dev->ofdev); +} +EXPORT_SYMBOL_GPL(soundbus_add_one); + +void soundbus_remove_one(struct soundbus_dev *dev) +{ + of_device_unregister(&dev->ofdev); +} +EXPORT_SYMBOL_GPL(soundbus_remove_one); + +int soundbus_register_driver(struct soundbus_driver *drv) +{ + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &soundbus_bus_type; + + /* register with core */ + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(soundbus_register_driver); + +void soundbus_unregister_driver(struct soundbus_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(soundbus_unregister_driver); + +module_init(soundbus_init); +module_exit(soundbus_exit); diff --git a/sound/aoa/soundbus/i2sbus/Makefile b/sound/aoa/soundbus/i2sbus/Makefile new file mode 100644 index 0000000..e57a5cf --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_SND_AOA_SOUNDBUS_I2S) += snd-aoa-i2sbus.o +snd-aoa-i2sbus-objs := i2sbus-core.o i2sbus-pcm.o i2sbus-control.o diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.c b/sound/aoa/soundbus/i2sbus/i2sbus-control.c new file mode 100644 index 0000000..f504079 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.c @@ -0,0 +1,192 @@ +/* + * i2sbus driver -- bus control routines + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#include +#include +#include +#include +#include +#include +#include "i2sbus.h" + +int i2sbus_control_init(struct macio_dev* dev, struct i2sbus_control **c) +{ + *c = kzalloc(sizeof(struct i2sbus_control), GFP_KERNEL); + if (!*c) + return -ENOMEM; + + INIT_LIST_HEAD(&(*c)->list); + + if (of_address_to_resource(dev->ofdev.node, 0, &(*c)->rsrc)) + goto err; + /* we really should be using feature calls instead of mapping + * these registers. It's safe for now since no one else is + * touching them... */ + (*c)->controlregs = ioremap((*c)->rsrc.start, + sizeof(struct i2s_control_regs)); + if (!(*c)->controlregs) + goto err; + + return 0; + err: + kfree(*c); + *c = NULL; + return -ENODEV; +} + +void i2sbus_control_destroy(struct i2sbus_control *c) +{ + iounmap(c->controlregs); + kfree(c); +} + +/* this is serialised externally */ +int i2sbus_control_add_dev(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev) +{ + struct device_node *np; + + np = i2sdev->sound.ofdev.node; + i2sdev->enable = pmf_find_function(np, "enable"); + i2sdev->cell_enable = pmf_find_function(np, "cell-enable"); + i2sdev->clock_enable = pmf_find_function(np, "clock-enable"); + i2sdev->cell_disable = pmf_find_function(np, "cell-disable"); + i2sdev->clock_disable = pmf_find_function(np, "clock-disable"); + + /* if the bus number is not 0 or 1 we absolutely need to use + * the platform functions -- there's nothing in Darwin that + * would allow seeing a system behind what the FCRs are then, + * and I don't want to go parsing a bunch of platform functions + * by hand to try finding a system... */ + if (i2sdev->bus_number != 0 && i2sdev->bus_number != 1 && + (!i2sdev->enable || + !i2sdev->cell_enable || !i2sdev->clock_enable || + !i2sdev->cell_disable || !i2sdev->clock_disable)) { + pmf_put_function(i2sdev->enable); + pmf_put_function(i2sdev->cell_enable); + pmf_put_function(i2sdev->clock_enable); + pmf_put_function(i2sdev->cell_disable); + pmf_put_function(i2sdev->clock_disable); + return -ENODEV; + } + + list_add(&i2sdev->item, &c->list); + + return 0; +} + +void i2sbus_control_remove_dev(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev) +{ + /* this is serialised externally */ + list_del(&i2sdev->item); + if (list_empty(&c->list)) + i2sbus_control_destroy(c); +} + +int i2sbus_control_enable(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev) +{ + struct pmf_args args = { .count = 0 }; + int cc; + + if (i2sdev->enable) + return pmf_call_one(i2sdev->enable, &args); + + switch (i2sdev->bus_number) { + case 0: + cc = in_le32(&c->controlregs->cell_control); + out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_0_ENABLE); + break; + case 1: + cc = in_le32(&c->controlregs->cell_control); + out_le32(&c->controlregs->cell_control, cc | CTRL_CLOCK_INTF_1_ENABLE); + break; + default: + return -ENODEV; + } + return 0; +} + +int i2sbus_control_cell(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev, + int enable) +{ + struct pmf_args args = { .count = 0 }; + int cc; + + switch (enable) { + case 0: + if (i2sdev->cell_disable) + return pmf_call_one(i2sdev->cell_disable, &args); + break; + case 1: + if (i2sdev->cell_enable) + return pmf_call_one(i2sdev->cell_enable, &args); + break; + default: + printk(KERN_ERR "i2sbus: INVALID CELL ENABLE VALUE\n"); + return -ENODEV; + } + switch (i2sdev->bus_number) { + case 0: + cc = in_le32(&c->controlregs->cell_control); + cc &= ~CTRL_CLOCK_CELL_0_ENABLE; + cc |= enable * CTRL_CLOCK_CELL_0_ENABLE; + out_le32(&c->controlregs->cell_control, cc); + break; + case 1: + cc = in_le32(&c->controlregs->cell_control); + cc &= ~CTRL_CLOCK_CELL_1_ENABLE; + cc |= enable * CTRL_CLOCK_CELL_1_ENABLE; + out_le32(&c->controlregs->cell_control, cc); + break; + default: + return -ENODEV; + } + return 0; +} + +int i2sbus_control_clock(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev, + int enable) +{ + struct pmf_args args = { .count = 0 }; + int cc; + + switch (enable) { + case 0: + if (i2sdev->clock_disable) + return pmf_call_one(i2sdev->clock_disable, &args); + break; + case 1: + if (i2sdev->clock_enable) + return pmf_call_one(i2sdev->clock_enable, &args); + break; + default: + printk(KERN_ERR "i2sbus: INVALID CLOCK ENABLE VALUE\n"); + return -ENODEV; + } + switch (i2sdev->bus_number) { + case 0: + cc = in_le32(&c->controlregs->cell_control); + cc &= ~CTRL_CLOCK_CLOCK_0_ENABLE; + cc |= enable * CTRL_CLOCK_CLOCK_0_ENABLE; + out_le32(&c->controlregs->cell_control, cc); + break; + case 1: + cc = in_le32(&c->controlregs->cell_control); + cc &= ~CTRL_CLOCK_CLOCK_1_ENABLE; + cc |= enable * CTRL_CLOCK_CLOCK_1_ENABLE; + out_le32(&c->controlregs->cell_control, cc); + break; + default: + return -ENODEV; + } + return 0; +} diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-control.h b/sound/aoa/soundbus/i2sbus/i2sbus-control.h new file mode 100644 index 0000000..bb05550 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-control.h @@ -0,0 +1,37 @@ +/* + * i2sbus driver -- bus register definitions + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#ifndef __I2SBUS_CONTROLREGS_H +#define __I2SBUS_CONTROLREGS_H + +/* i2s control registers, at least what we know about them */ + +#define __PAD(m,n) u8 __pad##m[n] +#define _PAD(line, n) __PAD(line, n) +#define PAD(n) _PAD(__LINE__, (n)) +struct i2s_control_regs { + PAD(0x38); + __le32 fcr0; /* 0x38 (unknown) */ + __le32 cell_control; /* 0x3c (fcr1) */ + __le32 fcr2; /* 0x40 (unknown) */ + __le32 fcr3; /* 0x44 (fcr3) */ + __le32 clock_control; /* 0x48 (unknown) */ + PAD(4); + /* total size: 0x50 bytes */ +} __attribute__((__packed__)); + +#define CTRL_CLOCK_CELL_0_ENABLE (1<<10) +#define CTRL_CLOCK_CLOCK_0_ENABLE (1<<12) +#define CTRL_CLOCK_SWRESET_0 (1<<11) +#define CTRL_CLOCK_INTF_0_ENABLE (1<<13) + +#define CTRL_CLOCK_CELL_1_ENABLE (1<<17) +#define CTRL_CLOCK_CLOCK_1_ENABLE (1<<18) +#define CTRL_CLOCK_SWRESET_1 (1<<19) +#define CTRL_CLOCK_INTF_1_ENABLE (1<<20) + +#endif /* __I2SBUS_CONTROLREGS_H */ diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-core.c b/sound/aoa/soundbus/i2sbus/i2sbus-core.c new file mode 100644 index 0000000..01c0724 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-core.c @@ -0,0 +1,388 @@ +/* + * i2sbus driver + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../soundbus.h" +#include "i2sbus.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Johannes Berg "); +MODULE_DESCRIPTION("Apple Soundbus: I2S support"); +/* for auto-loading, declare that we handle this weird + * string that macio puts into the relevant device */ +MODULE_ALIAS("of:Ni2sTi2sC"); + +static struct of_device_id i2sbus_match[] = { + { .name = "i2s" }, + { } +}; + +static int alloc_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, + struct dbdma_command_mem *r, + int numcmds) +{ + /* one more for rounding */ + r->size = (numcmds+1) * sizeof(struct dbdma_cmd); + /* We use the PCI APIs for now until the generic one gets fixed + * enough or until we get some macio-specific versions + */ + r->space = dma_alloc_coherent( + &macio_get_pci_dev(i2sdev->macio)->dev, + r->size, + &r->bus_addr, + GFP_KERNEL); + + if (!r->space) return -ENOMEM; + + memset(r->space, 0, r->size); + r->cmds = (void*)DBDMA_ALIGN(r->space); + r->bus_cmd_start = r->bus_addr + + (dma_addr_t)((char*)r->cmds - (char*)r->space); + + return 0; +} + +static void free_dbdma_descriptor_ring(struct i2sbus_dev *i2sdev, + struct dbdma_command_mem *r) +{ + if (!r->space) return; + + dma_free_coherent(&macio_get_pci_dev(i2sdev->macio)->dev, + r->size, r->space, r->bus_addr); +} + +static void i2sbus_release_dev(struct device *dev) +{ + struct i2sbus_dev *i2sdev; + int i; + + i2sdev = container_of(dev, struct i2sbus_dev, sound.ofdev.dev); + + if (i2sdev->intfregs) iounmap(i2sdev->intfregs); + if (i2sdev->out.dbdma) iounmap(i2sdev->out.dbdma); + if (i2sdev->in.dbdma) iounmap(i2sdev->in.dbdma); + for (i=0;i<3;i++) + if (i2sdev->allocated_resource[i]) + release_and_free_resource(i2sdev->allocated_resource[i]); + free_dbdma_descriptor_ring(i2sdev, &i2sdev->out.dbdma_ring); + free_dbdma_descriptor_ring(i2sdev, &i2sdev->in.dbdma_ring); + for (i=0;i<3;i++) + free_irq(i2sdev->interrupts[i], i2sdev); + i2sbus_control_remove_dev(i2sdev->control, i2sdev); + mutex_destroy(&i2sdev->lock); + kfree(i2sdev); +} + +static irqreturn_t i2sbus_bus_intr(int irq, void *devid, struct pt_regs *regs) +{ + struct i2sbus_dev *dev = devid; + u32 intreg; + + spin_lock(&dev->low_lock); + intreg = in_le32(&dev->intfregs->intr_ctl); + + /* acknowledge interrupt reasons */ + out_le32(&dev->intfregs->intr_ctl, intreg); + + spin_unlock(&dev->low_lock); + + return IRQ_HANDLED; +} + +static int force; +module_param(force, int, 0444); +MODULE_PARM_DESC(force, "Force loading i2sbus even when" + " no layout-id property is present"); + +/* FIXME: look at device node refcounting */ +static int i2sbus_add_dev(struct macio_dev *macio, + struct i2sbus_control *control, + struct device_node *np) +{ + struct i2sbus_dev *dev; + struct device_node *child = NULL, *sound = NULL; + int i; + static const char *rnames[] = { "i2sbus: %s (control)", + "i2sbus: %s (tx)", + "i2sbus: %s (rx)" }; + static irqreturn_t (*ints[])(int irq, void *devid, + struct pt_regs *regs) = { + i2sbus_bus_intr, + i2sbus_tx_intr, + i2sbus_rx_intr + }; + + if (strlen(np->name) != 5) + return 0; + if (strncmp(np->name, "i2s-", 4)) + return 0; + + if (macio_irq_count(macio) != 3) + return 0; + + dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL); + if (!dev) + return 0; + + i = 0; + while ((child = of_get_next_child(np, child))) { + if (strcmp(child->name, "sound") == 0) { + i++; + sound = child; + } + } + if (i == 1) { + u32 *layout_id; + layout_id = (u32*) get_property(sound, "layout-id", NULL); + if (layout_id) { + snprintf(dev->sound.modalias, 32, + "sound-layout-%d", *layout_id); + force = 1; + } + } + /* for the time being, until we can handle non-layout-id + * things in some fabric, refuse to attach if there is no + * layout-id property or we haven't been forced to attach. + * When there are two i2s busses and only one has a layout-id, + * then this depends on the order, but that isn't important + * either as the second one in that case is just a modem. */ + if (!force) { + kfree(dev); + return -ENODEV; + } + + mutex_init(&dev->lock); + spin_lock_init(&dev->low_lock); + dev->sound.ofdev.node = np; + dev->sound.ofdev.dma_mask = macio->ofdev.dma_mask; + dev->sound.ofdev.dev.dma_mask = &dev->sound.ofdev.dma_mask; + dev->sound.ofdev.dev.parent = &macio->ofdev.dev; + dev->sound.ofdev.dev.release = i2sbus_release_dev; + dev->sound.attach_codec = i2sbus_attach_codec; + dev->sound.detach_codec = i2sbus_detach_codec; + dev->sound.pcmid = -1; + dev->macio = macio; + dev->control = control; + dev->bus_number = np->name[4] - 'a'; + INIT_LIST_HEAD(&dev->sound.codec_list); + + for (i=0;i<3;i++) { + dev->interrupts[i] = -1; + snprintf(dev->rnames[i], sizeof(dev->rnames[i]), rnames[i], np->name); + } + for (i=0;i<3;i++) { + if (request_irq(macio_irq(macio, i), ints[i], 0, + dev->rnames[i], dev)) + goto err; + dev->interrupts[i] = macio_irq(macio, i); + } + + for (i=0;i<3;i++) { + if (of_address_to_resource(np, i, &dev->resources[i])) + goto err; + /* if only we could use our resource dev->resources[i]... + * but request_resource doesn't know about parents and + * contained resources... */ + dev->allocated_resource[i] = + request_mem_region(dev->resources[i].start, + dev->resources[i].end - + dev->resources[i].start + 1, + dev->rnames[i]); + if (!dev->allocated_resource[i]) { + printk(KERN_ERR "i2sbus: failed to claim resource %d!\n", i); + goto err; + } + } + /* should do sanity checking here about length of them */ + dev->intfregs = ioremap(dev->resources[0].start, + dev->resources[0].end-dev->resources[0].start+1); + dev->out.dbdma = ioremap(dev->resources[1].start, + dev->resources[1].end-dev->resources[1].start+1); + dev->in.dbdma = ioremap(dev->resources[2].start, + dev->resources[2].end-dev->resources[2].start+1); + if (!dev->intfregs || !dev->out.dbdma || !dev->in.dbdma) + goto err; + + if (alloc_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring, + MAX_DBDMA_COMMANDS)) + goto err; + if (alloc_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring, + MAX_DBDMA_COMMANDS)) + goto err; + + if (i2sbus_control_add_dev(dev->control, dev)) { + printk(KERN_ERR "i2sbus: control layer didn't like bus\n"); + goto err; + } + + if (soundbus_add_one(&dev->sound)) { + printk(KERN_DEBUG "i2sbus: device registration error!\n"); + goto err; + } + + /* enable this cell */ + i2sbus_control_cell(dev->control, dev, 1); + i2sbus_control_enable(dev->control, dev); + i2sbus_control_clock(dev->control, dev, 1); + + return 1; + err: + for (i=0;i<3;i++) + if (dev->interrupts[i] != -1) + free_irq(dev->interrupts[i], dev); + free_dbdma_descriptor_ring(dev, &dev->out.dbdma_ring); + free_dbdma_descriptor_ring(dev, &dev->in.dbdma_ring); + if (dev->intfregs) iounmap(dev->intfregs); + if (dev->out.dbdma) iounmap(dev->out.dbdma); + if (dev->in.dbdma) iounmap(dev->in.dbdma); + for (i=0;i<3;i++) + if (dev->allocated_resource[i]) + release_and_free_resource(dev->allocated_resource[i]); + mutex_destroy(&dev->lock); + kfree(dev); + return 0; +} + +static int i2sbus_probe(struct macio_dev* dev, const struct of_device_id *match) +{ + struct device_node *np = NULL; + int got = 0, err; + struct i2sbus_control *control = NULL; + + err = i2sbus_control_init(dev, &control); + if (err) + return err; + if (!control) { + printk(KERN_ERR "i2sbus_control_init API breakage\n"); + return -ENODEV; + } + + while ((np = of_get_next_child(dev->ofdev.node, np))) { + if (device_is_compatible(np, "i2sbus") || + device_is_compatible(np, "i2s-modem")) { + got += i2sbus_add_dev(dev, control, np); + } + } + + if (!got) { + /* found none, clean up */ + i2sbus_control_destroy(control); + return -ENODEV; + } + + dev->ofdev.dev.driver_data = control; + + return 0; +} + +static int i2sbus_remove(struct macio_dev* dev) +{ + struct i2sbus_control *control = dev->ofdev.dev.driver_data; + struct i2sbus_dev *i2sdev, *tmp; + + list_for_each_entry_safe(i2sdev, tmp, &control->list, item) + soundbus_remove_one(&i2sdev->sound); + + return 0; +} + +#ifdef CONFIG_PM +static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state) +{ + struct i2sbus_control *control = dev->ofdev.dev.driver_data; + struct codec_info_item *cii; + struct i2sbus_dev* i2sdev; + int err, ret = 0; + + list_for_each_entry(i2sdev, &control->list, item) { + /* Notify Alsa */ + if (i2sdev->sound.pcm) { + /* Suspend PCM streams */ + snd_pcm_suspend_all(i2sdev->sound.pcm); + /* Probably useless as we handle + * power transitions ourselves */ + snd_power_change_state(i2sdev->sound.pcm->card, + SNDRV_CTL_POWER_D3hot); + } + /* Notify codecs */ + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { + err = 0; + if (cii->codec->suspend) + err = cii->codec->suspend(cii, state); + if (err) + ret = err; + } + } + return ret; +} + +static int i2sbus_resume(struct macio_dev* dev) +{ + struct i2sbus_control *control = dev->ofdev.dev.driver_data; + struct codec_info_item *cii; + struct i2sbus_dev* i2sdev; + int err, ret = 0; + + list_for_each_entry(i2sdev, &control->list, item) { + /* Notify codecs so they can re-initialize */ + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { + err = 0; + if (cii->codec->resume) + err = cii->codec->resume(cii); + if (err) + ret = err; + } + /* Notify Alsa */ + if (i2sdev->sound.pcm) { + /* Same comment as above, probably useless */ + snd_power_change_state(i2sdev->sound.pcm->card, + SNDRV_CTL_POWER_D0); + } + } + + return ret; +} +#endif /* CONFIG_PM */ + +static int i2sbus_shutdown(struct macio_dev* dev) +{ + return 0; +} + +static struct macio_driver i2sbus_drv = { + .name = "soundbus-i2s", + .owner = THIS_MODULE, + .match_table = i2sbus_match, + .probe = i2sbus_probe, + .remove = i2sbus_remove, +#ifdef CONFIG_PM + .suspend = i2sbus_suspend, + .resume = i2sbus_resume, +#endif + .shutdown = i2sbus_shutdown, +}; + +static int __init soundbus_i2sbus_init(void) +{ + return macio_register_driver(&i2sbus_drv); +} + +static void __exit soundbus_i2sbus_exit(void) +{ + macio_unregister_driver(&i2sbus_drv); +} + +module_init(soundbus_i2sbus_init); +module_exit(soundbus_i2sbus_exit); diff --git a/sound/aoa/soundbus/i2sbus/i2sbus-interface.h b/sound/aoa/soundbus/i2sbus/i2sbus-interface.h new file mode 100644 index 0000000..c6b5f54 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus-interface.h @@ -0,0 +1,187 @@ +/* + * i2sbus driver -- interface register definitions + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#ifndef __I2SBUS_INTERFACE_H +#define __I2SBUS_INTERFACE_H + +/* i2s bus control registers, at least what we know about them */ + +#define __PAD(m,n) u8 __pad##m[n] +#define _PAD(line, n) __PAD(line, n) +#define PAD(n) _PAD(__LINE__, (n)) +struct i2s_interface_regs { + __le32 intr_ctl; /* 0x00 */ + PAD(12); + __le32 serial_format; /* 0x10 */ + PAD(12); + __le32 codec_msg_out; /* 0x20 */ + PAD(12); + __le32 codec_msg_in; /* 0x30 */ + PAD(12); + __le32 frame_count; /* 0x40 */ + PAD(12); + __le32 frame_match; /* 0x50 */ + PAD(12); + __le32 data_word_sizes; /* 0x60 */ + PAD(12); + __le32 peak_level_sel; /* 0x70 */ + PAD(12); + __le32 peak_level_in0; /* 0x80 */ + PAD(12); + __le32 peak_level_in1; /* 0x90 */ + PAD(12); + /* total size: 0x100 bytes */ +} __attribute__((__packed__)); + +/* interrupt register is just a bitfield with + * interrupt enable and pending bits */ +#define I2S_REG_INTR_CTL 0x00 +# define I2S_INT_FRAME_COUNT (1<<31) +# define I2S_PENDING_FRAME_COUNT (1<<30) +# define I2S_INT_MESSAGE_FLAG (1<<29) +# define I2S_PENDING_MESSAGE_FLAG (1<<28) +# define I2S_INT_NEW_PEAK (1<<27) +# define I2S_PENDING_NEW_PEAK (1<<26) +# define I2S_INT_CLOCKS_STOPPED (1<<25) +# define I2S_PENDING_CLOCKS_STOPPED (1<<24) +# define I2S_INT_EXTERNAL_SYNC_ERROR (1<<23) +# define I2S_PENDING_EXTERNAL_SYNC_ERROR (1<<22) +# define I2S_INT_EXTERNAL_SYNC_OK (1<<21) +# define I2S_PENDING_EXTERNAL_SYNC_OK (1<<20) +# define I2S_INT_NEW_SAMPLE_RATE (1<<19) +# define I2S_PENDING_NEW_SAMPLE_RATE (1<<18) +# define I2S_INT_STATUS_FLAG (1<<17) +# define I2S_PENDING_STATUS_FLAG (1<<16) + +/* serial format register is more interesting :) + * It contains: + * - clock source + * - MClk divisor + * - SClk divisor + * - SClk master flag + * - serial format (sony, i2s 64x, i2s 32x, dav, silabs) + * - external sample frequency interrupt (don't understand) + * - external sample frequency + */ +#define I2S_REG_SERIAL_FORMAT 0x10 +/* clock source. You get either 18.432, 45.1584 or 49.1520 MHz */ +# define I2S_SF_CLOCK_SOURCE_SHIFT 30 +# define I2S_SF_CLOCK_SOURCE_MASK (3< + * + * GPL v2, can be found in COPYING. + */ + +#include +#include +/* So apparently there's a reason for requiring driver.h + * to be included first, even if I don't know it... */ +#include +#include +#include +#include +#include "../soundbus.h" +#include "i2sbus.h" + +static inline void get_pcm_info(struct i2sbus_dev *i2sdev, int in, + struct pcm_info **pi, struct pcm_info **other) +{ + if (in) { + if (pi) + *pi = &i2sdev->in; + if (other) + *other = &i2sdev->out; + } else { + if (pi) + *pi = &i2sdev->out; + if (other) + *other = &i2sdev->in; + } +} + +static int clock_and_divisors(int mclk, int sclk, int rate, int *out) +{ + /* sclk must be derived from mclk! */ + if (mclk % sclk) + return -1; + /* derive sclk register value */ + if (i2s_sf_sclkdiv(mclk / sclk, out)) + return -1; + + if (I2S_CLOCK_SPEED_18MHz % (rate * mclk) == 0) { + if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_18MHz / (rate * mclk), out)) { + *out |= I2S_SF_CLOCK_SOURCE_18MHz; + return 0; + } + } + if (I2S_CLOCK_SPEED_45MHz % (rate * mclk) == 0) { + if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_45MHz / (rate * mclk), out)) { + *out |= I2S_SF_CLOCK_SOURCE_45MHz; + return 0; + } + } + if (I2S_CLOCK_SPEED_49MHz % (rate * mclk) == 0) { + if (!i2s_sf_mclkdiv(I2S_CLOCK_SPEED_49MHz / (rate * mclk), out)) { + *out |= I2S_SF_CLOCK_SOURCE_49MHz; + return 0; + } + } + return -1; +} + +#define CHECK_RATE(rate) \ + do { if (rates & SNDRV_PCM_RATE_ ##rate) { \ + int dummy; \ + if (clock_and_divisors(sysclock_factor, \ + bus_factor, rate, &dummy)) \ + rates &= ~SNDRV_PCM_RATE_ ##rate; \ + } } while (0) + +static int i2sbus_pcm_open(struct i2sbus_dev *i2sdev, int in) +{ + struct pcm_info *pi, *other; + struct soundbus_dev *sdev; + int masks_inited = 0, err; + struct codec_info_item *cii, *rev; + struct snd_pcm_hardware *hw; + u64 formats = 0; + unsigned int rates = 0; + struct transfer_info v; + int result = 0; + int bus_factor = 0, sysclock_factor = 0; + int found_this; + + mutex_lock(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, &other); + + hw = &pi->substream->runtime->hw; + sdev = &i2sdev->sound; + + if (pi->active) { + /* alsa messed up */ + result = -EBUSY; + goto out_unlock; + } + + /* we now need to assign the hw */ + list_for_each_entry(cii, &sdev->codec_list, list) { + struct transfer_info *ti = cii->codec->transfers; + bus_factor = cii->codec->bus_factor; + sysclock_factor = cii->codec->sysclock_factor; + while (ti->formats && ti->rates) { + v = *ti; + if (ti->transfer_in == in + && cii->codec->usable(cii, ti, &v)) { + if (masks_inited) { + formats &= v.formats; + rates &= v.rates; + } else { + formats = v.formats; + rates = v.rates; + masks_inited = 1; + } + } + ti++; + } + } + if (!masks_inited || !bus_factor || !sysclock_factor) { + result = -ENODEV; + goto out_unlock; + } + /* bus dependent stuff */ + hw->info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME; + + CHECK_RATE(5512); + CHECK_RATE(8000); + CHECK_RATE(11025); + CHECK_RATE(16000); + CHECK_RATE(22050); + CHECK_RATE(32000); + CHECK_RATE(44100); + CHECK_RATE(48000); + CHECK_RATE(64000); + CHECK_RATE(88200); + CHECK_RATE(96000); + CHECK_RATE(176400); + CHECK_RATE(192000); + hw->rates = rates; + + /* well. the codec might want 24 bits only, and we'll + * ever only transfer 24 bits, but they are top-aligned! + * So for alsa, we claim that we're doing full 32 bit + * while in reality we'll ignore the lower 8 bits of + * that when doing playback (they're transferred as 0 + * as far as I know, no codecs we have are 32-bit capable + * so I can't really test) and when doing recording we'll + * always have those lower 8 bits recorded as 0 */ + if (formats & SNDRV_PCM_FMTBIT_S24_BE) + formats |= SNDRV_PCM_FMTBIT_S32_BE; + if (formats & SNDRV_PCM_FMTBIT_U24_BE) + formats |= SNDRV_PCM_FMTBIT_U32_BE; + /* now mask off what we can support. I suppose we could + * also support S24_3LE and some similar formats, but I + * doubt there's a codec that would be able to use that, + * so we don't support it here. */ + hw->formats = formats & (SNDRV_PCM_FMTBIT_S16_BE | + SNDRV_PCM_FMTBIT_U16_BE | + SNDRV_PCM_FMTBIT_S32_BE | + SNDRV_PCM_FMTBIT_U32_BE); + + /* we need to set the highest and lowest rate possible. + * These are the highest and lowest rates alsa can + * support properly in its bitfield. + * Below, we'll use that to restrict to the rate + * currently in use (if any). */ + hw->rate_min = 5512; + hw->rate_max = 192000; + /* if the other stream is active, then we can only + * support what it is currently using. + * FIXME: I lied. This comment is wrong. We can support + * anything that works with the same serial format, ie. + * when recording 24 bit sound we can well play 16 bit + * sound at the same time iff using the same transfer mode. + */ + if (other->active) { + /* FIXME: is this guaranteed by the alsa api? */ + hw->formats &= (1ULL << i2sdev->format); + /* see above, restrict rates to the one we already have */ + hw->rate_min = i2sdev->rate; + hw->rate_max = i2sdev->rate; + } + + hw->channels_min = 2; + hw->channels_max = 2; + /* these are somewhat arbitrary */ + hw->buffer_bytes_max = 131072; + hw->period_bytes_min = 256; + hw->period_bytes_max = 16384; + hw->periods_min = 3; + hw->periods_max = MAX_DBDMA_COMMANDS; + list_for_each_entry(cii, &sdev->codec_list, list) { + if (cii->codec->open) { + err = cii->codec->open(cii, pi->substream); + if (err) { + result = err; + /* unwind */ + found_this = 0; + list_for_each_entry_reverse(rev, + &sdev->codec_list, list) { + if (found_this && rev->codec->close) { + rev->codec->close(rev, + pi->substream); + } + if (rev == cii) + found_this = 1; + } + goto out_unlock; + } + } + } + + out_unlock: + mutex_unlock(&i2sdev->lock); + return result; +} + +#undef CHECK_RATE + +static int i2sbus_pcm_close(struct i2sbus_dev *i2sdev, int in) +{ + struct codec_info_item *cii; + struct pcm_info *pi; + int err = 0, tmp; + + mutex_lock(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, NULL); + + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { + if (cii->codec->close) { + tmp = cii->codec->close(cii, pi->substream); + if (tmp) + err = tmp; + } + } + + pi->substream = NULL; + pi->active = 0; + mutex_unlock(&i2sdev->lock); + return err; +} + +static int i2sbus_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); +} + +static int i2sbus_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int i2sbus_pcm_prepare(struct i2sbus_dev *i2sdev, int in) +{ + /* whee. Hard work now. The user has selected a bitrate + * and bit format, so now we have to program our + * I2S controller appropriately. */ + struct snd_pcm_runtime *runtime; + struct dbdma_cmd *command; + int i, periodsize; + dma_addr_t offset; + struct bus_info bi; + struct codec_info_item *cii; + int sfr = 0; /* serial format register */ + int dws = 0; /* data word sizes reg */ + int input_16bit; + struct pcm_info *pi, *other; + int cnt; + int result = 0; + + mutex_lock(&i2sdev->lock); + + get_pcm_info(i2sdev, in, &pi, &other); + + if (pi->dbdma_ring.running) { + result = -EBUSY; + goto out_unlock; + } + + runtime = pi->substream->runtime; + pi->active = 1; + if (other->active && + ((i2sdev->format != runtime->format) + || (i2sdev->rate != runtime->rate))) { + result = -EINVAL; + goto out_unlock; + } + + i2sdev->format = runtime->format; + i2sdev->rate = runtime->rate; + + periodsize = snd_pcm_lib_period_bytes(pi->substream); + pi->current_period = 0; + + /* generate dbdma command ring first */ + command = pi->dbdma_ring.cmds; + offset = runtime->dma_addr; + for (i = 0; i < pi->substream->runtime->periods; + i++, command++, offset += periodsize) { + memset(command, 0, sizeof(struct dbdma_cmd)); + command->command = + cpu_to_le16((in ? INPUT_MORE : OUTPUT_MORE) | INTR_ALWAYS); + command->phy_addr = cpu_to_le32(offset); + command->req_count = cpu_to_le16(periodsize); + command->xfer_status = cpu_to_le16(0); + } + /* last one branches back to first */ + command--; + command->command |= cpu_to_le16(BR_ALWAYS); + command->cmd_dep = cpu_to_le32(pi->dbdma_ring.bus_cmd_start); + + /* ok, let's set the serial format and stuff */ + switch (runtime->format) { + /* 16 bit formats */ + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_BE: + /* FIXME: if we add different bus factors we need to + * do more here!! */ + bi.bus_factor = 0; + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { + bi.bus_factor = cii->codec->bus_factor; + break; + } + if (!bi.bus_factor) { + result = -ENODEV; + goto out_unlock; + } + input_16bit = 1; + break; + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_BE: + /* force 64x bus speed, otherwise the data cannot be + * transferred quickly enough! */ + bi.bus_factor = 64; + input_16bit = 0; + break; + default: + result = -EINVAL; + goto out_unlock; + } + /* we assume all sysclocks are the same! */ + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { + bi.sysclock_factor = cii->codec->sysclock_factor; + break; + } + + if (clock_and_divisors(bi.sysclock_factor, + bi.bus_factor, + runtime->rate, + &sfr) < 0) { + result = -EINVAL; + goto out_unlock; + } + switch (bi.bus_factor) { + case 32: + sfr |= I2S_SF_SERIAL_FORMAT_I2S_32X; + break; + case 64: + sfr |= I2S_SF_SERIAL_FORMAT_I2S_64X; + break; + } + /* FIXME: THIS ASSUMES MASTER ALL THE TIME */ + sfr |= I2S_SF_SCLK_MASTER; + + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) { + int err = 0; + if (cii->codec->prepare) + err = cii->codec->prepare(cii, &bi, pi->substream); + if (err) { + result = err; + goto out_unlock; + } + } + /* codecs are fine with it, so set our clocks */ + if (input_16bit) + dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | + (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | + I2S_DWS_DATA_IN_16BIT | I2S_DWS_DATA_OUT_16BIT; + else + dws = (2 << I2S_DWS_NUM_CHANNELS_IN_SHIFT) | + (2 << I2S_DWS_NUM_CHANNELS_OUT_SHIFT) | + I2S_DWS_DATA_IN_24BIT | I2S_DWS_DATA_OUT_24BIT; + + /* early exit if already programmed correctly */ + /* not locking these is fine since we touch them only in this function */ + if (in_le32(&i2sdev->intfregs->serial_format) == sfr + && in_le32(&i2sdev->intfregs->data_word_sizes) == dws) + goto out_unlock; + + /* let's notify the codecs about clocks going away. + * For now we only do mastering on the i2s cell... */ + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) + if (cii->codec->switch_clock) + cii->codec->switch_clock(cii, CLOCK_SWITCH_PREPARE_SLAVE); + + i2sbus_control_enable(i2sdev->control, i2sdev); + i2sbus_control_cell(i2sdev->control, i2sdev, 1); + + out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); + + i2sbus_control_clock(i2sdev->control, i2sdev, 0); + + msleep(1); + + /* wait for clock stopped. This can apparently take a while... */ + cnt = 100; + while (cnt-- && + !(in_le32(&i2sdev->intfregs->intr_ctl) & I2S_PENDING_CLOCKS_STOPPED)) { + msleep(5); + } + out_le32(&i2sdev->intfregs->intr_ctl, I2S_PENDING_CLOCKS_STOPPED); + + /* not locking these is fine since we touch them only in this function */ + out_le32(&i2sdev->intfregs->serial_format, sfr); + out_le32(&i2sdev->intfregs->data_word_sizes, dws); + + i2sbus_control_enable(i2sdev->control, i2sdev); + i2sbus_control_cell(i2sdev->control, i2sdev, 1); + i2sbus_control_clock(i2sdev->control, i2sdev, 1); + msleep(1); + + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) + if (cii->codec->switch_clock) + cii->codec->switch_clock(cii, CLOCK_SWITCH_SLAVE); + + out_unlock: + mutex_unlock(&i2sdev->lock); + return result; +} + +static struct dbdma_cmd STOP_CMD = { + .command = __constant_cpu_to_le16(DBDMA_STOP), +}; + +static int i2sbus_pcm_trigger(struct i2sbus_dev *i2sdev, int in, int cmd) +{ + struct codec_info_item *cii; + struct pcm_info *pi; + int timeout; + struct dbdma_cmd tmp; + int result = 0; + unsigned long flags; + + spin_lock_irqsave(&i2sdev->low_lock, flags); + + get_pcm_info(i2sdev, in, &pi, NULL); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + if (pi->dbdma_ring.running) { + result = -EALREADY; + goto out_unlock; + } + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) + if (cii->codec->start) + cii->codec->start(cii, pi->substream); + pi->dbdma_ring.running = 1; + + /* reset dma engine */ + out_le32(&pi->dbdma->control, + 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); + timeout = 100; + while (in_le32(&pi->dbdma->status) & RUN && timeout--) + udelay(1); + if (timeout <= 0) { + printk(KERN_ERR + "i2sbus: error waiting for dma reset\n"); + result = -ENXIO; + goto out_unlock; + } + + /* write dma command buffer address to the dbdma chip */ + out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); + /* post PCI write */ + mb(); + (void)in_le32(&pi->dbdma->status); + + /* change first command to STOP */ + tmp = *pi->dbdma_ring.cmds; + *pi->dbdma_ring.cmds = STOP_CMD; + + /* set running state, remember that the first command is STOP */ + out_le32(&pi->dbdma->control, RUN | (RUN << 16)); + timeout = 100; + /* wait for STOP to be executed */ + while (in_le32(&pi->dbdma->status) & ACTIVE && timeout--) + udelay(1); + if (timeout <= 0) { + printk(KERN_ERR "i2sbus: error waiting for dma stop\n"); + result = -ENXIO; + goto out_unlock; + } + /* again, write dma command buffer address to the dbdma chip, + * this time of the first real command */ + *pi->dbdma_ring.cmds = tmp; + out_le32(&pi->dbdma->cmdptr, pi->dbdma_ring.bus_cmd_start); + /* post write */ + mb(); + (void)in_le32(&pi->dbdma->status); + + /* reset dma engine again */ + out_le32(&pi->dbdma->control, + 0 | (RUN | PAUSE | FLUSH | WAKE) << 16); + timeout = 100; + while (in_le32(&pi->dbdma->status) & RUN && timeout--) + udelay(1); + if (timeout <= 0) { + printk(KERN_ERR + "i2sbus: error waiting for dma reset\n"); + result = -ENXIO; + goto out_unlock; + } + + /* wake up the chip with the next descriptor */ + out_le32(&pi->dbdma->control, + (RUN | WAKE) | ((RUN | WAKE) << 16)); + /* get the frame count */ + pi->frame_count = in_le32(&i2sdev->intfregs->frame_count); + + /* off you go! */ + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (!pi->dbdma_ring.running) { + result = -EALREADY; + goto out_unlock; + } + + /* turn off all relevant bits */ + out_le32(&pi->dbdma->control, + (RUN | WAKE | FLUSH | PAUSE) << 16); + { + /* FIXME: move to own function */ + int timeout = 5000; + while ((in_le32(&pi->dbdma->status) & RUN) + && --timeout > 0) + udelay(1); + if (!timeout) + printk(KERN_ERR + "i2sbus: timed out turning " + "off dbdma engine!\n"); + } + + pi->dbdma_ring.running = 0; + list_for_each_entry(cii, &i2sdev->sound.codec_list, list) + if (cii->codec->stop) + cii->codec->stop(cii, pi->substream); + break; + default: + result = -EINVAL; + goto out_unlock; + } + + out_unlock: + spin_unlock_irqrestore(&i2sdev->low_lock, flags); + return result; +} + +static snd_pcm_uframes_t i2sbus_pcm_pointer(struct i2sbus_dev *i2sdev, int in) +{ + struct pcm_info *pi; + u32 fc; + + get_pcm_info(i2sdev, in, &pi, NULL); + + fc = in_le32(&i2sdev->intfregs->frame_count); + fc = fc - pi->frame_count; + + return (bytes_to_frames(pi->substream->runtime, + pi->current_period * + snd_pcm_lib_period_bytes(pi->substream)) + + fc) % pi->substream->runtime->buffer_size; +} + +static inline void handle_interrupt(struct i2sbus_dev *i2sdev, int in) +{ + struct pcm_info *pi; + u32 fc; + u32 delta; + + spin_lock(&i2sdev->low_lock); + get_pcm_info(i2sdev, in, &pi, NULL); + + if (!pi->dbdma_ring.running) { + /* there was still an interrupt pending + * while we stopped. or maybe another + * processor (not the one that was stopping + * the DMA engine) was spinning above + * waiting for the lock. */ + goto out_unlock; + } + + fc = in_le32(&i2sdev->intfregs->frame_count); + /* a counter overflow does not change the calculation. */ + delta = fc - pi->frame_count; + + /* update current_period */ + while (delta >= pi->substream->runtime->period_size) { + pi->current_period++; + delta = delta - pi->substream->runtime->period_size; + } + + if (unlikely(delta)) { + /* Some interrupt came late, so check the dbdma. + * This special case exists to syncronize the frame_count with + * the dbdma transfer, but is hit every once in a while. */ + int period; + + period = (in_le32(&pi->dbdma->cmdptr) + - pi->dbdma_ring.bus_cmd_start) + / sizeof(struct dbdma_cmd); + pi->current_period = pi->current_period + % pi->substream->runtime->periods; + + while (pi->current_period != period) { + pi->current_period++; + pi->current_period %= pi->substream->runtime->periods; + /* Set delta to zero, as the frame_count value is too + * high (otherwise the code path will not be executed). + * This corrects the fact that the frame_count is too + * low at the beginning due to buffering. */ + delta = 0; + } + } + + pi->frame_count = fc - delta; + pi->current_period %= pi->substream->runtime->periods; + + spin_unlock(&i2sdev->low_lock); + /* may call _trigger again, hence needs to be unlocked */ + snd_pcm_period_elapsed(pi->substream); + return; + out_unlock: + spin_unlock(&i2sdev->low_lock); +} + +irqreturn_t i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs) +{ + handle_interrupt((struct i2sbus_dev *)devid, 0); + return IRQ_HANDLED; +} + +irqreturn_t i2sbus_rx_intr(int irq, void *devid, struct pt_regs * regs) +{ + handle_interrupt((struct i2sbus_dev *)devid, 1); + return IRQ_HANDLED; +} + +static int i2sbus_playback_open(struct snd_pcm_substream *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + i2sdev->out.substream = substream; + return i2sbus_pcm_open(i2sdev, 0); +} + +static int i2sbus_playback_close(struct snd_pcm_substream *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + int err; + + if (!i2sdev) + return -EINVAL; + if (i2sdev->out.substream != substream) + return -EINVAL; + err = i2sbus_pcm_close(i2sdev, 0); + if (!err) + i2sdev->out.substream = NULL; + return err; +} + +static int i2sbus_playback_prepare(struct snd_pcm_substream *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + if (i2sdev->out.substream != substream) + return -EINVAL; + return i2sbus_pcm_prepare(i2sdev, 0); +} + +static int i2sbus_playback_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + if (i2sdev->out.substream != substream) + return -EINVAL; + return i2sbus_pcm_trigger(i2sdev, 0, cmd); +} + +static snd_pcm_uframes_t i2sbus_playback_pointer(struct snd_pcm_substream + *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + if (i2sdev->out.substream != substream) + return 0; + return i2sbus_pcm_pointer(i2sdev, 0); +} + +static struct snd_pcm_ops i2sbus_playback_ops = { + .open = i2sbus_playback_open, + .close = i2sbus_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = i2sbus_hw_params, + .hw_free = i2sbus_hw_free, + .prepare = i2sbus_playback_prepare, + .trigger = i2sbus_playback_trigger, + .pointer = i2sbus_playback_pointer, +}; + +static int i2sbus_record_open(struct snd_pcm_substream *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + i2sdev->in.substream = substream; + return i2sbus_pcm_open(i2sdev, 1); +} + +static int i2sbus_record_close(struct snd_pcm_substream *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + int err; + + if (!i2sdev) + return -EINVAL; + if (i2sdev->in.substream != substream) + return -EINVAL; + err = i2sbus_pcm_close(i2sdev, 1); + if (!err) + i2sdev->in.substream = NULL; + return err; +} + +static int i2sbus_record_prepare(struct snd_pcm_substream *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + if (i2sdev->in.substream != substream) + return -EINVAL; + return i2sbus_pcm_prepare(i2sdev, 1); +} + +static int i2sbus_record_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + if (i2sdev->in.substream != substream) + return -EINVAL; + return i2sbus_pcm_trigger(i2sdev, 1, cmd); +} + +static snd_pcm_uframes_t i2sbus_record_pointer(struct snd_pcm_substream + *substream) +{ + struct i2sbus_dev *i2sdev = snd_pcm_substream_chip(substream); + + if (!i2sdev) + return -EINVAL; + if (i2sdev->in.substream != substream) + return 0; + return i2sbus_pcm_pointer(i2sdev, 1); +} + +static struct snd_pcm_ops i2sbus_record_ops = { + .open = i2sbus_record_open, + .close = i2sbus_record_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = i2sbus_hw_params, + .hw_free = i2sbus_hw_free, + .prepare = i2sbus_record_prepare, + .trigger = i2sbus_record_trigger, + .pointer = i2sbus_record_pointer, +}; + +static void i2sbus_private_free(struct snd_pcm *pcm) +{ + struct i2sbus_dev *i2sdev = snd_pcm_chip(pcm); + struct codec_info_item *p, *tmp; + + i2sdev->sound.pcm = NULL; + i2sdev->out.created = 0; + i2sdev->in.created = 0; + list_for_each_entry_safe(p, tmp, &i2sdev->sound.codec_list, list) { + printk(KERN_ERR "i2sbus: a codec didn't unregister!\n"); + list_del(&p->list); + module_put(p->codec->owner); + kfree(p); + } + soundbus_dev_put(&i2sdev->sound); + module_put(THIS_MODULE); +} + +/* FIXME: this function needs an error handling strategy with labels */ +int +i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, + struct codec_info *ci, void *data) +{ + int err, in = 0, out = 0; + struct transfer_info *tmp; + struct i2sbus_dev *i2sdev = soundbus_dev_to_i2sbus_dev(dev); + struct codec_info_item *cii; + + if (!dev->pcmname || dev->pcmid == -1) { + printk(KERN_ERR "i2sbus: pcm name and id must be set!\n"); + return -EINVAL; + } + + list_for_each_entry(cii, &dev->codec_list, list) { + if (cii->codec_data == data) + return -EALREADY; + } + + if (!ci->transfers || !ci->transfers->formats + || !ci->transfers->rates || !ci->usable) + return -EINVAL; + + /* we currently code the i2s transfer on the clock, and support only + * 32 and 64 */ + if (ci->bus_factor != 32 && ci->bus_factor != 64) + return -EINVAL; + + /* If you want to fix this, you need to keep track of what transport infos + * are to be used, which codecs they belong to, and then fix all the + * sysclock/busclock stuff above to depend on which is usable */ + list_for_each_entry(cii, &dev->codec_list, list) { + if (cii->codec->sysclock_factor != ci->sysclock_factor) { + printk(KERN_DEBUG + "cannot yet handle multiple different sysclocks!\n"); + return -EINVAL; + } + if (cii->codec->bus_factor != ci->bus_factor) { + printk(KERN_DEBUG + "cannot yet handle multiple different bus clocks!\n"); + return -EINVAL; + } + } + + tmp = ci->transfers; + while (tmp->formats && tmp->rates) { + if (tmp->transfer_in) + in = 1; + else + out = 1; + tmp++; + } + + cii = kzalloc(sizeof(struct codec_info_item), GFP_KERNEL); + if (!cii) { + printk(KERN_DEBUG "i2sbus: failed to allocate cii\n"); + return -ENOMEM; + } + + /* use the private data to point to the codec info */ + cii->sdev = soundbus_dev_get(dev); + cii->codec = ci; + cii->codec_data = data; + + if (!cii->sdev) { + printk(KERN_DEBUG + "i2sbus: failed to get soundbus dev reference\n"); + kfree(cii); + return -ENODEV; + } + + if (!try_module_get(THIS_MODULE)) { + printk(KERN_DEBUG "i2sbus: failed to get module reference!\n"); + soundbus_dev_put(dev); + kfree(cii); + return -EBUSY; + } + + if (!try_module_get(ci->owner)) { + printk(KERN_DEBUG + "i2sbus: failed to get module reference to codec owner!\n"); + module_put(THIS_MODULE); + soundbus_dev_put(dev); + kfree(cii); + return -EBUSY; + } + + if (!dev->pcm) { + err = snd_pcm_new(card, + dev->pcmname, + dev->pcmid, + 0, + 0, + &dev->pcm); + if (err) { + printk(KERN_DEBUG "i2sbus: failed to create pcm\n"); + kfree(cii); + module_put(ci->owner); + soundbus_dev_put(dev); + module_put(THIS_MODULE); + return err; + } + } + + /* ALSA yet again sucks. + * If it is ever fixed, remove this line. See below. */ + out = in = 1; + + if (!i2sdev->out.created && out) { + if (dev->pcm->card != card) { + /* eh? */ + printk(KERN_ERR + "Can't attach same bus to different cards!\n"); + module_put(ci->owner); + kfree(cii); + soundbus_dev_put(dev); + module_put(THIS_MODULE); + return -EINVAL; + } + if ((err = + snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 1))) { + module_put(ci->owner); + kfree(cii); + soundbus_dev_put(dev); + module_put(THIS_MODULE); + return err; + } + snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, + &i2sbus_playback_ops); + i2sdev->out.created = 1; + } + + if (!i2sdev->in.created && in) { + if (dev->pcm->card != card) { + printk(KERN_ERR + "Can't attach same bus to different cards!\n"); + module_put(ci->owner); + kfree(cii); + soundbus_dev_put(dev); + module_put(THIS_MODULE); + return -EINVAL; + } + if ((err = + snd_pcm_new_stream(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 1))) { + module_put(ci->owner); + kfree(cii); + soundbus_dev_put(dev); + module_put(THIS_MODULE); + return err; + } + snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, + &i2sbus_record_ops); + i2sdev->in.created = 1; + } + + /* so we have to register the pcm after adding any substream + * to it because alsa doesn't create the devices for the + * substreams when we add them later. + * Therefore, force in and out on both busses (above) and + * register the pcm now instead of just after creating it. + */ + err = snd_device_register(card, dev->pcm); + if (err) { + printk(KERN_ERR "i2sbus: error registering new pcm\n"); + module_put(ci->owner); + kfree(cii); + soundbus_dev_put(dev); + module_put(THIS_MODULE); + return err; + } + /* no errors any more, so let's add this to our list */ + list_add(&cii->list, &dev->codec_list); + + dev->pcm->private_data = i2sdev; + dev->pcm->private_free = i2sbus_private_free; + + /* well, we really should support scatter/gather DMA */ + snd_pcm_lib_preallocate_pages_for_all( + dev->pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(macio_get_pci_dev(i2sdev->macio)), + 64 * 1024, 64 * 1024); + + return 0; +} + +void i2sbus_detach_codec(struct soundbus_dev *dev, void *data) +{ + struct codec_info_item *cii = NULL, *i; + + list_for_each_entry(i, &dev->codec_list, list) { + if (i->codec_data == data) { + cii = i; + break; + } + } + if (cii) { + list_del(&cii->list); + module_put(cii->codec->owner); + kfree(cii); + } + /* no more codecs, but still a pcm? */ + if (list_empty(&dev->codec_list) && dev->pcm) { + /* the actual cleanup is done by the callback above! */ + snd_device_free(dev->pcm->card, dev->pcm); + } +} diff --git a/sound/aoa/soundbus/i2sbus/i2sbus.h b/sound/aoa/soundbus/i2sbus/i2sbus.h new file mode 100644 index 0000000..cfa5162 --- /dev/null +++ b/sound/aoa/soundbus/i2sbus/i2sbus.h @@ -0,0 +1,112 @@ +/* + * i2sbus driver -- private definitions + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#ifndef __I2SBUS_H +#define __I2SBUS_H +#include +#include +#include +#include +#include +#include +#include "i2sbus-interface.h" +#include "i2sbus-control.h" +#include "../soundbus.h" + +struct i2sbus_control { + volatile struct i2s_control_regs __iomem *controlregs; + struct resource rsrc; + struct list_head list; +}; + +#define MAX_DBDMA_COMMANDS 32 + +struct dbdma_command_mem { + dma_addr_t bus_addr; + dma_addr_t bus_cmd_start; + struct dbdma_cmd *cmds; + void *space; + int size; + u32 running:1; +}; + +struct pcm_info { + u32 created:1, /* has this direction been created with alsa? */ + active:1; /* is this stream active? */ + /* runtime information */ + struct snd_pcm_substream *substream; + int current_period; + u32 frame_count; + struct dbdma_command_mem dbdma_ring; + volatile struct dbdma_regs __iomem *dbdma; +}; + +struct i2sbus_dev { + struct soundbus_dev sound; + struct macio_dev *macio; + struct i2sbus_control *control; + volatile struct i2s_interface_regs __iomem *intfregs; + + struct resource resources[3]; + struct resource *allocated_resource[3]; + int interrupts[3]; + char rnames[3][32]; + + /* info about currently active substreams */ + struct pcm_info out, in; + snd_pcm_format_t format; + unsigned int rate; + + /* list for a single controller */ + struct list_head item; + /* number of bus on controller */ + int bus_number; + /* for use by control layer */ + struct pmf_function *enable, + *cell_enable, + *cell_disable, + *clock_enable, + *clock_disable; + + /* locks */ + /* spinlock for low-level interrupt locking */ + spinlock_t low_lock; + /* mutex for high-level consistency */ + struct mutex lock; +}; + +#define soundbus_dev_to_i2sbus_dev(sdev) \ + container_of(sdev, struct i2sbus_dev, sound) + +/* pcm specific functions */ +extern int +i2sbus_attach_codec(struct soundbus_dev *dev, struct snd_card *card, + struct codec_info *ci, void *data); +extern void +i2sbus_detach_codec(struct soundbus_dev *dev, void *data); +extern irqreturn_t +i2sbus_tx_intr(int irq, void *devid, struct pt_regs *regs); +extern irqreturn_t +i2sbus_rx_intr(int irq, void *devid, struct pt_regs *regs); + +/* control specific functions */ +extern int i2sbus_control_init(struct macio_dev* dev, + struct i2sbus_control **c); +extern void i2sbus_control_destroy(struct i2sbus_control *c); +extern int i2sbus_control_add_dev(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev); +extern void i2sbus_control_remove_dev(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev); +extern int i2sbus_control_enable(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev); +extern int i2sbus_control_cell(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev, + int enable); +extern int i2sbus_control_clock(struct i2sbus_control *c, + struct i2sbus_dev *i2sdev, + int enable); +#endif /* __I2SBUS_H */ diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h new file mode 100644 index 0000000..5c27297 --- /dev/null +++ b/sound/aoa/soundbus/soundbus.h @@ -0,0 +1,202 @@ +/* + * soundbus generic definitions + * + * Copyright 2006 Johannes Berg + * + * GPL v2, can be found in COPYING. + */ +#ifndef __SOUNDBUS_H +#define __SOUNDBUS_H + +#include +#include +#include + + +/* When switching from master to slave or the other way around, + * you don't want to have the codec chip acting as clock source + * while the bus still is. + * More importantly, while switch from slave to master, you need + * to turn off the chip's master function first, but then there's + * no clock for a while and other chips might reset, so we notify + * their drivers after having switched. + * The constants here are codec-point of view, so when we switch + * the soundbus to master we tell the codec we're going to switch + * and give it CLOCK_SWITCH_PREPARE_SLAVE! + */ +enum clock_switch { + CLOCK_SWITCH_PREPARE_SLAVE, + CLOCK_SWITCH_PREPARE_MASTER, + CLOCK_SWITCH_SLAVE, + CLOCK_SWITCH_MASTER, + CLOCK_SWITCH_NOTIFY, +}; + +/* information on a transfer the codec can take */ +struct transfer_info { + u64 formats; /* SNDRV_PCM_FMTBIT_* */ + unsigned int rates; /* SNDRV_PCM_RATE_* */ + /* flags */ + u32 transfer_in:1, /* input = 1, output = 0 */ + must_be_clock_source:1; + /* for codecs to distinguish among their TIs */ + int tag; +}; + +struct codec_info_item { + struct codec_info *codec; + void *codec_data; + struct soundbus_dev *sdev; + /* internal, to be used by the soundbus provider */ + struct list_head list; +}; + +/* for prepare, where the codecs need to know + * what we're going to drive the bus with */ +struct bus_info { + /* see below */ + int sysclock_factor; + int bus_factor; +}; + +/* information on the codec itself, plus function pointers */ +struct codec_info { + /* the module this lives in */ + struct module *owner; + + /* supported transfer possibilities, array terminated by + * formats or rates being 0. */ + struct transfer_info *transfers; + + /* Master clock speed factor + * to be used (master clock speed = sysclock_factor * sampling freq) + * Unused if the soundbus provider has no such notion. + */ + int sysclock_factor; + + /* Bus factor, bus clock speed = bus_factor * sampling freq) + * Unused if the soundbus provider has no such notion. + */ + int bus_factor; + + /* operations */ + /* clock switching, see above */ + int (*switch_clock)(struct codec_info_item *cii, + enum clock_switch clock); + + /* called for each transfer_info when the user + * opens the pcm device to determine what the + * hardware can support at this point in time. + * That can depend on other user-switchable controls. + * Return 1 if usable, 0 if not. + * out points to another instance of a transfer_info + * which is initialised to the values in *ti, and + * it's format and rate values can be modified by + * the callback if it is necessary to further restrict + * the formats that can be used at the moment, for + * example when one codec has multiple logical codec + * info structs for multiple inputs. + */ + int (*usable)(struct codec_info_item *cii, + struct transfer_info *ti, + struct transfer_info *out); + + /* called when pcm stream is opened, probably not implemented + * most of the time since it isn't too useful */ + int (*open)(struct codec_info_item *cii, + struct snd_pcm_substream *substream); + + /* called when the pcm stream is closed, at this point + * the user choices can all be unlocked (see below) */ + int (*close)(struct codec_info_item *cii, + struct snd_pcm_substream *substream); + + /* if the codec must forbid some user choices because + * they are not valid with the substream/transfer info, + * it must do so here. Example: no digital output for + * incompatible framerate, say 8KHz, on Onyx. + * If the selected stuff in the substream is NOT + * compatible, you have to reject this call! */ + int (*prepare)(struct codec_info_item *cii, + struct bus_info *bi, + struct snd_pcm_substream *substream); + + /* start() is called before data is pushed to the codec. + * Note that start() must be atomic! */ + int (*start)(struct codec_info_item *cii, + struct snd_pcm_substream *substream); + + /* stop() is called after data is no longer pushed to the codec. + * Note that stop() must be atomic! */ + int (*stop)(struct codec_info_item *cii, + struct snd_pcm_substream *substream); + + int (*suspend)(struct codec_info_item *cii, pm_message_t state); + int (*resume)(struct codec_info_item *cii); +}; + +/* information on a soundbus device */ +struct soundbus_dev { + /* the bus it belongs to */ + struct list_head onbuslist; + + /* the of device it represents */ + struct of_device ofdev; + + /* what modules go by */ + char modalias[32]; + + /* These fields must be before attach_codec can be called. + * They should be set by the owner of the alsa card object + * that is needed, and whoever sets them must make sure + * that they are unique within that alsa card object. */ + char *pcmname; + int pcmid; + + /* this is assigned by the soundbus provider in attach_codec */ + struct snd_pcm *pcm; + + /* operations */ + /* attach a codec to this soundbus, give the alsa + * card object the PCMs for this soundbus should be in. + * The 'data' pointer must be unique, it is used as the + * key for detach_codec(). */ + int (*attach_codec)(struct soundbus_dev *dev, struct snd_card *card, + struct codec_info *ci, void *data); + void (*detach_codec)(struct soundbus_dev *dev, void *data); + /* TODO: suspend/resume */ + + /* private for the soundbus provider */ + struct list_head codec_list; + u32 have_out:1, have_in:1; +}; +#define to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev.dev) +#define of_to_soundbus_device(d) container_of(d, struct soundbus_dev, ofdev) + +extern int soundbus_add_one(struct soundbus_dev *dev); +extern void soundbus_remove_one(struct soundbus_dev *dev); + +extern struct soundbus_dev *soundbus_dev_get(struct soundbus_dev *dev); +extern void soundbus_dev_put(struct soundbus_dev *dev); + +struct soundbus_driver { + char *name; + struct module *owner; + + /* we don't implement any matching at all */ + + int (*probe)(struct soundbus_dev* dev); + int (*remove)(struct soundbus_dev* dev); + + int (*suspend)(struct soundbus_dev* dev, pm_message_t state); + int (*resume)(struct soundbus_dev* dev); + int (*shutdown)(struct soundbus_dev* dev); + + struct device_driver driver; +}; +#define to_soundbus_driver(drv) container_of(drv,struct soundbus_driver, driver) + +extern int soundbus_register_driver(struct soundbus_driver *drv); +extern void soundbus_unregister_driver(struct soundbus_driver *drv); + +#endif /* __SOUNDBUS_H */ diff --git a/sound/aoa/soundbus/sysfs.c b/sound/aoa/soundbus/sysfs.c new file mode 100644 index 0000000..d31f814 --- /dev/null +++ b/sound/aoa/soundbus/sysfs.c @@ -0,0 +1,43 @@ +#include +#include +#include +/* FIX UP */ +#include "soundbus.h" + +#define soundbus_config_of_attr(field, format_string) \ +static ssize_t \ +field##_show (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct soundbus_dev *mdev = to_soundbus_device (dev); \ + return sprintf (buf, format_string, mdev->ofdev.node->field); \ +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct soundbus_dev *sdev = to_soundbus_device(dev); + struct of_device *of = &sdev->ofdev; + int length; + + if (*sdev->modalias) { + strlcpy(buf, sdev->modalias, sizeof(sdev->modalias) + 1); + strcat(buf, "\n"); + length = strlen(buf); + } else { + length = sprintf(buf, "of:N%sT%s\n", + of->node->name, of->node->type); + } + + return length; +} + +soundbus_config_of_attr (name, "%s\n"); +soundbus_config_of_attr (type, "%s\n"); + +struct device_attribute soundbus_dev_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(type), + __ATTR_RO(modalias), + __ATTR_NULL +}; diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c index 5f22d70..8435fdd 100644 --- a/sound/arm/aaci.c +++ b/sound/arm/aaci.c @@ -360,7 +360,7 @@ static int aaci_pcm_open(struct aaci *aa if (ret) goto out; - ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT, + ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED, DRIVER_NAME, aaci); if (ret) goto out; @@ -779,8 +779,9 @@ static struct aaci * __devinit aaci_init strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver)); strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), - "%s at 0x%08lx, irq %d", - card->shortname, dev->res.start, dev->irq[0]); + "%s at 0x%016llx, irq %d", + card->shortname, (unsigned long long)dev->res.start, + dev->irq[0]); aaci = card->private_data; mutex_init(&aaci->ac97_sem); diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c index 13057d9..c79a9af 100644 --- a/sound/arm/sa11xx-uda1341.c +++ b/sound/arm/sa11xx-uda1341.c @@ -59,7 +59,6 @@ * ***************************************************************************************************/ -#include #include #include #include @@ -112,7 +111,7 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("SA1100/SA1111 + UDA1341TS driver for ALSA"); MODULE_SUPPORTED_DEVICE("{{UDA1341,iPAQ H3600 UDA1341TS}}"); -static char *id = NULL; /* ID for this card */ +static char *id; /* ID for this card */ module_param(id, charp, 0444); MODULE_PARM_DESC(id, "ID string for SA1100/SA1111 + UDA1341TS soundcard."); @@ -984,11 +983,15 @@ static int __init sa11xx_uda1341_init(vo if ((err = platform_driver_register(&sa11xx_uda1341_driver)) < 0) return err; device = platform_device_register_simple(SA11XX_UDA1341_DRIVER, -1, NULL, 0); - if (IS_ERR(device)) { - platform_driver_unregister(&sa11xx_uda1341_driver); - return PTR_ERR(device); - } - return 0; + if (!IS_ERR(device)) { + if (platform_get_drvdata(device)) + return 0; + platform_device_unregister(device); + err = -ENODEV + } else + err = PTR_ERR(device); + platform_driver_unregister(&sa11xx_uda1341_driver); + return err; } static void __exit sa11xx_uda1341_exit(void) diff --git a/sound/core/Kconfig b/sound/core/Kconfig index 4262a1c..b292752 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -122,8 +122,8 @@ config SND_SEQ_RTCTIMER_DEFAULT If in doubt, say Y. config SND_DYNAMIC_MINORS - bool "Dynamic device file minor numbers (EXPERIMENTAL)" - depends on SND && EXPERIMENTAL + bool "Dynamic device file minor numbers" + depends on SND help If you say Y here, the minor numbers of ALSA device files in /dev/snd/ are allocated dynamically. This allows you to have diff --git a/sound/core/control.c b/sound/core/control.c index 22565c9..bb397ea 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -176,6 +176,8 @@ #endif read_unlock(&card->ctl_files_rwlock); } +EXPORT_SYMBOL(snd_ctl_notify); + /** * snd_ctl_new - create a control instance from the template * @control: the control template @@ -204,6 +206,8 @@ struct snd_kcontrol *snd_ctl_new(struct return kctl; } +EXPORT_SYMBOL(snd_ctl_new); + /** * snd_ctl_new1 - create a control instance from the template * @ncontrol: the initialization record @@ -242,6 +246,8 @@ struct snd_kcontrol *snd_ctl_new1(const return snd_ctl_new(&kctl, access); } +EXPORT_SYMBOL(snd_ctl_new1); + /** * snd_ctl_free_one - release the control instance * @kcontrol: the control instance @@ -259,6 +265,8 @@ void snd_ctl_free_one(struct snd_kcontro } } +EXPORT_SYMBOL(snd_ctl_free_one); + static unsigned int snd_ctl_hole_check(struct snd_card *card, unsigned int count) { @@ -347,6 +355,8 @@ int snd_ctl_add(struct snd_card *card, s return err; } +EXPORT_SYMBOL(snd_ctl_add); + /** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance @@ -373,6 +383,8 @@ int snd_ctl_remove(struct snd_card *card return 0; } +EXPORT_SYMBOL(snd_ctl_remove); + /** * snd_ctl_remove_id - remove the control of the given id and release it * @card: the card instance @@ -399,6 +411,8 @@ int snd_ctl_remove_id(struct snd_card *c return ret; } +EXPORT_SYMBOL(snd_ctl_remove_id); + /** * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it * @file: active control handle @@ -461,6 +475,8 @@ int snd_ctl_rename_id(struct snd_card *c return 0; } +EXPORT_SYMBOL(snd_ctl_rename_id); + /** * snd_ctl_find_numid - find the control instance with the given number-id * @card: the card instance @@ -487,6 +503,8 @@ struct snd_kcontrol *snd_ctl_find_numid( return NULL; } +EXPORT_SYMBOL(snd_ctl_find_numid); + /** * snd_ctl_find_id - find the control instance with the given id * @card: the card instance @@ -527,6 +545,8 @@ struct snd_kcontrol *snd_ctl_find_id(str return NULL; } +EXPORT_SYMBOL(snd_ctl_find_id); + static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, unsigned int cmd, void __user *arg) { @@ -704,6 +724,8 @@ int snd_ctl_elem_read(struct snd_card *c return result; } +EXPORT_SYMBOL(snd_ctl_elem_read); + static int snd_ctl_elem_read_user(struct snd_card *card, struct snd_ctl_elem_value __user *_control) { @@ -767,6 +789,8 @@ int snd_ctl_elem_write(struct snd_card * return result; } +EXPORT_SYMBOL(snd_ctl_elem_write); + static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_ctl_elem_value __user *_control) { @@ -1199,11 +1223,15 @@ int snd_ctl_register_ioctl(snd_kctl_ioct return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls); } +EXPORT_SYMBOL(snd_ctl_register_ioctl); + #ifdef CONFIG_COMPAT int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls); } + +EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); #endif /* @@ -1236,12 +1264,15 @@ int snd_ctl_unregister_ioctl(snd_kctl_io return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls); } +EXPORT_SYMBOL(snd_ctl_unregister_ioctl); + #ifdef CONFIG_COMPAT int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) { return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls); } +EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); #endif static int snd_ctl_fasync(int fd, struct file * file, int on) diff --git a/sound/core/device.c b/sound/core/device.c index b1cf6ec..6ce4da4 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -63,6 +63,8 @@ int snd_device_new(struct snd_card *card return 0; } +EXPORT_SYMBOL(snd_device_new); + /** * snd_device_free - release the device from the card * @card: the card instance @@ -107,6 +109,8 @@ int snd_device_free(struct snd_card *car return -ENXIO; } +EXPORT_SYMBOL(snd_device_free); + /** * snd_device_disconnect - disconnect the device * @card: the card instance @@ -182,6 +186,8 @@ int snd_device_register(struct snd_card return -ENXIO; } +EXPORT_SYMBOL(snd_device_register); + /* * register all the devices on the card. * called from init.c diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 2524e66..8bd0dcc 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -486,7 +486,6 @@ static void __init snd_hwdep_proc_init(v struct snd_info_entry *entry; if ((entry = snd_info_create_module_entry(THIS_MODULE, "hwdep", NULL)) != NULL) { - entry->c.text.read_size = PAGE_SIZE; entry->c.text.read = snd_hwdep_proc_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/info.c b/sound/core/info.c index 2582b74..340332c 100644 --- a/sound/core/info.c +++ b/sound/core/info.c @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include #include -#include #include #include @@ -82,6 +80,24 @@ static int snd_info_version_init(void); static int snd_info_version_done(void); +/* resize the proc r/w buffer */ +static int resize_info_buffer(struct snd_info_buffer *buffer, + unsigned int nsize) +{ + char *nbuf; + + nsize = PAGE_ALIGN(nsize); + nbuf = kmalloc(nsize, GFP_KERNEL); + if (! nbuf) + return -ENOMEM; + + memcpy(nbuf, buffer->buffer, buffer->len); + kfree(buffer->buffer); + buffer->buffer = nbuf; + buffer->len = nsize; + return 0; +} + /** * snd_iprintf - printf on the procfs buffer * @buffer: the procfs buffer @@ -95,30 +111,43 @@ int snd_iprintf(struct snd_info_buffer * { va_list args; int len, res; + int err = 0; + might_sleep(); if (buffer->stop || buffer->error) return 0; len = buffer->len - buffer->size; va_start(args, fmt); - res = vsnprintf(buffer->curr, len, fmt, args); - va_end(args); - if (res >= len) { - buffer->stop = 1; - return 0; + for (;;) { + res = vsnprintf(buffer->buffer + buffer->curr, len, fmt, args); + if (res < len) + break; + err = resize_info_buffer(buffer, buffer->len + PAGE_SIZE); + if (err < 0) + break; + len = buffer->len - buffer->size; } + va_end(args); + + if (err < 0) + return err; buffer->curr += res; buffer->size += res; return res; } +EXPORT_SYMBOL(snd_iprintf); + /* */ -static struct proc_dir_entry *snd_proc_root = NULL; -struct snd_info_entry *snd_seq_root = NULL; +static struct proc_dir_entry *snd_proc_root; +struct snd_info_entry *snd_seq_root; +EXPORT_SYMBOL(snd_seq_root); + #ifdef CONFIG_SND_OSSEMUL -struct snd_info_entry *snd_oss_root = NULL; +struct snd_info_entry *snd_oss_root; #endif static inline void snd_info_entry_prepare(struct proc_dir_entry *de) @@ -221,7 +250,7 @@ static ssize_t snd_info_entry_write(stru struct snd_info_private_data *data; struct snd_info_entry *entry; struct snd_info_buffer *buf; - size_t size = 0; + ssize_t size = 0; loff_t pos; data = file->private_data; @@ -237,14 +266,20 @@ static ssize_t snd_info_entry_write(stru buf = data->wbuffer; if (buf == NULL) return -EIO; - if (pos >= buf->len) - return -ENOMEM; - size = buf->len - pos; - size = min(count, size); - if (copy_from_user(buf->buffer + pos, buffer, size)) + mutex_lock(&entry->access); + if (pos + count >= buf->len) { + if (resize_info_buffer(buf, pos + count)) { + mutex_unlock(&entry->access); + return -ENOMEM; + } + } + if (copy_from_user(buf->buffer + pos, buffer, count)) { + mutex_unlock(&entry->access); return -EFAULT; - if ((long)buf->size < pos + size) - buf->size = pos + size; + } + buf->size = pos + count; + mutex_unlock(&entry->access); + size = count; break; case SNDRV_INFO_CONTENT_DATA: if (entry->c.ops->write) @@ -279,18 +314,14 @@ static int snd_info_entry_open(struct in } mode = file->f_flags & O_ACCMODE; if (mode == O_RDONLY || mode == O_RDWR) { - if ((entry->content == SNDRV_INFO_CONTENT_TEXT && - !entry->c.text.read_size) || - (entry->content == SNDRV_INFO_CONTENT_DATA && + if ((entry->content == SNDRV_INFO_CONTENT_DATA && entry->c.ops->read == NULL)) { err = -ENODEV; goto __error; } } if (mode == O_WRONLY || mode == O_RDWR) { - if ((entry->content == SNDRV_INFO_CONTENT_TEXT && - !entry->c.text.write_size) || - (entry->content == SNDRV_INFO_CONTENT_DATA && + if ((entry->content == SNDRV_INFO_CONTENT_DATA && entry->c.ops->write == NULL)) { err = -ENODEV; goto __error; @@ -306,49 +337,23 @@ static int snd_info_entry_open(struct in case SNDRV_INFO_CONTENT_TEXT: if (mode == O_RDONLY || mode == O_RDWR) { buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (buffer == NULL) { - kfree(data); - err = -ENOMEM; - goto __error; - } - buffer->len = (entry->c.text.read_size + - (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); - buffer->buffer = vmalloc(buffer->len); - if (buffer->buffer == NULL) { - kfree(buffer); - kfree(data); - err = -ENOMEM; - goto __error; - } - buffer->curr = buffer->buffer; + if (buffer == NULL) + goto __nomem; data->rbuffer = buffer; + buffer->len = PAGE_SIZE; + buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); + if (buffer->buffer == NULL) + goto __nomem; } if (mode == O_WRONLY || mode == O_RDWR) { buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); - if (buffer == NULL) { - if (mode == O_RDWR) { - vfree(data->rbuffer->buffer); - kfree(data->rbuffer); - } - kfree(data); - err = -ENOMEM; - goto __error; - } - buffer->len = (entry->c.text.write_size + - (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); - buffer->buffer = vmalloc(buffer->len); - if (buffer->buffer == NULL) { - if (mode == O_RDWR) { - vfree(data->rbuffer->buffer); - kfree(data->rbuffer); - } - kfree(buffer); - kfree(data); - err = -ENOMEM; - goto __error; - } - buffer->curr = buffer->buffer; + if (buffer == NULL) + goto __nomem; data->wbuffer = buffer; + buffer->len = PAGE_SIZE; + buffer->buffer = kmalloc(buffer->len, GFP_KERNEL); + if (buffer->buffer == NULL) + goto __nomem; } break; case SNDRV_INFO_CONTENT_DATA: /* data */ @@ -373,6 +378,17 @@ static int snd_info_entry_open(struct in } return 0; + __nomem: + if (data->rbuffer) { + kfree(data->rbuffer->buffer); + kfree(data->rbuffer); + } + if (data->wbuffer) { + kfree(data->wbuffer->buffer); + kfree(data->wbuffer); + } + kfree(data); + err = -ENOMEM; __error: module_put(entry->module); __error1: @@ -391,11 +407,11 @@ static int snd_info_entry_release(struct entry = data->entry; switch (entry->content) { case SNDRV_INFO_CONTENT_TEXT: - if (mode == O_RDONLY || mode == O_RDWR) { - vfree(data->rbuffer->buffer); + if (data->rbuffer) { + kfree(data->rbuffer->buffer); kfree(data->rbuffer); } - if (mode == O_WRONLY || mode == O_RDWR) { + if (data->wbuffer) { if (entry->c.text.write) { entry->c.text.write(entry, data->wbuffer); if (data->wbuffer->error) { @@ -404,7 +420,7 @@ static int snd_info_entry_release(struct data->wbuffer->error); } } - vfree(data->wbuffer->buffer); + kfree(data->wbuffer->buffer); kfree(data->wbuffer); } break; @@ -664,29 +680,29 @@ int snd_info_get_line(struct snd_info_bu if (len <= 0 || buffer->stop || buffer->error) return 1; while (--len > 0) { - c = *buffer->curr++; + c = buffer->buffer[buffer->curr++]; if (c == '\n') { - if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { + if (buffer->curr >= buffer->size) buffer->stop = 1; - } break; } *line++ = c; - if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { + if (buffer->curr >= buffer->size) { buffer->stop = 1; break; } } while (c != '\n' && !buffer->stop) { - c = *buffer->curr++; - if ((buffer->curr - buffer->buffer) >= (long)buffer->size) { + c = buffer->buffer[buffer->curr++]; + if (buffer->curr >= buffer->size) buffer->stop = 1; - } } *line = '\0'; return 0; } +EXPORT_SYMBOL(snd_info_get_line); + /** * snd_info_get_str - parse a string token * @dest: the buffer to store the string token @@ -723,6 +739,8 @@ char *snd_info_get_str(char *dest, char return src; } +EXPORT_SYMBOL(snd_info_get_str); + /** * snd_info_create_entry - create an info entry * @name: the proc file name @@ -774,6 +792,8 @@ struct snd_info_entry *snd_info_create_m return entry; } +EXPORT_SYMBOL(snd_info_create_module_entry); + /** * snd_info_create_card_entry - create an info entry for the given card * @card: the card instance @@ -797,6 +817,8 @@ struct snd_info_entry *snd_info_create_c return entry; } +EXPORT_SYMBOL(snd_info_create_card_entry); + static int snd_info_dev_free_entry(struct snd_device *device) { struct snd_info_entry *entry = device->device_data; @@ -867,6 +889,8 @@ int snd_card_proc_new(struct snd_card *c return 0; } +EXPORT_SYMBOL(snd_card_proc_new); + /** * snd_info_free_entry - release the info entry * @entry: the info entry @@ -883,6 +907,8 @@ void snd_info_free_entry(struct snd_info kfree(entry); } +EXPORT_SYMBOL(snd_info_free_entry); + /** * snd_info_register - register the info entry * @entry: the info entry @@ -913,6 +939,8 @@ int snd_info_register(struct snd_info_en return 0; } +EXPORT_SYMBOL(snd_info_register); + /** * snd_info_unregister - de-register the info entry * @entry: the info entry @@ -937,11 +965,13 @@ int snd_info_unregister(struct snd_info_ return 0; } +EXPORT_SYMBOL(snd_info_unregister); + /* */ -static struct snd_info_entry *snd_info_version_entry = NULL; +static struct snd_info_entry *snd_info_version_entry; static void snd_info_version_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { @@ -958,7 +988,6 @@ static int __init snd_info_version_init( entry = snd_info_create_module_entry(THIS_MODULE, "version", NULL); if (entry == NULL) return -ENOMEM; - entry->c.text.read_size = 256; entry->c.text.read = snd_info_version_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/info_oss.c b/sound/core/info_oss.c index f9ce854..bb2c40d 100644 --- a/sound/core/info_oss.c +++ b/sound/core/info_oss.c @@ -64,6 +64,8 @@ int snd_oss_info_register(int dev, int n return 0; } +EXPORT_SYMBOL(snd_oss_info_register); + extern void snd_card_info_read_oss(struct snd_info_buffer *buffer); static int snd_sndstat_show_strings(struct snd_info_buffer *buf, char *id, int dev) @@ -117,7 +119,6 @@ int snd_info_minor_register(void) memset(snd_sndstat_strings, 0, sizeof(snd_sndstat_strings)); if ((entry = snd_info_create_module_entry(THIS_MODULE, "sndstat", snd_oss_root)) != NULL) { - entry->c.text.read_size = 2048; entry->c.text.read = snd_sndstat_proc_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/init.c b/sound/core/init.c index 39ed2e5..4d92588 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -38,12 +38,15 @@ struct snd_shutdown_f_ops { struct snd_shutdown_f_ops *next; }; -unsigned int snd_cards_lock = 0; /* locked for registering/using */ -struct snd_card *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; -DEFINE_RWLOCK(snd_card_rwlock); +static unsigned int snd_cards_lock; /* locked for registering/using */ +struct snd_card *snd_cards[SNDRV_CARDS]; +EXPORT_SYMBOL(snd_cards); + +static DEFINE_MUTEX(snd_card_mutex); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag); +EXPORT_SYMBOL(snd_mixer_oss_notify_callback); #endif #ifdef CONFIG_PROC_FS @@ -66,7 +69,6 @@ static inline int init_info_for_card(str snd_printd("unable to create card entry\n"); return err; } - entry->c.text.read_size = PAGE_SIZE; entry->c.text.read = snd_card_id_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -110,7 +112,7 @@ struct snd_card *snd_card_new(int idx, c strlcpy(card->id, xid, sizeof(card->id)); } err = 0; - write_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); if (idx < 0) { int idx2; for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) @@ -128,12 +130,12 @@ struct snd_card *snd_card_new(int idx, c else err = -ENODEV; if (idx < 0 || err < 0) { - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i)\n", idx, snd_ecards_limit - 1); goto __error; } snd_cards_lock |= 1 << idx; /* lock it */ - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); card->number = idx; card->module = module; INIT_LIST_HEAD(&card->devices); @@ -169,6 +171,19 @@ #endif return NULL; } +EXPORT_SYMBOL(snd_card_new); + +/* return non-zero if a card is already locked */ +int snd_card_locked(int card) +{ + int locked; + + mutex_lock(&snd_card_mutex); + locked = snd_cards_lock & (1 << card); + mutex_unlock(&snd_card_mutex); + return locked; +} + static loff_t snd_disconnect_llseek(struct file *file, loff_t offset, int orig) { return -ENODEV; @@ -236,9 +251,9 @@ int snd_card_disconnect(struct snd_card spin_unlock(&card->files_lock); /* phase 1: disable fops (user space) operations for ALSA API */ - write_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); snd_cards[card->number] = NULL; - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); /* phase 2: replace file->f_op with special dummy operations */ @@ -298,6 +313,8 @@ #endif return 0; } +EXPORT_SYMBOL(snd_card_disconnect); + /** * snd_card_free - frees given soundcard structure * @card: soundcard structure @@ -315,9 +332,9 @@ int snd_card_free(struct snd_card *card) if (card == NULL) return -EINVAL; - write_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); snd_cards[card->number] = NULL; - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); #ifdef CONFIG_PM wake_up(&card->power_sleep); @@ -353,13 +370,15 @@ #endif card->s_f_ops = s_f_ops->next; kfree(s_f_ops); } - write_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); snd_cards_lock &= ~(1 << card->number); - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); kfree(card); return 0; } +EXPORT_SYMBOL(snd_card_free); + static void snd_card_free_thread(void * __card) { struct snd_card *card = __card; @@ -405,6 +424,8 @@ int snd_card_free_in_thread(struct snd_c return -EFAULT; } +EXPORT_SYMBOL(snd_card_free_in_thread); + static void choose_default_id(struct snd_card *card) { int i, len, idx_flag = 0, loops = SNDRV_CARDS; @@ -487,16 +508,16 @@ int snd_card_register(struct snd_card *c snd_assert(card != NULL, return -EINVAL); if ((err = snd_device_register_all(card)) < 0) return err; - write_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); if (snd_cards[card->number]) { /* already registered */ - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); return 0; } if (card->id[0] == '\0') choose_default_id(card); snd_cards[card->number] = card; - write_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); init_info_for_card(card); #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) if (snd_mixer_oss_notify_callback) @@ -505,8 +526,10 @@ #endif return 0; } +EXPORT_SYMBOL(snd_card_register); + #ifdef CONFIG_PROC_FS -static struct snd_info_entry *snd_card_info_entry = NULL; +static struct snd_info_entry *snd_card_info_entry; static void snd_card_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -515,7 +538,7 @@ static void snd_card_info_read(struct sn struct snd_card *card; for (idx = count = 0; idx < SNDRV_CARDS; idx++) { - read_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); if ((card = snd_cards[idx]) != NULL) { count++; snd_iprintf(buffer, "%2i [%-15s]: %s - %s\n", @@ -526,7 +549,7 @@ static void snd_card_info_read(struct sn snd_iprintf(buffer, " %s\n", card->longname); } - read_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); } if (!count) snd_iprintf(buffer, "--- no soundcards ---\n"); @@ -540,12 +563,12 @@ void snd_card_info_read_oss(struct snd_i struct snd_card *card; for (idx = count = 0; idx < SNDRV_CARDS; idx++) { - read_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); if ((card = snd_cards[idx]) != NULL) { count++; snd_iprintf(buffer, "%s\n", card->longname); } - read_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); } if (!count) { snd_iprintf(buffer, "--- no soundcards ---\n"); @@ -563,11 +586,11 @@ static void snd_card_module_info_read(st struct snd_card *card; for (idx = 0; idx < SNDRV_CARDS; idx++) { - read_lock(&snd_card_rwlock); + mutex_lock(&snd_card_mutex); if ((card = snd_cards[idx]) != NULL) snd_iprintf(buffer, "%2i %s\n", idx, card->module->name); - read_unlock(&snd_card_rwlock); + mutex_unlock(&snd_card_mutex); } } #endif @@ -579,7 +602,6 @@ int __init snd_card_info_init(void) entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); if (! entry) return -ENOMEM; - entry->c.text.read_size = PAGE_SIZE; entry->c.text.read = snd_card_info_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -590,7 +612,6 @@ int __init snd_card_info_init(void) #ifdef MODULE entry = snd_info_create_module_entry(THIS_MODULE, "modules", NULL); if (entry) { - entry->c.text.read_size = PAGE_SIZE; entry->c.text.read = snd_card_module_info_read; if (snd_info_register(entry) < 0) snd_info_free_entry(entry); @@ -644,6 +665,8 @@ int snd_component_add(struct snd_card *c return 0; } +EXPORT_SYMBOL(snd_component_add); + /** * snd_card_file_add - add the file to the file list of the card * @card: soundcard structure @@ -676,6 +699,8 @@ int snd_card_file_add(struct snd_card *c return 0; } +EXPORT_SYMBOL(snd_card_file_add); + /** * snd_card_file_remove - remove the file from the file list * @card: soundcard structure @@ -717,6 +742,8 @@ int snd_card_file_remove(struct snd_card return 0; } +EXPORT_SYMBOL(snd_card_file_remove); + #ifdef CONFIG_PM /** * snd_power_wait - wait until the power-state is changed. @@ -753,4 +780,5 @@ int snd_power_wait(struct snd_card *card return result; } +EXPORT_SYMBOL(snd_power_wait); #endif /* CONFIG_PM */ diff --git a/sound/core/isadma.c b/sound/core/isadma.c index 1a37895..d523987 100644 --- a/sound/core/isadma.c +++ b/sound/core/isadma.c @@ -56,6 +56,8 @@ void snd_dma_program(unsigned long dma, release_dma_lock(flags); } +EXPORT_SYMBOL(snd_dma_program); + /** * snd_dma_disable - stop the ISA DMA transfer * @dma: the dma number @@ -72,6 +74,8 @@ void snd_dma_disable(unsigned long dma) release_dma_lock(flags); } +EXPORT_SYMBOL(snd_dma_disable); + /** * snd_dma_pointer - return the current pointer to DMA transfer buffer in bytes * @dma: the dma number @@ -101,3 +105,5 @@ #endif else return size - result; } + +EXPORT_SYMBOL(snd_dma_pointer); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 3fc6f97..bc0bd09 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -21,7 +21,6 @@ * */ -#include #include #include #include diff --git a/sound/core/memory.c b/sound/core/memory.c index 862d62d..fe59850 100644 --- a/sound/core/memory.c +++ b/sound/core/memory.c @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -55,6 +56,8 @@ #else #endif } +EXPORT_SYMBOL(copy_to_user_fromio); + /** * copy_from_user_toio - copy data from user-space to mmio-space * @dst: the destination pointer on mmio-space @@ -85,3 +88,5 @@ #else return 0; #endif } + +EXPORT_SYMBOL(copy_from_user_toio); diff --git a/sound/core/misc.c b/sound/core/misc.c index b53e563..03fc711 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -34,6 +34,8 @@ void release_and_free_resource(struct re } } +EXPORT_SYMBOL(release_and_free_resource); + #ifdef CONFIG_SND_VERBOSE_PRINTK void snd_verbose_printk(const char *file, int line, const char *format, ...) { @@ -51,6 +53,8 @@ void snd_verbose_printk(const char *file vprintk(format, args); va_end(args); } + +EXPORT_SYMBOL(snd_verbose_printk); #endif #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) @@ -71,4 +75,6 @@ void snd_verbose_printd(const char *file va_end(args); } + +EXPORT_SYMBOL(snd_verbose_printd); #endif diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 9c68bc3..71b5080 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1182,9 +1182,7 @@ static void snd_mixer_oss_proc_init(stru return; entry->content = SNDRV_INFO_CONTENT_TEXT; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 8192; entry->c.text.read = snd_mixer_oss_proc_read; - entry->c.text.write_size = 8192; entry->c.text.write = snd_mixer_oss_proc_write; entry->private_data = mixer; if (snd_info_register(entry) < 0) { diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index ac990bf..f5ff4f4 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -45,7 +45,7 @@ #include #define OSS_ALSAEMULVER _SIOR ('M', 249, int) -static int dsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int dsp_map[SNDRV_CARDS]; static int adsp_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; static int nonblock_open = 1; @@ -78,6 +78,487 @@ static inline void snd_leave_user(mm_seg set_fs(fs); } +/* + * helper functions to process hw_params + */ +static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin) +{ + int changed = 0; + if (i->min < min) { + i->min = min; + i->openmin = openmin; + changed = 1; + } else if (i->min == min && !i->openmin && openmin) { + i->openmin = 1; + changed = 1; + } + if (i->integer) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax) +{ + int changed = 0; + if (i->max > max) { + i->max = max; + i->openmax = openmax; + changed = 1; + } else if (i->max == max && !i->openmax && openmax) { + i->openmax = 1; + changed = 1; + } + if (i->integer) { + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } + if (snd_interval_checkempty(i)) { + snd_interval_none(i); + return -EINVAL; + } + return changed; +} + +static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) +{ + struct snd_interval t; + t.empty = 0; + t.min = t.max = val; + t.openmin = t.openmax = 0; + t.integer = 1; + return snd_interval_refine(i, &t); +} + +/** + * snd_pcm_hw_param_value_min + * @params: the hw_params instance + * @var: parameter to retrieve + * @dir: pointer to the direction (-1,0,1) or NULL + * + * Return the minimum value for field PAR. + */ +static unsigned int +snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_min(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const struct snd_interval *i = hw_param_interval_c(params, var); + if (dir) + *dir = i->openmin; + return snd_interval_min(i); + } + return -EINVAL; +} + +/** + * snd_pcm_hw_param_value_max + * @params: the hw_params instance + * @var: parameter to retrieve + * @dir: pointer to the direction (-1,0,1) or NULL + * + * Return the maximum value for field PAR. + */ +static unsigned int +snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir) +{ + if (hw_is_mask(var)) { + if (dir) + *dir = 0; + return snd_mask_max(hw_param_mask_c(params, var)); + } + if (hw_is_interval(var)) { + const struct snd_interval *i = hw_param_interval_c(params, var); + if (dir) + *dir = - (int) i->openmax; + return snd_interval_max(i); + } + return -EINVAL; +} + +static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, + const struct snd_mask *val) +{ + int changed; + changed = snd_mask_refine(hw_param_mask(params, var), val); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +static int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, + const struct snd_mask *val) +{ + int changed = _snd_pcm_hw_param_mask(params, var, val); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +static int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir > 0) { + open = 1; + } else if (dir < 0) { + if (val > 0) { + open = 1; + val--; + } + } + } + if (hw_is_mask(var)) + changed = snd_mask_refine_min(hw_param_mask(params, var), + val + !!open); + else if (hw_is_interval(var)) + changed = snd_interval_refine_min(hw_param_interval(params, var), + val, open); + else + return -EINVAL; + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_min + * @pcm: PCM instance + * @params: the hw_params instance + * @var: parameter to retrieve + * @val: minimal value + * @dir: pointer to the direction (-1,0,1) or NULL + * + * Inside configuration space defined by PARAMS remove from PAR all + * values < VAL. Reduce configuration space accordingly. + * Return new minimum or -EINVAL if the configuration space is empty + */ +static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int *dir) +{ + int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_min(params, var, dir); +} + +static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed; + int open = 0; + if (dir) { + if (dir < 0) { + open = 1; + } else if (dir > 0) { + open = 1; + val++; + } + } + if (hw_is_mask(var)) { + if (val == 0 && open) { + snd_mask_none(hw_param_mask(params, var)); + changed = -EINVAL; + } else + changed = snd_mask_refine_max(hw_param_mask(params, var), + val - !!open); + } else if (hw_is_interval(var)) + changed = snd_interval_refine_max(hw_param_interval(params, var), + val, open); + else + return -EINVAL; + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_max + * @pcm: PCM instance + * @params: the hw_params instance + * @var: parameter to retrieve + * @val: maximal value + * @dir: pointer to the direction (-1,0,1) or NULL + * + * Inside configuration space defined by PARAMS remove from PAR all + * values >= VAL + 1. Reduce configuration space accordingly. + * Return new maximum or -EINVAL if the configuration space is empty + */ +static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int *dir) +{ + int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value_max(params, var, dir); +} + +static int boundary_sub(int a, int adir, + int b, int bdir, + int *c, int *cdir) +{ + adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); + bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); + *c = a - b; + *cdir = adir - bdir; + if (*cdir == -2) { + (*c)--; + } else if (*cdir == 2) { + (*c)++; + } + return 0; +} + +static int boundary_lt(unsigned int a, int adir, + unsigned int b, int bdir) +{ + if (adir < 0) { + a--; + adir = 1; + } else if (adir > 0) + adir = 1; + if (bdir < 0) { + b--; + bdir = 1; + } else if (bdir > 0) + bdir = 1; + return a < b || (a == b && adir < bdir); +} + +/* Return 1 if min is nearer to best than max */ +static int boundary_nearer(int min, int mindir, + int best, int bestdir, + int max, int maxdir) +{ + int dmin, dmindir; + int dmax, dmaxdir; + boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); + boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); + return boundary_lt(dmin, dmindir, dmax, dmaxdir); +} + +/** + * snd_pcm_hw_param_near + * @pcm: PCM instance + * @params: the hw_params instance + * @var: parameter to retrieve + * @best: value to set + * @dir: pointer to the direction (-1,0,1) or NULL + * + * Inside configuration space defined by PARAMS set PAR to the available value + * nearest to VAL. Reduce configuration space accordingly. + * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, + * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. + * Return the value found. + */ +static int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int best, + int *dir) +{ + struct snd_pcm_hw_params *save = NULL; + int v; + unsigned int saved_min; + int last = 0; + int min, max; + int mindir, maxdir; + int valdir = dir ? *dir : 0; + /* FIXME */ + if (best > INT_MAX) + best = INT_MAX; + min = max = best; + mindir = maxdir = valdir; + if (maxdir > 0) + maxdir = 0; + else if (maxdir == 0) + maxdir = -1; + else { + maxdir = 1; + max--; + } + save = kmalloc(sizeof(*save), GFP_KERNEL); + if (save == NULL) + return -ENOMEM; + *save = *params; + saved_min = min; + min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); + if (min >= 0) { + struct snd_pcm_hw_params *params1; + if (max < 0) + goto _end; + if ((unsigned int)min == saved_min && mindir == valdir) + goto _end; + params1 = kmalloc(sizeof(*params1), GFP_KERNEL); + if (params1 == NULL) { + kfree(save); + return -ENOMEM; + } + *params1 = *save; + max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); + if (max < 0) { + kfree(params1); + goto _end; + } + if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { + *params = *params1; + last = 1; + } + kfree(params1); + } else { + *params = *save; + max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); + snd_assert(max >= 0, return -EINVAL); + last = 1; + } + _end: + kfree(save); + if (last) + v = snd_pcm_hw_param_last(pcm, params, var, dir); + else + v = snd_pcm_hw_param_first(pcm, params, var, dir); + snd_assert(v >= 0, return -EINVAL); + return v; +} + +static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed; + if (hw_is_mask(var)) { + struct snd_mask *m = hw_param_mask(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_mask_none(m); + } else { + if (dir > 0) + val++; + else if (dir < 0) + val--; + changed = snd_mask_refine_set(hw_param_mask(params, var), val); + } + } else if (hw_is_interval(var)) { + struct snd_interval *i = hw_param_interval(params, var); + if (val == 0 && dir < 0) { + changed = -EINVAL; + snd_interval_none(i); + } else if (dir == 0) + changed = snd_interval_refine_set(i, val); + else { + struct snd_interval t; + t.openmin = 1; + t.openmax = 1; + t.empty = 0; + t.integer = 0; + if (dir < 0) { + t.min = val - 1; + t.max = val; + } else { + t.min = val; + t.max = val+1; + } + changed = snd_interval_refine(i, &t); + } + } else + return -EINVAL; + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/** + * snd_pcm_hw_param_set + * @pcm: PCM instance + * @params: the hw_params instance + * @var: parameter to retrieve + * @val: value to set + * @dir: pointer to the direction (-1,0,1) or NULL + * + * Inside configuration space defined by PARAMS remove from PAR all + * values != VAL. Reduce configuration space accordingly. + * Return VAL or -EINVAL if the configuration space is empty + */ +static int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, unsigned int val, + int dir) +{ + int changed = _snd_pcm_hw_param_set(params, var, val, dir); + if (changed < 0) + return changed; + if (params->rmask) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_param_value(params, var, NULL); +} + +static int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var) +{ + int changed; + changed = snd_interval_setinteger(hw_param_interval(params, var)); + if (changed) { + params->cmask |= 1 << var; + params->rmask |= 1 << var; + } + return changed; +} + +/* + * plugin + */ + #ifdef CONFIG_SND_PCM_OSS_PLUGINS static int snd_pcm_oss_plugin_clear(struct snd_pcm_substream *substream) { @@ -203,7 +684,7 @@ static int snd_pcm_oss_period_size(struc oss_buffer_size = snd_pcm_plug_client_size(substream, snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL)) * oss_frame_size; oss_buffer_size = 1 << ld2(oss_buffer_size); - if (atomic_read(&runtime->mmap_count)) { + if (atomic_read(&substream->mmap_count)) { if (oss_buffer_size > runtime->oss.mmap_bytes) oss_buffer_size = runtime->oss.mmap_bytes; } @@ -338,7 +819,7 @@ static int snd_pcm_oss_change_params(str goto failure; } - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) direct = 1; else direct = substream->oss.setup.direct; @@ -347,7 +828,7 @@ static int snd_pcm_oss_change_params(str _snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS); _snd_pcm_hw_param_min(sparams, SNDRV_PCM_HW_PARAM_PERIODS, 2, 0); snd_mask_none(&mask); - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) snd_mask_set(&mask, SNDRV_PCM_ACCESS_MMAP_INTERLEAVED); else { snd_mask_set(&mask, SNDRV_PCM_ACCESS_RW_INTERLEAVED); @@ -466,7 +947,8 @@ #endif } else { sw_params->start_threshold = runtime->boundary; } - if (atomic_read(&runtime->mmap_count) || substream->stream == SNDRV_PCM_STREAM_CAPTURE) + if (atomic_read(&substream->mmap_count) || + substream->stream == SNDRV_PCM_STREAM_CAPTURE) sw_params->stop_threshold = runtime->boundary; else sw_params->stop_threshold = runtime->buffer_size; @@ -476,7 +958,7 @@ #endif sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : runtime->period_size; sw_params->xfer_align = 1; - if (atomic_read(&runtime->mmap_count) || + if (atomic_read(&substream->mmap_count) || substream->oss.setup.nosilence) { sw_params->silence_threshold = 0; sw_params->silence_size = 0; @@ -820,7 +1302,7 @@ static ssize_t snd_pcm_oss_write1(struct ssize_t tmp; struct snd_pcm_runtime *runtime = substream->runtime; - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) return -ENXIO; if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) @@ -850,7 +1332,7 @@ static ssize_t snd_pcm_oss_write1(struct if (runtime->oss.period_ptr == 0 || runtime->oss.period_ptr == runtime->oss.buffer_used) runtime->oss.buffer_used = 0; - else if ((substream->ffile->f_flags & O_NONBLOCK) != 0) + else if ((substream->f_flags & O_NONBLOCK) != 0) return xfer > 0 ? xfer : -EAGAIN; } } else { @@ -863,7 +1345,7 @@ static ssize_t snd_pcm_oss_write1(struct buf += tmp; bytes -= tmp; xfer += tmp; - if ((substream->ffile->f_flags & O_NONBLOCK) != 0 && + if ((substream->f_flags & O_NONBLOCK) != 0 && tmp != runtime->oss.period_bytes) break; } @@ -910,7 +1392,7 @@ static ssize_t snd_pcm_oss_read1(struct ssize_t tmp; struct snd_pcm_runtime *runtime = substream->runtime; - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) return -ENXIO; if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) @@ -1040,7 +1522,7 @@ static int snd_pcm_oss_sync(struct snd_p substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream != NULL) { runtime = substream->runtime; - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) goto __direct; if ((err = snd_pcm_oss_make_ready(substream)) < 0) return err; @@ -1101,10 +1583,10 @@ #endif * finish sync: drain the buffer */ __direct: - saved_f_flags = substream->ffile->f_flags; - substream->ffile->f_flags &= ~O_NONBLOCK; + saved_f_flags = substream->f_flags; + substream->f_flags &= ~O_NONBLOCK; err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL); - substream->ffile->f_flags = saved_f_flags; + substream->f_flags = saved_f_flags; if (err < 0) return err; runtime->oss.prepare = 1; @@ -1209,7 +1691,7 @@ static int snd_pcm_oss_get_formats(struc if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0) return err; - if (atomic_read(&substream->runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) direct = 1; else direct = substream->oss.setup.direct; @@ -1419,7 +1901,7 @@ #endif if (trigger & PCM_ENABLE_OUTPUT) { if (runtime->oss.trigger) goto _skip1; - if (atomic_read(&psubstream->runtime->mmap_count)) + if (atomic_read(&psubstream->mmap_count)) snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt); runtime->oss.trigger = 1; runtime->start_threshold = 1; @@ -1537,7 +2019,7 @@ static int snd_pcm_oss_get_ptr(struct sn if (err < 0) return err; info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size); - if (atomic_read(&runtime->mmap_count)) { + if (atomic_read(&substream->mmap_count)) { snd_pcm_sframes_t n; n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt; if (n < 0) @@ -1683,9 +2165,9 @@ static void snd_pcm_oss_init_substream(s substream->oss.oss = 1; substream->oss.setup = *setup; if (setup->nonblock) - substream->ffile->f_flags |= O_NONBLOCK; + substream->f_flags |= O_NONBLOCK; else if (setup->block) - substream->ffile->f_flags &= ~O_NONBLOCK; + substream->f_flags &= ~O_NONBLOCK; runtime = substream->runtime; runtime->oss.params = 1; runtime->oss.trigger = 1; @@ -1742,6 +2224,7 @@ static int snd_pcm_oss_open_file(struct (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX)) f_mode = FMODE_WRITE; + file->f_flags &= ~O_APPEND; for (idx = 0; idx < 2; idx++) { if (setup[idx].disable) continue; @@ -2059,6 +2542,7 @@ static ssize_t snd_pcm_oss_read(struct f substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; if (substream == NULL) return -ENXIO; + substream->f_flags = file->f_flags & O_NONBLOCK; #ifndef OSS_DEBUG return snd_pcm_oss_read1(substream, buf, count); #else @@ -2080,6 +2564,7 @@ static ssize_t snd_pcm_oss_write(struct substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream == NULL) return -ENXIO; + substream->f_flags = file->f_flags & O_NONBLOCK; result = snd_pcm_oss_write1(substream, buf, count); #ifdef OSS_DEBUG printk("pcm_oss: write %li bytes (wrote %li bytes)\n", (long)count, (long)result); @@ -2090,7 +2575,7 @@ #endif static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; else return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames; @@ -2099,7 +2584,7 @@ static int snd_pcm_oss_playback_ready(st static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt; else return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames; @@ -2342,9 +2827,7 @@ static void snd_pcm_oss_proc_init(struct if ((entry = snd_info_create_card_entry(pcm->card, "oss", pstr->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 8192; entry->c.text.read = snd_pcm_oss_proc_read; - entry->c.text.write_size = 8192; entry->c.text.write = snd_pcm_oss_proc_write; entry->private_data = pstr; if (snd_info_register(entry) < 0) { diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 84b0003..7581edd 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -351,10 +351,8 @@ static void snd_pcm_substream_proc_hw_pa snd_iprintf(buffer, "closed\n"); return; } - snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - snd_pcm_stream_unlock_irq(substream); return; } snd_iprintf(buffer, "access: %s\n", snd_pcm_access_name(runtime->access)); @@ -375,7 +373,6 @@ #if defined(CONFIG_SND_PCM_OSS) || defin snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames); } #endif - snd_pcm_stream_unlock_irq(substream); } static void snd_pcm_substream_proc_sw_params_read(struct snd_info_entry *entry, @@ -387,10 +384,8 @@ static void snd_pcm_substream_proc_sw_pa snd_iprintf(buffer, "closed\n"); return; } - snd_pcm_stream_lock_irq(substream); if (runtime->status->state == SNDRV_PCM_STATE_OPEN) { snd_iprintf(buffer, "no setup\n"); - snd_pcm_stream_unlock_irq(substream); return; } snd_iprintf(buffer, "tstamp_mode: %s\n", snd_pcm_tstamp_mode_name(runtime->tstamp_mode)); @@ -403,7 +398,6 @@ static void snd_pcm_substream_proc_sw_pa snd_iprintf(buffer, "silence_threshold: %lu\n", runtime->silence_threshold); snd_iprintf(buffer, "silence_size: %lu\n", runtime->silence_size); snd_iprintf(buffer, "boundary: %lu\n", runtime->boundary); - snd_pcm_stream_unlock_irq(substream); } static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, @@ -472,7 +466,7 @@ static int snd_pcm_stream_proc_init(stru pstr->proc_root = entry; if ((entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root)) != NULL) { - snd_info_set_text_ops(entry, pstr, 256, snd_pcm_stream_proc_info_read); + snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -483,9 +477,7 @@ static int snd_pcm_stream_proc_init(stru #ifdef CONFIG_SND_PCM_XRUN_DEBUG if ((entry = snd_info_create_card_entry(pcm->card, "xrun_debug", pstr->proc_root)) != NULL) { - entry->c.text.read_size = 64; entry->c.text.read = snd_pcm_xrun_debug_read; - entry->c.text.write_size = 64; entry->c.text.write = snd_pcm_xrun_debug_write; entry->mode |= S_IWUSR; entry->private_data = pstr; @@ -537,7 +529,8 @@ static int snd_pcm_substream_proc_init(s substream->proc_root = entry; if ((entry = snd_info_create_card_entry(card, "info", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_info_read); + snd_info_set_text_ops(entry, substream, + snd_pcm_substream_proc_info_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -546,7 +539,8 @@ static int snd_pcm_substream_proc_init(s substream->proc_info_entry = entry; if ((entry = snd_info_create_card_entry(card, "hw_params", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_hw_params_read); + snd_info_set_text_ops(entry, substream, + snd_pcm_substream_proc_hw_params_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -555,7 +549,8 @@ static int snd_pcm_substream_proc_init(s substream->proc_hw_params_entry = entry; if ((entry = snd_info_create_card_entry(card, "sw_params", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_sw_params_read); + snd_info_set_text_ops(entry, substream, + snd_pcm_substream_proc_sw_params_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -564,7 +559,8 @@ static int snd_pcm_substream_proc_init(s substream->proc_sw_params_entry = entry; if ((entry = snd_info_create_card_entry(card, "status", substream->proc_root)) != NULL) { - snd_info_set_text_ops(entry, substream, 256, snd_pcm_substream_proc_status_read); + snd_info_set_text_ops(entry, substream, + snd_pcm_substream_proc_status_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -666,11 +662,14 @@ #endif INIT_LIST_HEAD(&substream->self_group.substreams); list_add_tail(&substream->link_list, &substream->self_group.substreams); spin_lock_init(&substream->timer_lock); + atomic_set(&substream->mmap_count, 0); prev = substream; } return 0; } +EXPORT_SYMBOL(snd_pcm_new_stream); + /** * snd_pcm_new - create a new PCM instance * @card: the card instance @@ -730,6 +729,8 @@ int snd_pcm_new(struct snd_card *card, c return 0; } +EXPORT_SYMBOL(snd_pcm_new); + static void snd_pcm_free_stream(struct snd_pcm_str * pstr) { struct snd_pcm_substream *substream, *substream_next; @@ -829,6 +830,26 @@ int snd_pcm_attach_substream(struct snd_ return -EINVAL; } + if (file->f_flags & O_APPEND) { + if (prefer_subdevice < 0) { + if (pstr->substream_count > 1) + return -EINVAL; /* must be unique */ + substream = pstr->substream; + } else { + for (substream = pstr->substream; substream; + substream = substream->next) + if (substream->number == prefer_subdevice) + break; + } + if (! substream) + return -ENODEV; + if (! SUBSTREAM_BUSY(substream)) + return -EBADFD; + substream->ref_count++; + *rsubstream = substream; + return 0; + } + if (prefer_subdevice >= 0) { for (substream = pstr->substream; substream; substream = substream->next) if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) @@ -864,7 +885,6 @@ int snd_pcm_attach_substream(struct snd_ memset((void*)runtime->control, 0, size); init_waitqueue_head(&runtime->sleep); - atomic_set(&runtime->mmap_count, 0); init_timer(&runtime->tick_timer); runtime->tick_timer.function = snd_pcm_tick_timer_func; runtime->tick_timer.data = (unsigned long) substream; @@ -873,7 +893,8 @@ int snd_pcm_attach_substream(struct snd_ substream->runtime = runtime; substream->private_data = pcm->private_data; - substream->ffile = file; + substream->ref_count = 1; + substream->f_flags = file->f_flags; pstr->substream_opened++; *rsubstream = substream; return 0; @@ -882,7 +903,7 @@ int snd_pcm_attach_substream(struct snd_ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime; - substream->file = NULL; + runtime = substream->runtime; snd_assert(runtime != NULL, return); if (runtime->private_free != NULL) @@ -1022,6 +1043,8 @@ int snd_pcm_notify(struct snd_pcm_notify return 0; } +EXPORT_SYMBOL(snd_pcm_notify); + #ifdef CONFIG_PROC_FS /* * Info interface @@ -1049,15 +1072,14 @@ static void snd_pcm_proc_read(struct snd mutex_unlock(®ister_mutex); } -static struct snd_info_entry *snd_pcm_proc_entry = NULL; +static struct snd_info_entry *snd_pcm_proc_entry; static void snd_pcm_proc_init(void) { struct snd_info_entry *entry; if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { - snd_info_set_text_ops(entry, NULL, SNDRV_CARDS * SNDRV_PCM_DEVICES * 128, - snd_pcm_proc_read); + snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -1099,33 +1121,3 @@ static void __exit alsa_pcm_exit(void) module_init(alsa_pcm_init) module_exit(alsa_pcm_exit) - -EXPORT_SYMBOL(snd_pcm_new); -EXPORT_SYMBOL(snd_pcm_new_stream); -EXPORT_SYMBOL(snd_pcm_notify); -EXPORT_SYMBOL(snd_pcm_open_substream); -EXPORT_SYMBOL(snd_pcm_release_substream); - /* pcm_native.c */ -EXPORT_SYMBOL(snd_pcm_link_rwlock); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_pcm_suspend); -EXPORT_SYMBOL(snd_pcm_suspend_all); -#endif -EXPORT_SYMBOL(snd_pcm_kernel_ioctl); -EXPORT_SYMBOL(snd_pcm_mmap_data); -#if SNDRV_PCM_INFO_MMAP_IOMEM -EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); -#endif - /* pcm_misc.c */ -EXPORT_SYMBOL(snd_pcm_format_signed); -EXPORT_SYMBOL(snd_pcm_format_unsigned); -EXPORT_SYMBOL(snd_pcm_format_linear); -EXPORT_SYMBOL(snd_pcm_format_little_endian); -EXPORT_SYMBOL(snd_pcm_format_big_endian); -EXPORT_SYMBOL(snd_pcm_format_width); -EXPORT_SYMBOL(snd_pcm_format_physical_width); -EXPORT_SYMBOL(snd_pcm_format_size); -EXPORT_SYMBOL(snd_pcm_format_silence_64); -EXPORT_SYMBOL(snd_pcm_format_set_silence); -EXPORT_SYMBOL(snd_pcm_build_linear_format); -EXPORT_SYMBOL(snd_pcm_limit_hw_rates); diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index e513303..2b8aab6 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -497,9 +497,9 @@ static long snd_pcm_ioctl_compat(struct case SNDRV_PCM_IOCTL_LINK: case SNDRV_PCM_IOCTL_UNLINK: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - return snd_pcm_playback_ioctl1(substream, cmd, argp); + return snd_pcm_playback_ioctl1(file, substream, cmd, argp); else - return snd_pcm_capture_ioctl1(substream, cmd, argp); + return snd_pcm_capture_ioctl1(file, substream, cmd, argp); case SNDRV_PCM_IOCTL_HW_REFINE32: return snd_pcm_ioctl_hw_params_compat(substream, 1, argp); case SNDRV_PCM_IOCTL_HW_PARAMS32: diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index eedc6cb..0bb142a 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -289,6 +289,7 @@ void snd_pcm_set_ops(struct snd_pcm *pcm substream->ops = ops; } +EXPORT_SYMBOL(snd_pcm_set_ops); /** * snd_pcm_sync - set the PCM sync id @@ -306,13 +307,12 @@ void snd_pcm_set_sync(struct snd_pcm_sub runtime->sync.id32[3] = -1; } +EXPORT_SYMBOL(snd_pcm_set_sync); + /* * Standard ioctl routine */ -/* Code taken from alsa-lib */ -#define assert(a) snd_assert((a), return -EINVAL) - static inline unsigned int div32(unsigned int a, unsigned int b, unsigned int *r) { @@ -369,56 +369,6 @@ static inline unsigned int muldiv32(unsi return n; } -static int snd_interval_refine_min(struct snd_interval *i, unsigned int min, int openmin) -{ - int changed = 0; - assert(!snd_interval_empty(i)); - if (i->min < min) { - i->min = min; - i->openmin = openmin; - changed = 1; - } else if (i->min == min && !i->openmin && openmin) { - i->openmin = 1; - changed = 1; - } - if (i->integer) { - if (i->openmin) { - i->min++; - i->openmin = 0; - } - } - if (snd_interval_checkempty(i)) { - snd_interval_none(i); - return -EINVAL; - } - return changed; -} - -static int snd_interval_refine_max(struct snd_interval *i, unsigned int max, int openmax) -{ - int changed = 0; - assert(!snd_interval_empty(i)); - if (i->max > max) { - i->max = max; - i->openmax = openmax; - changed = 1; - } else if (i->max == max && !i->openmax && openmax) { - i->openmax = 1; - changed = 1; - } - if (i->integer) { - if (i->openmax) { - i->max--; - i->openmax = 0; - } - } - if (snd_interval_checkempty(i)) { - snd_interval_none(i); - return -EINVAL; - } - return changed; -} - /** * snd_interval_refine - refine the interval value of configurator * @i: the interval value to refine @@ -433,7 +383,7 @@ static int snd_interval_refine_max(struc int snd_interval_refine(struct snd_interval *i, const struct snd_interval *v) { int changed = 0; - assert(!snd_interval_empty(i)); + snd_assert(!snd_interval_empty(i), return -EINVAL); if (i->min < v->min) { i->min = v->min; i->openmin = v->openmin; @@ -472,9 +422,11 @@ int snd_interval_refine(struct snd_inter return changed; } +EXPORT_SYMBOL(snd_interval_refine); + static int snd_interval_refine_first(struct snd_interval *i) { - assert(!snd_interval_empty(i)); + snd_assert(!snd_interval_empty(i), return -EINVAL); if (snd_interval_single(i)) return 0; i->max = i->min; @@ -486,7 +438,7 @@ static int snd_interval_refine_first(str static int snd_interval_refine_last(struct snd_interval *i) { - assert(!snd_interval_empty(i)); + snd_assert(!snd_interval_empty(i), return -EINVAL); if (snd_interval_single(i)) return 0; i->min = i->max; @@ -496,16 +448,6 @@ static int snd_interval_refine_last(stru return 1; } -static int snd_interval_refine_set(struct snd_interval *i, unsigned int val) -{ - struct snd_interval t; - t.empty = 0; - t.min = t.max = val; - t.openmin = t.openmax = 0; - t.integer = 1; - return snd_interval_refine(i, &t); -} - void snd_interval_mul(const struct snd_interval *a, const struct snd_interval *b, struct snd_interval *c) { if (a->empty || b->empty) { @@ -621,7 +563,6 @@ void snd_interval_mulkdiv(const struct s c->integer = 0; } -#undef assert /* ---- */ @@ -727,6 +668,8 @@ int snd_interval_ratnum(struct snd_inter return err; } +EXPORT_SYMBOL(snd_interval_ratnum); + /** * snd_interval_ratden - refine the interval value * @i: interval to refine @@ -877,6 +820,8 @@ int snd_interval_list(struct snd_interva return changed; } +EXPORT_SYMBOL(snd_interval_list); + static int snd_interval_step(struct snd_interval *i, unsigned int min, unsigned int step) { unsigned int n; @@ -953,6 +898,8 @@ int snd_pcm_hw_rule_add(struct snd_pcm_r return 0; } +EXPORT_SYMBOL(snd_pcm_hw_rule_add); + /** * snd_pcm_hw_constraint_mask * @runtime: PCM runtime instance @@ -1007,6 +954,8 @@ int snd_pcm_hw_constraint_integer(struct return snd_interval_setinteger(constrs_interval(constrs, var)); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); + /** * snd_pcm_hw_constraint_minmax * @runtime: PCM runtime instance @@ -1028,6 +977,8 @@ int snd_pcm_hw_constraint_minmax(struct return snd_interval_refine(constrs_interval(constrs, var), &t); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); + static int snd_pcm_hw_rule_list(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -1055,6 +1006,8 @@ int snd_pcm_hw_constraint_list(struct sn var, -1); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_list); + static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -1087,6 +1040,8 @@ int snd_pcm_hw_constraint_ratnums(struct var, -1); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); + static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -1118,6 +1073,8 @@ int snd_pcm_hw_constraint_ratdens(struct var, -1); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); + static int snd_pcm_hw_rule_msbits(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -1149,6 +1106,8 @@ int snd_pcm_hw_constraint_msbits(struct SNDRV_PCM_HW_PARAM_SAMPLE_BITS, -1); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); + static int snd_pcm_hw_rule_step(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -1173,6 +1132,8 @@ int snd_pcm_hw_constraint_step(struct sn var, -1); } +EXPORT_SYMBOL(snd_pcm_hw_constraint_step); + static int snd_pcm_hw_rule_pow2(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { static int pow2_sizes[] = { @@ -1200,11 +1161,7 @@ int snd_pcm_hw_constraint_pow2(struct sn var, -1); } -/* To use the same code we have in alsa-lib */ -#define assert(i) snd_assert((i), return -EINVAL) -#ifndef INT_MIN -#define INT_MIN ((int)((unsigned int)INT_MAX+1)) -#endif +EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) @@ -1224,18 +1181,6 @@ static void _snd_pcm_hw_param_any(struct snd_BUG(); } -#if 0 -/* - * snd_pcm_hw_param_any - */ -int snd_pcm_hw_param_any(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var) -{ - _snd_pcm_hw_param_any(params, var); - return snd_pcm_hw_refine(pcm, params); -} -#endif /* 0 */ - void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params) { unsigned int k; @@ -1247,18 +1192,7 @@ void _snd_pcm_hw_params_any(struct snd_p params->info = ~0U; } -#if 0 -/* - * snd_pcm_hw_params_any - * - * Fill PARAMS with full configuration space boundaries - */ -int snd_pcm_hw_params_any(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params) -{ - _snd_pcm_hw_params_any(params); - return snd_pcm_hw_refine(pcm, params); -} -#endif /* 0 */ +EXPORT_SYMBOL(_snd_pcm_hw_params_any); /** * snd_pcm_hw_param_value @@ -1269,8 +1203,8 @@ #endif /* 0 */ * Return the value for field PAR if it's fixed in configuration space * defined by PARAMS. Return -EINVAL otherwise */ -static int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir) +int snd_pcm_hw_param_value(const struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir) { if (hw_is_mask(var)) { const struct snd_mask *mask = hw_param_mask_c(params, var); @@ -1288,61 +1222,10 @@ static int snd_pcm_hw_param_value(const *dir = i->openmin; return snd_interval_value(i); } - assert(0); - return -EINVAL; -} - -/** - * snd_pcm_hw_param_value_min - * @params: the hw_params instance - * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL - * - * Return the minimum value for field PAR. - */ -unsigned int snd_pcm_hw_param_value_min(const struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir) -{ - if (hw_is_mask(var)) { - if (dir) - *dir = 0; - return snd_mask_min(hw_param_mask_c(params, var)); - } - if (hw_is_interval(var)) { - const struct snd_interval *i = hw_param_interval_c(params, var); - if (dir) - *dir = i->openmin; - return snd_interval_min(i); - } - assert(0); return -EINVAL; } -/** - * snd_pcm_hw_param_value_max - * @params: the hw_params instance - * @var: parameter to retrieve - * @dir: pointer to the direction (-1,0,1) or NULL - * - * Return the maximum value for field PAR. - */ -unsigned int snd_pcm_hw_param_value_max(const struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir) -{ - if (hw_is_mask(var)) { - if (dir) - *dir = 0; - return snd_mask_max(hw_param_mask_c(params, var)); - } - if (hw_is_interval(var)) { - const struct snd_interval *i = hw_param_interval_c(params, var); - if (dir) - *dir = - (int) i->openmax; - return snd_interval_max(i); - } - assert(0); - return -EINVAL; -} +EXPORT_SYMBOL(snd_pcm_hw_param_value); void _snd_pcm_hw_param_setempty(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) @@ -1360,42 +1243,7 @@ void _snd_pcm_hw_param_setempty(struct s } } -int _snd_pcm_hw_param_setinteger(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var) -{ - int changed; - assert(hw_is_interval(var)); - changed = snd_interval_setinteger(hw_param_interval(params, var)); - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} - -#if 0 -/* - * snd_pcm_hw_param_setinteger - * - * Inside configuration space defined by PARAMS remove from PAR all - * non integer values. Reduce configuration space accordingly. - * Return -EINVAL if the configuration space is empty - */ -int snd_pcm_hw_param_setinteger(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var) -{ - int changed = _snd_pcm_hw_param_setinteger(params, var); - if (changed < 0) - return changed; - if (params->rmask) { - int err = snd_pcm_hw_refine(pcm, params); - if (err < 0) - return err; - } - return 0; -} -#endif /* 0 */ +EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); static int _snd_pcm_hw_param_first(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) @@ -1405,10 +1253,8 @@ static int _snd_pcm_hw_param_first(struc changed = snd_mask_refine_first(hw_param_mask(params, var)); else if (hw_is_interval(var)) changed = snd_interval_refine_first(hw_param_interval(params, var)); - else { - assert(0); + else return -EINVAL; - } if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; @@ -1428,20 +1274,22 @@ static int _snd_pcm_hw_param_first(struc * values > minimum. Reduce configuration space accordingly. * Return the minimum. */ -static int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir) +int snd_pcm_hw_param_first(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir) { int changed = _snd_pcm_hw_param_first(params, var); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); - assert(err >= 0); + snd_assert(err >= 0, return err); } return snd_pcm_hw_param_value(params, var, dir); } +EXPORT_SYMBOL(snd_pcm_hw_param_first); + static int _snd_pcm_hw_param_last(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { @@ -1450,10 +1298,8 @@ static int _snd_pcm_hw_param_last(struct changed = snd_mask_refine_last(hw_param_mask(params, var)); else if (hw_is_interval(var)) changed = snd_interval_refine_last(hw_param_interval(params, var)); - else { - assert(0); + else return -EINVAL; - } if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; @@ -1473,381 +1319,21 @@ static int _snd_pcm_hw_param_last(struct * values < maximum. Reduce configuration space accordingly. * Return the maximum. */ -static int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, - struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, int *dir) +int snd_pcm_hw_param_last(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params, + snd_pcm_hw_param_t var, int *dir) { int changed = _snd_pcm_hw_param_last(params, var); if (changed < 0) return changed; if (params->rmask) { int err = snd_pcm_hw_refine(pcm, params); - assert(err >= 0); + snd_assert(err >= 0, return err); } return snd_pcm_hw_param_value(params, var, dir); } -int _snd_pcm_hw_param_min(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, int dir) -{ - int changed; - int open = 0; - if (dir) { - if (dir > 0) { - open = 1; - } else if (dir < 0) { - if (val > 0) { - open = 1; - val--; - } - } - } - if (hw_is_mask(var)) - changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!open); - else if (hw_is_interval(var)) - changed = snd_interval_refine_min(hw_param_interval(params, var), val, open); - else { - assert(0); - return -EINVAL; - } - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} - -/** - * snd_pcm_hw_param_min - * @pcm: PCM instance - * @params: the hw_params instance - * @var: parameter to retrieve - * @val: minimal value - * @dir: pointer to the direction (-1,0,1) or NULL - * - * Inside configuration space defined by PARAMS remove from PAR all - * values < VAL. Reduce configuration space accordingly. - * Return new minimum or -EINVAL if the configuration space is empty - */ -static int snd_pcm_hw_param_min(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, - int *dir) -{ - int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0); - if (changed < 0) - return changed; - if (params->rmask) { - int err = snd_pcm_hw_refine(pcm, params); - if (err < 0) - return err; - } - return snd_pcm_hw_param_value_min(params, var, dir); -} - -static int _snd_pcm_hw_param_max(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, - int dir) -{ - int changed; - int open = 0; - if (dir) { - if (dir < 0) { - open = 1; - } else if (dir > 0) { - open = 1; - val++; - } - } - if (hw_is_mask(var)) { - if (val == 0 && open) { - snd_mask_none(hw_param_mask(params, var)); - changed = -EINVAL; - } else - changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!open); - } else if (hw_is_interval(var)) - changed = snd_interval_refine_max(hw_param_interval(params, var), val, open); - else { - assert(0); - return -EINVAL; - } - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} - -/** - * snd_pcm_hw_param_max - * @pcm: PCM instance - * @params: the hw_params instance - * @var: parameter to retrieve - * @val: maximal value - * @dir: pointer to the direction (-1,0,1) or NULL - * - * Inside configuration space defined by PARAMS remove from PAR all - * values >= VAL + 1. Reduce configuration space accordingly. - * Return new maximum or -EINVAL if the configuration space is empty - */ -static int snd_pcm_hw_param_max(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, - int *dir) -{ - int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0); - if (changed < 0) - return changed; - if (params->rmask) { - int err = snd_pcm_hw_refine(pcm, params); - if (err < 0) - return err; - } - return snd_pcm_hw_param_value_max(params, var, dir); -} - -int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, int dir) -{ - int changed; - if (hw_is_mask(var)) { - struct snd_mask *m = hw_param_mask(params, var); - if (val == 0 && dir < 0) { - changed = -EINVAL; - snd_mask_none(m); - } else { - if (dir > 0) - val++; - else if (dir < 0) - val--; - changed = snd_mask_refine_set(hw_param_mask(params, var), val); - } - } else if (hw_is_interval(var)) { - struct snd_interval *i = hw_param_interval(params, var); - if (val == 0 && dir < 0) { - changed = -EINVAL; - snd_interval_none(i); - } else if (dir == 0) - changed = snd_interval_refine_set(i, val); - else { - struct snd_interval t; - t.openmin = 1; - t.openmax = 1; - t.empty = 0; - t.integer = 0; - if (dir < 0) { - t.min = val - 1; - t.max = val; - } else { - t.min = val; - t.max = val+1; - } - changed = snd_interval_refine(i, &t); - } - } else { - assert(0); - return -EINVAL; - } - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} - -/** - * snd_pcm_hw_param_set - * @pcm: PCM instance - * @params: the hw_params instance - * @var: parameter to retrieve - * @val: value to set - * @dir: pointer to the direction (-1,0,1) or NULL - * - * Inside configuration space defined by PARAMS remove from PAR all - * values != VAL. Reduce configuration space accordingly. - * Return VAL or -EINVAL if the configuration space is empty - */ -int snd_pcm_hw_param_set(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int val, int dir) -{ - int changed = _snd_pcm_hw_param_set(params, var, val, dir); - if (changed < 0) - return changed; - if (params->rmask) { - int err = snd_pcm_hw_refine(pcm, params); - if (err < 0) - return err; - } - return snd_pcm_hw_param_value(params, var, NULL); -} - -static int _snd_pcm_hw_param_mask(struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, const struct snd_mask *val) -{ - int changed; - assert(hw_is_mask(var)); - changed = snd_mask_refine(hw_param_mask(params, var), val); - if (changed) { - params->cmask |= 1 << var; - params->rmask |= 1 << var; - } - return changed; -} - -/** - * snd_pcm_hw_param_mask - * @pcm: PCM instance - * @params: the hw_params instance - * @var: parameter to retrieve - * @val: mask to apply - * - * Inside configuration space defined by PARAMS remove from PAR all values - * not contained in MASK. Reduce configuration space accordingly. - * This function can be called only for SNDRV_PCM_HW_PARAM_ACCESS, - * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. - * Return 0 on success or -EINVAL - * if the configuration space is empty - */ -int snd_pcm_hw_param_mask(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, const struct snd_mask *val) -{ - int changed = _snd_pcm_hw_param_mask(params, var, val); - if (changed < 0) - return changed; - if (params->rmask) { - int err = snd_pcm_hw_refine(pcm, params); - if (err < 0) - return err; - } - return 0; -} - -static int boundary_sub(int a, int adir, - int b, int bdir, - int *c, int *cdir) -{ - adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); - bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); - *c = a - b; - *cdir = adir - bdir; - if (*cdir == -2) { - assert(*c > INT_MIN); - (*c)--; - } else if (*cdir == 2) { - assert(*c < INT_MAX); - (*c)++; - } - return 0; -} - -static int boundary_lt(unsigned int a, int adir, - unsigned int b, int bdir) -{ - assert(a > 0 || adir >= 0); - assert(b > 0 || bdir >= 0); - if (adir < 0) { - a--; - adir = 1; - } else if (adir > 0) - adir = 1; - if (bdir < 0) { - b--; - bdir = 1; - } else if (bdir > 0) - bdir = 1; - return a < b || (a == b && adir < bdir); -} - -/* Return 1 if min is nearer to best than max */ -static int boundary_nearer(int min, int mindir, - int best, int bestdir, - int max, int maxdir) -{ - int dmin, dmindir; - int dmax, dmaxdir; - boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); - boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); - return boundary_lt(dmin, dmindir, dmax, dmaxdir); -} - -/** - * snd_pcm_hw_param_near - * @pcm: PCM instance - * @params: the hw_params instance - * @var: parameter to retrieve - * @best: value to set - * @dir: pointer to the direction (-1,0,1) or NULL - * - * Inside configuration space defined by PARAMS set PAR to the available value - * nearest to VAL. Reduce configuration space accordingly. - * This function cannot be called for SNDRV_PCM_HW_PARAM_ACCESS, - * SNDRV_PCM_HW_PARAM_FORMAT, SNDRV_PCM_HW_PARAM_SUBFORMAT. - * Return the value found. - */ -int snd_pcm_hw_param_near(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params, - snd_pcm_hw_param_t var, unsigned int best, int *dir) -{ - struct snd_pcm_hw_params *save = NULL; - int v; - unsigned int saved_min; - int last = 0; - int min, max; - int mindir, maxdir; - int valdir = dir ? *dir : 0; - /* FIXME */ - if (best > INT_MAX) - best = INT_MAX; - min = max = best; - mindir = maxdir = valdir; - if (maxdir > 0) - maxdir = 0; - else if (maxdir == 0) - maxdir = -1; - else { - maxdir = 1; - max--; - } - save = kmalloc(sizeof(*save), GFP_KERNEL); - if (save == NULL) - return -ENOMEM; - *save = *params; - saved_min = min; - min = snd_pcm_hw_param_min(pcm, params, var, min, &mindir); - if (min >= 0) { - struct snd_pcm_hw_params *params1; - if (max < 0) - goto _end; - if ((unsigned int)min == saved_min && mindir == valdir) - goto _end; - params1 = kmalloc(sizeof(*params1), GFP_KERNEL); - if (params1 == NULL) { - kfree(save); - return -ENOMEM; - } - *params1 = *save; - max = snd_pcm_hw_param_max(pcm, params1, var, max, &maxdir); - if (max < 0) { - kfree(params1); - goto _end; - } - if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { - *params = *params1; - last = 1; - } - kfree(params1); - } else { - *params = *save; - max = snd_pcm_hw_param_max(pcm, params, var, max, &maxdir); - assert(max >= 0); - last = 1; - } - _end: - kfree(save); - if (last) - v = snd_pcm_hw_param_last(pcm, params, var, dir); - else - v = snd_pcm_hw_param_first(pcm, params, var, dir); - assert(v >= 0); - return v; -} +EXPORT_SYMBOL(snd_pcm_hw_param_last); /** * snd_pcm_hw_param_choose @@ -1859,39 +1345,32 @@ int snd_pcm_hw_param_near(struct snd_pcm * first access, first format, first subformat, min channels, * min rate, min period time, max buffer size, min tick time */ -int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, struct snd_pcm_hw_params *params) -{ - int err; - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_ACCESS, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_FORMAT, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_SUBFORMAT, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_CHANNELS, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_RATE, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_last(pcm, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, NULL); - assert(err >= 0); - - err = snd_pcm_hw_param_first(pcm, params, SNDRV_PCM_HW_PARAM_TICK_TIME, NULL); - assert(err >= 0); +int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, + struct snd_pcm_hw_params *params) +{ + static int vars[] = { + SNDRV_PCM_HW_PARAM_ACCESS, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_SUBFORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + SNDRV_PCM_HW_PARAM_TICK_TIME, + -1 + }; + int err, *v; + for (v = vars; *v != -1; v++) { + if (*v != SNDRV_PCM_HW_PARAM_BUFFER_SIZE) + err = snd_pcm_hw_param_first(pcm, params, *v, NULL); + else + err = snd_pcm_hw_param_last(pcm, params, *v, NULL); + snd_assert(err >= 0, return err); + } return 0; } -#undef assert - static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, void *arg) { @@ -1967,6 +1446,8 @@ int snd_pcm_lib_ioctl(struct snd_pcm_sub return -ENXIO; } +EXPORT_SYMBOL(snd_pcm_lib_ioctl); + /* * Conditions */ @@ -2101,6 +1582,8 @@ void snd_pcm_period_elapsed(struct snd_p kill_fasync(&runtime->fasync, SIGIO, POLL_IN); } +EXPORT_SYMBOL(snd_pcm_period_elapsed); + static int snd_pcm_lib_write_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -2299,7 +1782,7 @@ snd_pcm_sframes_t snd_pcm_lib_write(stru if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); + nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) @@ -2308,6 +1791,8 @@ snd_pcm_sframes_t snd_pcm_lib_write(stru snd_pcm_lib_write_transfer); } +EXPORT_SYMBOL(snd_pcm_lib_write); + static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -2362,7 +1847,7 @@ snd_pcm_sframes_t snd_pcm_lib_writev(str if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); + nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; @@ -2370,6 +1855,8 @@ snd_pcm_sframes_t snd_pcm_lib_writev(str nonblock, snd_pcm_lib_writev_transfer); } +EXPORT_SYMBOL(snd_pcm_lib_writev); + static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -2572,12 +2059,14 @@ snd_pcm_sframes_t snd_pcm_lib_read(struc if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); + nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED) return -EINVAL; return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer); } +EXPORT_SYMBOL(snd_pcm_lib_read); + static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream, unsigned int hwoff, unsigned long data, unsigned int off, @@ -2629,58 +2118,10 @@ snd_pcm_sframes_t snd_pcm_lib_readv(stru if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - nonblock = !!(substream->ffile->f_flags & O_NONBLOCK); + nonblock = !!(substream->f_flags & O_NONBLOCK); if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) return -EINVAL; return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer); } -/* - * Exported symbols - */ - -EXPORT_SYMBOL(snd_interval_refine); -EXPORT_SYMBOL(snd_interval_list); -EXPORT_SYMBOL(snd_interval_ratnum); -EXPORT_SYMBOL(_snd_pcm_hw_params_any); -EXPORT_SYMBOL(_snd_pcm_hw_param_min); -EXPORT_SYMBOL(_snd_pcm_hw_param_set); -EXPORT_SYMBOL(_snd_pcm_hw_param_setempty); -EXPORT_SYMBOL(_snd_pcm_hw_param_setinteger); -EXPORT_SYMBOL(snd_pcm_hw_param_value_min); -EXPORT_SYMBOL(snd_pcm_hw_param_value_max); -EXPORT_SYMBOL(snd_pcm_hw_param_mask); -EXPORT_SYMBOL(snd_pcm_hw_param_first); -EXPORT_SYMBOL(snd_pcm_hw_param_last); -EXPORT_SYMBOL(snd_pcm_hw_param_near); -EXPORT_SYMBOL(snd_pcm_hw_param_set); -EXPORT_SYMBOL(snd_pcm_hw_refine); -EXPORT_SYMBOL(snd_pcm_hw_constraints_init); -EXPORT_SYMBOL(snd_pcm_hw_constraints_complete); -EXPORT_SYMBOL(snd_pcm_hw_constraint_list); -EXPORT_SYMBOL(snd_pcm_hw_constraint_step); -EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums); -EXPORT_SYMBOL(snd_pcm_hw_constraint_ratdens); -EXPORT_SYMBOL(snd_pcm_hw_constraint_msbits); -EXPORT_SYMBOL(snd_pcm_hw_constraint_minmax); -EXPORT_SYMBOL(snd_pcm_hw_constraint_integer); -EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2); -EXPORT_SYMBOL(snd_pcm_hw_rule_add); -EXPORT_SYMBOL(snd_pcm_set_ops); -EXPORT_SYMBOL(snd_pcm_set_sync); -EXPORT_SYMBOL(snd_pcm_lib_ioctl); -EXPORT_SYMBOL(snd_pcm_stop); -EXPORT_SYMBOL(snd_pcm_period_elapsed); -EXPORT_SYMBOL(snd_pcm_lib_write); -EXPORT_SYMBOL(snd_pcm_lib_read); -EXPORT_SYMBOL(snd_pcm_lib_writev); EXPORT_SYMBOL(snd_pcm_lib_readv); -EXPORT_SYMBOL(snd_pcm_lib_buffer_bytes); -EXPORT_SYMBOL(snd_pcm_lib_period_bytes); -/* pcm_memory.c */ -EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); -EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); -EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); -EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); -EXPORT_SYMBOL(snd_pcm_lib_free_pages); diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 428f8c1..067d205 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -126,6 +126,8 @@ int snd_pcm_lib_preallocate_free_for_all return 0; } +EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all); + #ifdef CONFIG_SND_VERBOSE_PROCFS /* * read callback for prealloc proc file @@ -191,9 +193,7 @@ static inline void preallocate_info_init struct snd_info_entry *entry; if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) { - entry->c.text.read_size = 64; entry->c.text.read = snd_pcm_lib_preallocate_proc_read; - entry->c.text.write_size = 64; entry->c.text.write = snd_pcm_lib_preallocate_proc_write; entry->mode |= S_IWUSR; entry->private_data = substream; @@ -253,6 +253,8 @@ int snd_pcm_lib_preallocate_pages(struct return snd_pcm_lib_preallocate_pages1(substream, size, max); } +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); + /** * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) * @pcm: the pcm instance @@ -280,6 +282,8 @@ int snd_pcm_lib_preallocate_pages_for_al return 0; } +EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all); + /** * snd_pcm_sgbuf_ops_page - get the page struct at the given offset * @substream: the pcm substream instance @@ -298,6 +302,8 @@ struct page *snd_pcm_sgbuf_ops_page(stru return sgbuf->page_table[idx]; } +EXPORT_SYMBOL(snd_pcm_sgbuf_ops_page); + /** * snd_pcm_lib_malloc_pages - allocate the DMA buffer * @substream: the substream to allocate the DMA buffer to @@ -349,6 +355,8 @@ int snd_pcm_lib_malloc_pages(struct snd_ return 1; /* area was changed */ } +EXPORT_SYMBOL(snd_pcm_lib_malloc_pages); + /** * snd_pcm_lib_free_pages - release the allocated DMA buffer. * @substream: the substream to release the DMA buffer @@ -374,3 +382,5 @@ int snd_pcm_lib_free_pages(struct snd_pc snd_pcm_set_runtime_buffer(substream, NULL); return 0; } + +EXPORT_SYMBOL(snd_pcm_lib_free_pages); diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index 593c77f..0019c59 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -207,6 +207,8 @@ int snd_pcm_format_signed(snd_pcm_format return val; } +EXPORT_SYMBOL(snd_pcm_format_signed); + /** * snd_pcm_format_unsigned - Check the PCM format is unsigned linear * @format: the format to check @@ -224,6 +226,8 @@ int snd_pcm_format_unsigned(snd_pcm_form return !val; } +EXPORT_SYMBOL(snd_pcm_format_unsigned); + /** * snd_pcm_format_linear - Check the PCM format is linear * @format: the format to check @@ -235,6 +239,8 @@ int snd_pcm_format_linear(snd_pcm_format return snd_pcm_format_signed(format) >= 0; } +EXPORT_SYMBOL(snd_pcm_format_linear); + /** * snd_pcm_format_little_endian - Check the PCM format is little-endian * @format: the format to check @@ -252,6 +258,8 @@ int snd_pcm_format_little_endian(snd_pcm return val; } +EXPORT_SYMBOL(snd_pcm_format_little_endian); + /** * snd_pcm_format_big_endian - Check the PCM format is big-endian * @format: the format to check @@ -269,6 +277,8 @@ int snd_pcm_format_big_endian(snd_pcm_fo return !val; } +EXPORT_SYMBOL(snd_pcm_format_big_endian); + /** * snd_pcm_format_width - return the bit-width of the format * @format: the format to check @@ -286,6 +296,8 @@ int snd_pcm_format_width(snd_pcm_format_ return val; } +EXPORT_SYMBOL(snd_pcm_format_width); + /** * snd_pcm_format_physical_width - return the physical bit-width of the format * @format: the format to check @@ -303,6 +315,8 @@ int snd_pcm_format_physical_width(snd_pc return val; } +EXPORT_SYMBOL(snd_pcm_format_physical_width); + /** * snd_pcm_format_size - return the byte size of samples on the given format * @format: the format to check @@ -318,6 +332,8 @@ ssize_t snd_pcm_format_size(snd_pcm_form return samples * phys_width / 8; } +EXPORT_SYMBOL(snd_pcm_format_size); + /** * snd_pcm_format_silence_64 - return the silent data in 8 bytes array * @format: the format to check @@ -333,6 +349,8 @@ const unsigned char *snd_pcm_format_sile return pcm_formats[format].silence; } +EXPORT_SYMBOL(snd_pcm_format_silence_64); + /** * snd_pcm_format_set_silence - set the silence data on the buffer * @format: the PCM format @@ -402,6 +420,8 @@ #endif return 0; } +EXPORT_SYMBOL(snd_pcm_format_set_silence); + /* [width][unsigned][bigendian] */ static int linear_formats[4][2][2] = { {{ SNDRV_PCM_FORMAT_S8, SNDRV_PCM_FORMAT_S8}, @@ -432,6 +452,8 @@ snd_pcm_format_t snd_pcm_build_linear_fo return linear_formats[width][!!unsignd][!!big_endian]; } +EXPORT_SYMBOL(snd_pcm_build_linear_format); + /** * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields * @runtime: the runtime instance @@ -463,3 +485,5 @@ int snd_pcm_limit_hw_rates(struct snd_pc } return 0; } + +EXPORT_SYMBOL(snd_pcm_limit_hw_rates); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 0860c5a..439f047 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -71,8 +71,9 @@ static int snd_pcm_open(struct file *fil */ DEFINE_RWLOCK(snd_pcm_link_rwlock); -static DECLARE_RWSEM(snd_pcm_link_rwsem); +EXPORT_SYMBOL(snd_pcm_link_rwlock); +static DECLARE_RWSEM(snd_pcm_link_rwsem); static inline mm_segment_t snd_enter_user(void) { @@ -319,6 +320,8 @@ #endif return 0; } +EXPORT_SYMBOL(snd_pcm_hw_refine); + static int snd_pcm_hw_refine_user(struct snd_pcm_substream *substream, struct snd_pcm_hw_params __user * _params) { @@ -369,7 +372,7 @@ static int snd_pcm_hw_params(struct snd_ #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) if (!substream->oss.oss) #endif - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) return -EBADFD; params->rmask = ~0U; @@ -482,7 +485,7 @@ static int snd_pcm_hw_free(struct snd_pc return -EBADFD; } snd_pcm_stream_unlock_irq(substream); - if (atomic_read(&runtime->mmap_count)) + if (atomic_read(&substream->mmap_count)) return -EBADFD; if (substream->ops->hw_free) result = substream->ops->hw_free(substream); @@ -936,6 +939,8 @@ int snd_pcm_stop(struct snd_pcm_substrea return snd_pcm_action(&snd_pcm_action_stop, substream, state); } +EXPORT_SYMBOL(snd_pcm_stop); + /** * snd_pcm_drain_done * @substream: the PCM substream @@ -1085,6 +1090,8 @@ int snd_pcm_suspend(struct snd_pcm_subst return err; } +EXPORT_SYMBOL(snd_pcm_suspend); + /** * snd_pcm_suspend_all * @pcm: the PCM instance @@ -1114,6 +1121,8 @@ int snd_pcm_suspend_all(struct snd_pcm * return 0; } +EXPORT_SYMBOL(snd_pcm_suspend_all); + /* resume */ static int snd_pcm_pre_resume(struct snd_pcm_substream *substream, int state) @@ -1275,13 +1284,16 @@ static int snd_pcm_reset(struct snd_pcm_ /* * prepare ioctl */ -static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, int state) +/* we use the second argument for updating f_flags */ +static int snd_pcm_pre_prepare(struct snd_pcm_substream *substream, + int f_flags) { struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (snd_pcm_running(substream)) return -EBUSY; + substream->f_flags = f_flags; return 0; } @@ -1310,17 +1322,26 @@ static struct action_ops snd_pcm_action_ /** * snd_pcm_prepare * @substream: the PCM substream instance + * @file: file to refer f_flags * * Prepare the PCM substream to be triggerable. */ -static int snd_pcm_prepare(struct snd_pcm_substream *substream) +static int snd_pcm_prepare(struct snd_pcm_substream *substream, + struct file *file) { int res; struct snd_card *card = substream->pcm->card; + int f_flags; + + if (file) + f_flags = file->f_flags; + else + f_flags = substream->f_flags; snd_power_lock(card); if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0) - res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0); + res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, + substream, f_flags); snd_power_unlock(card); return res; } @@ -1331,7 +1352,7 @@ static int snd_pcm_prepare(struct snd_pc static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state) { - if (substream->ffile->f_flags & O_NONBLOCK) + if (substream->f_flags & O_NONBLOCK) return -EAGAIN; substream->runtime->trigger_master = substream; return 0; @@ -1448,8 +1469,6 @@ static int snd_pcm_drain(struct snd_pcm_ } } up_read(&snd_pcm_link_rwsem); - if (! num_drecs) - goto _error; snd_pcm_stream_lock_irq(substream); /* resume pause */ @@ -2006,6 +2025,10 @@ static void pcm_release_private(struct s void snd_pcm_release_substream(struct snd_pcm_substream *substream) { + substream->ref_count--; + if (substream->ref_count > 0) + return; + snd_pcm_drop(substream); if (substream->hw_opened) { if (substream->ops->hw_free != NULL) @@ -2020,6 +2043,8 @@ void snd_pcm_release_substream(struct sn snd_pcm_detach_substream(substream); } +EXPORT_SYMBOL(snd_pcm_release_substream); + int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream) @@ -2030,6 +2055,11 @@ int snd_pcm_open_substream(struct snd_pc err = snd_pcm_attach_substream(pcm, stream, file, &substream); if (err < 0) return err; + if (substream->ref_count > 1) { + *rsubstream = substream; + return 0; + } + substream->no_mmap_ctrl = 0; err = snd_pcm_hw_constraints_init(substream); if (err < 0) { @@ -2056,6 +2086,8 @@ int snd_pcm_open_substream(struct snd_pc return err; } +EXPORT_SYMBOL(snd_pcm_open_substream); + static int snd_pcm_open_file(struct file *file, struct snd_pcm *pcm, int stream, @@ -2073,17 +2105,20 @@ static int snd_pcm_open_file(struct file if (err < 0) return err; - pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); - if (pcm_file == NULL) { - snd_pcm_release_substream(substream); - return -ENOMEM; + if (substream->ref_count > 1) + pcm_file = substream->file; + else { + pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL); + if (pcm_file == NULL) { + snd_pcm_release_substream(substream); + return -ENOMEM; + } + str = substream->pstr; + substream->file = pcm_file; + substream->pcm_release = pcm_release_private; + pcm_file->substream = substream; + snd_pcm_add_file(str, pcm_file); } - str = substream->pstr; - substream->file = pcm_file; - substream->pcm_release = pcm_release_private; - pcm_file->substream = substream; - snd_pcm_add_file(str, pcm_file); - file->private_data = pcm_file; *rpcm_file = pcm_file; return 0; @@ -2170,7 +2205,6 @@ static int snd_pcm_release(struct inode pcm_file = file->private_data; substream = pcm_file->substream; snd_assert(substream != NULL, return -ENXIO); - snd_assert(!atomic_read(&substream->runtime->mmap_count), ); pcm = substream->pcm; fasync_helper(-1, file, 0, &substream->runtime->fasync); mutex_lock(&pcm->open_mutex); @@ -2493,7 +2527,8 @@ static int snd_pcm_sync_ptr(struct snd_p return 0; } -static int snd_pcm_common_ioctl1(struct snd_pcm_substream *substream, +static int snd_pcm_common_ioctl1(struct file *file, + struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { snd_assert(substream != NULL, return -ENXIO); @@ -2518,7 +2553,7 @@ static int snd_pcm_common_ioctl1(struct case SNDRV_PCM_IOCTL_CHANNEL_INFO: return snd_pcm_channel_info_user(substream, arg); case SNDRV_PCM_IOCTL_PREPARE: - return snd_pcm_prepare(substream); + return snd_pcm_prepare(substream, file); case SNDRV_PCM_IOCTL_RESET: return snd_pcm_reset(substream); case SNDRV_PCM_IOCTL_START: @@ -2560,7 +2595,8 @@ #endif return -ENOTTY; } -static int snd_pcm_playback_ioctl1(struct snd_pcm_substream *substream, +static int snd_pcm_playback_ioctl1(struct file *file, + struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { snd_assert(substream != NULL, return -ENXIO); @@ -2636,10 +2672,11 @@ static int snd_pcm_playback_ioctl1(struc return result < 0 ? result : 0; } } - return snd_pcm_common_ioctl1(substream, cmd, arg); + return snd_pcm_common_ioctl1(file, substream, cmd, arg); } -static int snd_pcm_capture_ioctl1(struct snd_pcm_substream *substream, +static int snd_pcm_capture_ioctl1(struct file *file, + struct snd_pcm_substream *substream, unsigned int cmd, void __user *arg) { snd_assert(substream != NULL, return -ENXIO); @@ -2715,7 +2752,7 @@ static int snd_pcm_capture_ioctl1(struct return result < 0 ? result : 0; } } - return snd_pcm_common_ioctl1(substream, cmd, arg); + return snd_pcm_common_ioctl1(file, substream, cmd, arg); } static long snd_pcm_playback_ioctl(struct file *file, unsigned int cmd, @@ -2728,7 +2765,8 @@ static long snd_pcm_playback_ioctl(struc if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; - return snd_pcm_playback_ioctl1(pcm_file->substream, cmd, (void __user *)arg); + return snd_pcm_playback_ioctl1(file, pcm_file->substream, cmd, + (void __user *)arg); } static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd, @@ -2741,7 +2779,8 @@ static long snd_pcm_capture_ioctl(struct if (((cmd >> 8) & 0xff) != 'A') return -ENOTTY; - return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg); + return snd_pcm_capture_ioctl1(file, pcm_file->substream, cmd, + (void __user *)arg); } int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, @@ -2753,12 +2792,12 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_ fs = snd_enter_user(); switch (substream->stream) { case SNDRV_PCM_STREAM_PLAYBACK: - result = snd_pcm_playback_ioctl1(substream, - cmd, (void __user *)arg); + result = snd_pcm_playback_ioctl1(NULL, substream, cmd, + (void __user *)arg); break; case SNDRV_PCM_STREAM_CAPTURE: - result = snd_pcm_capture_ioctl1(substream, - cmd, (void __user *)arg); + result = snd_pcm_capture_ioctl1(NULL, substream, cmd, + (void __user *)arg); break; default: result = -EINVAL; @@ -2768,6 +2807,8 @@ int snd_pcm_kernel_ioctl(struct snd_pcm_ return result; } +EXPORT_SYMBOL(snd_pcm_kernel_ioctl); + static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count, loff_t * offset) { @@ -3134,7 +3175,7 @@ static int snd_pcm_default_mmap(struct s area->vm_ops = &snd_pcm_vm_ops_data; area->vm_private_data = substream; area->vm_flags |= VM_RESERVED; - atomic_inc(&substream->runtime->mmap_count); + atomic_inc(&substream->mmap_count); return 0; } @@ -3166,9 +3207,11 @@ #endif (substream->runtime->dma_addr + offset) >> PAGE_SHIFT, size, area->vm_page_prot)) return -EAGAIN; - atomic_inc(&substream->runtime->mmap_count); + atomic_inc(&substream->mmap_count); return 0; } + +EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem); #endif /* SNDRV_PCM_INFO_MMAP */ /* @@ -3212,6 +3255,8 @@ int snd_pcm_mmap_data(struct snd_pcm_sub return snd_pcm_default_mmap(substream, area); } +EXPORT_SYMBOL(snd_pcm_mmap_data); + static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) { struct snd_pcm_file * pcm_file; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 87b47c9..8c15c66 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -43,7 +43,7 @@ MODULE_DESCRIPTION("Midlevel RawMidi cod MODULE_LICENSE("GPL"); #ifdef CONFIG_SND_OSSEMUL -static int midi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0}; +static int midi_map[SNDRV_CARDS]; static int amidi_map[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1}; module_param_array(midi_map, int, NULL, 0444); MODULE_PARM_DESC(midi_map, "Raw MIDI device number assigned to 1st OSS device."); @@ -1561,7 +1561,6 @@ #endif /* CONFIG_SND_OSSEMUL */ entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root); if (entry) { entry->private_data = rmidi; - entry->c.text.read_size = 1024; entry->c.text.read = snd_rawmidi_proc_info_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index b991978..e723413 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -291,7 +291,6 @@ register_proc(void) entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = NULL; - entry->c.text.read_size = 1024; entry->c.text.read = info_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/seq/seq.c b/sound/core/seq/seq.c index 20f954b..2f0d877 100644 --- a/sound/core/seq/seq.c +++ b/sound/core/seq/seq.c @@ -129,25 +129,3 @@ static void __exit alsa_seq_exit(void) module_init(alsa_seq_init) module_exit(alsa_seq_exit) - - /* seq_clientmgr.c */ -EXPORT_SYMBOL(snd_seq_create_kernel_client); -EXPORT_SYMBOL(snd_seq_delete_kernel_client); -EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); -EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); -EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); -EXPORT_SYMBOL(snd_seq_kernel_client_ctl); -EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); -EXPORT_SYMBOL(snd_seq_set_queue_tempo); - /* seq_memory.c */ -EXPORT_SYMBOL(snd_seq_expand_var_event); -EXPORT_SYMBOL(snd_seq_dump_var_event); - /* seq_ports.c */ -EXPORT_SYMBOL(snd_seq_event_port_attach); -EXPORT_SYMBOL(snd_seq_event_port_detach); - /* seq_lock.c */ -#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG) -/*EXPORT_SYMBOL(snd_seq_sleep_in_lock);*/ -/*EXPORT_SYMBOL(snd_seq_sleep_timeout_in_lock);*/ -EXPORT_SYMBOL(snd_use_lock_sync_helper); -#endif diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index bb15d9e..532a660 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1714,6 +1714,8 @@ int snd_seq_set_queue_tempo(int client, return snd_seq_queue_timer_set_tempo(tempo->queue, client, tempo); } +EXPORT_SYMBOL(snd_seq_set_queue_tempo); + static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client, void __user *arg) { @@ -2264,6 +2266,8 @@ int snd_seq_create_kernel_client(struct return client->number; } +EXPORT_SYMBOL(snd_seq_create_kernel_client); + /* exported to kernel modules */ int snd_seq_delete_kernel_client(int client) { @@ -2280,6 +2284,7 @@ int snd_seq_delete_kernel_client(int cli return 0; } +EXPORT_SYMBOL(snd_seq_delete_kernel_client); /* skeleton to enqueue event, called from snd_seq_kernel_client_enqueue * and snd_seq_kernel_client_enqueue_blocking @@ -2328,6 +2333,8 @@ int snd_seq_kernel_client_enqueue(int cl return kernel_client_enqueue(client, ev, NULL, 0, atomic, hop); } +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue); + /* * exported, called by kernel clients to enqueue events (with blocking) * @@ -2340,6 +2347,7 @@ int snd_seq_kernel_client_enqueue_blocki return kernel_client_enqueue(client, ev, file, 1, atomic, hop); } +EXPORT_SYMBOL(snd_seq_kernel_client_enqueue_blocking); /* * exported, called by kernel clients to dispatch events directly to other @@ -2376,6 +2384,7 @@ int snd_seq_kernel_client_dispatch(int c return result; } +EXPORT_SYMBOL(snd_seq_kernel_client_dispatch); /* * exported, called by kernel clients to perform same functions as with @@ -2396,6 +2405,7 @@ int snd_seq_kernel_client_ctl(int client return result; } +EXPORT_SYMBOL(snd_seq_kernel_client_ctl); /* exported (for OSS emulator) */ int snd_seq_kernel_client_write_poll(int clientid, struct file *file, poll_table *wait) @@ -2413,6 +2423,8 @@ int snd_seq_kernel_client_write_poll(int return 0; } +EXPORT_SYMBOL(snd_seq_kernel_client_write_poll); + /*---------------------------------------------------------------------------*/ #ifdef CONFIG_PROC_FS diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index d9a3e5a..4260de9 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -80,7 +80,7 @@ static LIST_HEAD(opslist); static int num_ops; static DEFINE_MUTEX(ops_mutex); #ifdef CONFIG_PROC_FS -static struct snd_info_entry *info_entry = NULL; +static struct snd_info_entry *info_entry; #endif /* @@ -380,6 +380,12 @@ static struct ops_list * create_driver(c /* set up driver entry */ strlcpy(ops->id, id, sizeof(ops->id)); mutex_init(&ops->reg_mutex); + /* + * The ->reg_mutex locking rules are per-driver, so we create + * separate per-driver lock classes: + */ + lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id); + ops->driver = DRIVER_EMPTY; INIT_LIST_HEAD(&ops->dev_list); /* lock this instance */ @@ -555,7 +561,6 @@ #ifdef CONFIG_PROC_FS if (info_entry == NULL) return -ENOMEM; info_entry->content = SNDRV_INFO_CONTENT_TEXT; - info_entry->c.text.read_size = 2048; info_entry->c.text.read = snd_seq_device_info; if (snd_info_register(info_entry) < 0) { snd_info_free_entry(info_entry); diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 2a283a5..e55488d 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -66,7 +66,7 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY)); static int ports = 1; -static int duplex = 0; +static int duplex; module_param(ports, int, 0444); MODULE_PARM_DESC(ports, "number of ports to be created"); @@ -171,7 +171,9 @@ create_port(int idx, int type) pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; if (duplex) pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; - pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC + | SNDRV_SEQ_PORT_TYPE_SOFTWARE + | SNDRV_SEQ_PORT_TYPE_PORT; memset(&pcb, 0, sizeof(pcb)); pcb.owner = THIS_MODULE; pcb.unuse = dummy_unuse; diff --git a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c index acce21a..142e9e6 100644 --- a/sound/core/seq/seq_info.c +++ b/sound/core/seq/seq_info.c @@ -34,8 +34,8 @@ static struct snd_info_entry *timer_entr static struct snd_info_entry * __init -create_info_entry(char *name, int size, void (*read)(struct snd_info_entry *, - struct snd_info_buffer *)) +create_info_entry(char *name, void (*read)(struct snd_info_entry *, + struct snd_info_buffer *)) { struct snd_info_entry *entry; @@ -43,7 +43,6 @@ create_info_entry(char *name, int size, if (entry == NULL) return NULL; entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->c.text.read_size = size; entry->c.text.read = read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -55,11 +54,11 @@ create_info_entry(char *name, int size, /* create all our /proc entries */ int __init snd_seq_info_init(void) { - queues_entry = create_info_entry("queues", 512 + (256 * SNDRV_SEQ_MAX_QUEUES), + queues_entry = create_info_entry("queues", snd_seq_info_queues_read); - clients_entry = create_info_entry("clients", 512 + (256 * SNDRV_SEQ_MAX_CLIENTS), + clients_entry = create_info_entry("clients", snd_seq_info_clients_read); - timer_entry = create_info_entry("timer", 1024, snd_seq_info_timer_read); + timer_entry = create_info_entry("timer", snd_seq_info_timer_read); return 0; } diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c index a837a94..1a34941 100644 --- a/sound/core/seq/seq_lock.c +++ b/sound/core/seq/seq_lock.c @@ -44,4 +44,6 @@ void snd_use_lock_sync_helper(snd_use_lo } } +EXPORT_SYMBOL(snd_use_lock_sync_helper); + #endif diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index 40b4f67..4bffe50 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -118,6 +118,8 @@ int snd_seq_dump_var_event(const struct return 0; } +EXPORT_SYMBOL(snd_seq_dump_var_event); + /* * exported: @@ -167,6 +169,7 @@ int snd_seq_expand_var_event(const struc return err < 0 ? err : newlen; } +EXPORT_SYMBOL(snd_seq_expand_var_event); /* * release this cell, free extended data if available diff --git a/sound/core/seq/seq_memory.h b/sound/core/seq/seq_memory.h index 39c60d9..63e9143 100644 --- a/sound/core/seq/seq_memory.h +++ b/sound/core/seq/seq_memory.h @@ -31,7 +31,7 @@ struct snd_seq_event_cell { struct snd_seq_event_cell *next; /* next cell */ }; -/* design note: the pool is a contigious block of memory, if we dynamicly +/* design note: the pool is a contiguous block of memory, if we dynamicly want to add additional cells to the pool be better store this in another pool as we need to know the base address of the pool when releasing memory. */ diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 9caa137..1daa5b0 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -278,6 +278,7 @@ snd_seq_midisynth_register_port(struct s struct seq_midisynth *msynth, *ms; struct snd_seq_port_info *port; struct snd_rawmidi_info *info; + struct snd_rawmidi *rmidi = dev->private_data; int newclient = 0; unsigned int p, ports; struct snd_seq_port_callback pcallbacks; @@ -320,8 +321,8 @@ snd_seq_midisynth_register_port(struct s } client->seq_client = snd_seq_create_kernel_client( - card, 0, "%s", info->name[0] ? - (const char *)info->name : "External MIDI"); + card, 0, "%s", card->shortname[0] ? + (const char *)card->shortname : "External MIDI"); if (client->seq_client < 0) { kfree(client); mutex_unlock(®ister_mutex); @@ -376,7 +377,9 @@ snd_seq_midisynth_register_port(struct s if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) && info->flags & SNDRV_RAWMIDI_INFO_DUPLEX) port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; - port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC + | SNDRV_SEQ_PORT_TYPE_HARDWARE + | SNDRV_SEQ_PORT_TYPE_PORT; port->midi_channels = 16; memset(&pcallbacks, 0, sizeof(pcallbacks)); pcallbacks.owner = THIS_MODULE; @@ -387,6 +390,8 @@ snd_seq_midisynth_register_port(struct s pcallbacks.unuse = midisynth_unuse; pcallbacks.event_input = event_process_midi; port->kernel = &pcallbacks; + if (rmidi->ops && rmidi->ops->get_port_info) + rmidi->ops->get_port_info(rmidi, p, port); if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0) goto __nomem; ms->seq_client = client->seq_client; diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 41e078c..8c64b58 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -221,7 +221,6 @@ static void clear_subscriber_list(struct { struct list_head *p, *n; - down_write(&grp->list_mutex); list_for_each_safe(p, n, &grp->list_head) { struct snd_seq_subscribers *subs; struct snd_seq_client *c; @@ -259,7 +258,6 @@ static void clear_subscriber_list(struct snd_seq_client_unlock(c); } } - up_write(&grp->list_mutex); } /* delete port data */ @@ -324,10 +322,8 @@ int snd_seq_delete_all_ports(struct snd_ mutex_lock(&client->ports_mutex); write_lock_irqsave(&client->ports_lock, flags); if (! list_empty(&client->ports_list_head)) { - __list_add(&deleted_list, - client->ports_list_head.prev, - client->ports_list_head.next); - INIT_LIST_HEAD(&client->ports_list_head); + list_add(&deleted_list, &client->ports_list_head); + list_del_init(&client->ports_list_head); } else { INIT_LIST_HEAD(&deleted_list); } @@ -518,7 +514,7 @@ int snd_seq_port_connect(struct snd_seq_ atomic_set(&subs->ref_count, 2); down_write(&src->list_mutex); - down_write(&dest->list_mutex); + down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0; err = -EBUSY; @@ -591,7 +587,7 @@ int snd_seq_port_disconnect(struct snd_s unsigned long flags; down_write(&src->list_mutex); - down_write(&dest->list_mutex); + down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING); /* look for the connection */ list_for_each(p, &src->list_head) { @@ -677,6 +673,7 @@ int snd_seq_event_port_attach(int client return ret; } +EXPORT_SYMBOL(snd_seq_event_port_attach); /* * Detach the driver from a port. @@ -696,3 +693,5 @@ int snd_seq_event_port_detach(int client return err; } + +EXPORT_SYMBOL(snd_seq_event_port_detach); diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c index f4edec6..0cfa06c 100644 --- a/sound/core/seq/seq_virmidi.c +++ b/sound/core/seq/seq_virmidi.c @@ -390,7 +390,9 @@ static int snd_virmidi_dev_attach_seq(st pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE; pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ; pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX; - pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC; + pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC + | SNDRV_SEQ_PORT_TYPE_SOFTWARE + | SNDRV_SEQ_PORT_TYPE_PORT; pinfo->midi_channels = 16; memset(&pcallbacks, 0, sizeof(pcallbacks)); pcallbacks.owner = THIS_MODULE; diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c index 74745da..6e4d4ab 100644 --- a/sound/core/sgbuf.c +++ b/sound/core/sgbuf.c @@ -19,7 +19,6 @@ * */ -#include #include #include #include diff --git a/sound/core/sound.c b/sound/core/sound.c index 108e430..264f2ef 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -32,15 +32,15 @@ #include #include #include #include -#include #include #define SNDRV_OS_MINORS 256 static int major = CONFIG_SND_MAJOR; int snd_major; +EXPORT_SYMBOL(snd_major); + static int cards_limit = 1; -static int device_mode = S_IFCHR | S_IRUGO | S_IWUGO; MODULE_AUTHOR("Jaroslav Kysela "); MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); @@ -49,10 +49,6 @@ module_param(major, int, 0444); MODULE_PARM_DESC(major, "Major # for sound driver."); module_param(cards_limit, int, 0444); MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); -#ifdef CONFIG_DEVFS_FS -module_param(device_mode, int, 0444); -MODULE_PARM_DESC(device_mode, "Device file permission mask for devfs."); -#endif MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); /* this one holds the actual max. card number currently available. @@ -60,6 +56,7 @@ MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MA * modules are loaded manually, this limit number increases, too. */ int snd_ecards_limit; +EXPORT_SYMBOL(snd_ecards_limit); static struct snd_minor *snd_minors[SNDRV_OS_MINORS]; static DEFINE_MUTEX(sound_mutex); @@ -78,20 +75,17 @@ #ifdef CONFIG_KMOD */ void snd_request_card(int card) { - int locked; - if (! current->fs->root) return; - read_lock(&snd_card_rwlock); - locked = snd_cards_lock & (1 << card); - read_unlock(&snd_card_rwlock); - if (locked) + if (snd_card_locked(card)) return; if (card < 0 || card >= cards_limit) return; request_module("snd-card-%i", card); } +EXPORT_SYMBOL(snd_request_card); + static void snd_request_other(int minor) { char *str; @@ -133,6 +127,8 @@ void *snd_lookup_minor_data(unsigned int return private_data; } +EXPORT_SYMBOL(snd_lookup_minor_data); + static int snd_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); @@ -271,8 +267,6 @@ #endif return minor; } snd_minors[minor] = preg; - if (type != SNDRV_DEVICE_TYPE_CONTROL || preg->card >= cards_limit) - devfs_mk_cdev(MKDEV(major, minor), S_IFCHR | device_mode, "snd/%s", name); if (card) device = card->dev; class_device_create(sound_class, NULL, MKDEV(major, minor), device, "%s", name); @@ -281,6 +275,8 @@ #endif return 0; } +EXPORT_SYMBOL(snd_register_device); + /** * snd_unregister_device - unregister the device on the given card * @type: the device type, SNDRV_DEVICE_TYPE_XXX @@ -310,9 +306,6 @@ int snd_unregister_device(int type, stru return -EINVAL; } - if (mptr->type != SNDRV_DEVICE_TYPE_CONTROL || - mptr->card >= cards_limit) /* created in sound.c */ - devfs_remove("snd/%s", mptr->name); class_device_destroy(sound_class, MKDEV(major, minor)); snd_minors[minor] = NULL; @@ -321,12 +314,14 @@ int snd_unregister_device(int type, stru return 0; } +EXPORT_SYMBOL(snd_unregister_device); + #ifdef CONFIG_PROC_FS /* * INFO PART */ -static struct snd_info_entry *snd_minor_info_entry = NULL; +static struct snd_info_entry *snd_minor_info_entry; static const char *snd_device_type_name(int type) { @@ -381,7 +376,6 @@ int __init snd_minor_info_init(void) entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); if (entry) { - entry->c.text.read_size = PAGE_SIZE; entry->c.text.read = snd_minor_info_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -406,24 +400,17 @@ #endif /* CONFIG_PROC_FS */ static int __init alsa_sound_init(void) { - short controlnum; - snd_major = major; snd_ecards_limit = cards_limit; - devfs_mk_dir("snd"); if (register_chrdev(major, "alsa", &snd_fops)) { snd_printk(KERN_ERR "unable to register native major device number %d\n", major); - devfs_remove("snd"); return -EIO; } if (snd_info_init() < 0) { unregister_chrdev(major, "alsa"); - devfs_remove("snd"); return -ENOMEM; } snd_info_minor_register(); - for (controlnum = 0; controlnum < cards_limit; controlnum++) - devfs_mk_cdev(MKDEV(major, controlnum<<5), S_IFCHR | device_mode, "snd/controlC%d", controlnum); #ifndef MODULE printk(KERN_INFO "Advanced Linux Sound Architecture Driver Version " CONFIG_SND_VERSION CONFIG_SND_DATE ".\n"); #endif @@ -432,105 +419,11 @@ #endif static void __exit alsa_sound_exit(void) { - short controlnum; - - for (controlnum = 0; controlnum < cards_limit; controlnum++) - devfs_remove("snd/controlC%d", controlnum); - snd_info_minor_unregister(); snd_info_done(); if (unregister_chrdev(major, "alsa") != 0) snd_printk(KERN_ERR "unable to unregister major device number %d\n", major); - devfs_remove("snd"); } module_init(alsa_sound_init) module_exit(alsa_sound_exit) - - /* sound.c */ -EXPORT_SYMBOL(snd_major); -EXPORT_SYMBOL(snd_ecards_limit); -#if defined(CONFIG_KMOD) -EXPORT_SYMBOL(snd_request_card); -#endif -EXPORT_SYMBOL(snd_register_device); -EXPORT_SYMBOL(snd_unregister_device); -EXPORT_SYMBOL(snd_lookup_minor_data); -#if defined(CONFIG_SND_OSSEMUL) -EXPORT_SYMBOL(snd_register_oss_device); -EXPORT_SYMBOL(snd_unregister_oss_device); -EXPORT_SYMBOL(snd_lookup_oss_minor_data); -#endif - /* memory.c */ -EXPORT_SYMBOL(copy_to_user_fromio); -EXPORT_SYMBOL(copy_from_user_toio); - /* init.c */ -EXPORT_SYMBOL(snd_cards); -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) -EXPORT_SYMBOL(snd_mixer_oss_notify_callback); -#endif -EXPORT_SYMBOL(snd_card_new); -EXPORT_SYMBOL(snd_card_disconnect); -EXPORT_SYMBOL(snd_card_free); -EXPORT_SYMBOL(snd_card_free_in_thread); -EXPORT_SYMBOL(snd_card_register); -EXPORT_SYMBOL(snd_component_add); -EXPORT_SYMBOL(snd_card_file_add); -EXPORT_SYMBOL(snd_card_file_remove); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_power_wait); -#endif - /* device.c */ -EXPORT_SYMBOL(snd_device_new); -EXPORT_SYMBOL(snd_device_register); -EXPORT_SYMBOL(snd_device_free); - /* isadma.c */ -#ifdef CONFIG_ISA_DMA_API -EXPORT_SYMBOL(snd_dma_program); -EXPORT_SYMBOL(snd_dma_disable); -EXPORT_SYMBOL(snd_dma_pointer); -#endif - /* info.c */ -#ifdef CONFIG_PROC_FS -EXPORT_SYMBOL(snd_seq_root); -EXPORT_SYMBOL(snd_iprintf); -EXPORT_SYMBOL(snd_info_get_line); -EXPORT_SYMBOL(snd_info_get_str); -EXPORT_SYMBOL(snd_info_create_module_entry); -EXPORT_SYMBOL(snd_info_create_card_entry); -EXPORT_SYMBOL(snd_info_free_entry); -EXPORT_SYMBOL(snd_info_register); -EXPORT_SYMBOL(snd_info_unregister); -EXPORT_SYMBOL(snd_card_proc_new); -#endif - /* info_oss.c */ -#if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS) -EXPORT_SYMBOL(snd_oss_info_register); -#endif - /* control.c */ -EXPORT_SYMBOL(snd_ctl_new); -EXPORT_SYMBOL(snd_ctl_new1); -EXPORT_SYMBOL(snd_ctl_free_one); -EXPORT_SYMBOL(snd_ctl_add); -EXPORT_SYMBOL(snd_ctl_remove); -EXPORT_SYMBOL(snd_ctl_remove_id); -EXPORT_SYMBOL(snd_ctl_rename_id); -EXPORT_SYMBOL(snd_ctl_find_numid); -EXPORT_SYMBOL(snd_ctl_find_id); -EXPORT_SYMBOL(snd_ctl_notify); -EXPORT_SYMBOL(snd_ctl_register_ioctl); -EXPORT_SYMBOL(snd_ctl_unregister_ioctl); -#ifdef CONFIG_COMPAT -EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); -EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); -#endif -EXPORT_SYMBOL(snd_ctl_elem_read); -EXPORT_SYMBOL(snd_ctl_elem_write); - /* misc.c */ -EXPORT_SYMBOL(release_and_free_resource); -#ifdef CONFIG_SND_VERBOSE_PRINTK -EXPORT_SYMBOL(snd_verbose_printk); -#endif -#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK) -EXPORT_SYMBOL(snd_verbose_printd); -#endif diff --git a/sound/core/sound_oss.c b/sound/core/sound_oss.c index 9055c6d..74f0fe5 100644 --- a/sound/core/sound_oss.c +++ b/sound/core/sound_oss.c @@ -58,6 +58,8 @@ void *snd_lookup_oss_minor_data(unsigned return private_data; } +EXPORT_SYMBOL(snd_lookup_oss_minor_data); + static int snd_oss_kernel_minor(int type, struct snd_card *card, int dev) { int minor; @@ -158,6 +160,8 @@ int snd_register_oss_device(int type, st return -EBUSY; } +EXPORT_SYMBOL(snd_register_oss_device); + int snd_unregister_oss_device(int type, struct snd_card *card, int dev) { int minor = snd_oss_kernel_minor(type, card, dev); @@ -197,13 +201,15 @@ int snd_unregister_oss_device(int type, return 0; } +EXPORT_SYMBOL(snd_unregister_oss_device); + /* * INFO PART */ #ifdef CONFIG_PROC_FS -static struct snd_info_entry *snd_minor_info_oss_entry = NULL; +static struct snd_info_entry *snd_minor_info_oss_entry; static const char *snd_oss_device_type_name(int type) { @@ -252,7 +258,6 @@ int __init snd_minor_info_oss_init(void) entry = snd_info_create_module_entry(THIS_MODULE, "devices", snd_oss_root); if (entry) { - entry->c.text.read_size = PAGE_SIZE; entry->c.text.read = snd_minor_info_oss_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/core/timer.c b/sound/core/timer.c index cdeeb63..78199f5 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1061,7 +1061,6 @@ #ifdef CONFIG_PROC_FS static void snd_timer_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - unsigned long flags; struct snd_timer *timer; struct snd_timer_instance *ti; struct list_head *p, *q; @@ -1095,7 +1094,6 @@ static void snd_timer_proc_read(struct s if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) snd_iprintf(buffer, " SLAVE"); snd_iprintf(buffer, "\n"); - spin_lock_irqsave(&timer->lock, flags); list_for_each(q, &timer->open_list_head) { ti = list_entry(q, struct snd_timer_instance, open_list); snd_iprintf(buffer, " Client %s : %s\n", @@ -1104,12 +1102,11 @@ static void snd_timer_proc_read(struct s SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped"); } - spin_unlock_irqrestore(&timer->lock, flags); } mutex_unlock(®ister_mutex); } -static struct snd_info_entry *snd_timer_proc_entry = NULL; +static struct snd_info_entry *snd_timer_proc_entry; static void __init snd_timer_proc_init(void) { @@ -1117,7 +1114,6 @@ static void __init snd_timer_proc_init(v entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); if (entry != NULL) { - entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128; entry->c.text.read = snd_timer_proc_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index ae0df54..ffeafaf 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c @@ -677,6 +677,10 @@ static int __init alsa_card_dummy_init(v i, NULL, 0); if (IS_ERR(device)) continue; + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + continue; + } devices[i] = device; cards++; } diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c index 77b0600..17cc105 100644 --- a/sound/drivers/mpu401/mpu401.c +++ b/sound/drivers/mpu401/mpu401.c @@ -83,7 +83,7 @@ static int snd_mpu401_create(int dev, st if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0, - irq[dev], irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL)) < 0) { + irq[dev], irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL)) < 0) { printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]); goto _err; } @@ -160,8 +160,9 @@ static int __devinit snd_mpu401_pnp(int return -ENODEV; } if (pnp_port_len(device, 0) < IO_EXTENT) { - snd_printk(KERN_ERR "PnP port length is %ld, expected %d\n", - pnp_port_len(device, 0), IO_EXTENT); + snd_printk(KERN_ERR "PnP port length is %llu, expected %d\n", + (unsigned long long)pnp_port_len(device, 0), + IO_EXTENT); return -ENODEV; } port[dev] = pnp_port_start(device, 0); @@ -253,6 +254,10 @@ #endif i, NULL, 0); if (IS_ERR(device)) continue; + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + continue; + } platform_devices[i] = device; snd_mpu401_devices++; } diff --git a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c index b49a45c..4bf07ca 100644 --- a/sound/drivers/mpu401/mpu401_uart.c +++ b/sound/drivers/mpu401/mpu401_uart.c @@ -58,22 +58,26 @@ #define MPU401_ENTER_UART 0x3f #define MPU401_ACK 0xfe /* Build in lowlevel io */ -static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr) +static void mpu401_write_port(struct snd_mpu401 *mpu, unsigned char data, + unsigned long addr) { outb(data, addr); } -static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, unsigned long addr) +static unsigned char mpu401_read_port(struct snd_mpu401 *mpu, + unsigned long addr) { return inb(addr); } -static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, unsigned long addr) +static void mpu401_write_mmio(struct snd_mpu401 *mpu, unsigned char data, + unsigned long addr) { writeb(data, (void __iomem *)addr); } -static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, unsigned long addr) +static unsigned char mpu401_read_mmio(struct snd_mpu401 *mpu, + unsigned long addr) { return readb((void __iomem *)addr); } @@ -86,20 +90,13 @@ static void snd_mpu401_uart_clear_rx(str mpu->read(mpu, MPU401D(mpu)); #ifdef CONFIG_SND_DEBUG if (timeout <= 0) - snd_printk("cmd: clear rx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); + snd_printk(KERN_ERR "cmd: clear rx timeout (status = 0x%x)\n", + mpu->read(mpu, MPU401C(mpu))); #endif } -static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) +static void uart_interrupt_tx(struct snd_mpu401 *mpu) { - spin_lock(&mpu->input_lock); - if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) { - snd_mpu401_uart_input_read(mpu); - } else { - snd_mpu401_uart_clear_rx(mpu); - } - spin_unlock(&mpu->input_lock); - /* ok. for better Tx performance try do some output when input is done */ if (test_bit(MPU401_MODE_BIT_OUTPUT, &mpu->mode) && test_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode)) { spin_lock(&mpu->output_lock); @@ -108,6 +105,22 @@ static void _snd_mpu401_uart_interrupt(s } } +static void _snd_mpu401_uart_interrupt(struct snd_mpu401 *mpu) +{ + if (mpu->info_flags & MPU401_INFO_INPUT) { + spin_lock(&mpu->input_lock); + if (test_bit(MPU401_MODE_BIT_INPUT, &mpu->mode)) + snd_mpu401_uart_input_read(mpu); + else + snd_mpu401_uart_clear_rx(mpu); + spin_unlock(&mpu->input_lock); + } + if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) + /* ok. for better Tx performance try do some output + when input is done */ + uart_interrupt_tx(mpu); +} + /** * snd_mpu401_uart_interrupt - generic MPU401-UART interrupt handler * @irq: the irq number @@ -116,7 +129,8 @@ static void _snd_mpu401_uart_interrupt(s * * Processes the interrupt for MPU401-UART i/o. */ -irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t snd_mpu401_uart_interrupt(int irq, void *dev_id, + struct pt_regs *regs) { struct snd_mpu401 *mpu = dev_id; @@ -126,6 +140,29 @@ irqreturn_t snd_mpu401_uart_interrupt(in return IRQ_HANDLED; } +EXPORT_SYMBOL(snd_mpu401_uart_interrupt); + +/** + * snd_mpu401_uart_interrupt_tx - generic MPU401-UART transmit irq handler + * @irq: the irq number + * @dev_id: mpu401 instance + * @regs: the reigster + * + * Processes the interrupt for MPU401-UART output. + */ +irqreturn_t snd_mpu401_uart_interrupt_tx(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct snd_mpu401 *mpu = dev_id; + + if (mpu == NULL) + return IRQ_NONE; + uart_interrupt_tx(mpu); + return IRQ_HANDLED; +} + +EXPORT_SYMBOL(snd_mpu401_uart_interrupt_tx); + /* * timer callback * reprogram the timer and call the interrupt job @@ -159,7 +196,8 @@ static void snd_mpu401_uart_add_timer (s mpu->timer.expires = 1 + jiffies; add_timer(&mpu->timer); } - mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : MPU401_MODE_OUTPUT_TIMER; + mpu->timer_invoked |= input ? MPU401_MODE_INPUT_TIMER : + MPU401_MODE_OUTPUT_TIMER; spin_unlock_irqrestore (&mpu->timer_lock, flags); } @@ -172,7 +210,8 @@ static void snd_mpu401_uart_remove_timer spin_lock_irqsave (&mpu->timer_lock, flags); if (mpu->timer_invoked) { - mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : ~MPU401_MODE_OUTPUT_TIMER; + mpu->timer_invoked &= input ? ~MPU401_MODE_INPUT_TIMER : + ~MPU401_MODE_OUTPUT_TIMER; if (! mpu->timer_invoked) del_timer(&mpu->timer); } @@ -180,11 +219,12 @@ static void snd_mpu401_uart_remove_timer } /* - + * send a UART command + * return zero if successful, non-zero for some errors */ static int snd_mpu401_uart_cmd(struct snd_mpu401 * mpu, unsigned char cmd, - int ack) + int ack) { unsigned long flags; int timeout, ok; @@ -196,11 +236,13 @@ static int snd_mpu401_uart_cmd(struct sn } /* ok. standard MPU-401 initialization */ if (mpu->hardware != MPU401_HW_SB) { - for (timeout = 1000; timeout > 0 && !snd_mpu401_output_ready(mpu); timeout--) + for (timeout = 1000; timeout > 0 && + !snd_mpu401_output_ready(mpu); timeout--) udelay(10); #ifdef CONFIG_SND_DEBUG if (!timeout) - snd_printk("cmd: tx timeout (status = 0x%x)\n", mpu->read(mpu, MPU401C(mpu))); + snd_printk(KERN_ERR "cmd: tx timeout (status = 0x%x)\n", + mpu->read(mpu, MPU401C(mpu))); #endif } mpu->write(mpu, cmd, MPU401C(mpu)); @@ -215,12 +257,14 @@ #endif } if (!ok && mpu->read(mpu, MPU401D(mpu)) == MPU401_ACK) ok = 1; - } else { + } else ok = 1; - } spin_unlock_irqrestore(&mpu->input_lock, flags); if (!ok) { - snd_printk("cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)\n", cmd, mpu->port, mpu->read(mpu, MPU401C(mpu)), mpu->read(mpu, MPU401D(mpu))); + snd_printk(KERN_ERR "cmd: 0x%x failed at 0x%lx " + "(status = 0x%x, data = 0x%x)\n", cmd, mpu->port, + mpu->read(mpu, MPU401C(mpu)), + mpu->read(mpu, MPU401D(mpu))); return 1; } return 0; @@ -314,7 +358,8 @@ static int snd_mpu401_uart_output_close( /* * trigger input callback */ -static void snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) +static void +snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up) { unsigned long flags; struct snd_mpu401 *mpu; @@ -322,7 +367,8 @@ static void snd_mpu401_uart_input_trigge mpu = substream->rmidi->private_data; if (up) { - if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) { + if (! test_and_set_bit(MPU401_MODE_BIT_INPUT_TRIGGER, + &mpu->mode)) { /* first time - flush FIFO */ while (max-- > 0) mpu->read(mpu, MPU401D(mpu)); @@ -352,13 +398,11 @@ static void snd_mpu401_uart_input_read(s unsigned char byte; while (max-- > 0) { - if (snd_mpu401_input_avail(mpu)) { - byte = mpu->read(mpu, MPU401D(mpu)); - if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) - snd_rawmidi_receive(mpu->substream_input, &byte, 1); - } else { + if (! snd_mpu401_input_avail(mpu)) break; /* input not available */ - } + byte = mpu->read(mpu, MPU401D(mpu)); + if (test_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode)) + snd_rawmidi_receive(mpu->substream_input, &byte, 1); } } @@ -380,16 +424,16 @@ static void snd_mpu401_uart_output_write int max = 256, timeout; do { - if (snd_rawmidi_transmit_peek(mpu->substream_output, &byte, 1) == 1) { + if (snd_rawmidi_transmit_peek(mpu->substream_output, + &byte, 1) == 1) { for (timeout = 100; timeout > 0; timeout--) { - if (snd_mpu401_output_ready(mpu)) { - mpu->write(mpu, byte, MPU401D(mpu)); - snd_rawmidi_transmit_ack(mpu->substream_output, 1); + if (snd_mpu401_output_ready(mpu)) break; - } } if (timeout == 0) break; /* Tx FIFO full - try again later */ + mpu->write(mpu, byte, MPU401D(mpu)); + snd_rawmidi_transmit_ack(mpu->substream_output, 1); } else { snd_mpu401_uart_remove_timer (mpu, 0); break; /* no other data - leave the tx loop */ @@ -400,7 +444,8 @@ static void snd_mpu401_uart_output_write /* * output trigger callback */ -static void snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) +static void +snd_mpu401_uart_output_trigger(struct snd_rawmidi_substream *substream, int up) { unsigned long flags; struct snd_mpu401 *mpu; @@ -413,14 +458,16 @@ static void snd_mpu401_uart_output_trigg * since the output timer might have been removed in * snd_mpu401_uart_output_write(). */ - snd_mpu401_uart_add_timer(mpu, 0); + if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) + snd_mpu401_uart_add_timer(mpu, 0); /* output pending data */ spin_lock_irqsave(&mpu->output_lock, flags); snd_mpu401_uart_output_write(mpu); spin_unlock_irqrestore(&mpu->output_lock, flags); } else { - snd_mpu401_uart_remove_timer(mpu, 0); + if (! (mpu->info_flags & MPU401_INFO_TX_IRQ)) + snd_mpu401_uart_remove_timer(mpu, 0); clear_bit(MPU401_MODE_BIT_OUTPUT_TRIGGER, &mpu->mode); } } @@ -458,7 +505,7 @@ static void snd_mpu401_uart_free(struct * @device: the device index, zero-based * @hardware: the hardware type, MPU401_HW_XXXX * @port: the base address of MPU401 port - * @integrated: non-zero if the port was already reserved by the chip + * @info_flags: bitflags MPU401_INFO_XXX * @irq: the irq number, -1 if no interrupt for mpu * @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved. * @rrawmidi: the pointer to store the new rawmidi instance @@ -473,17 +520,24 @@ static void snd_mpu401_uart_free(struct */ int snd_mpu401_uart_new(struct snd_card *card, int device, unsigned short hardware, - unsigned long port, int integrated, + unsigned long port, + unsigned int info_flags, int irq, int irq_flags, struct snd_rawmidi ** rrawmidi) { struct snd_mpu401 *mpu; struct snd_rawmidi *rmidi; + int in_enable, out_enable; int err; if (rrawmidi) *rrawmidi = NULL; - if ((err = snd_rawmidi_new(card, "MPU-401U", device, 1, 1, &rmidi)) < 0) + if (! (info_flags & (MPU401_INFO_INPUT | MPU401_INFO_OUTPUT))) + info_flags |= MPU401_INFO_INPUT | MPU401_INFO_OUTPUT; + in_enable = (info_flags & MPU401_INFO_INPUT) ? 1 : 0; + out_enable = (info_flags & MPU401_INFO_OUTPUT) ? 1 : 0; + if ((err = snd_rawmidi_new(card, "MPU-401U", device, + out_enable, in_enable, &rmidi)) < 0) return err; mpu = kzalloc(sizeof(*mpu), GFP_KERNEL); if (mpu == NULL) { @@ -497,23 +551,23 @@ int snd_mpu401_uart_new(struct snd_card spin_lock_init(&mpu->output_lock); spin_lock_init(&mpu->timer_lock); mpu->hardware = hardware; - if (!integrated) { + if (! (info_flags & MPU401_INFO_INTEGRATED)) { int res_size = hardware == MPU401_HW_PC98II ? 4 : 2; - if ((mpu->res = request_region(port, res_size, "MPU401 UART")) == NULL) { - snd_printk(KERN_ERR "mpu401_uart: unable to grab port 0x%lx size %d\n", port, res_size); + mpu->res = request_region(port, res_size, "MPU401 UART"); + if (mpu->res == NULL) { + snd_printk(KERN_ERR "mpu401_uart: " + "unable to grab port 0x%lx size %d\n", + port, res_size); snd_device_free(card, rmidi); return -EBUSY; } } - switch (hardware) { - case MPU401_HW_AUREAL: + if (info_flags & MPU401_INFO_MMIO) { mpu->write = mpu401_write_mmio; mpu->read = mpu401_read_mmio; - break; - default: + } else { mpu->write = mpu401_write_port; mpu->read = mpu401_read_port; - break; } mpu->port = port; if (hardware == MPU401_HW_PC98II) @@ -521,30 +575,40 @@ int snd_mpu401_uart_new(struct snd_card else mpu->cport = port + 1; if (irq >= 0 && irq_flags) { - if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, "MPU401 UART", (void *) mpu)) { - snd_printk(KERN_ERR "mpu401_uart: unable to grab IRQ %d\n", irq); + if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags, + "MPU401 UART", (void *) mpu)) { + snd_printk(KERN_ERR "mpu401_uart: " + "unable to grab IRQ %d\n", irq); snd_device_free(card, rmidi); return -EBUSY; } } + mpu->info_flags = info_flags; mpu->irq = irq; mpu->irq_flags = irq_flags; if (card->shortname[0]) - snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", card->shortname); + snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI", + card->shortname); else - sprintf(rmidi->name, "MPU-401 MIDI %d-%d", card->number, device); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_mpu401_uart_output); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_mpu401_uart_input); - rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; + sprintf(rmidi->name, "MPU-401 MIDI %d-%d",card->number, device); + if (out_enable) { + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_mpu401_uart_output); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT; + } + if (in_enable) { + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_mpu401_uart_input); + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT; + if (out_enable) + rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX; + } mpu->rmidi = rmidi; if (rrawmidi) *rrawmidi = rmidi; return 0; } -EXPORT_SYMBOL(snd_mpu401_uart_interrupt); EXPORT_SYMBOL(snd_mpu401_uart_new); /* diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c index b7a0b42..e064d6c 100644 --- a/sound/drivers/mtpav.c +++ b/sound/drivers/mtpav.c @@ -590,7 +590,7 @@ static int __init snd_mtpav_get_ISA(stru return -EBUSY; } mcard->port = port; - if (request_irq(irq, snd_mtpav_irqh, SA_INTERRUPT, "MOTU MTPAV", mcard)) { + if (request_irq(irq, snd_mtpav_irqh, IRQF_DISABLED, "MOTU MTPAV", mcard)) { snd_printk("MTVAP IRQ %d busy\n", irq); return -EBUSY; } @@ -770,11 +770,15 @@ static int __init alsa_card_mtpav_init(v return err; device = platform_device_register_simple(SND_MTPAV_DRIVER, -1, NULL, 0); - if (IS_ERR(device)) { - platform_driver_unregister(&snd_mtpav_driver); - return PTR_ERR(device); - } - return 0; + if (!IS_ERR(device)) { + if (platform_get_drvdata(device)) + return 0; + platform_device_unregister(device); + err = -ENODEV; + } else + err = PTR_ERR(device); + platform_driver_unregister(&snd_mtpav_driver); + return err; } static void __exit alsa_card_mtpav_exit(void) diff --git a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c index 4f85569..87fe376 100644 --- a/sound/drivers/opl3/opl3_lib.c +++ b/sound/drivers/opl3/opl3_lib.c @@ -316,6 +316,8 @@ #endif } } +EXPORT_SYMBOL(snd_opl3_interrupt); + /* */ @@ -369,6 +371,8 @@ int snd_opl3_new(struct snd_card *card, return 0; } +EXPORT_SYMBOL(snd_opl3_new); + int snd_opl3_init(struct snd_opl3 *opl3) { if (! opl3->command) { @@ -393,6 +397,8 @@ int snd_opl3_init(struct snd_opl3 *opl3) return 0; } +EXPORT_SYMBOL(snd_opl3_init); + int snd_opl3_create(struct snd_card *card, unsigned long l_port, unsigned long r_port, @@ -451,6 +457,8 @@ int snd_opl3_create(struct snd_card *car return 0; } +EXPORT_SYMBOL(snd_opl3_create); + int snd_opl3_timer_new(struct snd_opl3 * opl3, int timer1_dev, int timer2_dev) { int err; @@ -468,6 +476,8 @@ int snd_opl3_timer_new(struct snd_opl3 * return 0; } +EXPORT_SYMBOL(snd_opl3_timer_new); + int snd_opl3_hwdep_new(struct snd_opl3 * opl3, int device, int seq_device, struct snd_hwdep ** rhwdep) @@ -526,17 +536,8 @@ #endif return 0; } -EXPORT_SYMBOL(snd_opl3_interrupt); -EXPORT_SYMBOL(snd_opl3_new); -EXPORT_SYMBOL(snd_opl3_init); -EXPORT_SYMBOL(snd_opl3_create); -EXPORT_SYMBOL(snd_opl3_timer_new); EXPORT_SYMBOL(snd_opl3_hwdep_new); -/* opl3_synth.c */ -EXPORT_SYMBOL(snd_opl3_regmap); -EXPORT_SYMBOL(snd_opl3_reset); - /* * INIT part */ diff --git a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c index fccf019..5fd3a4c 100644 --- a/sound/drivers/opl3/opl3_oss.c +++ b/sound/drivers/opl3/opl3_oss.c @@ -100,7 +100,8 @@ static int snd_opl3_oss_create_port(stru SNDRV_SEQ_PORT_CAP_WRITE, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | SNDRV_SEQ_PORT_TYPE_MIDI_GM | - SNDRV_SEQ_PORT_TYPE_SYNTH, + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, voices, voices, name); if (opl3->oss_chset->port < 0) { diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index 57becf3..96762c9 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -203,7 +203,9 @@ static int snd_opl3_synth_create_port(st SNDRV_SEQ_PORT_CAP_SUBS_WRITE, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | SNDRV_SEQ_PORT_TYPE_MIDI_GM | - SNDRV_SEQ_PORT_TYPE_SYNTH, + SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 16, voices, name); if (opl3->chset->port < 0) { diff --git a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c index 6db503f..a4b3543 100644 --- a/sound/drivers/opl3/opl3_synth.c +++ b/sound/drivers/opl3/opl3_synth.c @@ -58,6 +58,8 @@ char snd_opl3_regmap[MAX_OPL2_VOICES][4] { 0x12, 0x15, 0x00, 0x00 } /* is selected (only left reg block) */ }; +EXPORT_SYMBOL(snd_opl3_regmap); + /* * prototypes */ @@ -228,6 +230,7 @@ void snd_opl3_reset(struct snd_opl3 * op opl3->rhythm = 0; } +EXPORT_SYMBOL(snd_opl3_reset); static int snd_opl3_play_note(struct snd_opl3 * opl3, struct snd_dm_fm_note * note) { @@ -445,3 +448,4 @@ static int snd_opl3_set_connection(struc return 0; } + diff --git a/sound/drivers/opl4/opl4_lib.c b/sound/drivers/opl4/opl4_lib.c index 4bc860a..01997f2 100644 --- a/sound/drivers/opl4/opl4_lib.c +++ b/sound/drivers/opl4/opl4_lib.c @@ -43,6 +43,8 @@ void snd_opl4_write(struct snd_opl4 *opl outb(value, opl4->pcm_port + 1); } +EXPORT_SYMBOL(snd_opl4_write); + u8 snd_opl4_read(struct snd_opl4 *opl4, u8 reg) { snd_opl4_wait(opl4); @@ -52,6 +54,8 @@ u8 snd_opl4_read(struct snd_opl4 *opl4, return inb(opl4->pcm_port + 1); } +EXPORT_SYMBOL(snd_opl4_read); + void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size) { unsigned long flags; @@ -76,6 +80,8 @@ void snd_opl4_read_memory(struct snd_opl spin_unlock_irqrestore(&opl4->reg_lock, flags); } +EXPORT_SYMBOL(snd_opl4_read_memory); + void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, int size) { unsigned long flags; @@ -100,6 +106,8 @@ void snd_opl4_write_memory(struct snd_op spin_unlock_irqrestore(&opl4->reg_lock, flags); } +EXPORT_SYMBOL(snd_opl4_write_memory); + static void snd_opl4_enable_opl4(struct snd_opl4 *opl4) { outb(OPL3_REG_MODE, opl4->fm_port + 2); @@ -256,10 +264,6 @@ #endif return 0; } -EXPORT_SYMBOL(snd_opl4_write); -EXPORT_SYMBOL(snd_opl4_read); -EXPORT_SYMBOL(snd_opl4_write_memory); -EXPORT_SYMBOL(snd_opl4_read_memory); EXPORT_SYMBOL(snd_opl4_create); static int __init alsa_opl4_init(void) diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c index dc0dcdc..43d8a2b 100644 --- a/sound/drivers/opl4/opl4_seq.c +++ b/sound/drivers/opl4/opl4_seq.c @@ -164,7 +164,9 @@ static int snd_opl4_seq_new_device(struc SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | - SNDRV_SEQ_PORT_TYPE_MIDI_GM, + SNDRV_SEQ_PORT_TYPE_MIDI_GM | + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 16, 24, "OPL4 Wavetable Port"); if (opl4->chset->port < 0) { diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c index c01b4c5..52afb4b 100644 --- a/sound/drivers/serial-u16550.c +++ b/sound/drivers/serial-u16550.c @@ -795,7 +795,7 @@ static int __init snd_uart16550_create(s if (irq >= 0 && irq != SNDRV_AUTO_IRQ) { if (request_irq(irq, snd_uart16550_interrupt, - SA_INTERRUPT, "Serial MIDI", (void *) uart)) { + IRQF_DISABLED, "Serial MIDI", (void *) uart)) { snd_printk("irq %d busy. Using Polling.\n", irq); } else { uart->irq = irq; @@ -998,6 +998,10 @@ static int __init alsa_card_serial_init( i, NULL, 0); if (IS_ERR(device)) continue; + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + continue; + } devices[i] = device; cards++; } diff --git a/sound/drivers/virmidi.c b/sound/drivers/virmidi.c index 26eb249..59171f8 100644 --- a/sound/drivers/virmidi.c +++ b/sound/drivers/virmidi.c @@ -171,6 +171,10 @@ static int __init alsa_card_virmidi_init i, NULL, 0); if (IS_ERR(device)) continue; + if (!platform_get_drvdata(device)) { + platform_device_unregister(device); + continue; + } devices[i] = device; cards++; } diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index fa4a2b5..a601682 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -70,6 +70,8 @@ #endif return -EIO; } +EXPORT_SYMBOL(snd_vx_check_reg_bit); + /* * vx_send_irq_dsp - set command irq bit * @num: the requested IRQ type, IRQ_XXX @@ -465,6 +467,8 @@ #endif return 0; } +EXPORT_SYMBOL(snd_vx_load_boot_image); + /* * vx_test_irq_src - query the source of interrupts * @@ -545,6 +549,7 @@ irqreturn_t snd_vx_irq_handler(int irq, return IRQ_HANDLED; } +EXPORT_SYMBOL(snd_vx_irq_handler); /* */ @@ -635,7 +640,7 @@ static void vx_proc_init(struct vx_core struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "vx-status", &entry)) - snd_info_set_text_ops(entry, chip, 1024, vx_proc_read); + snd_info_set_text_ops(entry, chip, vx_proc_read); } @@ -657,6 +662,8 @@ int snd_vx_dsp_boot(struct vx_core *chip return 0; } +EXPORT_SYMBOL(snd_vx_dsp_boot); + /** * snd_vx_dsp_load - load the DSP image */ @@ -705,6 +712,8 @@ int snd_vx_dsp_load(struct vx_core *chip return 0; } +EXPORT_SYMBOL(snd_vx_dsp_load); + #ifdef CONFIG_PM /* * suspend @@ -721,6 +730,8 @@ int snd_vx_suspend(struct vx_core *chip, return 0; } +EXPORT_SYMBOL(snd_vx_suspend); + /* * resume */ @@ -747,6 +758,7 @@ int snd_vx_resume(struct vx_core *chip) return 0; } +EXPORT_SYMBOL(snd_vx_resume); #endif /** @@ -790,6 +802,8 @@ struct vx_core *snd_vx_create(struct snd return chip; } +EXPORT_SYMBOL(snd_vx_create); + /* * module entries */ @@ -804,19 +818,3 @@ static void __exit alsa_vx_core_exit(voi module_init(alsa_vx_core_init) module_exit(alsa_vx_core_exit) - -/* - * exports - */ -EXPORT_SYMBOL(snd_vx_check_reg_bit); -EXPORT_SYMBOL(snd_vx_create); -EXPORT_SYMBOL(snd_vx_setup_firmware); -EXPORT_SYMBOL(snd_vx_free_firmware); -EXPORT_SYMBOL(snd_vx_irq_handler); -EXPORT_SYMBOL(snd_vx_dsp_boot); -EXPORT_SYMBOL(snd_vx_dsp_load); -EXPORT_SYMBOL(snd_vx_load_boot_image); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_vx_suspend); -EXPORT_SYMBOL(snd_vx_resume); -#endif diff --git a/sound/drivers/vx/vx_hwdep.c b/sound/drivers/vx/vx_hwdep.c index d837783..e1920af 100644 --- a/sound/drivers/vx/vx_hwdep.c +++ b/sound/drivers/vx/vx_hwdep.c @@ -250,3 +250,6 @@ #endif } #endif /* SND_VX_FW_LOADER */ + +EXPORT_SYMBOL(snd_vx_setup_firmware); +EXPORT_SYMBOL(snd_vx_free_firmware); diff --git a/sound/i2c/i2c.c b/sound/i2c/i2c.c index edfe76f..b60fb18 100644 --- a/sound/i2c/i2c.c +++ b/sound/i2c/i2c.c @@ -106,6 +106,8 @@ int snd_i2c_bus_create(struct snd_card * return 0; } +EXPORT_SYMBOL(snd_i2c_bus_create); + int snd_i2c_device_create(struct snd_i2c_bus *bus, const char *name, unsigned char addr, struct snd_i2c_device **rdevice) { @@ -124,6 +126,8 @@ int snd_i2c_device_create(struct snd_i2c return 0; } +EXPORT_SYMBOL(snd_i2c_device_create); + int snd_i2c_device_free(struct snd_i2c_device *device) { if (device->bus) @@ -134,22 +138,29 @@ int snd_i2c_device_free(struct snd_i2c_d return 0; } +EXPORT_SYMBOL(snd_i2c_device_free); + int snd_i2c_sendbytes(struct snd_i2c_device *device, unsigned char *bytes, int count) { return device->bus->ops->sendbytes(device, bytes, count); } +EXPORT_SYMBOL(snd_i2c_sendbytes); int snd_i2c_readbytes(struct snd_i2c_device *device, unsigned char *bytes, int count) { return device->bus->ops->readbytes(device, bytes, count); } +EXPORT_SYMBOL(snd_i2c_readbytes); + int snd_i2c_probeaddr(struct snd_i2c_bus *bus, unsigned short addr) { return bus->ops->probeaddr(bus, addr); } +EXPORT_SYMBOL(snd_i2c_probeaddr); + /* * bit-operations */ @@ -320,12 +331,6 @@ static int snd_i2c_bit_probeaddr(struct return err; } -EXPORT_SYMBOL(snd_i2c_bus_create); -EXPORT_SYMBOL(snd_i2c_device_create); -EXPORT_SYMBOL(snd_i2c_device_free); -EXPORT_SYMBOL(snd_i2c_sendbytes); -EXPORT_SYMBOL(snd_i2c_readbytes); -EXPORT_SYMBOL(snd_i2c_probeaddr); static int __init alsa_i2c_init(void) { diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c index 746500e..b074fdd 100644 --- a/sound/i2c/l3/uda1341.c +++ b/sound/i2c/l3/uda1341.c @@ -517,9 +517,9 @@ static void __devinit snd_uda1341_proc_i struct snd_info_entry *entry; if (! snd_card_proc_new(card, "uda1341", &entry)) - snd_info_set_text_ops(entry, clnt, 1024, snd_uda1341_proc_read); + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_read); if (! snd_card_proc_new(card, "uda1341-regs", &entry)) - snd_info_set_text_ops(entry, clnt, 1024, snd_uda1341_proc_regs_read); + snd_info_set_text_ops(entry, clnt, snd_uda1341_proc_regs_read); } /* }}} */ diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c index 045e32a..dc7cc20 100644 --- a/sound/i2c/other/ak4xxx-adda.c +++ b/sound/i2c/other/ak4xxx-adda.c @@ -34,7 +34,8 @@ MODULE_AUTHOR("Jaroslav Kysela ops.lock(ak, chip); ak->ops.write(ak, chip, reg, val); @@ -52,6 +53,67 @@ void snd_akm4xxx_write(struct snd_akm4xx ak->ops.unlock(ak, chip); } +EXPORT_SYMBOL(snd_akm4xxx_write); + +/* reset procedure for AK4524 and AK4528 */ +static void ak4524_reset(struct snd_akm4xxx *ak, int state) +{ + unsigned int chip; + unsigned char reg, maxreg; + + if (ak->type == SND_AK4528) + maxreg = 0x06; + else + maxreg = 0x08; + for (chip = 0; chip < ak->num_dacs/2; chip++) { + snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03); + if (state) + continue; + /* DAC volumes */ + for (reg = 0x04; reg < maxreg; reg++) + snd_akm4xxx_write(ak, chip, reg, + snd_akm4xxx_get(ak, chip, reg)); + if (ak->type == SND_AK4528) + continue; + /* IPGA */ + for (reg = 0x04; reg < 0x06; reg++) + snd_akm4xxx_write(ak, chip, reg, + snd_akm4xxx_get_ipga(ak, chip, reg)); + } +} + +/* reset procedure for AK4355 and AK4358 */ +static void ak4355_reset(struct snd_akm4xxx *ak, int state) +{ + unsigned char reg; + + if (state) { + snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */ + return; + } + for (reg = 0x00; reg < 0x0b; reg++) + if (reg != 0x01) + snd_akm4xxx_write(ak, 0, reg, + snd_akm4xxx_get(ak, 0, reg)); + snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */ +} + +/* reset procedure for AK4381 */ +static void ak4381_reset(struct snd_akm4xxx *ak, int state) +{ + unsigned int chip; + unsigned char reg; + + for (chip = 0; chip < ak->num_dacs/2; chip++) { + snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f); + if (state) + continue; + for (reg = 0x01; reg < 0x05; reg++) + snd_akm4xxx_write(ak, chip, reg, + snd_akm4xxx_get(ak, chip, reg)); + } +} + /* * reset the AKM codecs * @state: 1 = reset codec, 0 = restore the registers @@ -60,52 +122,26 @@ void snd_akm4xxx_write(struct snd_akm4xx */ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state) { - unsigned int chip; - unsigned char reg; - switch (ak->type) { case SND_AK4524: case SND_AK4528: - for (chip = 0; chip < ak->num_dacs/2; chip++) { - snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03); - if (state) - continue; - /* DAC volumes */ - for (reg = 0x04; reg < (ak->type == SND_AK4528 ? 0x06 : 0x08); reg++) - snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg)); - if (ak->type == SND_AK4528) - continue; - /* IPGA */ - for (reg = 0x04; reg < 0x06; reg++) - snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get_ipga(ak, chip, reg)); - } + ak4524_reset(ak, state); break; case SND_AK4529: /* FIXME: needed for ak4529? */ break; case SND_AK4355: case SND_AK4358: - if (state) { - snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */ - return; - } - for (reg = 0x00; reg < 0x0b; reg++) - if (reg != 0x01) - snd_akm4xxx_write(ak, 0, reg, snd_akm4xxx_get(ak, 0, reg)); - snd_akm4xxx_write(ak, 0, 0x01, 0x01); /* un-reset, unmute */ + ak4355_reset(ak, state); break; case SND_AK4381: - for (chip = 0; chip < ak->num_dacs/2; chip++) { - snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f); - if (state) - continue; - for (reg = 0x01; reg < 0x05; reg++) - snd_akm4xxx_write(ak, chip, reg, snd_akm4xxx_get(ak, chip, reg)); - } + ak4381_reset(ak, state); break; } } +EXPORT_SYMBOL(snd_akm4xxx_reset); + /* * initialize all the ak4xxx chips */ @@ -153,7 +189,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx }; static unsigned char inits_ak4355[] = { 0x01, 0x02, /* 1: reset and soft-mute */ - 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */ + 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, + * disable DZF, sharp roll-off, RSTN#=0 */ 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */ // 0x02, 0x2e, /* quad speed */ 0x03, 0x01, /* 3: de-emphasis off */ @@ -169,7 +206,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx }; static unsigned char inits_ak4358[] = { 0x01, 0x02, /* 1: reset and soft-mute */ - 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, disable DZF, sharp roll-off, RSTN#=0 */ + 0x00, 0x06, /* 0: mode3(i2s), disable auto-clock detect, + * disable DZF, sharp roll-off, RSTN#=0 */ 0x02, 0x0e, /* 2: DA's power up, normal speed, RSTN#=0 */ // 0x02, 0x2e, /* quad speed */ 0x03, 0x01, /* 3: de-emphasis off */ @@ -187,7 +225,8 @@ void snd_akm4xxx_init(struct snd_akm4xxx }; static unsigned char inits_ak4381[] = { 0x00, 0x0c, /* 0: mode3(i2s), disable auto-clock detect */ - 0x01, 0x02, /* 1: de-emphasis off, normal speed, sharp roll-off, DZF off */ + 0x01, 0x02, /* 1: de-emphasis off, normal speed, + * sharp roll-off, DZF off */ // 0x01, 0x12, /* quad speed */ 0x02, 0x00, /* 2: DZF disabled */ 0x03, 0x00, /* 3: LATT 0 */ @@ -239,12 +278,15 @@ void snd_akm4xxx_init(struct snd_akm4xxx } } +EXPORT_SYMBOL(snd_akm4xxx_init); + #define AK_GET_CHIP(val) (((val) >> 8) & 0xff) #define AK_GET_ADDR(val) ((val) & 0xff) #define AK_GET_SHIFT(val) (((val) >> 16) & 0x7f) #define AK_GET_INVERT(val) (((val) >> 23) & 1) #define AK_GET_MASK(val) (((val) >> 24) & 0xff) -#define AK_COMPOSE(chip,addr,shift,mask) (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24)) +#define AK_COMPOSE(chip,addr,shift,mask) \ + (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24)) #define AK_INVERT (1<<23) static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol, @@ -292,6 +334,64 @@ static int snd_akm4xxx_volume_put(struct return change; } +static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int mask = AK_GET_MASK(kcontrol->private_value); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + return 0; +} + +static int snd_akm4xxx_stereo_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int invert = AK_GET_INVERT(kcontrol->private_value); + unsigned int mask = AK_GET_MASK(kcontrol->private_value); + unsigned char val = snd_akm4xxx_get(ak, chip, addr); + + ucontrol->value.integer.value[0] = invert ? mask - val : val; + + val = snd_akm4xxx_get(ak, chip, addr+1); + ucontrol->value.integer.value[1] = invert ? mask - val : val; + + return 0; +} + +static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); + int chip = AK_GET_CHIP(kcontrol->private_value); + int addr = AK_GET_ADDR(kcontrol->private_value); + int invert = AK_GET_INVERT(kcontrol->private_value); + unsigned int mask = AK_GET_MASK(kcontrol->private_value); + unsigned char nval = ucontrol->value.integer.value[0] % (mask+1); + int change0, change1; + + if (invert) + nval = mask - nval; + change0 = snd_akm4xxx_get(ak, chip, addr) != nval; + if (change0) + snd_akm4xxx_write(ak, chip, addr, nval); + + nval = ucontrol->value.integer.value[1] % (mask+1); + if (invert) + nval = mask - nval; + change1 = snd_akm4xxx_get(ak, chip, addr+1) != nval; + if (change1) + snd_akm4xxx_write(ak, chip, addr+1, nval); + + + return change0 || change1; +} + static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -308,7 +408,8 @@ static int snd_akm4xxx_ipga_gain_get(str struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol); int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); - ucontrol->value.integer.value[0] = snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f; + ucontrol->value.integer.value[0] = + snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f; return 0; } @@ -336,7 +437,8 @@ static int snd_akm4xxx_deemphasis_info(s uinfo->value.enumerated.items = 4; if (uinfo->value.enumerated.item >= 4) uinfo->value.enumerated.item = 3; - strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); return 0; } @@ -347,7 +449,8 @@ static int snd_akm4xxx_deemphasis_get(st int chip = AK_GET_CHIP(kcontrol->private_value); int addr = AK_GET_ADDR(kcontrol->private_value); int shift = AK_GET_SHIFT(kcontrol->private_value); - ucontrol->value.enumerated.item[0] = (snd_akm4xxx_get(ak, chip, addr) >> shift) & 3; + ucontrol->value.enumerated.item[0] = + (snd_akm4xxx_get(ak, chip, addr) >> shift) & 3; return 0; } @@ -361,7 +464,8 @@ static int snd_akm4xxx_deemphasis_put(st unsigned char nval = ucontrol->value.enumerated.item[0] & 3; int change; - nval = (nval << shift) | (snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift)); + nval = (nval << shift) | + (snd_akm4xxx_get(ak, chip, addr) & ~(3 << shift)); change = snd_akm4xxx_get(ak, chip, addr) != nval; if (change) snd_akm4xxx_write(ak, chip, addr, nval); @@ -377,51 +481,86 @@ int snd_akm4xxx_build_controls(struct sn unsigned int idx, num_emphs; struct snd_kcontrol *ctl; int err; + int mixer_ch = 0; + int num_stereo; ctl = kmalloc(sizeof(*ctl), GFP_KERNEL); if (! ctl) return -ENOMEM; - for (idx = 0; idx < ak->num_dacs; ++idx) { + for (idx = 0; idx < ak->num_dacs; ) { memset(ctl, 0, sizeof(*ctl)); - strcpy(ctl->id.name, "DAC Volume"); - ctl->id.index = idx + ak->idx_offset * 2; + if (ak->channel_names == NULL) { + strcpy(ctl->id.name, "DAC Volume"); + num_stereo = 1; + ctl->id.index = mixer_ch + ak->idx_offset * 2; + } else { + strcpy(ctl->id.name, ak->channel_names[mixer_ch]); + num_stereo = ak->num_stereo[mixer_ch]; + ctl->id.index = 0; + } ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; ctl->count = 1; - ctl->info = snd_akm4xxx_volume_info; - ctl->get = snd_akm4xxx_volume_get; - ctl->put = snd_akm4xxx_volume_put; + if (num_stereo == 2) { + ctl->info = snd_akm4xxx_stereo_volume_info; + ctl->get = snd_akm4xxx_stereo_volume_get; + ctl->put = snd_akm4xxx_stereo_volume_put; + } else { + ctl->info = snd_akm4xxx_volume_info; + ctl->get = snd_akm4xxx_volume_get; + ctl->put = snd_akm4xxx_volume_put; + } switch (ak->type) { case SND_AK4524: - ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); /* register 6 & 7 */ + /* register 6 & 7 */ + ctl->private_value = + AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127); break; case SND_AK4528: - ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */ + /* register 4 & 5 */ + ctl->private_value = + AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); break; case SND_AK4529: { - int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; /* registers 2-7 and b,c */ - ctl->private_value = AK_COMPOSE(0, val, 0, 255) | AK_INVERT; + /* registers 2-7 and b,c */ + int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb; + ctl->private_value = + AK_COMPOSE(0, val, 0, 255) | AK_INVERT; break; } case SND_AK4355: - ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */ + /* register 4-9, chip #0 only */ + ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); break; case SND_AK4358: if (idx >= 6) - ctl->private_value = AK_COMPOSE(0, idx + 5, 0, 255); /* register 4-9, chip #0 only */ + /* register 4-9, chip #0 only */ + ctl->private_value = + AK_COMPOSE(0, idx + 5, 0, 255); else - ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255); /* register 4-9, chip #0 only */ + /* register 4-9, chip #0 only */ + ctl->private_value = + AK_COMPOSE(0, idx + 4, 0, 255); break; case SND_AK4381: - ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); /* register 3 & 4 */ + /* register 3 & 4 */ + ctl->private_value = + AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255); break; default: err = -EINVAL; goto __error; } + ctl->private_data = ak; - if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0) + err = snd_ctl_add(ak->card, + snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| + SNDRV_CTL_ELEM_ACCESS_WRITE)); + if (err < 0) goto __error; + + idx += num_stereo; + mixer_ch++; } for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) { memset(ctl, 0, sizeof(*ctl)); @@ -432,9 +571,14 @@ int snd_akm4xxx_build_controls(struct sn ctl->info = snd_akm4xxx_volume_info; ctl->get = snd_akm4xxx_volume_get; ctl->put = snd_akm4xxx_volume_put; - ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); /* register 4 & 5 */ + /* register 4 & 5 */ + ctl->private_value = + AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127); ctl->private_data = ak; - if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0) + err = snd_ctl_add(ak->card, + snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| + SNDRV_CTL_ELEM_ACCESS_WRITE)); + if (err < 0) goto __error; memset(ctl, 0, sizeof(*ctl)); @@ -445,9 +589,13 @@ int snd_akm4xxx_build_controls(struct sn ctl->info = snd_akm4xxx_ipga_gain_info; ctl->get = snd_akm4xxx_ipga_gain_get; ctl->put = snd_akm4xxx_ipga_gain_put; - ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); /* register 4 & 5 */ + /* register 4 & 5 */ + ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0); ctl->private_data = ak; - if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0) + err = snd_ctl_add(ak->card, + snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| + SNDRV_CTL_ELEM_ACCESS_WRITE)); + if (err < 0) goto __error; } if (ak->type == SND_AK4355 || ak->type == SND_AK4358) @@ -466,11 +614,13 @@ int snd_akm4xxx_build_controls(struct sn switch (ak->type) { case SND_AK4524: case SND_AK4528: - ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); /* register 3 */ + /* register 3 */ + ctl->private_value = AK_COMPOSE(idx, 3, 0, 0); break; case SND_AK4529: { int shift = idx == 3 ? 6 : (2 - idx) * 2; - ctl->private_value = AK_COMPOSE(0, 8, shift, 0); /* register 8 with shift */ + /* register 8 with shift */ + ctl->private_value = AK_COMPOSE(0, 8, shift, 0); break; } case SND_AK4355: @@ -482,7 +632,10 @@ int snd_akm4xxx_build_controls(struct sn break; } ctl->private_data = ak; - if ((err = snd_ctl_add(ak->card, snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE))) < 0) + err = snd_ctl_add(ak->card, + snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ| + SNDRV_CTL_ELEM_ACCESS_WRITE)); + if (err < 0) goto __error; } err = 0; @@ -492,6 +645,8 @@ int snd_akm4xxx_build_controls(struct sn return err; } +EXPORT_SYMBOL(snd_akm4xxx_build_controls); + static int __init alsa_akm4xxx_module_init(void) { return 0; @@ -503,8 +658,3 @@ static void __exit alsa_akm4xxx_module_e module_init(alsa_akm4xxx_module_init) module_exit(alsa_akm4xxx_module_exit) - -EXPORT_SYMBOL(snd_akm4xxx_write); -EXPORT_SYMBOL(snd_akm4xxx_reset); -EXPORT_SYMBOL(snd_akm4xxx_init); -EXPORT_SYMBOL(snd_akm4xxx_build_controls); diff --git a/sound/isa/ad1816a/ad1816a.c b/sound/isa/ad1816a/ad1816a.c index 31f299a..b33a5fb 100644 --- a/sound/isa/ad1816a/ad1816a.c +++ b/sound/isa/ad1816a/ad1816a.c @@ -232,7 +232,7 @@ static int __devinit snd_card_ad1816a_pr if (mpu_port[dev] > 0) { if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - mpu_port[dev], 0, mpu_irq[dev], SA_INTERRUPT, + mpu_port[dev], 0, mpu_irq[dev], IRQF_DISABLED, NULL) < 0) printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]); } diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c index fd8fe16..8fcf2c1 100644 --- a/sound/isa/ad1816a/ad1816a_lib.c +++ b/sound/isa/ad1816a/ad1816a_lib.c @@ -599,7 +599,7 @@ int __devinit snd_ad1816a_create(struct snd_ad1816a_free(chip); return -EBUSY; } - if (request_irq(irq, snd_ad1816a_interrupt, SA_INTERRUPT, "AD1816A", (void *) chip)) { + if (request_irq(irq, snd_ad1816a_interrupt, IRQF_DISABLED, "AD1816A", (void *) chip)) { snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq); snd_ad1816a_free(chip); return -EBUSY; diff --git a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c index e0f8baa..e711f87 100644 --- a/sound/isa/ad1848/ad1848_lib.c +++ b/sound/isa/ad1848/ad1848_lib.c @@ -902,7 +902,7 @@ int snd_ad1848_create(struct snd_card *c snd_ad1848_free(chip); return -EBUSY; } - if (request_irq(irq, snd_ad1848_interrupt, SA_INTERRUPT, "AD1848", (void *) chip)) { + if (request_irq(irq, snd_ad1848_interrupt, IRQF_DISABLED, "AD1848", (void *) chip)) { snd_printk(KERN_ERR "ad1848: can't grab IRQ %d\n", irq); snd_ad1848_free(chip); return -EBUSY; diff --git a/sound/isa/als100.c b/sound/isa/als100.c index a52bd8a..f2bcfb2 100644 --- a/sound/isa/als100.c +++ b/sound/isa/als100.c @@ -250,7 +250,7 @@ static int __devinit snd_card_als100_pro if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100, mpu_port[dev], 0, - mpu_irq[dev], SA_INTERRUPT, + mpu_irq[dev], IRQF_DISABLED, NULL) < 0) snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); } diff --git a/sound/isa/azt2320.c b/sound/isa/azt2320.c index 15e5928..b615538 100644 --- a/sound/isa/azt2320.c +++ b/sound/isa/azt2320.c @@ -279,7 +279,7 @@ static int __devinit snd_card_azt2320_pr if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) { if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320, mpu_port[dev], 0, - mpu_irq[dev], SA_INTERRUPT, + mpu_irq[dev], IRQF_DISABLED, NULL) < 0) snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]); } diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c index 397310f..696a5c8 100644 --- a/sound/isa/cs423x/cs4231.c +++ b/sound/isa/cs423x/cs4231.c @@ -122,7 +122,7 @@ static int __init snd_cs4231_probe(struc if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, mpu_port[dev], 0, mpu_irq[dev], - mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, + mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0) printk(KERN_WARNING "cs4231: MPU401 not detected\n"); } diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c index 823db82..fbb2017 100644 --- a/sound/isa/cs423x/cs4231_lib.c +++ b/sound/isa/cs423x/cs4231_lib.c @@ -1454,7 +1454,7 @@ int snd_cs4231_create(struct snd_card *c return -ENODEV; } chip->cport = cport; - if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, SA_INTERRUPT, "CS4231", (void *) chip)) { + if (!(hwshare & CS4231_HWSHARE_IRQ) && request_irq(irq, snd_cs4231_interrupt, IRQF_DISABLED, "CS4231", (void *) chip)) { snd_printk(KERN_ERR "cs4231: can't grab IRQ %d\n", irq); snd_cs4231_free(chip); return -EBUSY; diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c index f7fa779..07ffd5c 100644 --- a/sound/isa/cs423x/cs4236.c +++ b/sound/isa/cs423x/cs4236.c @@ -535,7 +535,7 @@ #endif if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232, mpu_port[dev], 0, mpu_irq[dev], - mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, NULL) < 0) + mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0) printk(KERN_WARNING IDENT ": MPU401 not detected\n"); } diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c index 0acb4e5..c20a4b1 100644 --- a/sound/isa/dt019x.c +++ b/sound/isa/dt019x.c @@ -240,7 +240,7 @@ static int __devinit snd_card_dt019x_pro MPU401_HW_MPU401, mpu_port[dev], 0, mpu_irq[dev], - mpu_irq[dev] >= 0 ? SA_INTERRUPT : 0, + mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0) snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", mpu_port[dev]); } diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c index e90689e..7f29f56 100644 --- a/sound/isa/es1688/es1688.c +++ b/sound/isa/es1688/es1688.c @@ -153,7 +153,7 @@ static int __init snd_es1688_probe(struc if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, chip->mpu_port, 0, xmpu_irq, - SA_INTERRUPT, + IRQF_DISABLED, NULL)) < 0) goto _err; } diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c index 702ad51..7e985d3 100644 --- a/sound/isa/es1688/es1688_lib.c +++ b/sound/isa/es1688/es1688_lib.c @@ -659,7 +659,7 @@ int snd_es1688_create(struct snd_card *c snd_es1688_free(chip); return -EBUSY; } - if (request_irq(irq, snd_es1688_interrupt, SA_INTERRUPT, "ES1688", (void *) chip)) { + if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) { snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq); snd_es1688_free(chip); return -EBUSY; diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c index e6945db..34998de 100644 --- a/sound/isa/es18xx.c +++ b/sound/isa/es18xx.c @@ -1837,7 +1837,7 @@ static int __devinit snd_es18xx_new_devi return -EBUSY; } - if (request_irq(irq, snd_es18xx_interrupt, SA_INTERRUPT, "ES18xx", (void *) chip)) { + if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx", (void *) chip)) { snd_es18xx_free(chip); snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq); return -EBUSY; @@ -2088,7 +2088,8 @@ static int __devinit snd_audiodrive_pnp( kfree(cfg); return -EAGAIN; } - snd_printdd("pnp: port=0x%lx\n", pnp_port_start(acard->devc, 0)); + snd_printdd("pnp: port=0x%llx\n", + (unsigned long long)pnp_port_start(acard->devc, 0)); /* PnP initialization */ pdev = acard->dev; pnp_init_resource_table(cfg); diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c index c19ba29..42db375 100644 --- a/sound/isa/gus/gus_irq.c +++ b/sound/isa/gus/gus_irq.c @@ -136,7 +136,7 @@ void snd_gus_irq_profile_init(struct snd struct snd_info_entry *entry; if (! snd_card_proc_new(gus->card, "gusirq", &entry)) - snd_info_set_text_ops(entry, gus, 1024, snd_gus_irq_info_read); + snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read); } #endif diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c index 53eeaf3..b680fdd 100644 --- a/sound/isa/gus/gus_main.c +++ b/sound/isa/gus/gus_main.c @@ -179,7 +179,7 @@ int snd_gus_create(struct snd_card *card snd_gus_free(gus); return -EBUSY; } - if (irq >= 0 && request_irq(irq, snd_gus_interrupt, SA_INTERRUPT, "GUS GF1", (void *) gus)) { + if (irq >= 0 && request_irq(irq, snd_gus_interrupt, IRQF_DISABLED, "GUS GF1", (void *) gus)) { snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq); snd_gus_free(gus); return -EBUSY; diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c index 3c0d27a..f50c276 100644 --- a/sound/isa/gus/gus_mem.c +++ b/sound/isa/gus/gus_mem.c @@ -264,10 +264,8 @@ #endif if (snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; #ifdef CONFIG_SND_DEBUG - if (! snd_card_proc_new(gus->card, "gusmem", &entry)) { - snd_info_set_text_ops(entry, gus, 1024, snd_gf1_mem_info_read); - entry->c.text.read_size = 256 * 1024; - } + if (! snd_card_proc_new(gus->card, "gusmem", &entry)) + snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read); #endif return 0; } diff --git a/sound/isa/gus/gus_synth.c b/sound/isa/gus/gus_synth.c index 2767cc1..3e4d4d6 100644 --- a/sound/isa/gus/gus_synth.c +++ b/sound/isa/gus/gus_synth.c @@ -194,7 +194,9 @@ static int snd_gus_synth_create_port(str &callbacks, SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | - SNDRV_SEQ_PORT_TYPE_SYNTH, + SNDRV_SEQ_PORT_TYPE_SYNTH | + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 16, 0, name); if (p->chset->port < 0) { diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c index 05852fc..22cdddb 100644 --- a/sound/isa/gus/gusextreme.c +++ b/sound/isa/gus/gusextreme.c @@ -301,7 +301,7 @@ static int __init snd_gusextreme_probe(s (err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688, es1688->mpu_port, 0, xmpu_irq, - SA_INTERRUPT, + IRQF_DISABLED, NULL)) < 0) goto out; diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c index fcf2c8f..ac11cae 100644 --- a/sound/isa/gus/gusmax.c +++ b/sound/isa/gus/gusmax.c @@ -292,7 +292,7 @@ static int __init snd_gusmax_probe(struc goto _err; } - if (request_irq(xirq, snd_gusmax_interrupt, SA_INTERRUPT, "GUS MAX", (void *)maxcard)) { + if (request_irq(xirq, snd_gusmax_interrupt, IRQF_DISABLED, "GUS MAX", (void *)maxcard)) { snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq); err = -EBUSY; goto _err; diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c index 4298d33..ea69f25 100644 --- a/sound/isa/gus/interwave.c +++ b/sound/isa/gus/interwave.c @@ -70,9 +70,9 @@ static int dma1[SNDRV_CARDS] = SNDRV_DEF static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29}; /* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */ -static int midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int midi[SNDRV_CARDS]; static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; -static int effect[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int effect[SNDRV_CARDS]; #ifdef SNDRV_STB #define PFX "interwave-stb: " @@ -611,10 +611,10 @@ #endif if (dma2[dev] >= 0) dma2[dev] = pnp_dma(pdev, 1); irq[dev] = pnp_irq(pdev, 0); - snd_printdd("isapnp IW: sb port=0x%lx, gf1 port=0x%lx, codec port=0x%lx\n", - pnp_port_start(pdev, 0), - pnp_port_start(pdev, 1), - pnp_port_start(pdev, 2)); + snd_printdd("isapnp IW: sb port=0x%llx, gf1 port=0x%llx, codec port=0x%llx\n", + (unsigned long long)pnp_port_start(pdev, 0), + (unsigned long long)pnp_port_start(pdev, 1), + (unsigned long long)pnp_port_start(pdev, 2)); snd_printdd("isapnp IW: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]); #ifdef SNDRV_STB /* Tone Control initialization */ @@ -706,7 +706,7 @@ #endif if ((err = snd_gus_initialize(gus)) < 0) return err; - if (request_irq(xirq, snd_interwave_interrupt, SA_INTERRUPT, + if (request_irq(xirq, snd_interwave_interrupt, IRQF_DISABLED, "InterWave", iwcard)) { snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq); return -EBUSY; diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c index 6d88905..4031b61 100644 --- a/sound/isa/opl3sa2.c +++ b/sound/isa/opl3sa2.c @@ -59,7 +59,7 @@ static long midi_port[SNDRV_CARDS] = SND static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 0,1,3,5,9,11,12,15 */ static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */ -static int opl3sa3_ymode[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* 0,1,2,3 */ /*SL Added*/ +static int opl3sa3_ymode[SNDRV_CARDS]; /* 0,1,2,3 */ /*SL Added*/ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for OPL3-SA soundcard."); @@ -221,7 +221,7 @@ static void snd_opl3sa2_write(struct snd spin_unlock_irqrestore(&chip->reg_lock, flags); } -static int __init snd_opl3sa2_detect(struct snd_opl3sa2 *chip) +static int __devinit snd_opl3sa2_detect(struct snd_opl3sa2 *chip) { struct snd_card *card; unsigned long port; @@ -489,7 +489,7 @@ static void snd_opl3sa2_master_free(stru chip->master_volume = NULL; } -static int __init snd_opl3sa2_mixer(struct snd_opl3sa2 *chip) +static int __devinit snd_opl3sa2_mixer(struct snd_opl3sa2 *chip) { struct snd_card *card = chip->card; struct snd_ctl_elem_id id1, id2; @@ -583,8 +583,8 @@ static int snd_opl3sa2_resume(struct snd #endif /* CONFIG_PM */ #ifdef CONFIG_PNP -static int __init snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip, - struct pnp_dev *pdev) +static int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip, + struct pnp_dev *pdev) { struct pnp_resource_table * cfg; int err; @@ -683,7 +683,7 @@ static int __devinit snd_opl3sa2_probe(s chip->single_dma = 1; if ((err = snd_opl3sa2_detect(chip)) < 0) return err; - if (request_irq(xirq, snd_opl3sa2_interrupt, SA_INTERRUPT, "OPL3-SA2", chip)) { + if (request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED, "OPL3-SA2", chip)) { snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq); return -ENODEV; } @@ -862,7 +862,7 @@ #endif }; #endif /* CONFIG_PNP */ -static int __init snd_opl3sa2_nonpnp_probe(struct platform_device *pdev) +static int __devinit snd_opl3sa2_nonpnp_probe(struct platform_device *pdev) { struct snd_card *card; int err; diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c index e6bfcf7..1dd9837 100644 --- a/sound/isa/opti9xx/miro.c +++ b/sound/isa/opti9xx/miro.c @@ -967,7 +967,7 @@ static void __init snd_miro_proc_init(st struct snd_info_entry *entry; if (! snd_card_proc_new(miro->card, "miro", &entry)) - snd_info_set_text_ops(entry, miro, 1024, snd_miro_proc_read); + snd_info_set_text_ops(entry, miro, snd_miro_proc_read); } /* @@ -1382,7 +1382,7 @@ static int __init snd_miro_probe(struct rmidi = NULL; else if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - miro->mpu_port, 0, miro->mpu_irq, SA_INTERRUPT, + miro->mpu_port, 0, miro->mpu_irq, IRQF_DISABLED, &rmidi))) snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", miro->mpu_port); diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c index 8ee0d70..9d528ae 100644 --- a/sound/isa/opti9xx/opti92x-ad1848.c +++ b/sound/isa/opti9xx/opti92x-ad1848.c @@ -1291,7 +1291,7 @@ static int snd_opti93x_create(struct snd } codec->dma2 = chip->dma2; - if (request_irq(chip->irq, snd_opti93x_interrupt, SA_INTERRUPT, DRIVER_NAME" - WSS", codec)) { + if (request_irq(chip->irq, snd_opti93x_interrupt, IRQF_DISABLED, DRIVER_NAME" - WSS", codec)) { snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", chip->irq); snd_opti93x_free(codec); return -EBUSY; @@ -1863,7 +1863,7 @@ #endif /* CS4231 || OPTi93X */ rmidi = NULL; else if ((error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - chip->mpu_port, 0, chip->mpu_irq, SA_INTERRUPT, + chip->mpu_port, 0, chip->mpu_irq, IRQF_DISABLED, &rmidi))) snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n", chip->mpu_port); diff --git a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c index c0b8d61..658179e 100644 --- a/sound/isa/sb/emu8000.c +++ b/sound/isa/sb/emu8000.c @@ -131,7 +131,7 @@ snd_emu8000_dma_chan(struct snd_emu8000 /* */ -static void __init +static void __devinit snd_emu8000_read_wait(struct snd_emu8000 *emu) { while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { @@ -143,7 +143,7 @@ snd_emu8000_read_wait(struct snd_emu8000 /* */ -static void __init +static void __devinit snd_emu8000_write_wait(struct snd_emu8000 *emu) { while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { @@ -156,7 +156,7 @@ snd_emu8000_write_wait(struct snd_emu800 /* * detect a card at the given port */ -static int __init +static int __devinit snd_emu8000_detect(struct snd_emu8000 *emu) { /* Initialise */ @@ -182,7 +182,7 @@ snd_emu8000_detect(struct snd_emu8000 *e /* * intiailize audio channels */ -static void __init +static void __devinit init_audio(struct snd_emu8000 *emu) { int ch; @@ -223,7 +223,7 @@ init_audio(struct snd_emu8000 *emu) /* * initialize DMA address */ -static void __init +static void __devinit init_dma(struct snd_emu8000 *emu) { EMU8000_SMALR_WRITE(emu, 0); @@ -327,7 +327,7 @@ static unsigned short init4[128] /*__dev * Taken from the oss driver, not obvious from the doc how this * is meant to work */ -static void __init +static void __devinit send_array(struct snd_emu8000 *emu, unsigned short *data, int size) { int i; @@ -349,7 +349,7 @@ send_array(struct snd_emu8000 *emu, unsi * Send initialization arrays to start up, this just follows the * initialisation sequence in the adip. */ -static void __init +static void __devinit init_arrays(struct snd_emu8000 *emu) { send_array(emu, init1, ARRAY_SIZE(init1)/4); @@ -375,7 +375,7 @@ #define UNIQUE_ID2 0x9d53 * seems that the only way to do this is to use the one channel and keep * reallocating between read and write. */ -static void __init +static void __devinit size_dram(struct snd_emu8000 *emu) { int i, size; @@ -500,7 +500,7 @@ snd_emu8000_init_fm(struct snd_emu8000 * /* * The main initialization routine. */ -static void __init +static void __devinit snd_emu8000_init_hw(struct snd_emu8000 *emu) { int i; @@ -1019,7 +1019,7 @@ static struct snd_kcontrol_new *mixer_de /* * create and attach mixer elements for WaveTable treble/bass controls */ -static int __init +static int __devinit snd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu) { int i, err = 0; @@ -1069,7 +1069,7 @@ static int snd_emu8000_dev_free(struct s /* * initialize and register emu8000 synth device. */ -int __init +int __devinit snd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports, struct snd_seq_device **awe_ret) { diff --git a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c index 80b1cf8..1be16c9 100644 --- a/sound/isa/sb/emu8000_patch.c +++ b/sound/isa/sb/emu8000_patch.c @@ -23,7 +23,7 @@ #include "emu8000_local.h" #include #include -static int emu8000_reset_addr = 0; +static int emu8000_reset_addr; module_param(emu8000_reset_addr, int, 0444); MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)"); diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c index 6333f90..d64e67f 100644 --- a/sound/isa/sb/sb16.c +++ b/sound/isa/sb/sb16.c @@ -85,7 +85,7 @@ static int dma8[SNDRV_CARDS] = SNDRV_DEF static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 5,6,7 */ static int mic_agc[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; #ifdef CONFIG_SND_SB16_CSP -static int csp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int csp[SNDRV_CARDS]; #endif #ifdef SNDRV_SBAWE_EMU8000 static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; @@ -327,7 +327,8 @@ #ifdef SNDRV_SBAWE_EMU8000 goto __wt_error; } awe_port[dev] = pnp_port_start(pdev, 0); - snd_printdd("pnp SB16: wavetable port=0x%lx\n", pnp_port_start(pdev, 0)); + snd_printdd("pnp SB16: wavetable port=0x%llx\n", + (unsigned long long)pnp_port_start(pdev, 0)); } else { __wt_error: if (pdev) { diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c index 9703c68..fcd6380 100644 --- a/sound/isa/sb/sb16_csp.c +++ b/sound/isa/sb/sb16_csp.c @@ -1101,7 +1101,7 @@ static int init_proc_entry(struct snd_sb struct snd_info_entry *entry; sprintf(name, "cspD%d", device); if (! snd_card_proc_new(p->chip->card, name, &entry)) - snd_info_set_text_ops(entry, p, 1024, info_read); + snd_info_set_text_ops(entry, p, info_read); return 0; } diff --git a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c index c549ace..0b67edd 100644 --- a/sound/isa/sb/sb8_midi.c +++ b/sound/isa/sb/sb8_midi.c @@ -32,20 +32,22 @@ #include #include #include -/* - - */ -irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb * chip) +irqreturn_t snd_sb8dsp_midi_interrupt(struct snd_sb *chip) { struct snd_rawmidi *rmidi; int max = 64; char byte; - if (chip == NULL || (rmidi = chip->rmidi) == NULL) { + if (!chip) + return IRQ_NONE; + + rmidi = chip->rmidi; + if (!rmidi) { inb(SBP(chip, DATA_AVAIL)); /* ack interrupt */ return IRQ_NONE; } + spin_lock(&chip->midi_input_lock); while (max-- > 0) { if (inb(SBP(chip, DATA_AVAIL)) & 0x80) { @@ -59,10 +61,6 @@ irqreturn_t snd_sb8dsp_midi_interrupt(st return IRQ_HANDLED; } -/* - - */ - static int snd_sb8dsp_midi_input_open(struct snd_rawmidi_substream *substream) { unsigned long flags; @@ -252,10 +250,6 @@ static void snd_sb8dsp_midi_output_trigg snd_sb8dsp_midi_output_write(substream); } -/* - - */ - static struct snd_rawmidi_ops snd_sb8dsp_midi_output = { .open = snd_sb8dsp_midi_output_open, diff --git a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c index f343a82..f17de2b 100644 --- a/sound/isa/sb/sb_common.c +++ b/sound/isa/sb/sb_common.c @@ -232,7 +232,7 @@ int snd_sbdsp_create(struct snd_card *ca chip->port = port; if (request_irq(irq, irq_handler, hardware == SB_HW_ALS4000 ? - SA_INTERRUPT | SA_SHIRQ : SA_INTERRUPT, + IRQF_DISABLED | IRQF_SHARED : IRQF_DISABLED, "SoundBlaster", (void *) chip)) { snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq); snd_sbdsp_free(chip); diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c index 09c8e8c..8742fa5 100644 --- a/sound/isa/sgalaxy.c +++ b/sound/isa/sgalaxy.c @@ -147,7 +147,7 @@ #endif if (tmp < 0) return -EINVAL; - if (request_irq(irq, snd_sgalaxy_dummy_interrupt, SA_INTERRUPT, "sgalaxy", NULL)) { + if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) { snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq); return -EIO; } diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c index d2a856f..b1f2582 100644 --- a/sound/isa/sscape.c +++ b/sound/isa/sscape.c @@ -897,11 +897,10 @@ static int __devinit create_mpu401(struc struct snd_rawmidi *rawmidi; int err; -#define MPU401_SHARE_HARDWARE 1 if ((err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, - port, MPU401_SHARE_HARDWARE, - irq, SA_INTERRUPT, + port, MPU401_INFO_INTEGRATED, + irq, IRQF_DISABLED, &rawmidi)) == 0) { struct snd_mpu401 *mpu = (struct snd_mpu401 *) rawmidi->private_data; mpu->open_input = mpu401_open; diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c index 7ae86f8..a8f8d2f 100644 --- a/sound/isa/wavefront/wavefront.c +++ b/sound/isa/wavefront/wavefront.c @@ -50,7 +50,7 @@ static int ics2115_irq[SNDRV_CARDS] = SN static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */ static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3,5,6,7 */ -static int use_cs4232_midi[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int use_cs4232_midi[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for WaveFront soundcard."); @@ -467,7 +467,7 @@ snd_wavefront_probe (struct snd_card *ca return -EBUSY; } if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt, - SA_INTERRUPT, "ICS2115", acard)) { + IRQF_DISABLED, "ICS2115", acard)) { snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]); return -EBUSY; } @@ -497,7 +497,7 @@ snd_wavefront_probe (struct snd_card *ca if ((err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232, cs4232_mpu_port[dev], 0, cs4232_mpu_irq[dev], - SA_INTERRUPT, + IRQF_DISABLED, NULL)) < 0) { snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n"); return err; diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index cf476fe..c31b386 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -465,13 +465,13 @@ snd_au1000_pcm_new(struct snd_au1000 *au flags = claim_dma_lock(); if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX, - "AC97 TX", au1000_dma_interrupt, SA_INTERRUPT, + "AC97 TX", au1000_dma_interrupt, IRQF_DISABLED, au1000->stream[PLAYBACK])) < 0) { release_dma_lock(flags); return -EBUSY; } if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX, - "AC97 RX", au1000_dma_interrupt, SA_INTERRUPT, + "AC97 RX", au1000_dma_interrupt, IRQF_DISABLED, au1000->stream[CAPTURE])) < 0){ release_dma_lock(flags); return -EBUSY; diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index 558c6ed..f4980ca 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -98,8 +98,8 @@ config SOUND_HAL2 tristate "SGI HAL2 sound (EXPERIMENTAL)" depends on SOUND_PRIME && SGI_IP22 && EXPERIMENTAL help - Say Y or M if you have an SGI Indy system and want to be able to - use it's on-board A2 audio system. + Say Y or M if you have an SGI Indy or Indigo2 system and want to be able to + use its on-board A2 audio system. config SOUND_IT8172 tristate "IT8172G Sound" @@ -114,8 +114,9 @@ config SOUND_VRC5477 with the AC97 codec. config SOUND_AU1550_AC97 - tristate "Au1550 AC97 Sound" - depends on SOUND_PRIME && SOC_AU1550 + tristate "Au1550/Au1200 AC97 Sound" + select SND_AC97_CODEC + depends on SOUND_PRIME && (SOC_AU1550 || SOC_AU1200) config SOUND_TRIDENT tristate "Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core" @@ -492,6 +493,19 @@ config SOUND_CS4232 See for more information on configuring this card. +config SOUND_SSCAPE + tristate "Ensoniq SoundScape support" + depends on SOUND_OSS + help + Answer Y if you have a sound card based on the Ensoniq SoundScape + chipset. Such cards are being manufactured at least by Ensoniq, Spea + and Reveal (Reveal makes also other cards). + + If you compile the driver into the kernel, you have to add + "sscape=,,,," to the kernel command + line. + + config SOUND_VMIDI tristate "Loopback MIDI device support" depends on SOUND_OSS @@ -838,6 +852,6 @@ config SOUND_SH_DAC_AUDIO depends on SOUND_PRIME && CPU_SH3 config SOUND_SH_DAC_AUDIO_CHANNEL - int " DAC channel" + int "DAC channel" default "1" depends on SOUND_SH_DAC_AUDIO diff --git a/sound/oss/ad1816.c b/sound/oss/ad1816.c index 95586de..2905783 100644 --- a/sound/oss/ad1816.c +++ b/sound/oss/ad1816.c @@ -41,7 +41,6 @@ */ -#include #include #include #include diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c index e04fa49..3b45e11 100644 --- a/sound/oss/ad1848.c +++ b/sound/oss/ad1848.c @@ -41,7 +41,6 @@ * Tested. Believed fully functional. */ -#include #include #include #include diff --git a/sound/oss/ad1889.c b/sound/oss/ad1889.c index a4ca756..f56f870 100644 --- a/sound/oss/ad1889.c +++ b/sound/oss/ad1889.c @@ -26,7 +26,6 @@ * * $Id: ad1889.c,v 1.3 2002/10/19 21:31:44 grundler Exp $ */ -#include #include #include #include @@ -1011,7 +1010,7 @@ static int __devinit ad1889_probe(struct goto out2; } - if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) { + if (request_irq(pcidev->irq, ad1889_interrupt, IRQF_SHARED, DEVNAME, dev) != 0) { printk(KERN_ERR DEVNAME ": unable to request interrupt\n"); goto out3; } diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c index b556263..51e1fde 100644 --- a/sound/oss/aedsp16.c +++ b/sound/oss/aedsp16.c @@ -23,7 +23,6 @@ * Include the main OSS Lite header file. It include all the os, OSS Lite, etc * headers needed by this source. */ -#include #include #include #include diff --git a/sound/oss/ali5455.c b/sound/oss/ali5455.c index 62bb936..70dcd70 100644 --- a/sound/oss/ali5455.c +++ b/sound/oss/ali5455.c @@ -3460,7 +3460,7 @@ #endif card->channel[4].num = 4; /* claim our iospace and irq */ request_region(card->iobase, 256, card_names[pci_id->driver_data]); - if (request_irq(card->irq, &ali_interrupt, SA_SHIRQ, + if (request_irq(card->irq, &ali_interrupt, IRQF_SHARED, card_names[pci_id->driver_data], card)) { printk(KERN_ERR "ali_audio: unable to allocate irq %d\n", card->irq); diff --git a/sound/oss/au1000.c b/sound/oss/au1000.c index eacb0ae..e379623 100644 --- a/sound/oss/au1000.c +++ b/sound/oss/au1000.c @@ -2015,14 +2015,14 @@ #endif if ((s->dma_dac.dmanr = request_au1000_dma(DMA_ID_AC97C_TX, "audio DAC", dac_dma_interrupt, - SA_INTERRUPT, s)) < 0) { + IRQF_DISABLED, s)) < 0) { err("Can't get DAC DMA"); goto err_dma1; } if ((s->dma_adc.dmanr = request_au1000_dma(DMA_ID_AC97C_RX, "audio ADC", adc_dma_interrupt, - SA_INTERRUPT, s)) < 0) { + IRQF_DISABLED, s)) < 0) { err("Can't get ADC DMA"); goto err_dma2; } diff --git a/sound/oss/au1550_ac97.c b/sound/oss/au1550_ac97.c index c1168fa..4cdb862 100644 --- a/sound/oss/au1550_ac97.c +++ b/sound/oss/au1550_ac97.c @@ -57,9 +57,9 @@ #include #include #include #include -#include #include #include +#include #undef OSS_DOCUMENTED_MIXER_SEMANTICS @@ -213,7 +213,8 @@ rdcodec(struct ac97_codec *codec, u8 add } if (i == POLL_COUNT) { err("rdcodec: read poll expired!"); - return 0; + data = 0; + goto out; } /* wait for command done? @@ -226,7 +227,8 @@ rdcodec(struct ac97_codec *codec, u8 add } if (i == POLL_COUNT) { err("rdcodec: read cmdwait expired!"); - return 0; + data = 0; + goto out; } data = au_readl(PSC_AC97CDC) & 0xffff; @@ -237,6 +239,7 @@ rdcodec(struct ac97_codec *codec, u8 add au_writel(PSC_AC97EVNT_CD, PSC_AC97EVNT); au_sync(); + out: spin_unlock_irqrestore(&s->lock, flags); return data; @@ -1892,6 +1895,8 @@ static /*const */ struct file_operations MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com"); MODULE_DESCRIPTION("Au1550 AC97 Audio Driver"); +MODULE_LICENSE("GPL"); + static int __devinit au1550_probe(void) diff --git a/sound/oss/btaudio.c b/sound/oss/btaudio.c index bfe3b53..324a81f 100644 --- a/sound/oss/btaudio.c +++ b/sound/oss/btaudio.c @@ -966,7 +966,7 @@ static int __devinit btaudio_probe(struc btwrite(~0U, REG_INT_STAT); pci_set_master(pci_dev); - if ((rc = request_irq(bta->irq, btaudio_irq, SA_SHIRQ|SA_INTERRUPT, + if ((rc = request_irq(bta->irq, btaudio_irq, IRQF_SHARED|IRQF_DISABLED, "btaudio",(void *)bta)) < 0) { printk(KERN_WARNING "btaudio: can't request irq (rc=%d)\n",rc); diff --git a/sound/oss/cmpci.c b/sound/oss/cmpci.c index de60a05..ea51aaf 100644 --- a/sound/oss/cmpci.c +++ b/sound/oss/cmpci.c @@ -3122,7 +3122,7 @@ #endif wrmixer(s, DSP_MIX_DATARESETIDX, 0); /* request irq */ - if ((ret = request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s))) { + if ((ret = request_irq(s->irq, cm_interrupt, IRQF_SHARED, "cmpci", s))) { printk(KERN_ERR "cmpci: irq %u in use\n", s->irq); goto err_irq; } diff --git a/sound/oss/cs4232.c b/sound/oss/cs4232.c index c7f86f0..b6924c7 100644 --- a/sound/oss/cs4232.c +++ b/sound/oss/cs4232.c @@ -47,7 +47,6 @@ * Marcus Meissner Added ISA PnP support. */ -#include #include #include #include @@ -405,7 +404,7 @@ static const struct pnp_device_id cs4232 MODULE_DEVICE_TABLE(pnp, cs4232_pnp_table); -static int cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +static int __init cs4232_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) { struct address_info *isapnpcfg; diff --git a/sound/oss/cs4281/cs4281m.c b/sound/oss/cs4281/cs4281m.c index 0004442..0400a41 100644 --- a/sound/oss/cs4281/cs4281m.c +++ b/sound/oss/cs4281/cs4281m.c @@ -4346,7 +4346,7 @@ #endif s->pcidev = pcidev; s->irq = pcidev->irq; if (request_irq - (s->irq, cs4281_interrupt, SA_SHIRQ, "Crystal CS4281", s)) { + (s->irq, cs4281_interrupt, IRQF_SHARED, "Crystal CS4281", s)) { CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR "cs4281: irq %u in use\n", s->irq)); goto err_irq; diff --git a/sound/oss/cs46xx.c b/sound/oss/cs46xx.c index 53881bc..5195bf9 100644 --- a/sound/oss/cs46xx.c +++ b/sound/oss/cs46xx.c @@ -147,7 +147,7 @@ #define CSDEBUG 1 * that should be printed on any released driver. */ #if CSDEBUG -#define CS_DBGOUT(mask,level,x) if((cs_debuglevel >= (level)) && ((mask) & cs_debugmask)) {x;} +#define CS_DBGOUT(mask,level,x) if ((cs_debuglevel >= (level)) && ((mask) & cs_debugmask)) {x;} #else #define CS_DBGOUT(mask,level,x) #endif @@ -175,19 +175,19 @@ #define CS_IOCTL_CMD_SUSPEND 0x1 // susp #define CS_IOCTL_CMD_RESUME 0x2 // resume #if CSDEBUG -static unsigned long cs_debuglevel=1; /* levels range from 1-9 */ +static unsigned long cs_debuglevel = 1; /* levels range from 1-9 */ module_param(cs_debuglevel, ulong, 0644); -static unsigned long cs_debugmask=CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */ +static unsigned long cs_debugmask = CS_INIT | CS_ERROR; /* use CS_DBGOUT with various mask values */ module_param(cs_debugmask, ulong, 0644); #endif static unsigned long hercules_egpio_disable; /* if non-zero set all EGPIO to 0 */ module_param(hercules_egpio_disable, ulong, 0); -static unsigned long initdelay=700; /* PM delay in millisecs */ +static unsigned long initdelay = 700; /* PM delay in millisecs */ module_param(initdelay, ulong, 0); -static unsigned long powerdown=-1; /* turn on/off powerdown processing in driver */ +static unsigned long powerdown = -1; /* turn on/off powerdown processing in driver */ module_param(powerdown, ulong, 0); #define DMABUF_DEFAULTORDER 3 -static unsigned long defaultorder=DMABUF_DEFAULTORDER; +static unsigned long defaultorder = DMABUF_DEFAULTORDER; module_param(defaultorder, ulong, 0); static int external_amp; @@ -200,8 +200,8 @@ module_param(thinkpad, bool, 0); * powerdown. also set thinkpad to 1 to disable powerdown, * but also to enable the clkrun functionality. */ -static unsigned cs_powerdown=1; -static unsigned cs_laptop_wait=1; +static unsigned cs_powerdown = 1; +static unsigned cs_laptop_wait = 1; /* An instance of the 4610 channel */ struct cs_channel @@ -319,7 +319,7 @@ struct cs_card { atomic_t mixer_use_cnt; /* PCI device stuff */ - struct pci_dev * pci_dev; + struct pci_dev *pci_dev; struct list_head list; unsigned int pctl, cctl; /* Hardware DMA flag sets */ @@ -384,7 +384,7 @@ struct cs_card { static int cs_open_mixdev(struct inode *inode, struct file *file); static int cs_release_mixdev(struct inode *inode, struct file *file); static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg); + unsigned long arg); static int cs_hardware_init(struct cs_card *card); static int cs46xx_powerup(struct cs_card *card, unsigned int type); static int cs461x_powerdown(struct cs_card *card, unsigned int type, int suspendflag); @@ -423,8 +423,7 @@ static void printioctl(unsigned int x) [SOUND_MIXER_VOLUME] = 9 /* Master Volume */ }; - switch(x) - { + switch (x) { case SOUND_MIXER_CS_GETDBGMASK: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_CS_GETDBGMASK: ") ); break; @@ -521,7 +520,6 @@ static void printioctl(unsigned int x) case SOUND_PCM_READ_FILTER: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_PCM_READ_FILTER: ") ); break; - case SOUND_MIXER_PRIVATE1: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_PRIVATE1: ") ); break; @@ -543,10 +541,8 @@ static void printioctl(unsigned int x) case SOUND_OLD_MIXER_INFO: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_OLD_MIXER_INFO: ") ); break; - default: - switch (_IOC_NR(x)) - { + switch (_IOC_NR(x)) { case SOUND_MIXER_VOLUME: CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_VOLUME: ") ); break; @@ -579,14 +575,11 @@ static void printioctl(unsigned int x) break; default: i = _IOC_NR(x); - if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) - { + if (i >= SOUND_MIXER_NRDEVICES || !(vidx = mixtable1[i])) { CS_DBGOUT(CS_IOCTL, 4, printk("UNKNOWN IOCTL: 0x%.8x NR=%d ",x,i) ); - } - else - { + } else { CS_DBGOUT(CS_IOCTL, 4, printk("SOUND_MIXER_IOCTL AC9x: 0x%.8x NR=%d ", - x,i) ); + x,i)); } break; } @@ -601,22 +594,22 @@ #endif static void cs461x_poke(struct cs_card *codec, unsigned long reg, unsigned int val) { - writel(val, codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); + writel(val, codec->ba1.idx[(reg >> 16) & 3] + (reg & 0xffff)); } static unsigned int cs461x_peek(struct cs_card *codec, unsigned long reg) { - return readl(codec->ba1.idx[(reg >> 16) & 3]+(reg&0xffff)); + return readl(codec->ba1.idx[(reg >> 16) & 3] + (reg & 0xffff)); } static void cs461x_pokeBA0(struct cs_card *codec, unsigned long reg, unsigned int val) { - writel(val, codec->ba0+reg); + writel(val, codec->ba0 + reg); } static unsigned int cs461x_peekBA0(struct cs_card *codec, unsigned long reg) { - return readl(codec->ba0+reg); + return readl(codec->ba0 + reg); } @@ -625,26 +618,26 @@ static void cs_ac97_set(struct ac97_code static struct cs_channel *cs_alloc_pcm_channel(struct cs_card *card) { - if(card->channel[1].used==1) + if (card->channel[1].used == 1) return NULL; - card->channel[1].used=1; - card->channel[1].num=1; + card->channel[1].used = 1; + card->channel[1].num = 1; return &card->channel[1]; } static struct cs_channel *cs_alloc_rec_pcm_channel(struct cs_card *card) { - if(card->channel[0].used==1) + if (card->channel[0].used == 1) return NULL; - card->channel[0].used=1; - card->channel[0].num=0; + card->channel[0].used = 1; + card->channel[0].num = 0; return &card->channel[0]; } static void cs_free_pcm_channel(struct cs_card *card, int channel) { card->channel[channel].state = NULL; - card->channel[channel].used=0; + card->channel[channel].used = 0; } /* @@ -655,15 +648,15 @@ static void cs_free_pcm_channel(struct c */ static void cs_set_divisor(struct dmabuf *dmabuf) { - if(dmabuf->type == CS_TYPE_DAC) + if (dmabuf->type == CS_TYPE_DAC) dmabuf->divisor = 1; - else if( !(dmabuf->fmt & CS_FMT_STEREO) && + else if (!(dmabuf->fmt & CS_FMT_STEREO) && (dmabuf->fmt & CS_FMT_16BIT)) dmabuf->divisor = 2; - else if( (dmabuf->fmt & CS_FMT_STEREO) && + else if ((dmabuf->fmt & CS_FMT_STEREO) && !(dmabuf->fmt & CS_FMT_16BIT)) dmabuf->divisor = 2; - else if( !(dmabuf->fmt & CS_FMT_STEREO) && + else if (!(dmabuf->fmt & CS_FMT_STEREO) && !(dmabuf->fmt & CS_FMT_16BIT)) dmabuf->divisor = 4; else @@ -680,13 +673,12 @@ static void cs_set_divisor(struct dmabuf */ static void cs_mute(struct cs_card *card, int state) { - struct ac97_codec *dev=card->ac97_codec[0]; + struct ac97_codec *dev = card->ac97_codec[0]; CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: cs_mute()+ %s\n", - (state == CS_TRUE) ? "Muting" : "UnMuting") ); + (state == CS_TRUE) ? "Muting" : "UnMuting")); - if(state == CS_TRUE) - { + if (state == CS_TRUE) { /* * fix pops when powering up on thinkpads */ @@ -703,9 +695,7 @@ static void cs_mute(struct cs_card *card cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, 0x8000); cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, 0x8000); cs_ac97_set(dev, (u8)BA0_AC97_PCM_OUT_VOLUME, 0x8000); - } - else - { + } else { cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME, card->pm.u32AC97_master_volume); cs_ac97_set(dev, (u8)BA0_AC97_HEADPHONE_VOLUME, card->pm.u32AC97_headphone_volume); cs_ac97_set(dev, (u8)BA0_AC97_MASTER_VOLUME_MONO, card->pm.u32AC97_master_volume_mono); @@ -757,7 +747,6 @@ static unsigned int cs_set_dac_rate(stru /* * Fill in the SampleRateConverter control block. */ - spin_lock_irqsave(&state->card->lock, flags); cs461x_poke(state->card, BA1_PSRC, ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF)); @@ -770,7 +759,7 @@ static unsigned int cs_set_dac_rate(stru } /* set recording sample rate */ -static unsigned int cs_set_adc_rate(struct cs_state * state, unsigned int rate) +static unsigned int cs_set_adc_rate(struct cs_state *state, unsigned int rate) { struct dmabuf *dmabuf = &state->dmabuf; struct cs_card *card = state->card; @@ -815,7 +804,6 @@ static unsigned int cs_set_adc_rate(stru * dividend:remainder(ulOther / GOF_PER_SEC) * initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) */ - tmp1 = rate << 16; coeffIncr = tmp1 / 48000; tmp1 -= coeffIncr * 48000; @@ -891,7 +879,7 @@ static void cs_play_setup(struct cs_stat CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()+\n") ); cs461x_poke(card, BA1_PVOL, 0x80008000); - if(!dmabuf->SGok) + if (!dmabuf->SGok) cs461x_poke(card, BA1_PBA, virt_to_bus(dmabuf->pbuf)); Count = 4; @@ -899,16 +887,14 @@ static void cs_play_setup(struct cs_stat if ((dmabuf->fmt & CS_FMT_STEREO)) { playFormat &= ~DMA_RQ_C2_AC_MONO_TO_STEREO; Count *= 2; - } - else + } else playFormat |= DMA_RQ_C2_AC_MONO_TO_STEREO; if ((dmabuf->fmt & CS_FMT_16BIT)) { playFormat &= ~(DMA_RQ_C2_AC_8_TO_16_BIT | DMA_RQ_C2_AC_SIGNED_CONVERT); Count *= 2; - } - else + } else playFormat |= (DMA_RQ_C2_AC_8_TO_16_BIT | DMA_RQ_C2_AC_SIGNED_CONVERT); @@ -919,7 +905,6 @@ static void cs_play_setup(struct cs_stat cs461x_poke(card, BA1_PDTC, tmp | --Count); CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_play_setup()-\n") ); - } static struct InitStruct @@ -944,8 +929,7 @@ static void SetCaptureSPValues(struct cs { unsigned i, offset; CS_DBGOUT(CS_FUNCTION, 8, printk("cs46xx: SetCaptureSPValues()+\n") ); - for(i=0; icard; struct dmabuf *dmabuf = &state->dmabuf; - CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n") ); + CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_rec_setup()+\n")); SetCaptureSPValues(card); /* @@ -994,14 +978,11 @@ static inline unsigned cs_get_dma_addr(s /* * granularity is byte boundary, good part. */ - if(dmabuf->enable & DAC_RUNNING) - { + if (dmabuf->enable & DAC_RUNNING) offset = cs461x_peek(state->card, BA1_PBA); - } else /* ADC_RUNNING must be set */ - { offset = cs461x_peek(state->card, BA1_CBA); - } + CS_DBGOUT(CS_PARMS | CS_FUNCTION, 9, printk("cs46xx: cs_get_dma_addr() %d\n",offset) ); offset = (u32)bus_to_virt((unsigned long)offset) - (u32)dmabuf->rawbuf; @@ -1015,8 +996,7 @@ static void resync_dma_ptrs(struct cs_st struct dmabuf *dmabuf; CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: resync_dma_ptrs()+ \n") ); - if(state) - { + if (state) { dmabuf = &state->dmabuf; dmabuf->hwptr=dmabuf->swptr = 0; dmabuf->pringbuf = 0; @@ -1149,13 +1129,13 @@ static int alloc_dmabuf(struct cs_state /* * check for order within limits, but do not overwrite value. */ - if((defaultorder > 1) && (defaultorder < 12)) + if ((defaultorder > 1) && (defaultorder < 12)) df = defaultorder; else df = 2; for (order = df; order >= DMABUF_MINORDER; order--) - if ( (rawbuf = (void *) pci_alloc_consistent( + if ((rawbuf = (void *)pci_alloc_consistent( card->pci_dev, PAGE_SIZE << order, &dmabuf->dmaaddr))) break; if (!rawbuf) { @@ -1181,8 +1161,7 @@ static int alloc_dmabuf(struct cs_state /* * only allocate the conversion buffer for the ADC */ - if(dmabuf->type == CS_TYPE_DAC) - { + if (dmabuf->type == CS_TYPE_DAC) { dmabuf->tmpbuff = NULL; dmabuf->buforder_tmpbuff = 0; return 0; @@ -1258,8 +1237,7 @@ static int __prog_dmabuf(struct cs_state /* * check for CAPTURE and use only non-sg for initial release */ - if(dmabuf->type == CS_TYPE_ADC) - { + if (dmabuf->type == CS_TYPE_ADC) { CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf() ADC\n")); /* * add in non-sg support for capture. @@ -1313,9 +1291,7 @@ static int __prog_dmabuf(struct cs_state CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- 0 \n")); return 0; - } - else if (dmabuf->type == CS_TYPE_DAC) - { + } else if (dmabuf->type == CS_TYPE_DAC) { /* * Must be DAC */ @@ -1337,8 +1313,7 @@ static int __prog_dmabuf(struct cs_state allocated_pages = 1 << dmabuf->buforder; allocated_bytes = allocated_pages*PAGE_SIZE; - if(allocated_pages < 2) - { + if (allocated_pages < 2) { CS_DBGOUT(CS_FUNCTION, 4, printk( "cs46xx: prog_dmabuf() Error: allocated_pages too small (%d)\n", (unsigned)allocated_pages)); @@ -1353,14 +1328,14 @@ static int __prog_dmabuf(struct cs_state /* Set up S/G variables. */ *ptmp = virt_to_bus(dmabuf->rawbuf); - *(ptmp+1) = 0x00000008; - for(tmp1= 1; tmp1 < nSGpages; tmp1++) { - *(ptmp+2*tmp1) = virt_to_bus( (dmabuf->rawbuf)+4096*tmp1); - if( tmp1 == nSGpages-1) + *(ptmp + 1) = 0x00000008; + for (tmp1 = 1; tmp1 < nSGpages; tmp1++) { + *(ptmp + 2 * tmp1) = virt_to_bus((dmabuf->rawbuf) + 4096 * tmp1); + if (tmp1 == nSGpages - 1) tmp2 = 0xbfff0000; else - tmp2 = 0x80000000+8*(tmp1+1); - *(ptmp+2*tmp1+1) = tmp2; + tmp2 = 0x80000000 + 8 * (tmp1 + 1); + *(ptmp + 2 * tmp1 + 1) = tmp2; } SGarray[0] = 0x82c0200d; SGarray[1] = 0xffff0000; @@ -1368,18 +1343,17 @@ static int __prog_dmabuf(struct cs_state SGarray[3] = 0x00010600; SGarray[4] = *(ptmp+2); SGarray[5] = 0x80000010; - SGarray[6] = *ptmp; - SGarray[7] = *(ptmp+2); - SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10; - - if (dmabuf->SGok) { - dmabuf->numfrag = nSGpages; - dmabuf->fragsize = 4096; - dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt]; - dmabuf->fragshift = 12; - dmabuf->dmasize = dmabuf->numfrag*4096; - } - else { + SGarray[6] = *ptmp; + SGarray[7] = *(ptmp+2); + SGarray[8] = (virt_to_bus(dmabuf->pbuf) & 0xffff000) | 0x10; + + if (dmabuf->SGok) { + dmabuf->numfrag = nSGpages; + dmabuf->fragsize = 4096; + dmabuf->fragsamples = 4096 >> sample_shift[dmabuf->fmt]; + dmabuf->fragshift = 12; + dmabuf->dmasize = dmabuf->numfrag * 4096; + } else { SGarray[0] = 0xf2c0000f; SGarray[1] = 0x00000200; SGarray[2] = 0; @@ -1391,8 +1365,8 @@ static int __prog_dmabuf(struct cs_state dmabuf->dmasize = 4096; dmabuf->fragshift = 11; } - for(tmp1 = 0; tmp1 < sizeof(SGarray)/4; tmp1++) - cs461x_poke( state->card, BA1_PDTC+tmp1*4, SGarray[tmp1]); + for (tmp1 = 0; tmp1 < sizeof(SGarray) / 4; tmp1++) + cs461x_poke(state->card, BA1_PDTC+tmp1 * 4, SGarray[tmp1]); memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, dmabuf->dmasize); @@ -1416,9 +1390,7 @@ static int __prog_dmabuf(struct cs_state CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- \n")); return 0; - } - else - { + } else { CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: prog_dmabuf()- Invalid Type %d\n", dmabuf->type)); } @@ -1489,8 +1461,7 @@ static int drain_dac(struct cs_state *st } remove_wait_queue(&dmabuf->wait, &wait); current->state = TASK_RUNNING; - if (signal_pending(current)) - { + if (signal_pending(current)) { CS_DBGOUT(CS_FUNCTION, 4, printk("cs46xx: drain_dac()- -ERESTARTSYS\n")); /* * set to silence and let that clear the fifos. @@ -1514,8 +1485,7 @@ static void cs_update_ptr(struct cs_card /* error handling and process wake up for ADC */ state = card->states[0]; - if(state) - { + if (state) { dmabuf = &state->dmabuf; if (dmabuf->enable & ADC_RUNNING) { /* update hardware pointer */ @@ -1531,12 +1501,10 @@ static void cs_update_ptr(struct cs_card if (dmabuf->count > dmabuf->dmasize) dmabuf->count = dmabuf->dmasize; - if(dmabuf->mapped) - { + if (dmabuf->mapped) { if (wake && dmabuf->count >= (signed)dmabuf->fragsize) wake_up(&dmabuf->wait); - } else - { + } else { if (wake && dmabuf->count > 0) wake_up(&dmabuf->wait); } @@ -1547,8 +1515,7 @@ static void cs_update_ptr(struct cs_card * Now the DAC */ state = card->states[1]; - if(state) - { + if (state) { dmabuf = &state->dmabuf; /* error handling and process wake up for DAC */ if (dmabuf->enable & DAC_RUNNING) { @@ -1570,7 +1537,7 @@ static void cs_update_ptr(struct cs_card * in that, since dmasize is the buffer asked for * via mmap. */ - if( dmabuf->count > dmabuf->dmasize) + if (dmabuf->count > dmabuf->dmasize) dmabuf->count &= dmabuf->dmasize-1; } else { dmabuf->count -= diff; @@ -1578,13 +1545,10 @@ static void cs_update_ptr(struct cs_card * backfill with silence and clear out the last * "diff" number of bytes. */ - if(hwptr >= diff) - { + if (hwptr >= diff) { memset(dmabuf->rawbuf + hwptr - diff, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, diff); - } - else - { + } else { memset(dmabuf->rawbuf, (dmabuf->fmt & CS_FMT_16BIT) ? 0 : 0x80, (unsigned)hwptr); @@ -1602,12 +1566,12 @@ static void cs_update_ptr(struct cs_card * buffer underrun or buffer overrun, reset the * count of bytes written back to 0. */ - if(dmabuf->count < 0) - dmabuf->underrun=1; + if (dmabuf->count < 0) + dmabuf->underrun = 1; dmabuf->count = 0; dmabuf->error++; } - if (wake && dmabuf->count < (signed)dmabuf->dmasize/2) + if (wake && dmabuf->count < (signed)dmabuf->dmasize / 2) wake_up(&dmabuf->wait); } } @@ -1661,8 +1625,7 @@ static irqreturn_t cs_interrupt(int irq, status = cs461x_peekBA0(card, BA0_HISR); - if ((status & 0x7fffffff) == 0) - { + if ((status & 0x7fffffff) == 0) { cs461x_pokeBA0(card, BA0_HICR, HICR_CHGM|HICR_IEV); spin_unlock(&card->lock); return IRQ_HANDLED; /* Might be IRQ_NONE.. */ @@ -1671,15 +1634,14 @@ static irqreturn_t cs_interrupt(int irq, /* * check for playback or capture interrupt only */ - if( ((status & HISR_VC0) && playstate && playstate->dmabuf.ready) || - (((status & HISR_VC1) && recstate && recstate->dmabuf.ready)) ) - { + if (((status & HISR_VC0) && playstate && playstate->dmabuf.ready) || + (((status & HISR_VC1) && recstate && recstate->dmabuf.ready))) { CS_DBGOUT(CS_INTERRUPT, 8, printk( "cs46xx: cs_interrupt() interrupt bit(s) set (0x%x)\n",status)); cs_update_ptr(card, CS_TRUE); } - if( status & HISR_MIDI ) + if (status & HISR_MIDI) cs_handle_midi(card); /* clear 'em */ @@ -1694,7 +1656,7 @@ static irqreturn_t cs_interrupt(int irq, static ssize_t cs_midi_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1737,7 +1699,7 @@ static ssize_t cs_midi_read(struct file static ssize_t cs_midi_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; ssize_t ret; unsigned long flags; unsigned ptr; @@ -1785,7 +1747,7 @@ static ssize_t cs_midi_write(struct file static unsigned int cs_midi_poll(struct file *file, struct poll_table_struct *wait) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; unsigned long flags; unsigned int mask = 0; @@ -1810,12 +1772,11 @@ static unsigned int cs_midi_poll(struct static int cs_midi_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); - struct cs_card *card=NULL; + struct cs_card *card = NULL; unsigned long flags; struct list_head *entry; - list_for_each(entry, &cs46xx_devs) - { + list_for_each(entry, &cs46xx_devs) { card = list_entry(entry, struct cs_card, list); if (card->dev_midi == minor) break; @@ -1823,8 +1784,7 @@ static int cs_midi_open(struct inode *in if (entry == &cs46xx_devs) return -ENODEV; - if (!card) - { + if (!card) { CS_DBGOUT(CS_FUNCTION | CS_OPEN, 2, printk(KERN_INFO "cs46xx: cs46xx_midi_open(): Error - unable to find card struct\n")); return -ENODEV; @@ -1852,12 +1812,10 @@ static int cs_midi_open(struct inode *in cs461x_pokeBA0(card, BA0_MIDCR, 0x0000000f); /* Enable xmit, rcv. */ cs461x_pokeBA0(card, BA0_HICR, HICR_IEV | HICR_CHGM); /* Enable interrupts */ } - if (file->f_mode & FMODE_READ) { + if (file->f_mode & FMODE_READ) card->midi.ird = card->midi.iwr = card->midi.icnt = 0; - } - if (file->f_mode & FMODE_WRITE) { + if (file->f_mode & FMODE_WRITE) card->midi.ord = card->midi.owr = card->midi.ocnt = 0; - } spin_unlock_irqrestore(&card->midi.lock, flags); card->midi.open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); mutex_unlock(&card->midi.open_mutex); @@ -1867,7 +1825,7 @@ static int cs_midi_open(struct inode *in static int cs_midi_release(struct inode *inode, struct file *file) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; DECLARE_WAITQUEUE(wait, current); unsigned long flags; unsigned count, tmo; @@ -1933,11 +1891,10 @@ static /*const*/ struct file_operations static void CopySamples(char *dst, char *src, int count, unsigned fmt, struct dmabuf *dmabuf) { - s32 s32AudioSample; - s16 *psSrc=(s16 *)src; - s16 *psDst=(s16 *)dst; - u8 *pucDst=(u8 *)dst; + s16 *psSrc = (s16 *)src; + s16 *psDst = (s16 *)dst; + u8 *pucDst = (u8 *)dst; CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO "cs46xx: CopySamples()+ ") ); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO @@ -1947,34 +1904,29 @@ static void CopySamples(char *dst, char /* * See if the data should be output as 8-bit unsigned stereo. */ - if((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) - { + if ((fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) { /* * Convert each 16-bit signed stereo sample to 8-bit unsigned * stereo using rounding. */ psSrc = (s16 *)src; - count = count/2; - while(count--) - { + count = count / 2; + while (count--) *(pucDst++) = (u8)(((s16)(*psSrc++) + (s16)0x8000) >> 8); - } } /* * See if the data should be output at 8-bit unsigned mono. */ - else if(!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) - { + else if (!(fmt & CS_FMT_STEREO) && !(fmt & CS_FMT_16BIT)) { /* * Convert each 16-bit signed stereo sample to 8-bit unsigned * mono using averaging and rounding. */ psSrc = (s16 *)src; - count = count/2; - while(count--) - { - s32AudioSample = ((*psSrc)+(*(psSrc + 1)))/2 + (s32)0x80; - if(s32AudioSample > 0x7fff) + count = count / 2; + while (count--) { + s32AudioSample = ((*psSrc) + (*(psSrc + 1))) / 2 + (s32)0x80; + if (s32AudioSample > 0x7fff) s32AudioSample = 0x7fff; *(pucDst++) = (u8)(((s16)s32AudioSample + (s16)0x8000) >> 8); psSrc += 2; @@ -1983,17 +1935,15 @@ static void CopySamples(char *dst, char /* * See if the data should be output at 16-bit signed mono. */ - else if(!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT)) - { + else if (!(fmt & CS_FMT_STEREO) && (fmt & CS_FMT_16BIT)) { /* * Convert each 16-bit signed stereo sample to 16-bit signed * mono using averaging. */ psSrc = (s16 *)src; - count = count/2; - while(count--) - { - *(psDst++) = (s16)((*psSrc)+(*(psSrc + 1)))/2; + count = count / 2; + while (count--) { + *(psDst++) = (s16)((*psSrc) + (*(psSrc + 1))) / 2; psSrc += 2; } } @@ -2020,20 +1970,15 @@ static unsigned cs_copy_to_user( "cs_copy_to_user()+ fmt=0x%x cnt=%d dest=%p\n", dmabuf->fmt,(unsigned)cnt,dest) ); - if(cnt > dmabuf->dmasize) - { + if (cnt > dmabuf->dmasize) cnt = dmabuf->dmasize; - } - if(!cnt) - { + if (!cnt) { *copied = 0; return 0; } - if(dmabuf->divisor != 1) - { - if(!dmabuf->tmpbuff) - { - *copied = cnt/dmabuf->divisor; + if (dmabuf->divisor != 1) { + if (!dmabuf->tmpbuff) { + *copied = cnt / dmabuf->divisor; return 0; } @@ -2042,17 +1987,16 @@ static unsigned cs_copy_to_user( src = dmabuf->tmpbuff; cnt = cnt/dmabuf->divisor; } - if (copy_to_user(dest, src, cnt)) - { + if (copy_to_user(dest, src, cnt)) { CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_ERR "cs46xx: cs_copy_to_user()- fault dest=%p src=%p cnt=%d\n", - dest,src,cnt) ); + dest,src,cnt)); *copied = 0; return -EFAULT; } *copied = cnt; CS_DBGOUT(CS_FUNCTION, 2, printk(KERN_INFO - "cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt) ); + "cs46xx: cs_copy_to_user()- copied bytes is %d \n",cnt)); return 0; } @@ -2060,7 +2004,7 @@ static unsigned cs_copy_to_user( the user's buffer. it is filled by the dma machine and drained by this loop. */ static ssize_t cs_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { - struct cs_card *card = (struct cs_card *) file->private_data; + struct cs_card *card = file->private_data; struct cs_state *state; DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf; @@ -2068,12 +2012,12 @@ static ssize_t cs_read(struct file *file unsigned long flags; unsigned swptr; int cnt; - unsigned copied=0; + unsigned copied = 0; CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, printk("cs46xx: cs_read()+ %zd\n",count) ); - state = (struct cs_state *)card->states[0]; - if(!state) + state = card->states[0]; + if (!state) return -ENODEV; dmabuf = &state->dmabuf; @@ -2088,11 +2032,11 @@ static ssize_t cs_read(struct file *file add_wait_queue(&state->dmabuf.wait, &wait); while (count > 0) { - while(!(card->pm.flags & CS46XX_PM_IDLE)) - { + while (!(card->pm.flags & CS46XX_PM_IDLE)) { schedule(); if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; + if (!ret) + ret = -ERESTARTSYS; goto out; } } @@ -2112,19 +2056,20 @@ static ssize_t cs_read(struct file *file recorded */ start_adc(state); if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; + if (!ret) + ret = -EAGAIN; goto out; } mutex_unlock(&state->sem); schedule(); if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; + if (!ret) + ret = -ERESTARTSYS; goto out; } mutex_lock(&state->sem); - if (dmabuf->mapped) - { - if(!ret) + if (dmabuf->mapped) { + if (!ret) ret = -ENXIO; goto out; } @@ -2135,12 +2080,12 @@ static ssize_t cs_read(struct file *file "_read() copy_to cnt=%d count=%zd ", cnt,count) ); CS_DBGOUT(CS_WAVE_READ, 8, printk(KERN_INFO " .dmasize=%d .count=%d buffer=%p ret=%zd\n", - dmabuf->dmasize,dmabuf->count,buffer,ret) ); + dmabuf->dmasize,dmabuf->count,buffer,ret)); if (cs_copy_to_user(state, buffer, - (char *)dmabuf->rawbuf + swptr, cnt, &copied)) - { - if (!ret) ret = -EFAULT; + (char *)dmabuf->rawbuf + swptr, cnt, &copied)) { + if (!ret) + ret = -EFAULT; goto out; } swptr = (swptr + cnt) % dmabuf->dmasize; @@ -2167,7 +2112,7 @@ out2: the soundcard. it is drained by the dma machine and filled by this loop. */ static ssize_t cs_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { - struct cs_card *card = (struct cs_card *) file->private_data; + struct cs_card *card = file->private_data; struct cs_state *state; DECLARE_WAITQUEUE(wait, current); struct dmabuf *dmabuf; @@ -2178,16 +2123,15 @@ static ssize_t cs_write(struct file *fil CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 4, printk("cs46xx: cs_write called, count = %zd\n", count) ); - state = (struct cs_state *)card->states[1]; - if(!state) + state = card->states[1]; + if (!state) return -ENODEV; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; dmabuf = &state->dmabuf; mutex_lock(&state->sem); - if (dmabuf->mapped) - { + if (dmabuf->mapped) { ret = -ENXIO; goto out; } @@ -2201,11 +2145,11 @@ static ssize_t cs_write(struct file *fil * check for PM events and underrun/overrun in the loop. */ while (count > 0) { - while(!(card->pm.flags & CS46XX_PM_IDLE)) - { + while (!(card->pm.flags & CS46XX_PM_IDLE)) { schedule(); if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; + if (!ret) + ret = -ERESTARTSYS; goto out; } } @@ -2216,8 +2160,7 @@ static ssize_t cs_write(struct file *fil dmabuf->count = 0; dmabuf->swptr = dmabuf->hwptr; } - if (dmabuf->underrun) - { + if (dmabuf->underrun) { dmabuf->underrun = 0; dmabuf->hwptr = cs_get_dma_addr(state); dmabuf->swptr = dmabuf->hwptr; @@ -2238,34 +2181,35 @@ static ssize_t cs_write(struct file *fil played */ start_dac(state); if (file->f_flags & O_NONBLOCK) { - if (!ret) ret = -EAGAIN; + if (!ret) + ret = -EAGAIN; goto out; } mutex_unlock(&state->sem); schedule(); if (signal_pending(current)) { - if(!ret) ret = -ERESTARTSYS; + if (!ret) + ret = -ERESTARTSYS; goto out; } mutex_lock(&state->sem); - if (dmabuf->mapped) - { - if(!ret) + if (dmabuf->mapped) { + if (!ret) ret = -ENXIO; goto out; } continue; } if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { - if (!ret) ret = -EFAULT; + if (!ret) + ret = -EFAULT; goto out; } spin_lock_irqsave(&state->card->lock, flags); swptr = (swptr + cnt) % dmabuf->dmasize; dmabuf->swptr = swptr; dmabuf->count += cnt; - if(dmabuf->count > dmabuf->dmasize) - { + if (dmabuf->count > dmabuf->dmasize) { CS_DBGOUT(CS_WAVE_WRITE | CS_ERROR, 2, printk( "cs46xx: cs_write() d->count > dmasize - resetting\n")); dmabuf->count = dmabuf->dmasize; @@ -2284,38 +2228,32 @@ out: set_current_state(TASK_RUNNING); CS_DBGOUT(CS_WAVE_WRITE | CS_FUNCTION, 2, - printk("cs46xx: cs_write()- ret=%zd\n", ret) ); + printk("cs46xx: cs_write()- ret=%zd\n", ret)); return ret; } static unsigned int cs_poll(struct file *file, struct poll_table_struct *wait) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; struct dmabuf *dmabuf; struct cs_state *state; - unsigned long flags; unsigned int mask = 0; CS_DBGOUT(CS_FUNCTION, 2, printk("cs46xx: cs_poll()+ \n")); - if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) - { + if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) { return -EINVAL; } - if (file->f_mode & FMODE_WRITE) - { + if (file->f_mode & FMODE_WRITE) { state = card->states[1]; - if(state) - { + if (state) { dmabuf = &state->dmabuf; poll_wait(file, &dmabuf->wait, wait); } } - if (file->f_mode & FMODE_READ) - { + if (file->f_mode & FMODE_READ) { state = card->states[0]; - if(state) - { + if (state) { dmabuf = &state->dmabuf; poll_wait(file, &dmabuf->wait, wait); } @@ -2325,8 +2263,7 @@ static unsigned int cs_poll(struct file cs_update_ptr(card, CS_FALSE); if (file->f_mode & FMODE_READ) { state = card->states[0]; - if(state) - { + if (state) { dmabuf = &state->dmabuf; if (dmabuf->count >= (signed)dmabuf->fragsize) mask |= POLLIN | POLLRDNORM; @@ -2334,8 +2271,7 @@ static unsigned int cs_poll(struct file } if (file->f_mode & FMODE_WRITE) { state = card->states[1]; - if(state) - { + if (state) { dmabuf = &state->dmabuf; if (dmabuf->mapped) { if (dmabuf->count >= (signed)dmabuf->fragsize) @@ -2364,7 +2300,7 @@ static unsigned int cs_poll(struct file static int cs_mmap(struct file *file, struct vm_area_struct *vma) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; struct cs_state *state; struct dmabuf *dmabuf; int ret = 0; @@ -2376,8 +2312,7 @@ static int cs_mmap(struct file *file, st if (vma->vm_flags & VM_WRITE) { state = card->states[1]; - if(state) - { + if (state) { CS_DBGOUT(CS_OPEN, 2, printk( "cs46xx: cs_mmap() VM_WRITE - state TRUE prog_dmabuf DAC\n") ); if ((ret = prog_dmabuf(state)) != 0) @@ -2385,8 +2320,7 @@ static int cs_mmap(struct file *file, st } } else if (vma->vm_flags & VM_READ) { state = card->states[0]; - if(state) - { + if (state) { CS_DBGOUT(CS_OPEN, 2, printk( "cs46xx: cs_mmap() VM_READ - state TRUE prog_dmabuf ADC\n") ); if ((ret = prog_dmabuf(state)) != 0) @@ -2414,8 +2348,7 @@ static int cs_mmap(struct file *file, st mutex_lock(&state->sem); dmabuf = &state->dmabuf; - if (cs4x_pgoff(vma) != 0) - { + if (cs4x_pgoff(vma) != 0) { ret = -EINVAL; goto out; } @@ -2423,15 +2356,13 @@ static int cs_mmap(struct file *file, st CS_DBGOUT(CS_PARMS, 2, printk("cs46xx: cs_mmap(): size=%d\n",(unsigned)size) ); - if (size > (PAGE_SIZE << dmabuf->buforder)) - { + if (size > (PAGE_SIZE << dmabuf->buforder)) { ret = -EINVAL; goto out; } if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(dmabuf->rawbuf) >> PAGE_SHIFT, - size, vma->vm_page_prot)) - { + size, vma->vm_page_prot)) { ret = -EAGAIN; goto out; } @@ -2445,25 +2376,24 @@ out: static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; struct cs_state *state; - struct dmabuf *dmabuf=NULL; + struct dmabuf *dmabuf = NULL; unsigned long flags; audio_buf_info abinfo; count_info cinfo; - int val, valsave, mapped, ret; + int val, valsave, ret; + int mapped = 0; void __user *argp = (void __user *)arg; int __user *p = argp; - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped; } - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped; } @@ -2472,17 +2402,14 @@ #if CSDEBUG printioctl(cmd); #endif - switch (cmd) - { + switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, p); - case SNDCTL_DSP_RESET: /* FIXME: spin_lock ? */ if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; stop_dac(state); synchronize_irq(card->irq); @@ -2495,9 +2422,8 @@ #endif } } if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; stop_adc(state); synchronize_irq(card->irq); @@ -2511,20 +2437,17 @@ #endif } CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") ); return 0; - case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) return drain_dac(state, file->f_flags & O_NONBLOCK); return 0; - case SNDCTL_DSP_SPEED: /* set sample rate */ if (get_user(val, p)) return -EFAULT; if (val >= 0) { if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; stop_adc(state); dmabuf->ready = 0; @@ -2534,9 +2457,8 @@ #endif } } if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; stop_dac(state); dmabuf->ready = 0; @@ -2553,19 +2475,17 @@ #endif return put_user(dmabuf->rate, p); } return put_user(0, p); - case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ if (get_user(val, p)) return -EFAULT; if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; stop_dac(state); dmabuf->ready = 0; dmabuf->SGok = 0; - if(val) + if (val) dmabuf->fmt |= CS_FMT_STEREO; else dmabuf->fmt &= ~CS_FMT_STEREO; @@ -2577,14 +2497,13 @@ #endif } } if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; stop_adc(state); dmabuf->ready = 0; dmabuf->SGok = 0; - if(val) + if (val) dmabuf->fmt |= CS_FMT_STEREO; else dmabuf->fmt &= ~CS_FMT_STEREO; @@ -2596,12 +2515,10 @@ #endif } } return 0; - case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; if ((val = prog_dmabuf(state))) return val; @@ -2609,9 +2526,8 @@ #endif } } if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; if ((val = prog_dmabuf(state))) return val; @@ -2620,10 +2536,8 @@ #endif } } return put_user(0, p); - case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ return put_user(AFMT_S16_LE | AFMT_U8, p); - case SNDCTL_DSP_SETFMT: /* Select sample format */ if (get_user(val, p)) return -EFAULT; @@ -2635,88 +2549,75 @@ #endif val == AFMT_U8 ? "8Bit Unsigned" : "") ); valsave = val; if (val != AFMT_QUERY) { - if(val==AFMT_S16_LE || val==AFMT_U8) - { + if (val==AFMT_S16_LE || val==AFMT_U8) { if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; stop_dac(state); dmabuf->ready = 0; dmabuf->SGok = 0; - if(val==AFMT_S16_LE) + if (val == AFMT_S16_LE) dmabuf->fmt |= CS_FMT_16BIT; else dmabuf->fmt &= ~CS_FMT_16BIT; cs_set_divisor(dmabuf); - if((ret = prog_dmabuf(state))) + if ((ret = prog_dmabuf(state))) return ret; } } if (file->f_mode & FMODE_READ) { val = valsave; - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; stop_adc(state); dmabuf->ready = 0; dmabuf->SGok = 0; - if(val==AFMT_S16_LE) + if (val == AFMT_S16_LE) dmabuf->fmt |= CS_FMT_16BIT; else dmabuf->fmt &= ~CS_FMT_16BIT; cs_set_divisor(dmabuf); - if((ret = prog_dmabuf(state))) + if ((ret = prog_dmabuf(state))) return ret; } } - } - else - { + } else { CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk( "cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n", valsave) ); } - } - else - { - if(file->f_mode & FMODE_WRITE) - { - state = (struct cs_state *)card->states[1]; - if(state) + } else { + if (file->f_mode & FMODE_WRITE) { + state = card->states[1]; + if (state) dmabuf = &state->dmabuf; - } - else if(file->f_mode & FMODE_READ) - { - state = (struct cs_state *)card->states[0]; - if(state) + } else if (file->f_mode & FMODE_READ) { + state = card->states[0]; + if (state) dmabuf = &state->dmabuf; } } - if(dmabuf) - { - if(dmabuf->fmt & CS_FMT_16BIT) + if (dmabuf) { + if (dmabuf->fmt & CS_FMT_16BIT) return put_user(AFMT_S16_LE, p); else return put_user(AFMT_U8, p); } return put_user(0, p); - case SNDCTL_DSP_CHANNELS: if (get_user(val, p)) return -EFAULT; if (val != 0) { if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; stop_dac(state); dmabuf->ready = 0; dmabuf->SGok = 0; - if(val>1) + if (val > 1) dmabuf->fmt |= CS_FMT_STEREO; else dmabuf->fmt &= ~CS_FMT_STEREO; @@ -2726,14 +2627,13 @@ #endif } } if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; stop_adc(state); dmabuf->ready = 0; dmabuf->SGok = 0; - if(val>1) + if (val > 1) dmabuf->fmt |= CS_FMT_STEREO; else dmabuf->fmt &= ~CS_FMT_STEREO; @@ -2745,19 +2645,16 @@ #endif } return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, p); - case SNDCTL_DSP_POST: /* * There will be a longer than normal pause in the data. * so... do nothing, because there is nothing that we can do. */ return 0; - case SNDCTL_DSP_SUBDIVIDE: if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; if (dmabuf->subdivision) return -EINVAL; @@ -2769,9 +2666,8 @@ #endif } } if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; if (dmabuf->subdivision) return -EINVAL; @@ -2783,37 +2679,31 @@ #endif } } return 0; - case SNDCTL_DSP_SETFRAGMENT: if (get_user(val, p)) return -EFAULT; - if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; dmabuf->ossfragshift = val & 0xffff; dmabuf->ossmaxfrags = (val >> 16) & 0xffff; } } if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; dmabuf->ossfragshift = val & 0xffff; dmabuf->ossmaxfrags = (val >> 16) & 0xffff; } } return 0; - case SNDCTL_DSP_GETOSPACE: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; spin_lock_irqsave(&state->card->lock, flags); cs_update_ptr(card, CS_TRUE); @@ -2832,13 +2722,11 @@ #endif return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; } return -ENODEV; - case SNDCTL_DSP_GETISPACE: if (!(file->f_mode & FMODE_READ)) return -EINVAL; - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; spin_lock_irqsave(&state->card->lock, flags); cs_update_ptr(card, CS_TRUE); @@ -2850,48 +2738,39 @@ #endif return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; } return -ENODEV; - case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; - case SNDCTL_DSP_GETCAPS: return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, p); - case SNDCTL_DSP_GETTRIGGER: val = 0; CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") ); - if (file->f_mode & FMODE_WRITE) - { - state = (struct cs_state *)card->states[1]; - if(state) - { + if (file->f_mode & FMODE_WRITE) { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; - if(dmabuf->enable & DAC_RUNNING) + if (dmabuf->enable & DAC_RUNNING) val |= PCM_ENABLE_INPUT; } } - if (file->f_mode & FMODE_READ) - { - if(state) - { - state = (struct cs_state *)card->states[0]; + if (file->f_mode & FMODE_READ) { + if (state) { + state = card->states[0]; dmabuf = &state->dmabuf; - if(dmabuf->enable & ADC_RUNNING) + if (dmabuf->enable & ADC_RUNNING) val |= PCM_ENABLE_OUTPUT; } } CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()- val=0x%x\n",val) ); return put_user(val, p); - case SNDCTL_DSP_SETTRIGGER: if (get_user(val, p)) return -EFAULT; if (file->f_mode & FMODE_READ) { - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; if (val & PCM_ENABLE_INPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state))) @@ -2902,9 +2781,8 @@ #endif } } if (file->f_mode & FMODE_WRITE) { - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; if (val & PCM_ENABLE_OUTPUT) { if (!dmabuf->ready && (ret = prog_dmabuf(state))) @@ -2915,13 +2793,11 @@ #endif } } return 0; - case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; - state = (struct cs_state *)card->states[0]; - if(state) - { + state = card->states[0]; + if (state) { dmabuf = &state->dmabuf; spin_lock_irqsave(&state->card->lock, flags); cs_update_ptr(card, CS_TRUE); @@ -2934,28 +2810,23 @@ #endif return 0; } return -ENODEV; - case SNDCTL_DSP_GETOPTR: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; spin_lock_irqsave(&state->card->lock, flags); cs_update_ptr(card, CS_TRUE); cinfo.bytes = dmabuf->total_bytes; - if (dmabuf->mapped) - { + if (dmabuf->mapped) { cinfo.blocks = (cinfo.bytes >> dmabuf->fragshift) - dmabuf->blocks; CS_DBGOUT(CS_PARMS, 8, printk("total_bytes=%d blocks=%d dmabuf->blocks=%d\n", cinfo.bytes,cinfo.blocks,dmabuf->blocks) ); dmabuf->blocks = cinfo.bytes >> dmabuf->fragshift; - } - else - { + } else { cinfo.blocks = dmabuf->count >> dmabuf->fragshift; } cinfo.ptr = dmabuf->hwptr; @@ -2969,66 +2840,54 @@ #endif return 0; } return -ENODEV; - case SNDCTL_DSP_SETDUPLEX: return 0; - case SNDCTL_DSP_GETODELAY: if (!(file->f_mode & FMODE_WRITE)) return -EINVAL; - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; spin_lock_irqsave(&state->card->lock, flags); cs_update_ptr(card, CS_TRUE); val = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); - } - else + } else val = 0; return put_user(val, p); - case SOUND_PCM_READ_RATE: - if(file->f_mode & FMODE_READ) - state = (struct cs_state *)card->states[0]; + if (file->f_mode & FMODE_READ) + state = card->states[0]; else - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; return put_user(dmabuf->rate, p); } return put_user(0, p); - - case SOUND_PCM_READ_CHANNELS: - if(file->f_mode & FMODE_READ) - state = (struct cs_state *)card->states[0]; + if (file->f_mode & FMODE_READ) + state = card->states[0]; else - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1, p); } return put_user(0, p); - case SOUND_PCM_READ_BITS: - if(file->f_mode & FMODE_READ) - state = (struct cs_state *)card->states[0]; + if (file->f_mode & FMODE_READ) + state = card->states[0]; else - state = (struct cs_state *)card->states[1]; - if(state) - { + state = card->states[1]; + if (state) { dmabuf = &state->dmabuf; return put_user((dmabuf->fmt & CS_FMT_16BIT) ? AFMT_S16_LE : AFMT_U8, p); } return put_user(0, p); - case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: @@ -3057,18 +2916,15 @@ static void amp_voyetra(struct cs_card * /* Manage the EAPD bit on the Crystal 4297 and the Analog AD1885 */ - int old=card->amplifier; + int old = card->amplifier; card->amplifier+=change; - if(card->amplifier && !old) - { + if (card->amplifier && !old) { /* Turn the EAPD amp on */ cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) | 0x8000); - } - else if(old && !card->amplifier) - { + } else if(old && !card->amplifier) { /* Turn the EAPD amp off */ cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & @@ -3083,25 +2939,21 @@ static void amp_voyetra(struct cs_card * static void amp_hercules(struct cs_card *card, int change) { - int old=card->amplifier; - if(!card) - { + int old = card->amplifier; + if (!card) { CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO "cs46xx: amp_hercules() called before initialized.\n")); return; } card->amplifier+=change; - if( (card->amplifier && !old) && !(hercules_egpio_disable)) - { + if ((card->amplifier && !old) && !(hercules_egpio_disable)) { CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO "cs46xx: amp_hercules() external amp enabled\n")); cs461x_pokeBA0(card, BA0_EGPIODR, EGPIODR_GPOE2); /* enable EGPIO2 output */ cs461x_pokeBA0(card, BA0_EGPIOPTR, EGPIOPTR_GPPT2); /* open-drain on output */ - } - else if(old && !card->amplifier) - { + } else if (old && !card->amplifier) { CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO "cs46xx: amp_hercules() external amp disabled\n")); cs461x_pokeBA0(card, BA0_EGPIODR, 0); /* disable */ @@ -3124,31 +2976,28 @@ static void clkrun_hack(struct cs_card * u16 control; u8 pp; unsigned long port; - int old=card->active; + int old = card->active; card->active+=change; acpi_dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL); - if(acpi_dev == NULL) + if (acpi_dev == NULL) return; /* Not a thinkpad thats for sure */ /* Find the control port */ pci_read_config_byte(acpi_dev, 0x41, &pp); - port=pp<<8; + port = pp << 8; /* Read ACPI port */ - control=inw(port+0x10); + control = inw(port + 0x10); /* Flip CLKRUN off while running */ - if(!card->active && old) - { + if (!card->active && old) { CS_DBGOUT(CS_PARMS , 9, printk( KERN_INFO "cs46xx: clkrun() enable clkrun - change=%d active=%d\n", change,card->active)); outw(control|0x2000, port+0x10); - } - else - { + } else { /* * sometimes on a resume the bit is set, so always reset the bit. */ @@ -3162,20 +3011,19 @@ static void clkrun_hack(struct cs_card * static int cs_open(struct inode *inode, struct file *file) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; struct cs_state *state = NULL; struct dmabuf *dmabuf = NULL; struct list_head *entry; unsigned int minor = iminor(inode); - int ret=0; + int ret = 0; unsigned int tmp; CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()+ file=%p %s %s\n", file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); - list_for_each(entry, &cs46xx_devs) - { + list_for_each(entry, &cs46xx_devs) { card = list_entry(entry, struct cs_card, list); if (!((card->dev_audio ^ minor) & ~0xf)) @@ -3192,11 +3040,10 @@ static int cs_open(struct inode *inode, /* * hardcode state[0] for capture, [1] for playback */ - if(file->f_mode & FMODE_READ) - { + if (file->f_mode & FMODE_READ) { CS_DBGOUT(CS_WAVE_READ, 2, printk("cs46xx: cs_open() FMODE_READ\n") ); if (card->states[0] == NULL) { - state = card->states[0] = (struct cs_state *) + state = card->states[0] = kmalloc(sizeof(struct cs_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; @@ -3204,36 +3051,32 @@ static int cs_open(struct inode *inode, mutex_init(&state->sem); dmabuf = &state->dmabuf; dmabuf->pbuf = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if(dmabuf->pbuf==NULL) - { + if (dmabuf->pbuf == NULL) { kfree(state); - card->states[0]=NULL; + card->states[0] = NULL; return -ENOMEM; } - } - else - { + } else { state = card->states[0]; - if(state->open_mode & FMODE_READ) + if (state->open_mode & FMODE_READ) return -EBUSY; } dmabuf->channel = card->alloc_rec_pcm_channel(card); if (dmabuf->channel == NULL) { - kfree (card->states[0]); + kfree(card->states[0]); card->states[0] = NULL; return -ENODEV; } /* Now turn on external AMP if needed */ state->card = card; - state->card->active_ctrl(state->card,1); - state->card->amplifier_ctrl(state->card,1); + state->card->active_ctrl(state->card, 1); + state->card->amplifier_ctrl(state->card, 1); - if( (tmp = cs46xx_powerup(card, CS_POWER_ADC)) ) - { + if ((tmp = cs46xx_powerup(card, CS_POWER_ADC))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs46xx_powerup of ADC failed (0x%x)\n",tmp) ); + "cs46xx: cs46xx_powerup of ADC failed (0x%x)\n", tmp)); return -EIO; } @@ -3263,11 +3106,10 @@ static int cs_open(struct inode *inode, state->open_mode |= FMODE_READ; mutex_unlock(&state->open_mutex); } - if(file->f_mode & FMODE_WRITE) - { + if (file->f_mode & FMODE_WRITE) { CS_DBGOUT(CS_OPEN, 2, printk("cs46xx: cs_open() FMODE_WRITE\n") ); if (card->states[1] == NULL) { - state = card->states[1] = (struct cs_state *) + state = card->states[1] = kmalloc(sizeof(struct cs_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; @@ -3275,36 +3117,32 @@ static int cs_open(struct inode *inode, mutex_init(&state->sem); dmabuf = &state->dmabuf; dmabuf->pbuf = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if(dmabuf->pbuf==NULL) - { + if (dmabuf->pbuf == NULL) { kfree(state); - card->states[1]=NULL; + card->states[1] = NULL; return -ENOMEM; } - } - else - { + } else { state = card->states[1]; - if(state->open_mode & FMODE_WRITE) + if (state->open_mode & FMODE_WRITE) return -EBUSY; } dmabuf->channel = card->alloc_pcm_channel(card); if (dmabuf->channel == NULL) { - kfree (card->states[1]); + kfree(card->states[1]); card->states[1] = NULL; return -ENODEV; } /* Now turn on external AMP if needed */ state->card = card; - state->card->active_ctrl(state->card,1); - state->card->amplifier_ctrl(state->card,1); + state->card->active_ctrl(state->card, 1); + state->card->amplifier_ctrl(state->card, 1); - if( (tmp = cs46xx_powerup(card, CS_POWER_DAC)) ) - { + if ((tmp = cs46xx_powerup(card, CS_POWER_DAC))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs46xx_powerup of DAC failed (0x%x)\n",tmp) ); + "cs46xx: cs46xx_powerup of DAC failed (0x%x)\n", tmp)); return -EIO; } @@ -3333,33 +3171,29 @@ static int cs_open(struct inode *inode, state->open_mode |= FMODE_WRITE; mutex_unlock(&state->open_mutex); - if((ret = prog_dmabuf(state))) + if ((ret = prog_dmabuf(state))) return ret; } - CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n") ); + CS_DBGOUT(CS_OPEN | CS_FUNCTION, 2, printk("cs46xx: cs_open()- 0\n")); return nonseekable_open(inode, file); } static int cs_release(struct inode *inode, struct file *file) { - struct cs_card *card = (struct cs_card *)file->private_data; + struct cs_card *card = file->private_data; struct dmabuf *dmabuf; struct cs_state *state; unsigned int tmp; CS_DBGOUT(CS_RELEASE | CS_FUNCTION, 2, printk("cs46xx: cs_release()+ file=%p %s %s\n", file, file->f_mode & FMODE_WRITE ? "FMODE_WRITE" : "", - file->f_mode & FMODE_READ ? "FMODE_READ" : "") ); + file->f_mode & FMODE_READ ? "FMODE_READ" : "")); if (!(file->f_mode & (FMODE_WRITE | FMODE_READ))) - { return -EINVAL; - } state = card->states[1]; - if(state) - { - if ( (state->open_mode & FMODE_WRITE) & (file->f_mode & FMODE_WRITE) ) - { - CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_WRITE\n") ); + if (state) { + if ((state->open_mode & FMODE_WRITE) & (file->f_mode & FMODE_WRITE)) { + CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_WRITE\n")); dmabuf = &state->dmabuf; cs_clear_tail(state); drain_dac(state, file->f_flags & O_NONBLOCK); @@ -3375,8 +3209,7 @@ static int cs_release(struct inode *inod state->card->states[state->virt] = NULL; state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE )) ) - { + if ((tmp = cs461x_powerdown(card, CS_POWER_DAC, CS_FALSE))) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO "cs46xx: cs_release_mixdev() powerdown DAC failure (0x%x)\n",tmp) ); } @@ -3384,17 +3217,14 @@ static int cs_release(struct inode *inod /* Now turn off external AMP if needed */ state->card->amplifier_ctrl(state->card, -1); state->card->active_ctrl(state->card, -1); - kfree(state); } } state = card->states[0]; - if(state) - { - if ( (state->open_mode & FMODE_READ) & (file->f_mode & FMODE_READ) ) - { - CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_READ\n") ); + if (state) { + if ((state->open_mode & FMODE_READ) & (file->f_mode & FMODE_READ)) { + CS_DBGOUT(CS_RELEASE, 2, printk("cs46xx: cs_release() FMODE_READ\n")); dmabuf = &state->dmabuf; mutex_lock(&state->open_mutex); stop_adc(state); @@ -3407,8 +3237,7 @@ static int cs_release(struct inode *inod state->card->states[state->virt] = NULL; state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); - if( (tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE )) ) - { + if ((tmp = cs461x_powerdown(card, CS_POWER_ADC, CS_FALSE))) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO "cs46xx: cs_release_mixdev() powerdown ADC failure (0x%x)\n",tmp) ); } @@ -3416,12 +3245,11 @@ static int cs_release(struct inode *inod /* Now turn off external AMP if needed */ state->card->amplifier_ctrl(state->card, -1); state->card->active_ctrl(state->card, -1); - kfree(state); } } - CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk("cs46xx: cs_release()- 0\n") ); + CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 2, printk("cs46xx: cs_release()- 0\n")); return 0; } @@ -3474,21 +3302,18 @@ static void cs46xx_ac97_suspend(struct c CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()+\n")); - if(card->states[1]) - { + if (card->states[1]) { stop_dac(card->states[1]); resync_dma_ptrs(card->states[1]); } - if(card->states[0]) - { + if (card->states[0]) { stop_adc(card->states[0]); resync_dma_ptrs(card->states[0]); } - for(Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) - && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { + for (Count = 0x2, i = 0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) + && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); + Count += 2, i++) { card->pm.ac97[i] = cs_ac97_get(dev, BA0_AC97_RESET + Count); } /* @@ -3522,11 +3347,10 @@ static void cs46xx_ac97_suspend(struct c * well, for now, only power down the DAC/ADC and MIXER VREFON components. * trouble with removing VREF. */ - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON, CS_TRUE )) ) - { + if ((tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_TRUE))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp) ); + "cs46xx: cs46xx_ac97_suspend() failure (0x%x)\n",tmp)); } CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_suspend()-\n")); @@ -3566,16 +3390,13 @@ static void cs46xx_ac97_resume(struct cs * Restore just the first set of registers, from register number * 0x02 to the register number that ulHighestRegToRestore specifies. */ - for( Count = 0x2, i=0; - (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) - && (i < CS46XX_AC97_NUMBER_RESTORE_REGS); - Count += 2, i++) - { + for (Count = 0x2, i=0; (Count <= CS46XX_AC97_HIGHESTREGTORESTORE) && + (i < CS46XX_AC97_NUMBER_RESTORE_REGS); Count += 2, i++) { cs_ac97_set(dev, (u8)(BA0_AC97_RESET + Count), (u16)card->pm.ac97[i]); } /* Check if we have to init the amplifier */ - if(card->amp_init) + if (card->amp_init) card->amp_init(card); CS_DBGOUT(CS_PM, 9, printk("cs46xx: cs46xx_ac97_resume()-\n")); @@ -3585,30 +3406,27 @@ static void cs46xx_ac97_resume(struct cs static int cs46xx_restart_part(struct cs_card *card) { struct dmabuf *dmabuf; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk( "cs46xx: cs46xx_restart_part()+\n")); - if(card->states[1]) - { + if (card->states[1]) { dmabuf = &card->states[1]->dmabuf; dmabuf->ready = 0; resync_dma_ptrs(card->states[1]); cs_set_divisor(dmabuf); - if(__prog_dmabuf(card->states[1])) - { + if (__prog_dmabuf(card->states[1])) { CS_DBGOUT(CS_PM | CS_ERROR, 1, printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() dac error\n")); return -1; } cs_set_dac_rate(card->states[1], dmabuf->rate); } - if(card->states[0]) - { + if (card->states[0]) { dmabuf = &card->states[0]->dmabuf; dmabuf->ready = 0; resync_dma_ptrs(card->states[0]); cs_set_divisor(dmabuf); - if(__prog_dmabuf(card->states[0])) - { + if (__prog_dmabuf(card->states[0])) { CS_DBGOUT(CS_PM | CS_ERROR, 1, printk("cs46xx: cs46xx_restart_part()- (-1) prog_dmabuf() adc error\n")); return -1; @@ -3616,17 +3434,17 @@ static int cs46xx_restart_part(struct cs cs_set_adc_rate(card->states[0], dmabuf->rate); } card->pm.flags |= CS46XX_PM_RESUMED; - if(card->states[0]) + if (card->states[0]) start_adc(card->states[0]); - if(card->states[1]) + if (card->states[1]) start_dac(card->states[1]); card->pm.flags |= CS46XX_PM_IDLE; card->pm.flags &= ~(CS46XX_PM_SUSPENDING | CS46XX_PM_SUSPENDED | CS46XX_PM_RESUMING | CS46XX_PM_RESUMED); - if(card->states[0]) + if (card->states[0]) wake_up(&card->states[0]->dmabuf.wait); - if(card->states[1]) + if (card->states[1]) wake_up(&card->states[1]->dmabuf.wait); CS_DBGOUT(CS_PM | CS_FUNCTION, 4, @@ -3634,20 +3452,19 @@ static int cs46xx_restart_part(struct cs return 0; } - static void cs461x_reset(struct cs_card *card); static void cs461x_proc_stop(struct cs_card *card); static int cs46xx_suspend(struct cs_card *card, pm_message_t state) { unsigned int tmp; + CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk("cs46xx: cs46xx_suspend()+ flags=0x%x s=%p\n", (unsigned)card->pm.flags,card)); /* * check the current state, only suspend if IDLE */ - if(!(card->pm.flags & CS46XX_PM_IDLE)) - { + if (!(card->pm.flags & CS46XX_PM_IDLE)) { CS_DBGOUT(CS_PM | CS_ERROR, 2, printk("cs46xx: cs46xx_suspend() unable to suspend, not IDLE\n")); return 1; @@ -3679,13 +3496,11 @@ static int cs46xx_suspend(struct cs_card tmp = cs461x_peek(card, BA1_CCTL); cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); - if(card->states[1]) - { + if (card->states[1]) { card->pm.dmabuf_swptr_play = card->states[1]->dmabuf.swptr; card->pm.dmabuf_count_play = card->states[1]->dmabuf.count; } - if(card->states[0]) - { + if (card->states[0]) { card->pm.dmabuf_swptr_capture = card->states[0]->dmabuf.swptr; card->pm.dmabuf_count_capture = card->states[0]->dmabuf.count; } @@ -3736,8 +3551,7 @@ static int cs46xx_resume(struct cs_card CS_DBGOUT(CS_PM | CS_FUNCTION, 4, printk( "cs46xx: cs46xx_resume()+ flags=0x%x\n", (unsigned)card->pm.flags)); - if(!(card->pm.flags & CS46XX_PM_SUSPENDED)) - { + if (!(card->pm.flags & CS46XX_PM_SUSPENDED)) { CS_DBGOUT(CS_PM | CS_ERROR, 2, printk("cs46xx: cs46xx_resume() unable to resume, not SUSPENDED\n")); return 1; @@ -3747,10 +3561,8 @@ static int cs46xx_resume(struct cs_card printpm(card); card->active_ctrl(card, 1); - for(i=0;i<5;i++) - { - if (cs_hardware_init(card) != 0) - { + for (i = 0; i < 5; i++) { + if (cs_hardware_init(card) != 0) { CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( "cs46xx: cs46xx_resume()- ERROR in cs_hardware_init()\n")); mdelay(10 * cs_laptop_wait); @@ -3759,15 +3571,13 @@ static int cs46xx_resume(struct cs_card } break; } - if(i>=4) - { + if (i >= 4) { CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( "cs46xx: cs46xx_resume()- cs_hardware_init() failed, retried %d times.\n",i)); return 0; } - if(cs46xx_restart_part(card)) - { + if (cs46xx_restart_part(card)) { CS_DBGOUT(CS_PM | CS_ERROR, 4, printk( "cs46xx: cs46xx_resume(): cs46xx_restart_part() returned error\n")); } @@ -3835,7 +3645,7 @@ static u16 _cs_ac97_get(struct ac97_code /* * Wait for the read to occur. */ - if(!(card->pm.flags & CS46XX_PM_IDLE)) + if (!(card->pm.flags & CS46XX_PM_IDLE)) loopcnt = 2000; else loopcnt = 500 * cs_laptop_wait; @@ -3866,7 +3676,7 @@ static u16 _cs_ac97_get(struct ac97_code * Wait for the valid status bit to go active. */ - if(!(card->pm.flags & CS46XX_PM_IDLE)) + if (!(card->pm.flags & CS46XX_PM_IDLE)) loopcnt = 2000; else loopcnt = 1000; @@ -3885,7 +3695,7 @@ static u16 _cs_ac97_get(struct ac97_code /* * Make sure we got valid status. */ - if (!( (tmp=cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) { + if (!((tmp = cs461x_peekBA0(card, BA0_ACSTS)) & ACSTS_VSTS)) { CS_DBGOUT(CS_ERROR, 2, printk(KERN_WARNING "cs46xx: AC'97 read problem (ACSTS_VSTS), reg = 0x%x val=0x%x 0xffff \n", reg, tmp)); @@ -3923,12 +3733,9 @@ static void cs_ac97_set(struct ac97_code spin_lock(&card->ac97_lock); - if(reg == AC97_CD_VOL) - { + if (reg == AC97_CD_VOL) val2 = _cs_ac97_get(dev, AC97_CD_VOL); - } - - + /* * 1. Write ACCAD = Command Address Register = 46Ch for AC97 register address * 2. Write ACCDA = Command Data Register = 470h for data to write to AC97 @@ -3970,8 +3777,7 @@ static void cs_ac97_set(struct ac97_code /* * Make sure the write completed. */ - if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) - { + if (cs461x_peekBA0(card, BA0_ACCTL) & ACCTL_DCV) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: AC'97 write problem, reg = 0x%x, val = 0x%x\n", reg, val)); } @@ -3998,25 +3804,23 @@ static void cs_ac97_set(struct ac97_code /* CD mute change ? */ - if(reg==AC97_CD_VOL) - { + if (reg == AC97_CD_VOL) { /* Mute bit change ? */ - if((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) - { + if ((val2^val) & 0x8000 || + ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) { /* This is a hack but its cleaner than the alternatives. Right now card->ac97_codec[0] might be NULL as we are still doing codec setup. This does an early assignment to avoid the problem if it occurs */ - if(card->ac97_codec[0]==NULL) - card->ac97_codec[0]=dev; + if (card->ac97_codec[0] == NULL) + card->ac97_codec[0] = dev; /* Mute on */ - if(val&0x8000 || val == 0x1f1f) + if (val & 0x8000 || val == 0x1f1f) card->amplifier_ctrl(card, -1); - else /* Mute off power on */ - { - if(card->amp_init) + else { /* Mute off power on */ + if (card->amp_init) card->amp_init(card); card->amplifier_ctrl(card, 1); } @@ -4024,46 +3828,41 @@ static void cs_ac97_set(struct ac97_code } } - /* OSS /dev/mixer file operation methods */ static int cs_open_mixdev(struct inode *inode, struct file *file) { - int i=0; + int i = 0; unsigned int minor = iminor(inode); - struct cs_card *card=NULL; + struct cs_card *card = NULL; struct list_head *entry; unsigned int tmp; CS_DBGOUT(CS_FUNCTION | CS_OPEN, 4, printk(KERN_INFO "cs46xx: cs_open_mixdev()+\n")); - list_for_each(entry, &cs46xx_devs) - { + list_for_each(entry, &cs46xx_devs) { card = list_entry(entry, struct cs_card, list); for (i = 0; i < NR_AC97; i++) if (card->ac97_codec[i] != NULL && card->ac97_codec[i]->dev_mixer == minor) goto match; } - if (!card) - { + if (!card) { CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); return -ENODEV; } match: - if(!card->ac97_codec[i]) + if (!card->ac97_codec[i]) return -ENODEV; file->private_data = card->ac97_codec[i]; card->active_ctrl(card,1); - if(!CS_IN_USE(&card->mixer_use_cnt)) - { - if( (tmp = cs46xx_powerup(card, CS_POWER_MIXVON )) ) - { + if (!CS_IN_USE(&card->mixer_use_cnt)) { + if ((tmp = cs46xx_powerup(card, CS_POWER_MIXVON))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs_open_mixdev() powerup failure (0x%x)\n",tmp) ); + "cs46xx: cs_open_mixdev() powerup failure (0x%x)\n", tmp)); return -EIO; } } @@ -4077,7 +3876,7 @@ static int cs_open_mixdev(struct inode * static int cs_release_mixdev(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); - struct cs_card *card=NULL; + struct cs_card *card = NULL; struct list_head *entry; int i; unsigned int tmp; @@ -4092,15 +3891,13 @@ static int cs_release_mixdev(struct inod card->ac97_codec[i]->dev_mixer == minor) goto match; } - if (!card) - { + if (!card) { CS_DBGOUT(CS_FUNCTION | CS_OPEN | CS_ERROR, 2, printk(KERN_INFO "cs46xx: cs46xx_open_mixdev()- -ENODEV\n")); return -ENODEV; } match: - if(!CS_DEC_AND_TEST(&card->mixer_use_cnt)) - { + if (!CS_DEC_AND_TEST(&card->mixer_use_cnt)) { CS_DBGOUT(CS_FUNCTION | CS_RELEASE, 4, printk(KERN_INFO "cs46xx: cs_release_mixdev()- no powerdown, usecnt>0\n")); card->active_ctrl(card, -1); @@ -4110,10 +3907,9 @@ match: /* * ok, no outstanding mixer opens, so powerdown. */ - if( (tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE )) ) - { + if ((tmp = cs461x_powerdown(card, CS_POWER_MIXVON, CS_FALSE))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n",tmp) ); + "cs46xx: cs_release_mixdev() powerdown MIXVON failure (0x%x)\n", tmp)); card->active_ctrl(card, -1); card->amplifier_ctrl(card, -1); return -EIO; @@ -4126,76 +3922,60 @@ match: } static int cs_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) + unsigned long arg) { - struct ac97_codec *codec = (struct ac97_codec *)file->private_data; - struct cs_card *card=NULL; + struct ac97_codec *codec = file->private_data; + struct cs_card *card = NULL; struct list_head *entry; unsigned long __user *p = (long __user *)arg; - #if CSDEBUG_INTERFACE int val; - if( (cmd == SOUND_MIXER_CS_GETDBGMASK) || + if ( (cmd == SOUND_MIXER_CS_GETDBGMASK) || (cmd == SOUND_MIXER_CS_SETDBGMASK) || (cmd == SOUND_MIXER_CS_GETDBGLEVEL) || (cmd == SOUND_MIXER_CS_SETDBGLEVEL) || - (cmd == SOUND_MIXER_CS_APM)) - { - switch(cmd) - { - + (cmd == SOUND_MIXER_CS_APM)) { + switch (cmd) { case SOUND_MIXER_CS_GETDBGMASK: return put_user(cs_debugmask, p); - case SOUND_MIXER_CS_GETDBGLEVEL: return put_user(cs_debuglevel, p); - case SOUND_MIXER_CS_SETDBGMASK: if (get_user(val, p)) return -EFAULT; cs_debugmask = val; return 0; - case SOUND_MIXER_CS_SETDBGLEVEL: if (get_user(val, p)) return -EFAULT; cs_debuglevel = val; return 0; - case SOUND_MIXER_CS_APM: if (get_user(val, p)) return -EFAULT; - if(val == CS_IOCTL_CMD_SUSPEND) - { - list_for_each(entry, &cs46xx_devs) - { + if (val == CS_IOCTL_CMD_SUSPEND) { + list_for_each(entry, &cs46xx_devs) { card = list_entry(entry, struct cs_card, list); cs46xx_suspend(card, PMSG_ON); } - } - else if(val == CS_IOCTL_CMD_RESUME) - { - list_for_each(entry, &cs46xx_devs) - { + } else if (val == CS_IOCTL_CMD_RESUME) { + list_for_each(entry, &cs46xx_devs) { card = list_entry(entry, struct cs_card, list); cs46xx_resume(card); } - } - else - { + } else { CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO "cs46xx: mixer_ioctl(): invalid APM cmd (%d)\n", val)); } return 0; - default: CS_DBGOUT(CS_ERROR, 1, printk(KERN_INFO - "cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n") ); + "cs46xx: mixer_ioctl(): ERROR unknown debug cmd\n")); return 0; - } + } } #endif return codec->mixer_ioctl(codec, cmd, arg); @@ -4232,8 +4012,7 @@ static int __init cs_ac97_init(struct cs codec->codec_read = cs_ac97_get; codec->codec_write = cs_ac97_set; - if (ac97_probe_codec(codec) == 0) - { + if (ac97_probe_codec(codec) == 0) { CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO "cs46xx: cs_ac97_init()- codec number %d not found\n", num_ac97) ); @@ -4241,12 +4020,11 @@ static int __init cs_ac97_init(struct cs break; } CS_DBGOUT(CS_FUNCTION | CS_INIT, 2, printk(KERN_INFO - "cs46xx: cs_ac97_init() found codec %d\n",num_ac97) ); + "cs46xx: cs_ac97_init() found codec %d\n",num_ac97)); eid = cs_ac97_get(codec, AC97_EXTENDED_ID); - if(eid==0xFFFF) - { + if (eid == 0xFFFF) { printk(KERN_WARNING "cs46xx: codec %d not present\n",num_ac97); ac97_release_codec(codec); break; @@ -4285,27 +4063,23 @@ static void cs461x_download_image(struct { unsigned i, j, temp1, temp2, offset, count; unsigned char __iomem *pBA1 = ioremap(card->ba1_addr, 0x40000); - for( i=0; i < CLEAR__COUNT; i++) - { + for (i = 0; i < CLEAR__COUNT; i++) { offset = ClrStat[i].BA1__DestByteOffset; count = ClrStat[i].BA1__SourceSize; - for( temp1 = offset; temp1<(offset+count); temp1+=4 ) + for (temp1 = offset; temp1 < (offset + count); temp1 += 4) writel(0, pBA1+temp1); } - for(i=0; iac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON) - { - if(!muted) - { + if (tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp |= CS_AC97_POWER_CONTROL_MIXVOFF; cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); @@ -4492,16 +4259,14 @@ static int cs461x_powerdown(struct cs_ca * Check the status.. */ if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVOFF_ON) - { + CS_AC97_POWER_CONTROL_MIXVOFF_ON) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerdown MIXVOFF failed\n")); return 1; } } } - if(type & CS_POWER_MIXVON) - { + if (type & CS_POWER_MIXVON) { CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ MIXVON\n")); @@ -4509,15 +4274,13 @@ static int cs461x_powerdown(struct cs_ca * Power down the MIXER (VREF ON) on the AC97 card. */ tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON) - { - if(!muted) - { + if (tmp & CS_AC97_POWER_CONTROL_MIXVON_ON) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp |= CS_AC97_POWER_CONTROL_MIXVON; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp); /* * Now, we wait until we sample a ready state. */ @@ -4540,30 +4303,26 @@ static int cs461x_powerdown(struct cs_ca * Check the status.. */ if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVON_ON) - { + CS_AC97_POWER_CONTROL_MIXVON_ON) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerdown MIXVON failed\n")); return 1; } } } - if(type & CS_POWER_ADC) - { + if (type & CS_POWER_ADC) { /* * Power down the ADC on the AC97 card. */ CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ ADC\n")); tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_ADC_ON) - { - if(!muted) - { + if (tmp & CS_AC97_POWER_CONTROL_ADC_ON) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp |= CS_AC97_POWER_CONTROL_ADC; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp); /* * Now, we wait until we sample a ready state. @@ -4587,16 +4346,14 @@ static int cs461x_powerdown(struct cs_ca * Check the status.. */ if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_ADC_ON) - { + CS_AC97_POWER_CONTROL_ADC_ON) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerdown ADC failed\n")); return 1; } } } - if(type & CS_POWER_DAC) - { + if (type & CS_POWER_DAC) { /* * Power down the DAC on the AC97 card. */ @@ -4604,15 +4361,13 @@ static int cs461x_powerdown(struct cs_ca CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()+ DAC\n")); tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (tmp & CS_AC97_POWER_CONTROL_DAC_ON) - { - if(!muted) - { + if (tmp & CS_AC97_POWER_CONTROL_DAC_ON) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp |= CS_AC97_POWER_CONTROL_DAC; - cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); + cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp); /* * Now, we wait until we sample a ready state. */ @@ -4635,8 +4390,7 @@ static int cs461x_powerdown(struct cs_ca * Check the status.. */ if (cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_DAC_ON) - { + CS_AC97_POWER_CONTROL_DAC_ON) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerdown DAC failed\n")); return 1; @@ -4644,7 +4398,7 @@ static int cs461x_powerdown(struct cs_ca } } tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if(muted) + if (muted) cs_mute(card, CS_FALSE); CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs461x_powerdown()- 0 tmp=0x%x\n",tmp)); @@ -4654,23 +4408,22 @@ static int cs461x_powerdown(struct cs_ca static int cs46xx_powerup(struct cs_card *card, unsigned int type) { int count; - unsigned int tmp=0,muted=0; + unsigned int tmp = 0, muted = 0; CS_DBGOUT(CS_FUNCTION, 8, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ type=0x%x\n",type)); /* * check for VREF and powerup if need to. */ - if(type & CS_POWER_MIXVON) + if (type & CS_POWER_MIXVON) type |= CS_POWER_MIXVOFF; - if(type & (CS_POWER_DAC | CS_POWER_ADC)) + if (type & (CS_POWER_DAC | CS_POWER_ADC)) type |= CS_POWER_MIXVON | CS_POWER_MIXVOFF; /* * Power up indicated areas. */ - if(type & CS_POWER_MIXVOFF) - { + if (type & CS_POWER_MIXVOFF) { CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVOFF\n")); @@ -4678,12 +4431,10 @@ static int cs46xx_powerup(struct cs_card * Power up the MIXER (VREF ON) on the AC97 card. */ tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)) - { - if(!muted) - { + if (!(tmp & CS_AC97_POWER_CONTROL_MIXVOFF_ON)) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp &= ~CS_AC97_POWER_CONTROL_MIXVOFF; cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); @@ -4709,16 +4460,14 @@ static int cs46xx_powerup(struct cs_card * Check the status.. */ if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVOFF_ON)) - { + CS_AC97_POWER_CONTROL_MIXVOFF_ON)) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerup MIXVOFF failed\n")); return 1; } } } - if(type & CS_POWER_MIXVON) - { + if(type & CS_POWER_MIXVON) { CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ MIXVON\n")); @@ -4726,12 +4475,10 @@ static int cs46xx_powerup(struct cs_card * Power up the MIXER (VREF ON) on the AC97 card. */ tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)) - { - if(!muted) - { + if (!(tmp & CS_AC97_POWER_CONTROL_MIXVON_ON)) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp &= ~CS_AC97_POWER_CONTROL_MIXVON; cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); @@ -4757,27 +4504,23 @@ static int cs46xx_powerup(struct cs_card * Check the status.. */ if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_MIXVON_ON)) - { + CS_AC97_POWER_CONTROL_MIXVON_ON)) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerup MIXVON failed\n")); return 1; } } } - if(type & CS_POWER_ADC) - { + if (type & CS_POWER_ADC) { /* * Power up the ADC on the AC97 card. */ CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ ADC\n")); tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON)) - { - if(!muted) - { + if (!(tmp & CS_AC97_POWER_CONTROL_ADC_ON)) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp &= ~CS_AC97_POWER_CONTROL_ADC; cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); @@ -4804,16 +4547,14 @@ static int cs46xx_powerup(struct cs_card * Check the status.. */ if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_ADC_ON)) - { + CS_AC97_POWER_CONTROL_ADC_ON)) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerup ADC failed\n")); return 1; } } } - if(type & CS_POWER_DAC) - { + if (type & CS_POWER_DAC) { /* * Power up the DAC on the AC97 card. */ @@ -4821,12 +4562,10 @@ static int cs46xx_powerup(struct cs_card CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()+ DAC\n")); tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON)) - { - if(!muted) - { + if (!(tmp & CS_AC97_POWER_CONTROL_DAC_ON)) { + if (!muted) { cs_mute(card, CS_TRUE); - muted=1; + muted = 1; } tmp &= ~CS_AC97_POWER_CONTROL_DAC; cs_ac97_set(card->ac97_codec[0], AC97_POWER_CONTROL, tmp ); @@ -4852,8 +4591,7 @@ static int cs46xx_powerup(struct cs_card * Check the status.. */ if (!(cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL) & - CS_AC97_POWER_CONTROL_DAC_ON)) - { + CS_AC97_POWER_CONTROL_DAC_ON)) { CS_DBGOUT(CS_ERROR, 1, printk(KERN_WARNING "cs46xx: powerup DAC failed\n")); return 1; @@ -4861,14 +4599,13 @@ static int cs46xx_powerup(struct cs_card } } tmp = cs_ac97_get(card->ac97_codec[0], AC97_POWER_CONTROL); - if(muted) + if (muted) cs_mute(card, CS_FALSE); CS_DBGOUT(CS_FUNCTION, 4, printk(KERN_INFO "cs46xx: cs46xx_powerup()- 0 tmp=0x%x\n",tmp)); return 0; } - static void cs461x_proc_start(struct cs_card *card) { int cnt; @@ -4965,7 +4702,7 @@ static int cs_hardware_init(struct cs_ca * is not enough for some platforms! tested on an IBM Thinkpads and * reference cards. */ - if(!(card->pm.flags & CS46XX_PM_IDLE)) + if (!(card->pm.flags & CS46XX_PM_IDLE)) mdelay(initdelay); /* * Write the selected clock control setup to the hardware. Do not turn on @@ -5017,8 +4754,7 @@ static int cs_hardware_init(struct cs_ca * If we are resuming under 2.2.x then we can not schedule a timeout. * so, just spin the CPU. */ - if(card->pm.flags & CS46XX_PM_IDLE) - { + if (card->pm.flags & CS46XX_PM_IDLE) { /* * Wait for the card ready signal from the AC97 card. */ @@ -5033,9 +4769,7 @@ static int cs_hardware_init(struct cs_ca current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(1); } while (time_before(jiffies, end_time)); - } - else - { + } else { for (count = 0; count < 100; count++) { // First, we want to wait for a short time. udelay(25 * cs_laptop_wait); @@ -5064,8 +4798,7 @@ static int cs_hardware_init(struct cs_ca */ cs461x_pokeBA0(card, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); - if(card->pm.flags & CS46XX_PM_IDLE) - { + if (card->pm.flags & CS46XX_PM_IDLE) { /* * Wait until we've sampled input slots 3 and 4 as valid, meaning that * the card is pumping ADC data across the AC-link. @@ -5081,9 +4814,7 @@ static int cs_hardware_init(struct cs_ca current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(1); } while (time_before(jiffies, end_time)); - } - else - { + } else { for (count = 0; count < 100; count++) { // First, we want to wait for a short time. udelay(25 * cs_laptop_wait); @@ -5140,17 +4871,13 @@ static int cs_hardware_init(struct cs_ca cs461x_poke(card, BA1_CCTL, tmp & 0xffff0000); /* initialize AC97 codec and register /dev/mixer */ - if(card->pm.flags & CS46XX_PM_IDLE) - { - if (cs_ac97_init(card) <= 0) - { + if (card->pm.flags & CS46XX_PM_IDLE) { + if (cs_ac97_init(card) <= 0) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO - "cs46xx: cs_ac97_init() failure\n") ); + "cs46xx: cs_ac97_init() failure\n")); return -EIO; } - } - else - { + } else { cs46xx_ac97_resume(card); } @@ -5174,23 +4901,17 @@ static int cs_hardware_init(struct cs_ca * If IDLE then Power down the part. We will power components up * when we need them. */ - if(card->pm.flags & CS46XX_PM_IDLE) - { - if(!cs_powerdown) - { - if( (tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON )) ) - { + if (card->pm.flags & CS46XX_PM_IDLE) { + if (!cs_powerdown) { + if ((tmp = cs46xx_powerup(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO "cs46xx: cs461x_powerup() failure (0x%x)\n",tmp) ); return -EIO; } - } - else - { - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON, CS_FALSE )) ) - { + } else { + if ((tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_FALSE))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); return -EIO; @@ -5310,14 +5031,13 @@ MODULE_AUTHOR("Alan Cox name) { - if(cp->vendor == ss_vendor && cp->id == ss_card) - { + if (cp->vendor == ss_vendor && cp->id == ss_card) { card->amplifier_ctrl = cp->amp; - if(cp->active) + if (cp->active) card->active_ctrl = cp->active; - if(cp->amp_init) + if (cp->amp_init) card->amp_init = cp->amp_init; break; } cp++; } - if (cp->name==NULL) - { + if (cp->name == NULL) { printk(KERN_INFO "cs46xx: Unknown card (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); - } - else - { + } else { printk(KERN_INFO "cs46xx: %s (%04X:%04X) at 0x%08lx/0x%08lx, IRQ %d\n", cp->name, ss_vendor, ss_card, card->ba0_addr, card->ba1_addr, card->irq); } - if (card->amplifier_ctrl==NULL) - { + if (card->amplifier_ctrl == NULL) { card->amplifier_ctrl = amp_none; card->active_ctrl = clkrun_hack; } - if (external_amp == 1) - { + if (external_amp == 1) { printk(KERN_INFO "cs46xx: Crystal EAPD support forced on.\n"); card->amplifier_ctrl = amp_voyetra; } - if (thinkpad == 1) - { + if (thinkpad == 1) { printk(KERN_INFO "cs46xx: Activating CLKRUN hack for Thinkpad.\n"); card->active_ctrl = clkrun_hack; } @@ -5425,13 +5138,11 @@ static int __devinit cs46xx_probe(struct * and mdelay kernel code is replaced by a pm timer, or the delays * work well for battery and/or AC power both. */ - if(card->active_ctrl == clkrun_hack) - { + if (card->active_ctrl == clkrun_hack) { initdelay = 2100; cs_laptop_wait = 5; } - if((card->active_ctrl == clkrun_hack) && !(powerdown == 1)) - { + if ((card->active_ctrl == clkrun_hack) && !(powerdown == 1)) { /* * for some currently unknown reason, powering down the DAC and ADC component * blocks on thinkpads causes some funky behavior... distoorrrtion and ac97 @@ -5440,7 +5151,7 @@ static int __devinit cs46xx_probe(struct */ cs_powerdown = 0; } - if(powerdown == 0) + if (powerdown == 0) cs_powerdown = 0; card->active_ctrl(card, 1); @@ -5461,12 +5172,12 @@ static int __devinit cs46xx_probe(struct card->ba1.name.pmem, card->ba1.name.reg) ); - if(card->ba0 == 0 || card->ba1.name.data0 == 0 || + if (card->ba0 == 0 || card->ba1.name.data0 == 0 || card->ba1.name.data1 == 0 || card->ba1.name.pmem == 0 || card->ba1.name.reg == 0) goto fail2; - if (request_irq(card->irq, &cs_interrupt, SA_SHIRQ, "cs46xx", card)) { + if (request_irq(card->irq, &cs_interrupt, IRQF_SHARED, "cs46xx", card)) { printk(KERN_ERR "cs46xx: unable to allocate irq %d\n", card->irq); goto fail2; } @@ -5477,14 +5188,12 @@ static int __devinit cs46xx_probe(struct } /* register /dev/midi */ - if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0) + if ((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0) printk(KERN_ERR "cs46xx: unable to register midi\n"); card->pm.flags |= CS46XX_PM_IDLE; - for(i=0;i<5;i++) - { - if (cs_hardware_init(card) != 0) - { + for (i = 0; i < 5; i++) { + if (cs_hardware_init(card) != 0) { CS_DBGOUT(CS_ERROR, 4, printk( "cs46xx: ERROR in cs_hardware_init()... retrying\n")); for (j = 0; j < NR_AC97; j++) @@ -5497,12 +5206,11 @@ static int __devinit cs46xx_probe(struct } break; } - if(i>=4) - { + if(i >= 4) { CS_DBGOUT(CS_PM | CS_ERROR, 1, printk( "cs46xx: cs46xx_probe()- cs_hardware_init() failed, retried %d times.\n",i)); unregister_sound_dsp(card->dev_audio); - if(card->dev_midi) + if (card->dev_midi) unregister_sound_midi(card->dev_midi); goto fail; } @@ -5518,7 +5226,7 @@ static int __devinit cs46xx_probe(struct * Check if we have to init the amplifier, but probably already done * since the CD logic in the ac97 init code will turn on the ext amp. */ - if(cp->amp_init) + if (cp->amp_init) cp->amp_init(card); card->active_ctrl(card, -1); @@ -5536,15 +5244,15 @@ static int __devinit cs46xx_probe(struct fail: free_irq(card->irq, card); fail2: - if(card->ba0) + if (card->ba0) iounmap(card->ba0); - if(card->ba1.name.data0) + if (card->ba1.name.data0) iounmap(card->ba1.name.data0); - if(card->ba1.name.data1) + if (card->ba1.name.data1) iounmap(card->ba1.name.data1); - if(card->ba1.name.pmem) + if (card->ba1.name.pmem) iounmap(card->ba1.name.pmem); - if(card->ba1.name.reg) + if (card->ba1.name.reg) iounmap(card->ba1.name.reg); kfree(card); CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO @@ -5598,9 +5306,8 @@ static void __devexit cs46xx_remove(stru * Power down the DAC and ADC. We will power them up (if) when we need * them. */ - if( (tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | - CS_POWER_MIXVON, CS_TRUE )) ) - { + if ((tmp = cs461x_powerdown(card, CS_POWER_DAC | CS_POWER_ADC | + CS_POWER_MIXVON, CS_TRUE))) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk(KERN_INFO "cs46xx: cs461x_powerdown() failure (0x%x)\n",tmp) ); } @@ -5634,7 +5341,7 @@ static void __devexit cs46xx_remove(stru ac97_release_codec(card->ac97_codec[i]); } unregister_sound_dsp(card->dev_audio); - if(card->dev_midi) + if (card->dev_midi) unregister_sound_midi(card->dev_midi); list_del(&card->list); kfree(card); @@ -5693,8 +5400,7 @@ static int __init cs46xx_init_module(voi "cs46xx: cs46xx_init_module()+ \n")); rtn = pci_register_driver(&cs46xx_pci_driver); - if(rtn == -ENODEV) - { + if (rtn == -ENODEV) { CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( "cs46xx: Unable to detect valid cs46xx device\n")); } diff --git a/sound/oss/dmabuf.c b/sound/oss/dmabuf.c index baf4244..15ce711 100644 --- a/sound/oss/dmabuf.c +++ b/sound/oss/dmabuf.c @@ -547,7 +547,7 @@ int DMAbuf_activate_recording(int dev, s } return 0; } -/* aquires lock */ +/* acquires lock */ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock) { struct audio_operations *adev = audio_devs[dev]; @@ -821,7 +821,7 @@ #endif *size = len & ~SAMPLE_ROUNDUP; return (*size > 0); } -/* aquires lock */ +/* acquires lock */ int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock) { struct audio_operations *adev = audio_devs[dev]; @@ -855,7 +855,7 @@ int DMAbuf_getwrbuffer(int dev, char **b spin_unlock_irqrestore(&dmap->lock,flags); return 0; } -/* has to aquire dmap->lock */ +/* has to acquire dmap->lock */ int DMAbuf_move_wrpointer(int dev, int l) { struct audio_operations *adev = audio_devs[dev]; diff --git a/sound/oss/dmasound/dmasound.h b/sound/oss/dmasound/dmasound.h index a1b0b92..25dd5a3 100644 --- a/sound/oss/dmasound/dmasound.h +++ b/sound/oss/dmasound/dmasound.h @@ -13,7 +13,6 @@ #ifndef _dmasound_h_ #define _dmasound_h_ #include -#include #define SND_NDEVS 256 /* Number of supported devices */ #define SND_DEV_CTL 0 /* Control port /dev/mixer */ diff --git a/sound/oss/dmasound/dmasound_awacs.c b/sound/oss/dmasound/dmasound_awacs.c index c8e2103..4359903 100644 --- a/sound/oss/dmasound/dmasound_awacs.c +++ b/sound/oss/dmasound/dmasound_awacs.c @@ -67,7 +67,6 @@ #include #include -#include #include #include #include @@ -375,10 +374,7 @@ setup_audio_gpio(const char *name, const *gpio_pol = *pp; else *gpio_pol = 1; - if (np->n_intrs > 0) - return np->intrs[0].line; - - return 0; + return irq_of_parse_and_map(np, 0); } static inline void @@ -2865,14 +2861,13 @@ #endif * other info if necessary (early AWACS we want to read chip ids) */ - if (of_get_address(io, 2, NULL, NULL) == NULL || io->n_intrs < 3) { + if (of_get_address(io, 2, NULL, NULL) == NULL) { /* OK - maybe we need to use the 'awacs' node (on earlier * machines). */ if (awacs_node) { io = awacs_node ; - if (of_get_address(io, 2, NULL, NULL) == NULL || - io->n_intrs < 3) { + if (of_get_address(io, 2, NULL, NULL) == NULL) { printk("dmasound_pmac: can't use %s\n", io->full_name); return -ENODEV; @@ -2941,9 +2936,9 @@ #endif if (awacs_revision == AWACS_SCREAMER && awacs) awacs_recalibrate(); - awacs_irq = io->intrs[0].line; - awacs_tx_irq = io->intrs[1].line; - awacs_rx_irq = io->intrs[2].line; + awacs_irq = irq_of_parse_and_map(io, 0); + awacs_tx_irq = irq_of_parse_and_map(io, 1); + awacs_rx_irq = irq_of_parse_and_map(io, 2); /* Hack for legacy crap that will be killed someday */ awacs_node = io; diff --git a/sound/oss/dmasound/dmasound_paula.c b/sound/oss/dmasound/dmasound_paula.c index 494070a..68e1d8f 100644 --- a/sound/oss/dmasound/dmasound_paula.c +++ b/sound/oss/dmasound/dmasound_paula.c @@ -16,7 +16,6 @@ #include -#include #include #include #include diff --git a/sound/oss/emu10k1/main.c b/sound/oss/emu10k1/main.c index 3721c58..c4ce94d 100644 --- a/sound/oss/emu10k1/main.c +++ b/sound/oss/emu10k1/main.c @@ -1301,7 +1301,7 @@ static int __devinit emu10k1_probe(struc card->pci_dev = pci_dev; /* Reserve IRQ Line */ - if (request_irq(card->irq, emu10k1_interrupt, SA_SHIRQ, card_names[pci_id->driver_data], card)) { + if (request_irq(card->irq, emu10k1_interrupt, IRQF_SHARED, card_names[pci_id->driver_data], card)) { printk(KERN_ERR "emu10k1: IRQ in use\n"); ret = -EBUSY; goto err_irq; diff --git a/sound/oss/emu10k1/midi.c b/sound/oss/emu10k1/midi.c index 25ae8e4..8ac77df 100644 --- a/sound/oss/emu10k1/midi.c +++ b/sound/oss/emu10k1/midi.c @@ -45,7 +45,7 @@ #ifdef EMU10K1_SEQUENCER #include "../sound_config.h" #endif -static DEFINE_SPINLOCK(midi_spinlock __attribute((unused))); +static DEFINE_SPINLOCK(midi_spinlock); static void init_midi_hdr(struct midi_hdr *midihdr) { diff --git a/sound/oss/es1370.c b/sound/oss/es1370.c index 094f569..13f4831 100644 --- a/sound/oss/es1370.c +++ b/sound/oss/es1370.c @@ -2650,7 +2650,7 @@ static int __devinit es1370_probe(struct ret = -EBUSY; goto err_region; } - if ((ret=request_irq(s->irq, es1370_interrupt, SA_SHIRQ, "es1370",s))) { + if ((ret=request_irq(s->irq, es1370_interrupt, IRQF_SHARED, "es1370",s))) { printk(KERN_ERR "es1370: irq %u in use\n", s->irq); goto err_irq; } diff --git a/sound/oss/es1371.c b/sound/oss/es1371.c index 4400c85..a2ffe72 100644 --- a/sound/oss/es1371.c +++ b/sound/oss/es1371.c @@ -2905,7 +2905,7 @@ static int __devinit es1371_probe(struct res = -EBUSY; goto err_region; } - if ((res=request_irq(s->irq, es1371_interrupt, SA_SHIRQ, "es1371",s))) { + if ((res=request_irq(s->irq, es1371_interrupt, IRQF_SHARED, "es1371",s))) { printk(KERN_ERR PFX "irq %u in use\n", s->irq); goto err_irq; } diff --git a/sound/oss/esssolo1.c b/sound/oss/esssolo1.c index 6861563..82f40a0 100644 --- a/sound/oss/esssolo1.c +++ b/sound/oss/esssolo1.c @@ -2392,7 +2392,7 @@ static int __devinit solo1_probe(struct printk(KERN_ERR "solo1: io ports in use\n"); goto err_region4; } - if ((ret=request_irq(s->irq,solo1_interrupt,SA_SHIRQ,"ESS Solo1",s))) { + if ((ret=request_irq(s->irq,solo1_interrupt,IRQF_SHARED,"ESS Solo1",s))) { printk(KERN_ERR "solo1: irq %u in use\n", s->irq); goto err_irq; } diff --git a/sound/oss/forte.c b/sound/oss/forte.c index 0294eec..ea1c020 100644 --- a/sound/oss/forte.c +++ b/sound/oss/forte.c @@ -2026,7 +2026,7 @@ forte_probe (struct pci_dev *pci_dev, co chip->iobase = pci_resource_start (pci_dev, 0); chip->irq = pci_dev->irq; - if (request_irq (chip->irq, forte_interrupt, SA_SHIRQ, DRIVER_NAME, + if (request_irq (chip->irq, forte_interrupt, IRQF_SHARED, DRIVER_NAME, chip)) { printk (KERN_WARNING PFX "Unable to reserve IRQ"); ret = -EIO; @@ -2035,8 +2035,9 @@ forte_probe (struct pci_dev *pci_dev, co pci_set_drvdata (pci_dev, chip); - printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%04lX IRQ %u\n", - chip->iobase, pci_resource_end (pci_dev, 0), chip->irq); + printk (KERN_INFO PFX "FM801 chip found at 0x%04lX-0x%16llX IRQ %u\n", + chip->iobase, (unsigned long long)pci_resource_end (pci_dev, 0), + chip->irq); /* Power it up */ if ((ret = forte_chip_init (chip)) == 0) diff --git a/sound/oss/hal2.c b/sound/oss/hal2.c index dd4f59d..80ab402 100644 --- a/sound/oss/hal2.c +++ b/sound/oss/hal2.c @@ -1479,7 +1479,7 @@ #define HAL2_PBUS_DMACFG ((0 << HPC3_DMA hpc3->pbus_dmacfg[hal2->dac.pbus.pbusnr][0] = 0x8208844; hpc3->pbus_dmacfg[hal2->adc.pbus.pbusnr][0] = 0x8208844; - if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, SA_SHIRQ, + if (request_irq(SGI_HPCDMA_IRQ, hal2_interrupt, IRQF_SHARED, hal2str, hal2)) { printk(KERN_ERR "HAL2: Can't get irq %d\n", SGI_HPCDMA_IRQ); ret = -EAGAIN; diff --git a/sound/oss/i810_audio.c b/sound/oss/i810_audio.c index dd2b871..ddcddc2 100644 --- a/sound/oss/i810_audio.c +++ b/sound/oss/i810_audio.c @@ -3413,7 +3413,7 @@ #endif goto out_iospace; } - if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ, + if (request_irq(card->irq, &i810_interrupt, IRQF_SHARED, card_names[pci_id->driver_data], card)) { printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq); goto out_iospace; diff --git a/sound/oss/ite8172.c b/sound/oss/ite8172.c index 00ac1c9..68aab36 100644 --- a/sound/oss/ite8172.c +++ b/sound/oss/ite8172.c @@ -2019,7 +2019,7 @@ static int __devinit it8172_probe(struct s->io, s->io + pci_resource_len(pcidev,0)-1); goto err_region; } - if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, + if (request_irq(s->irq, it8172_interrupt, IRQF_DISABLED, IT8172_MODULE_NAME, s)) { err("irq %u in use", s->irq); goto err_irq; diff --git a/sound/oss/kahlua.c b/sound/oss/kahlua.c index 2835a7c..12e7b30 100644 --- a/sound/oss/kahlua.c +++ b/sound/oss/kahlua.c @@ -27,7 +27,6 @@ * same manner. */ -#include #include #include #include diff --git a/sound/oss/maestro.c b/sound/oss/maestro.c index e647f2f..1d98d10 100644 --- a/sound/oss/maestro.c +++ b/sound/oss/maestro.c @@ -3545,7 +3545,7 @@ #define SUBSYSTEM_VENDOR(x) (x&0xffff) mixer_push_state(card); } - if((ret=request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card))) + if((ret=request_irq(card->irq, ess_interrupt, IRQF_SHARED, card_names[card_type], card))) { printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); unregister_sound_mixer(card->dev_mixer); diff --git a/sound/oss/maestro3.c b/sound/oss/maestro3.c index 4a5e423..5548e3c 100644 --- a/sound/oss/maestro3.c +++ b/sound/oss/maestro3.c @@ -2694,7 +2694,7 @@ static int __devinit m3_probe(struct pci } } - if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) { + if(request_irq(card->irq, m3_interrupt, IRQF_SHARED, card_names[card->card_type], card)) { printk(KERN_ERR PFX "unable to allocate irq %d,\n", card->irq); diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c index 5dbfc0f..ba38d62 100644 --- a/sound/oss/msnd.c +++ b/sound/oss/msnd.c @@ -47,7 +47,7 @@ #define MSND_MAX_DEVS 4 static multisound_dev_t *devs[MSND_MAX_DEVS]; static int num_devs; -int __init msnd_register(multisound_dev_t *dev) +int msnd_register(multisound_dev_t *dev) { int i; diff --git a/sound/oss/msnd_classic.h b/sound/oss/msnd_classic.h index 83c3c46..7ffea52 100644 --- a/sound/oss/msnd_classic.h +++ b/sound/oss/msnd_classic.h @@ -30,7 +30,6 @@ #ifndef __MSND_CLASSIC_H #define __MSND_CLASSIC_H -#include #define DSP_NUMIO 0x10 diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c index 0c2db65..6d7763d 100644 --- a/sound/oss/msnd_pinnacle.c +++ b/sound/oss/msnd_pinnacle.c @@ -39,7 +39,6 @@ ********************************************************************/ #include -#include #include #include #include diff --git a/sound/oss/msnd_pinnacle.h b/sound/oss/msnd_pinnacle.h index e85aef4..cce9114 100644 --- a/sound/oss/msnd_pinnacle.h +++ b/sound/oss/msnd_pinnacle.h @@ -30,7 +30,6 @@ #ifndef __MSND_PINNACLE_H #define __MSND_PINNACLE_H -#include #define DSP_NUMIO 0x08 diff --git a/sound/oss/nec_vrc5477.c b/sound/oss/nec_vrc5477.c index 21c1954..6f7f2f0 100644 --- a/sound/oss/nec_vrc5477.c +++ b/sound/oss/nec_vrc5477.c @@ -1909,7 +1909,7 @@ #endif s->io, s->io + pci_resource_len(pcidev,0)-1); goto err_region; } - if (request_irq(s->irq, vrc5477_ac97_interrupt, SA_INTERRUPT, + if (request_irq(s->irq, vrc5477_ac97_interrupt, IRQF_DISABLED, VRC5477_AC97_MODULE_NAME, s)) { printk(KERN_ERR PFX "irq %u in use\n", s->irq); goto err_irq; diff --git a/sound/oss/nm256_audio.c b/sound/oss/nm256_audio.c index 6e662ac..7760ddd 100644 --- a/sound/oss/nm256_audio.c +++ b/sound/oss/nm256_audio.c @@ -733,7 +733,7 @@ static int nm256_grabInterrupt (struct nm256_info *card) { if (card->has_irq++ == 0) { - if (request_irq (card->irq, card->introutine, SA_SHIRQ, + if (request_irq (card->irq, card->introutine, IRQF_SHARED, "NM256_audio", card) < 0) { printk (KERN_ERR "NM256: can't obtain IRQ %d\n", card->irq); return -1; diff --git a/sound/oss/opl3sa2.c b/sound/oss/opl3sa2.c index 0e161c6..aec05a2 100644 --- a/sound/oss/opl3sa2.c +++ b/sound/oss/opl3sa2.c @@ -64,7 +64,6 @@ * */ -#include #include #include #include diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c index c9696dc..9766600 100644 --- a/sound/oss/pas2_card.c +++ b/sound/oss/pas2_card.c @@ -4,7 +4,6 @@ * Detection routine for the Pro Audio Spectrum cards. */ -#include #include #include #include diff --git a/sound/oss/pss.c b/sound/oss/pss.c index a617ccb..37ee234 100644 --- a/sound/oss/pss.c +++ b/sound/oss/pss.c @@ -57,7 +57,6 @@ */ -#include #include #include #include diff --git a/sound/oss/rme96xx.c b/sound/oss/rme96xx.c index a1ec9d1..f17d25b 100644 --- a/sound/oss/rme96xx.c +++ b/sound/oss/rme96xx.c @@ -994,7 +994,7 @@ static int __devinit rme96xx_probe(struc if (pci_enable_device(pcidev)) goto err_irq; - if (request_irq(s->irq, rme96xx_interrupt, SA_SHIRQ, "rme96xx", s)) { + if (request_irq(s->irq, rme96xx_interrupt, IRQF_SHARED, "rme96xx", s)) { printk(KERN_ERR RME_MESS" irq %u in use\n", s->irq); goto err_irq; } diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c index 4708cbd..8666291 100644 --- a/sound/oss/sb_card.c +++ b/sound/oss/sb_card.c @@ -22,7 +22,6 @@ * 02-07-2003 Bug made it into first release. Take two. */ -#include #include #include #include diff --git a/sound/oss/sb_common.c b/sound/oss/sb_common.c index 5f955e3..35bab6e 100644 --- a/sound/oss/sb_common.c +++ b/sound/oss/sb_common.c @@ -26,7 +26,6 @@ * Chris Rankin */ -#include #include #include #include @@ -678,7 +677,7 @@ int sb_dsp_init(struct address_info *hw_ * will get shared PCI irq lines we must cope. */ - int i=(devc->caps&SB_PCI_IRQ)?SA_SHIRQ:0; + int i=(devc->caps&SB_PCI_IRQ)?IRQF_SHARED:0; if (request_irq(hw_config->irq, sbintr, i, "soundblaster", devc) < 0) { diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c index fae05fe..180e95c 100644 --- a/sound/oss/sb_ess.c +++ b/sound/oss/sb_ess.c @@ -97,19 +97,19 @@ #undef FKS_TEST * * The documentation is an adventure: it's close but not fully accurate. I * found out that after a reset some registers are *NOT* reset, though the - * docs say the would be. Interresting ones are 0x7f, 0x7d and 0x7a. They are - * related to the Audio 2 channel. I also was suprised about the consequenses + * docs say the would be. Interesting ones are 0x7f, 0x7d and 0x7a. They are + * related to the Audio 2 channel. I also was surprised about the consequences * of writing 0x00 to 0x7f (which should be done by reset): The ES1887 moves * into ES1888 mode. This means that it claims IRQ 11, which happens to be my * ISDN adapter. Needless to say it no longer worked. I now understand why * after rebooting 0x7f already was 0x05, the value of my choice: the BIOS * did it. * - * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is decribed - * as if it's exactly the same as register 0xa1. This is *NOT* true. The - * description of 0x70 in ES1869 docs is accurate however. + * Oh, and this is another trap: in ES1887 docs mixer register 0x70 is + * described as if it's exactly the same as register 0xa1. This is *NOT* true. + * The description of 0x70 in ES1869 docs is accurate however. * Well, the assumption about ES1869 was wrong: register 0x70 is very much - * like register 0xa1, except that bit 7 is allways 1, whatever you want + * like register 0xa1, except that bit 7 is always 1, whatever you want * it to be. * * When using audio 2 mixer register 0x72 seems te be meaningless. Only 0xa2 @@ -117,10 +117,10 @@ #undef FKS_TEST * * Software reset not being able to reset all registers is great! Especially * the fact that register 0x78 isn't reset is great when you wanna change back - * to single dma operation (simplex): audio 2 is still operation, and uses the - * same dma as audio 1: your ess changes into a funny echo machine. + * to single dma operation (simplex): audio 2 is still operational, and uses + * the same dma as audio 1: your ess changes into a funny echo machine. * - * Received the new that ES1688 is detected as a ES1788. Did some thinking: + * Received the news that ES1688 is detected as a ES1788. Did some thinking: * the ES1887 detection scheme suggests in step 2 to try if bit 3 of register * 0x64 can be changed. This is inaccurate, first I inverted the * check: "If * can be modified, it's a 1688", which lead to a correct detection @@ -135,7 +135,7 @@ #undef FKS_TEST * About recognition of ESS chips * * The distinction of ES688, ES1688, ES1788, ES1887 and ES1888 is described in - * a (preliminary ??) datasheet on ES1887. It's aim is to identify ES1887, but + * a (preliminary ??) datasheet on ES1887. Its aim is to identify ES1887, but * during detection the text claims that "this chip may be ..." when a step * fails. This scheme is used to distinct between the above chips. * It appears however that some PnP chips like ES1868 are recognized as ES1788 @@ -156,9 +156,9 @@ #undef FKS_TEST * * The existing ES1688 support didn't take care of the ES1688+ recording * levels very well. Whenever a device was selected (recmask) for recording - * it's recording level was loud, and it couldn't be changed. The fact that + * its recording level was loud, and it couldn't be changed. The fact that * internal register 0xb4 could take care of RECLEV, didn't work meaning until - * it's value was restored every time the chip was reset; this reset the + * its value was restored every time the chip was reset; this reset the * value of 0xb4 too. I guess that's what 4front also had (have?) trouble with. * * About ES1887 support: @@ -169,9 +169,9 @@ #undef FKS_TEST * the latter case the recording volumes are 0. * Now recording levels of inputs can be controlled, by changing the playback * levels. Futhermore several devices can be recorded together (which is not - * possible with the ES1688. + * possible with the ES1688). * Besides the separate recording level control for each input, the common - * recordig level can also be controlled by RECLEV as described above. + * recording level can also be controlled by RECLEV as described above. * * Not only ES1887 have this recording mixer. I know the following from the * documentation: diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c index 3f7427c..7b168d8 100644 --- a/sound/oss/sh_dac_audio.c +++ b/sound/oss/sh_dac_audio.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -298,7 +297,7 @@ static int __init dac_audio_init(void) dac_audio_set_rate(); retval = - request_irq(TIMER1_IRQ, timer1_interrupt, SA_INTERRUPT, MODNAME, 0); + request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0); if (retval < 0) { printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n", TIMER1_IRQ); diff --git a/sound/oss/sonicvibes.c b/sound/oss/sonicvibes.c index 42bd276..8ea532d 100644 --- a/sound/oss/sonicvibes.c +++ b/sound/oss/sonicvibes.c @@ -2632,7 +2632,7 @@ static int __devinit sv_probe(struct pci wrindir(s, SV_CIPCMSR1, ((8000 * 65536 / FULLRATE) >> 8) & 0xff); wrindir(s, SV_CIADCOUTPUT, 0); /* request irq */ - if ((ret=request_irq(s->irq,sv_interrupt,SA_SHIRQ,"S3 SonicVibes",s))) { + if ((ret=request_irq(s->irq,sv_interrupt,IRQF_SHARED,"S3 SonicVibes",s))) { printk(KERN_ERR "sv: irq %u in use\n", s->irq); goto err_irq; } diff --git a/sound/oss/sound_config.h b/sound/oss/sound_config.h index 9f912b8..1a00a32 100644 --- a/sound/oss/sound_config.h +++ b/sound/oss/sound_config.h @@ -14,7 +14,6 @@ #ifndef _SOUND_CONFIG_H_ #define _SOUND_CONFIG_H_ -#include #include #include diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c index d33bb46..0860d67 100644 --- a/sound/oss/soundcard.c +++ b/sound/oss/soundcard.c @@ -22,7 +22,6 @@ * Christoph Hellwig : Some cleanup work (2000/03/01) */ -#include #include "sound_config.h" #include @@ -38,7 +37,6 @@ #include #include #include #include -#include #include #include #include @@ -564,9 +562,6 @@ #endif sound_dmap_flag = (dmabuf > 0 ? 1 : 0); for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { - devfs_mk_cdev(MKDEV(SOUND_MAJOR, dev_list[i].minor), - S_IFCHR | dev_list[i].mode, - "sound/%s", dev_list[i].name); class_device_create(sound_class, NULL, MKDEV(SOUND_MAJOR, dev_list[i].minor), NULL, "%s", dev_list[i].name); @@ -574,15 +569,10 @@ #endif if (!dev_list[i].num) continue; - for (j = 1; j < *dev_list[i].num; j++) { - devfs_mk_cdev(MKDEV(SOUND_MAJOR, - dev_list[i].minor + (j*0x10)), - S_IFCHR | dev_list[i].mode, - "sound/%s%d", dev_list[i].name, j); + for (j = 1; j < *dev_list[i].num; j++) class_device_create(sound_class, NULL, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)), NULL, "%s%d", dev_list[i].name, j); - } } if (sound_nblocks >= 1024) @@ -596,14 +586,11 @@ static void __exit oss_cleanup(void) int i, j; for (i = 0; i < sizeof (dev_list) / sizeof *dev_list; i++) { - devfs_remove("sound/%s", dev_list[i].name); class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor)); if (!dev_list[i].num) continue; - for (j = 1; j < *dev_list[i].num; j++) { - devfs_remove("sound/%s%d", dev_list[i].name, j); + for (j = 1; j < *dev_list[i].num; j++) class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10))); - } } unregister_sound_special(1); diff --git a/sound/oss/trident.c b/sound/oss/trident.c index e61a454..2813e4c 100644 --- a/sound/oss/trident.c +++ b/sound/oss/trident.c @@ -194,7 +194,6 @@ * sem - guard dmabuf, write re-entry etc */ -#include #include #include #include @@ -4473,7 +4472,7 @@ trident_probe(struct pci_dev *pci_dev, c /* claim our irq */ rc = -ENODEV; - if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, + if (request_irq(card->irq, &trident_interrupt, IRQF_SHARED, card_names[pci_id->driver_data], card)) { printk(KERN_ERR "trident: unable to allocate irq %d\n", card->irq); diff --git a/sound/oss/via82cxxx_audio.c b/sound/oss/via82cxxx_audio.c index 1a921ee..08d8c94 100644 --- a/sound/oss/via82cxxx_audio.c +++ b/sound/oss/via82cxxx_audio.c @@ -18,12 +18,12 @@ #define VIA_VERSION "1.9.1-ac4-2.5" -#include #include #include #include #include #include +#include #include #include #include @@ -308,7 +308,7 @@ struct via_info { unsigned sixchannel: 1; /* 8233/35 with 6 channel support */ unsigned volume: 1; - int locked_rate : 1; + unsigned locked_rate : 1; int mixer_vol; /* 8233/35 volume - not yet implemented */ @@ -2013,7 +2013,7 @@ static int via_interrupt_init (struct vi tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); } - if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + if (request_irq (card->pdev->irq, via_interrupt, IRQF_SHARED, VIA_MODULE_NAME, card)) { printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", card->pdev->irq); DPRINTK ("EXIT, returning -EBUSY\n"); @@ -2022,7 +2022,7 @@ static int via_interrupt_init (struct vi } else { - if (request_irq (card->pdev->irq, via_new_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { + if (request_irq (card->pdev->irq, via_new_interrupt, IRQF_SHARED, VIA_MODULE_NAME, card)) { printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", card->pdev->irq); DPRINTK ("EXIT, returning -EBUSY\n"); @@ -3522,7 +3522,7 @@ err_out_have_mixer: err_out_kfree: #ifndef VIA_NDEBUG - memset (card, 0xAB, sizeof (*card)); /* poison memory */ + memset (card, OSS_POISON_FREE, sizeof (*card)); /* poison memory */ #endif kfree (card); @@ -3559,7 +3559,7 @@ #endif via_ac97_cleanup (card); #ifndef VIA_NDEBUG - memset (card, 0xAB, sizeof (*card)); /* poison memory */ + memset (card, OSS_POISON_FREE, sizeof (*card)); /* poison memory */ #endif kfree (card); diff --git a/sound/oss/vidc.c b/sound/oss/vidc.c index 00fe5ce..8932d89 100644 --- a/sound/oss/vidc.c +++ b/sound/oss/vidc.c @@ -17,7 +17,6 @@ * We currently support a mixer device, but it is currently non-functional. */ -#include #include #include #include diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c index afcb524..22d2662 100644 --- a/sound/oss/waveartist.c +++ b/sound/oss/waveartist.c @@ -35,7 +35,6 @@ #define debug_flg (0) #include #include -#include #include #include #include diff --git a/sound/oss/wavfront.c b/sound/oss/wavfront.c index b1a4eeb..1dec395 100644 --- a/sound/oss/wavfront.c +++ b/sound/oss/wavfront.c @@ -2268,7 +2268,7 @@ static int __init wavefront_hw_reset (vo } if (request_irq (dev.irq, wavefrontintr, - SA_INTERRUPT|SA_SHIRQ, + IRQF_DISABLED|IRQF_SHARED, "wavefront synth", &dev) < 0) { printk (KERN_WARNING LOGNAME "IRQ %d not available!\n", dev.irq); diff --git a/sound/oss/wf_midi.c b/sound/oss/wf_midi.c index 7b167b7..3f3a390 100644 --- a/sound/oss/wf_midi.c +++ b/sound/oss/wf_midi.c @@ -820,7 +820,7 @@ int __init install_wf_mpu (void) /* OK, now we're configured to handle an interrupt ... */ - if (request_irq (phys_dev->irq, wf_mpuintr, SA_INTERRUPT|SA_SHIRQ, + if (request_irq (phys_dev->irq, wf_mpuintr, IRQF_DISABLED|IRQF_SHARED, "wavefront midi", phys_dev) < 0) { printk (KERN_ERR "WF-MPU: Failed to allocate IRQ%d\n", diff --git a/sound/oss/ymfpci.c b/sound/oss/ymfpci.c index bf90c12..6e22472 100644 --- a/sound/oss/ymfpci.c +++ b/sound/oss/ymfpci.c @@ -2573,7 +2573,7 @@ #endif goto out_disable_dsp; ymf_memload(codec); - if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { + if (request_irq(pcidev->irq, ymf_interrupt, IRQF_SHARED, "ymfpci", codec) != 0) { printk(KERN_ERR "ymfpci: unable to request IRQ %d\n", pcidev->irq); goto out_memfree; diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig index a208180..23e54ce 100644 --- a/sound/pci/Kconfig +++ b/sound/pci/Kconfig @@ -216,18 +216,160 @@ config SND_CS46XX_NEW_DSP This works better than the old code, so say Y. config SND_CS5535AUDIO - tristate "CS5535 Audio" + tristate "CS5535/CS5536 Audio" depends on SND && X86 && !X86_64 select SND_PCM select SND_AC97_CODEC help Say Y here to include support for audio on CS5535 chips. It is referred to as NS CS5535 IO or AMD CS5535 IO companion in - various literature. + various literature. This driver also supports the CS5536 audio + device. However, for both chips, on certain boards, you may + need to use ac97_quirk=hp_only if your board has physically + mapped headphone out to master output. If that works for you, + send lspci -vvv output to the mailing list so that your board + can be identified in the quirks list. To compile this driver as a module, choose M here: the module will be called snd-cs5535audio. +config SND_DARLA20 + tristate "(Echoaudio) Darla20" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Darla. + + To compile this driver as a module, choose M here: the module + will be called snd-darla20 + +config SND_GINA20 + tristate "(Echoaudio) Gina20" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Gina. + + To compile this driver as a module, choose M here: the module + will be called snd-gina20 + +config SND_LAYLA20 + tristate "(Echoaudio) Layla20" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Layla. + + To compile this driver as a module, choose M here: the module + will be called snd-layla20 + +config SND_DARLA24 + tristate "(Echoaudio) Darla24" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Darla24. + + To compile this driver as a module, choose M here: the module + will be called snd-darla24 + +config SND_GINA24 + tristate "(Echoaudio) Gina24" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Gina24. + + To compile this driver as a module, choose M here: the module + will be called snd-gina24 + +config SND_LAYLA24 + tristate "(Echoaudio) Layla24" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Layla24. + + To compile this driver as a module, choose M here: the module + will be called snd-layla24 + +config SND_MONA + tristate "(Echoaudio) Mona" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Mona. + + To compile this driver as a module, choose M here: the module + will be called snd-mona + +config SND_MIA + tristate "(Echoaudio) Mia" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Mia and Mia-midi. + + To compile this driver as a module, choose M here: the module + will be called snd-mia + +config SND_ECHO3G + tristate "(Echoaudio) 3G cards" + depends on SND + depends on FW_LOADER + select SND_RAWMIDI + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Gina3G and Layla3G. + + To compile this driver as a module, choose M here: the module + will be called snd-echo3g + +config SND_INDIGO + tristate "(Echoaudio) Indigo" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Indigo. + + To compile this driver as a module, choose M here: the module + will be called snd-indigo + +config SND_INDIGOIO + tristate "(Echoaudio) Indigo IO" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Indigo IO. + + To compile this driver as a module, choose M here: the module + will be called snd-indigoio + +config SND_INDIGODJ + tristate "(Echoaudio) Indigo DJ" + depends on SND + depends on FW_LOADER + select SND_PCM + help + Say 'Y' or 'M' to include support for Echoaudio Indigo DJ. + + To compile this driver as a module, choose M here: the module + will be called snd-indigodj + config SND_EMU10K1 tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)" depends on SND @@ -415,8 +557,8 @@ config SND_INTEL8X0 will be called snd-intel8x0. config SND_INTEL8X0M - tristate "Intel/SiS/nVidia/AMD MC97 Modem (EXPERIMENTAL)" - depends on SND && EXPERIMENTAL + tristate "Intel/SiS/nVidia/AMD MC97 Modem" + depends on SND select SND_AC97_CODEC help Say Y here to include support for the integrated MC97 modem on diff --git a/sound/pci/Makefile b/sound/pci/Makefile index cba5105..e06736d 100644 --- a/sound/pci/Makefile +++ b/sound/pci/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_SND) += \ ca0106/ \ cs46xx/ \ cs5535audio/ \ + echoaudio/ \ emu10k1/ \ hda/ \ ice1712/ \ diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index d052007..0abf280 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -253,6 +253,8 @@ void snd_ac97_write(struct snd_ac97 *ac9 ac97->bus->ops->write(ac97, reg, value); } +EXPORT_SYMBOL(snd_ac97_write); + /** * snd_ac97_read - read a value from the given register * @@ -281,6 +283,8 @@ static inline unsigned short snd_ac97_re return ac97->regs[reg]; } +EXPORT_SYMBOL(snd_ac97_read); + /** * snd_ac97_write_cache - write a value on the given register and update the cache * @ac97: the ac97 instance @@ -302,6 +306,8 @@ void snd_ac97_write_cache(struct snd_ac9 mutex_unlock(&ac97->reg_mutex); } +EXPORT_SYMBOL(snd_ac97_write_cache); + /** * snd_ac97_update - update the value on the given register * @ac97: the ac97 instance @@ -331,6 +337,8 @@ int snd_ac97_update(struct snd_ac97 *ac9 return change; } +EXPORT_SYMBOL(snd_ac97_update); + /** * snd_ac97_update_bits - update the bits on the given register * @ac97: the ac97 instance @@ -356,6 +364,8 @@ int snd_ac97_update_bits(struct snd_ac97 return change; } +EXPORT_SYMBOL(snd_ac97_update_bits); + /* no lock version - see snd_ac97_updat_bits() */ int snd_ac97_update_bits_nolock(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value) @@ -563,7 +573,7 @@ AC97_SINGLE("PC Speaker Playback Volume" }; static const struct snd_kcontrol_new snd_ac97_controls_mic_boost = - AC97_SINGLE("Mic Boost (+20dB)", AC97_MIC, 6, 1, 0); + AC97_SINGLE("Mic Boost (+20dB) Switch", AC97_MIC, 6, 1, 0); static const char* std_rec_sel[] = {"Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone"}; @@ -605,7 +615,7 @@ AC97_SINGLE("Simulated Stereo Enhancemen AC97_SINGLE("3D Control - Switch", AC97_GENERAL_PURPOSE, 13, 1, 0), AC97_SINGLE("Loudness (bass boost)", AC97_GENERAL_PURPOSE, 12, 1, 0), AC97_ENUM("Mono Output Select", std_enum[2]), -AC97_ENUM("Mic Select", std_enum[3]), +AC97_ENUM("Mic Select Capture Switch", std_enum[3]), AC97_SINGLE("ADC/DAC Loopback", AC97_GENERAL_PURPOSE, 7, 1, 0) }; @@ -1226,7 +1236,8 @@ static int snd_ac97_mixer_build(struct s ac97->regs[AC97_CENTER_LFE_MASTER] = 0x8080; /* build center controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) { + if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER)) + && !(ac97->flags & AC97_AD_MULTI)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_center[0], ac97))) < 0) return err; if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_center[1], ac97))) < 0) @@ -1238,7 +1249,8 @@ static int snd_ac97_mixer_build(struct s } /* build LFE controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) { + if ((snd_ac97_try_volume_mix(ac97, AC97_CENTER_LFE_MASTER+1)) + && !(ac97->flags & AC97_AD_MULTI)) { if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_lfe[0], ac97))) < 0) return err; if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_lfe[1], ac97))) < 0) @@ -1250,7 +1262,8 @@ static int snd_ac97_mixer_build(struct s } /* build surround controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) { + if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) + && !(ac97->flags & AC97_AD_MULTI)) { /* Surround Master (0x38) is with stereo mutes */ if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0) return err; @@ -1335,9 +1348,11 @@ static int snd_ac97_mixer_build(struct s } /* build Aux controls */ - if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { - if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) - return err; + if (!(ac97->flags & AC97_HAS_NO_AUX)) { + if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) { + if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0) + return err; + } } /* build PCM controls */ @@ -1682,6 +1697,7 @@ const char *snd_ac97_get_short_name(stru return "unknown codec"; } +EXPORT_SYMBOL(snd_ac97_get_short_name); /* wait for a while until registers are accessible after RESET * return 0 if ok, negative not ready @@ -1774,6 +1790,8 @@ int snd_ac97_bus(struct snd_card *card, return 0; } +EXPORT_SYMBOL(snd_ac97_bus); + /* stop no dev release warning */ static void ac97_device_release(struct device * dev) { @@ -2117,6 +2135,7 @@ #endif return 0; } +EXPORT_SYMBOL(snd_ac97_mixer); /* * Power down the chip. @@ -2166,6 +2185,8 @@ void snd_ac97_suspend(struct snd_ac97 *a snd_ac97_powerdown(ac97); } +EXPORT_SYMBOL(snd_ac97_suspend); + /* * restore ac97 status */ @@ -2267,6 +2288,8 @@ __reset_ready: snd_ac97_restore_iec958(ac97); } } + +EXPORT_SYMBOL(snd_ac97_resume); #endif @@ -2590,29 +2613,7 @@ int snd_ac97_tune_hardware(struct snd_ac return 0; } - -/* - * Exported symbols - */ - -EXPORT_SYMBOL(snd_ac97_write); -EXPORT_SYMBOL(snd_ac97_read); -EXPORT_SYMBOL(snd_ac97_write_cache); -EXPORT_SYMBOL(snd_ac97_update); -EXPORT_SYMBOL(snd_ac97_update_bits); -EXPORT_SYMBOL(snd_ac97_get_short_name); -EXPORT_SYMBOL(snd_ac97_bus); -EXPORT_SYMBOL(snd_ac97_mixer); -EXPORT_SYMBOL(snd_ac97_pcm_assign); -EXPORT_SYMBOL(snd_ac97_pcm_open); -EXPORT_SYMBOL(snd_ac97_pcm_close); -EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules); EXPORT_SYMBOL(snd_ac97_tune_hardware); -EXPORT_SYMBOL(snd_ac97_set_rate); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_ac97_resume); -EXPORT_SYMBOL(snd_ac97_suspend); -#endif /* * INIT part diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 4d9cf37..094cfc1 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -464,6 +464,10 @@ int patch_wolfson05(struct snd_ac97 * ac { /* WM9705, WM9710 */ ac97->build_ops = &patch_wolfson_wm9705_ops; +#ifdef CONFIG_TOUCHSCREEN_WM9705 + /* WM9705 touchscreen uses AUX and VIDEO for touch */ + ac97->flags |=3D AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX; +#endif return 0; } @@ -1367,6 +1371,13 @@ static void ad18xx_resume(struct snd_ac9 snd_ac97_restore_iec958(ac97); } + +static void ad1888_resume(struct snd_ac97 *ac97) +{ + ad18xx_resume(ac97); + snd_ac97_write_cache(ac97, AC97_CODEC_CLASS_REV, 0x8080); +} + #endif int patch_ad1819(struct snd_ac97 * ac97) @@ -1627,6 +1638,7 @@ static const struct snd_kcontrol_new snd * (SS vendor << 16 | device) */ static unsigned int ad1981_jacks_blacklist[] = { + 0x10140537, /* Thinkpad T41p */ 0x10140554, /* Thinkpad T42p/R50p */ 0 /* end */ }; @@ -1812,6 +1824,8 @@ static const struct snd_kcontrol_new snd .get = snd_ac97_ad1888_lohpsel_get, .put = snd_ac97_ad1888_lohpsel_put }, + AC97_SINGLE("V_REFOUT Enable", AC97_AD_MISC, 2, 1, 1), + AC97_SINGLE("High Pass Filter Enable", AC97_AD_TEST2, 12, 1, 1), AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -1839,7 +1853,7 @@ static struct snd_ac97_build_ops patch_a .build_post_spdif = patch_ad198x_post_spdif, .build_specific = patch_ad1888_specific, #ifdef CONFIG_PM - .resume = ad18xx_resume, + .resume = ad1888_resume, #endif .update_jacks = ad1888_update_jacks, }; @@ -2048,7 +2062,10 @@ int patch_alc650(struct snd_ac97 * ac97) /* Enable SPDIF-IN only on Rev.E and above */ val = snd_ac97_read(ac97, AC97_ALC650_CLOCK); /* SPDIF IN with pin 47 */ - if (ac97->spec.dev_flags) + if (ac97->spec.dev_flags && + /* ASUS A6KM requires EAPD */ + ! (ac97->subsystem_vendor == 0x1043 && + ac97->subsystem_device == 0x1103)) val |= 0x03; /* enable */ else val &= ~0x03; /* disable */ diff --git a/sound/pci/ac97/ac97_pcm.c b/sound/pci/ac97/ac97_pcm.c index 512a358..f684aa2 100644 --- a/sound/pci/ac97/ac97_pcm.c +++ b/sound/pci/ac97/ac97_pcm.c @@ -317,6 +317,8 @@ int snd_ac97_set_rate(struct snd_ac97 *a return 0; } +EXPORT_SYMBOL(snd_ac97_set_rate); + static unsigned short get_pslots(struct snd_ac97 *ac97, unsigned char *rate_table, unsigned short *spdif_slots) { if (!ac97_is_audio(ac97)) @@ -550,6 +552,8 @@ int snd_ac97_pcm_assign(struct snd_ac97_ return 0; } +EXPORT_SYMBOL(snd_ac97_pcm_assign); + /** * snd_ac97_pcm_open - opens the given AC97 pcm * @pcm: the ac97 pcm instance @@ -633,6 +637,8 @@ int snd_ac97_pcm_open(struct ac97_pcm *p return err; } +EXPORT_SYMBOL(snd_ac97_pcm_open); + /** * snd_ac97_pcm_close - closes the given AC97 pcm * @pcm: the ac97 pcm instance @@ -658,6 +664,8 @@ int snd_ac97_pcm_close(struct ac97_pcm * return 0; } +EXPORT_SYMBOL(snd_ac97_pcm_close); + static int double_rate_hw_constraint_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { @@ -709,3 +717,5 @@ int snd_ac97_pcm_double_rate_rules(struc SNDRV_PCM_HW_PARAM_RATE, -1); return err; } + +EXPORT_SYMBOL(snd_ac97_pcm_double_rate_rules); diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c index 4d523df..2118df5 100644 --- a/sound/pci/ac97/ac97_proc.c +++ b/sound/pci/ac97/ac97_proc.c @@ -433,7 +433,7 @@ void snd_ac97_proc_init(struct snd_ac97 prefix = ac97_is_audio(ac97) ? "ac97" : "mc97"; sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num); if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { - snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_read); + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; @@ -442,10 +442,9 @@ void snd_ac97_proc_init(struct snd_ac97 ac97->proc = entry; sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num); if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { - snd_info_set_text_ops(entry, ac97, 1024, snd_ac97_proc_regs_read); + snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); #ifdef CONFIG_SND_DEBUG entry->mode |= S_IWUSR; - entry->c.text.write_size = 1024; entry->c.text.write = snd_ac97_proc_regs_write; #endif if (snd_info_register(entry) < 0) { diff --git a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c index 0fb7b34..94c26ec 100644 --- a/sound/pci/ac97/ak4531_codec.c +++ b/sound/pci/ac97/ak4531_codec.c @@ -453,7 +453,7 @@ static void snd_ak4531_proc_init(struct struct snd_info_entry *entry; if (! snd_card_proc_new(card, "ak4531", &entry)) - snd_info_set_text_ops(entry, ak4531, 1024, snd_ak4531_proc_read); + snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); } #endif diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index eece1c7..f7aef8c 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -753,7 +753,7 @@ snd_ad1889_proc_init(struct snd_ad1889 * struct snd_info_entry *entry; if (!snd_card_proc_new(chip->card, chip->card->driver, &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_ad1889_proc_read); + snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read); } static struct ac97_quirk ac97_quirks[] = { @@ -947,7 +947,7 @@ snd_ad1889_create(struct snd_card *card, spin_lock_init(&chip->lock); /* only now can we call ad1889_free */ if (request_irq(pci->irq, snd_ad1889_interrupt, - SA_INTERRUPT|SA_SHIRQ, card->driver, (void*)chip)) { + IRQF_DISABLED|IRQF_SHARED, card->driver, (void*)chip)) { printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq); snd_ad1889_free(chip); return -EBUSY; diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c index e2dbc21..e0a815e 100644 --- a/sound/pci/ali5451/ali5451.c +++ b/sound/pci/ali5451/ali5451.c @@ -49,7 +49,7 @@ MODULE_SUPPORTED_DEVICE("{{ALI,M5451,pci static int index = SNDRV_DEFAULT_IDX1; /* Index */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ static int pcm_channels = 32; -static int spdif = 0; +static int spdif; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for ALI M5451 PCI Audio."); @@ -2173,7 +2173,7 @@ static void __devinit snd_ali_proc_init( { struct snd_info_entry *entry; if(!snd_card_proc_new(codec->card, "ali5451", &entry)) - snd_info_set_text_ops(entry, codec, 1024, snd_ali_proc_read); + snd_info_set_text_ops(entry, codec, snd_ali_proc_read); } static int __devinit snd_ali_resources(struct snd_ali *codec) @@ -2185,7 +2185,7 @@ static int __devinit snd_ali_resources(s return err; codec->port = pci_resource_start(codec->pci, 0); - if (request_irq(codec->pci->irq, snd_ali_card_interrupt, SA_INTERRUPT|SA_SHIRQ, "ALI 5451", (void *)codec)) { + if (request_irq(codec->pci->irq, snd_ali_card_interrupt, IRQF_DISABLED|IRQF_SHARED, "ALI 5451", (void *)codec)) { snd_printk(KERN_ERR "Unable to request irq.\n"); return -EBUSY; } diff --git a/sound/pci/als300.c b/sound/pci/als300.c index 901b08a..a9c3896 100644 --- a/sound/pci/als300.c +++ b/sound/pci/als300.c @@ -724,7 +724,7 @@ static int __devinit snd_als300_create(s else irq_handler = snd_als300_interrupt; - if (request_irq(pci->irq, irq_handler, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, irq_handler, IRQF_DISABLED|IRQF_SHARED, card->shortname, (void *)chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_als300_free(chip); diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c index 60423b1..a9f0806 100644 --- a/sound/pci/als4000.c +++ b/sound/pci/als4000.c @@ -746,8 +746,8 @@ static int __devinit snd_card_als4000_pr card->shortname, chip->alt_port, chip->irq); if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000, - gcr+0x30, 1, pci->irq, 0, - &chip->rmidi)) < 0) { + gcr+0x30, MPU401_INFO_INTEGRATED, + pci->irq, 0, &chip->rmidi)) < 0) { printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n", gcr+0x30); goto out_err; } diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d0f759d..9fbb065 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1504,7 +1504,7 @@ static void __devinit snd_atiixp_proc_in struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "atiixp", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read); + snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } #else /* !CONFIG_PROC_FS */ #define snd_atiixp_proc_init(chip) @@ -1578,7 +1578,7 @@ static int __devinit snd_atiixp_create(s return -EIO; } - if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_atiixp_free(chip); diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c index 12a34c3..7dcf494 100644 --- a/sound/pci/atiixp_modem.c +++ b/sound/pci/atiixp_modem.c @@ -1177,7 +1177,7 @@ static void __devinit snd_atiixp_proc_in struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_atiixp_proc_read); + snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); } #else #define snd_atiixp_proc_init(chip) @@ -1251,7 +1251,7 @@ static int __devinit snd_atiixp_create(s return -EIO; } - if (request_irq(pci->irq, snd_atiixp_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_atiixp_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_atiixp_free(chip); diff --git a/sound/pci/au88x0/au88x0.c b/sound/pci/au88x0/au88x0.c index 126870e..ef189d7 100644 --- a/sound/pci/au88x0/au88x0.c +++ b/sound/pci/au88x0/au88x0.c @@ -197,7 +197,7 @@ snd_vortex_create(struct snd_card *card, } if ((err = request_irq(pci->irq, vortex_interrupt, - SA_INTERRUPT | SA_SHIRQ, CARD_NAME_SHORT, + IRQF_DISABLED | IRQF_SHARED, CARD_NAME_SHORT, chip)) != 0) { printk(KERN_ERR "cannot grab irq\n"); goto irq_out; @@ -261,6 +261,13 @@ snd_vortex_probe(struct pci_dev *pci, co return err; } snd_vortex_workaround(pci, pcifix[dev]); + + // Card details needed in snd_vortex_midi + strcpy(card->driver, CARD_NAME_SHORT); + sprintf(card->shortname, "Aureal Vortex %s", CARD_NAME_SHORT); + sprintf(card->longname, "%s at 0x%lx irq %i", + card->shortname, chip->io, chip->irq); + // (4) Alloc components. // ADB pcm. if ((err = snd_vortex_new_pcm(chip, VORTEX_PCM_ADB, NR_ADB)) < 0) { @@ -323,11 +330,6 @@ #if 0 #endif // (5) - strcpy(card->driver, CARD_NAME_SHORT); - strcpy(card->shortname, CARD_NAME_SHORT); - sprintf(card->longname, "%s at 0x%lx irq %i", - card->shortname, chip->io, chip->irq); - if ((err = pci_read_config_word(pci, PCI_DEVICE_ID, &(chip->device))) < 0) { snd_card_free(card); diff --git a/sound/pci/au88x0/au88x0_mpu401.c b/sound/pci/au88x0/au88x0_mpu401.c index 873f486..c75d368 100644 --- a/sound/pci/au88x0/au88x0_mpu401.c +++ b/sound/pci/au88x0/au88x0_mpu401.c @@ -47,7 +47,7 @@ static int __devinit snd_vortex_midi(vor struct snd_rawmidi *rmidi; int temp, mode; struct snd_mpu401 *mpu; - int port; + unsigned long port; #ifdef VORTEX_MPU401_LEGACY /* EnableHardCodedMPU401Port() */ @@ -70,9 +70,6 @@ #endif temp |= (MIDI_CLOCK_DIV << 8) | ((mode >> 24) & 0xff) << 4; hwwrite(vortex->mmio, VORTEX_CTRL2, temp); hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_RESET); - /* Set some kind of mode */ - if (mode) - hwwrite(vortex->mmio, VORTEX_MIDI_CMD, MPU401_ENTER_UART); /* Check if anything is OK. */ temp = hwread(vortex->mmio, VORTEX_MIDI_DATA); @@ -98,7 +95,8 @@ #else port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA); if ((temp = snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port, - 1, 0, 0, &rmidi)) != 0) { + MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO, + 0, 0, &rmidi)) != 0) { hwwrite(vortex->mmio, VORTEX_CTRL, (hwread(vortex->mmio, VORTEX_CTRL) & ~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN); @@ -107,6 +105,9 @@ #else mpu = rmidi->private_data; mpu->cport = (unsigned long)(vortex->mmio + VORTEX_MIDI_CMD); #endif + /* Overwrite MIDI name */ + snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI %d", CARD_NAME_SHORT , vortex->card->number); + vortex->rmidi = rmidi; return 0; } diff --git a/sound/pci/au88x0/au88x0_xtalk.c b/sound/pci/au88x0/au88x0_xtalk.c index 4534e18..b4151e2 100644 --- a/sound/pci/au88x0/au88x0_xtalk.c +++ b/sound/pci/au88x0/au88x0_xtalk.c @@ -66,31 +66,20 @@ static xtalk_gains_t const asXtalkGainsA 0 //0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff,0x7FFF,0x7FFF,0x7FFF,0x7FFF,0x7fff }; -static xtalk_gains_t const asXtalkGainsZeros = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; +static xtalk_gains_t const asXtalkGainsZeros; -static xtalk_dline_t const alXtalkDlineZeros = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 -}; +static xtalk_dline_t const alXtalkDlineZeros; static xtalk_dline_t const alXtalkDlineTest = { 0xFC18, 0x03E8FFFF, 0x186A0, 0x7960FFFE, 1, 0xFFFFFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static xtalk_instate_t const asXtalkInStateZeros = { 0, 0, 0, 0 }; +static xtalk_instate_t const asXtalkInStateZeros; static xtalk_instate_t const asXtalkInStateTest = { 0xFF80, 0x0080, 0xFFFF, 0x0001 }; -static xtalk_state_t const asXtalkOutStateZeros = { - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0}, - {0, 0, 0, 0} -}; +static xtalk_state_t const asXtalkOutStateZeros; + static short const sDiamondKLeftEq = 0x401d; static short const sDiamondKRightEq = 0x401d; static short const sDiamondKLeftXt = 0xF90E; @@ -162,13 +151,7 @@ static xtalk_coefs_t const asXtalkNarrow {0, 0, 0, 0, 0} }; -static xtalk_coefs_t const asXtalkCoefsZeros = { - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0} -}; +static xtalk_coefs_t const asXtalkCoefsZeros; static xtalk_coefs_t const asXtalkCoefsPipe = { {0, 0, 0x0FA0, 0, 0}, {0, 0, 0x0FA0, 0, 0}, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 52a3645..15447a3 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -33,14 +33,21 @@ * in the first place >:-P}), * I was forced to base this driver on reverse engineering * (3 weeks' worth of evenings filled with driver work). - * (and no, I did NOT go the easy way: to pick up a PCI128 for 9 Euros) + * (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros) * * The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name * for compatibility reasons) has the following features: * * - builtin AC97 conformant codec (SNR over 80dB) - * (really AC97 compliant?? I really doubt it when looking - * at the mixer register layout) + * Note that "conformant" != "compliant"!! this chip's mixer register layout + * *differs* from the standard AC97 layout: + * they chose to not implement the headphone register (which is not a + * problem since it's merely optional), yet when doing this, they committed + * the grave sin of letting other registers follow immediately instead of + * keeping a headphone dummy register, thereby shifting the mixer register + * addresses illegally. So far unfortunately it looks like the very flexible + * ALSA AC97 support is still not enough to easily compensate for such a + * grave layout violation despite all tweaks and quirks mechanisms it offers. * - builtin genuine OPL3 * - full duplex 16bit playback/record at independent sampling rate * - MPU401 (+ legacy address support) FIXME: how to enable legacy addr?? @@ -90,10 +97,15 @@ * * TODO * - test MPU401 MIDI playback etc. - * - power management. See e.g. intel8x0 or cs4281. - * This would be nice since the chip runs a bit hot, and it's *required* - * anyway for proper ACPI power management. + * - add some power micro-management (disable various units of the card + * as long as they're unused). However this requires I/O ports which I + * haven't figured out yet and which thus might not even exist... + * The standard suspend/resume functionality could probably make use of + * some improvement, too... * - figure out what all unknown port bits are responsible for + * - figure out some cleverly evil scheme to possibly make ALSA AC97 code + * fully accept our quite incompatible ""AC97"" mixer and thus save some + * code (but I'm not too optimistic that doing this is possible at all) */ #include @@ -214,6 +226,16 @@ #endif struct pci_dev *pci; int irq; + +#ifdef CONFIG_PM + /* register value containers for power management + * Note: not always full I/O range preserved (just like Win driver!) */ + u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2]; + u16 saved_regs_io2 [AZF_IO_SIZE_IO2_PM / 2]; + u16 saved_regs_mpu [AZF_IO_SIZE_MPU_PM / 2]; + u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2]; + u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2]; +#endif }; static const struct pci_device_id snd_azf3328_ids[] __devinitdata = { @@ -317,10 +339,8 @@ snd_azf3328_mixer_write_volume_gradually else dst_vol_left &= ~0x80; - do - { - if (!left_done) - { + do { + if (!left_done) { if (curr_vol_left > dst_vol_left) curr_vol_left--; else @@ -330,8 +350,7 @@ snd_azf3328_mixer_write_volume_gradually left_done = 1; outb(curr_vol_left, portbase + 1); } - if (!right_done) - { + if (!right_done) { if (curr_vol_right > dst_vol_right) curr_vol_right--; else @@ -346,8 +365,7 @@ snd_azf3328_mixer_write_volume_gradually } if (delay) mdelay(delay); - } - while ((!left_done) || (!right_done)); + } while ((!left_done) || (!right_done)); snd_azf3328_dbgcallleave(); } @@ -514,15 +532,18 @@ snd_azf3328_info_mixer_enum(struct snd_k struct snd_ctl_elem_info *uinfo) { static const char * const texts1[] = { - "ModemOut1", "ModemOut2" + "Mic1", "Mic2" }; static const char * const texts2[] = { - "MonoSelectSource1", "MonoSelectSource2" + "Mix", "Mic" }; static const char * const texts3[] = { "Mic", "CD", "Video", "Aux", "Line", "Mix", "Mix Mono", "Phone" }; + static const char * const texts4[] = { + "pre 3D", "post 3D" + }; struct azf3328_mixer_reg reg; snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); @@ -531,14 +552,19 @@ snd_azf3328_info_mixer_enum(struct snd_k uinfo->value.enumerated.items = reg.enum_c; if (uinfo->value.enumerated.item > reg.enum_c - 1U) uinfo->value.enumerated.item = reg.enum_c - 1U; - if (reg.reg == IDX_MIXER_ADVCTL2) - { - if (reg.lchan_shift == 8) /* modem out sel */ + if (reg.reg == IDX_MIXER_ADVCTL2) { + switch(reg.lchan_shift) { + case 8: /* modem out sel */ strcpy(uinfo->value.enumerated.name, texts1[uinfo->value.enumerated.item]); - else /* mono sel source */ + break; + case 9: /* mono sel source */ strcpy(uinfo->value.enumerated.name, texts2[uinfo->value.enumerated.item]); - } - else + break; + case 15: /* PCM Out Path */ + strcpy(uinfo->value.enumerated.name, texts4[uinfo->value.enumerated.item]); + break; + } + } else strcpy(uinfo->value.enumerated.name, texts3[uinfo->value.enumerated.item] ); return 0; @@ -554,12 +580,10 @@ snd_azf3328_get_mixer_enum(struct snd_kc snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); val = snd_azf3328_mixer_inw(chip, reg.reg); - if (reg.reg == IDX_MIXER_REC_SELECT) - { + if (reg.reg == IDX_MIXER_REC_SELECT) { ucontrol->value.enumerated.item[0] = (val >> 8) & (reg.enum_c - 1); ucontrol->value.enumerated.item[1] = (val >> 0) & (reg.enum_c - 1); - } - else + } else ucontrol->value.enumerated.item[0] = (val >> reg.lchan_shift) & (reg.enum_c - 1); snd_azf3328_dbgmixer("get_enum: %02x is %04x -> %d|%d (shift %02d, enum_c %d)\n", @@ -579,16 +603,13 @@ snd_azf3328_put_mixer_enum(struct snd_kc snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); oreg = snd_azf3328_mixer_inw(chip, reg.reg); val = oreg; - if (reg.reg == IDX_MIXER_REC_SELECT) - { + if (reg.reg == IDX_MIXER_REC_SELECT) { if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U || ucontrol->value.enumerated.item[1] > reg.enum_c - 1U) return -EINVAL; val = (ucontrol->value.enumerated.item[0] << 8) | (ucontrol->value.enumerated.item[1] << 0); - } - else - { + } else { if (ucontrol->value.enumerated.item[0] > reg.enum_c - 1U) return -EINVAL; val &= ~((reg.enum_c - 1) << reg.lchan_shift); @@ -629,13 +650,14 @@ static const struct snd_kcontrol_new snd AZF3328_MIXER_VOL_MONO("Modem Playback Volume", IDX_MIXER_MODEMOUT, 0x1f, 1), AZF3328_MIXER_SWITCH("Modem Capture Switch", IDX_MIXER_MODEMIN, 15, 1), AZF3328_MIXER_VOL_MONO("Modem Capture Volume", IDX_MIXER_MODEMIN, 0x1f, 1), - AZF3328_MIXER_ENUM("Modem Out Select", IDX_MIXER_ADVCTL2, 2, 8), - AZF3328_MIXER_ENUM("Mono Select Source", IDX_MIXER_ADVCTL2, 2, 9), + AZF3328_MIXER_ENUM("Mic Select", IDX_MIXER_ADVCTL2, 2, 8), + AZF3328_MIXER_ENUM("Mono Output Select", IDX_MIXER_ADVCTL2, 2, 9), + AZF3328_MIXER_ENUM("PCM", IDX_MIXER_ADVCTL2, 2, 15), /* PCM Out Path, place in front since it controls *both* 3D and Bass/Treble! */ AZF3328_MIXER_VOL_SPECIAL("Tone Control - Treble", IDX_MIXER_BASSTREBLE, 0x07, 1, 0), AZF3328_MIXER_VOL_SPECIAL("Tone Control - Bass", IDX_MIXER_BASSTREBLE, 0x07, 9, 0), AZF3328_MIXER_SWITCH("3D Control - Switch", IDX_MIXER_ADVCTL2, 13, 0), - AZF3328_MIXER_VOL_SPECIAL("3D Control - Wide", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */ - AZF3328_MIXER_VOL_SPECIAL("3D Control - Space", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */ + AZF3328_MIXER_VOL_SPECIAL("3D Control - Width", IDX_MIXER_ADVCTL1, 0x07, 1, 0), /* "3D Width" */ + AZF3328_MIXER_VOL_SPECIAL("3D Control - Depth", IDX_MIXER_ADVCTL1, 0x03, 8, 0), /* "Hifi 3D" */ #if MIXER_TESTING AZF3328_MIXER_SWITCH("0", IDX_MIXER_ADVCTL2, 0, 0), AZF3328_MIXER_SWITCH("1", IDX_MIXER_ADVCTL2, 1, 0), @@ -813,22 +835,18 @@ snd_azf3328_setdmaa(struct snd_azf3328 * unsigned int is_running; snd_azf3328_dbgcallenter(); - if (do_recording) - { + if (do_recording) { /* access capture registers, i.e. skip playback reg section */ portbase = chip->codec_port + 0x20; is_running = chip->is_recording; - } - else - { + } else { /* access the playback register section */ portbase = chip->codec_port + 0x00; is_running = chip->is_playing; } /* AZF3328 uses a two buffer pointer DMA playback approach */ - if (!is_running) - { + if (!is_running) { unsigned long addr_area2; unsigned long count_areas, count_tmp; /* width 32bit -- overflow!! */ count_areas = size/2; @@ -961,6 +979,13 @@ #endif chip->is_playing = 1; snd_azf3328_dbgplay("STARTED PLAYBACK\n"); break; + case SNDRV_PCM_TRIGGER_RESUME: + snd_azf3328_dbgplay("RESUME PLAYBACK\n"); + /* resume playback if we were active */ + if (chip->is_playing) + snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_STOP: snd_azf3328_dbgplay("STOP PLAYBACK\n"); @@ -988,6 +1013,12 @@ #endif chip->is_playing = 0; snd_azf3328_dbgplay("STOPPED PLAYBACK\n"); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + snd_azf3328_dbgplay("SUSPEND PLAYBACK\n"); + /* make sure playback is stopped */ + snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); break; @@ -995,6 +1026,7 @@ #endif snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); break; default: + printk(KERN_ERR "FIXME: unknown trigger mode!\n"); return -EINVAL; } @@ -1068,6 +1100,13 @@ #endif chip->is_recording = 1; snd_azf3328_dbgplay("STARTED CAPTURE\n"); break; + case SNDRV_PCM_TRIGGER_RESUME: + snd_azf3328_dbgplay("RESUME CAPTURE\n"); + /* resume recording if we were active */ + if (chip->is_recording) + snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_STOP: snd_azf3328_dbgplay("STOP CAPTURE\n"); @@ -1088,6 +1127,12 @@ #endif chip->is_recording = 0; snd_azf3328_dbgplay("STOPPED CAPTURE\n"); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + snd_azf3328_dbgplay("SUSPEND CAPTURE\n"); + /* make sure recording is stopped */ + snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, + snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME); + break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n"); break; @@ -1095,6 +1140,7 @@ #endif snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n"); break; default: + printk(KERN_ERR "FIXME: unknown trigger mode!\n"); return -EINVAL; } @@ -1163,8 +1209,7 @@ snd_azf3328_interrupt(int irq, void *dev snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE), status); - if (status & IRQ_TIMER) - { + if (status & IRQ_TIMER) { /* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */ if (chip->timer) snd_timer_interrupt(chip->timer, chip->timer->sticks); @@ -1174,50 +1219,43 @@ snd_azf3328_interrupt(int irq, void *dev spin_unlock(&chip->reg_lock); snd_azf3328_dbgplay("azt3328: timer IRQ\n"); } - if (status & IRQ_PLAYBACK) - { + if (status & IRQ_PLAYBACK) { spin_lock(&chip->reg_lock); which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which); spin_unlock(&chip->reg_lock); - if (chip->pcm && chip->playback_substream) - { + if (chip->pcm && chip->playback_substream) { snd_pcm_period_elapsed(chip->playback_substream); snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n", which, inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS)); - } - else + } else snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); if (which & IRQ_PLAY_SOMETHING) snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n"); } - if (status & IRQ_RECORDING) - { + if (status & IRQ_RECORDING) { spin_lock(&chip->reg_lock); which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which); spin_unlock(&chip->reg_lock); - if (chip->pcm && chip->capture_substream) - { + if (chip->pcm && chip->capture_substream) { snd_pcm_period_elapsed(chip->capture_substream); snd_azf3328_dbgplay("REC period done (#%x), @ %x\n", which, inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS)); - } - else + } else snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); if (which & IRQ_REC_SOMETHING) snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n"); } /* MPU401 has less critical IRQ requirements * than timer and playback/recording, right? */ - if (status & IRQ_MPU401) - { + if (status & IRQ_MPU401) { snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs); /* hmm, do we have to ack the IRQ here somehow? @@ -1511,8 +1549,7 @@ snd_azf3328_timer_start(struct snd_timer snd_azf3328_dbgcallenter(); chip = snd_timer_chip(timer); delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK; - if (delay < 49) - { + if (delay < 49) { /* uhoh, that's not good, since user-space won't know about * this timing tweak * (we need to do it to avoid a lockup, though) */ @@ -1687,7 +1724,7 @@ snd_azf3328_create(struct snd_card *card chip->synth_port = pci_resource_start(pci, 3); chip->mixer_port = pci_resource_start(pci, 4); - if (request_irq(pci->irq, snd_azf3328_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) { + if (request_irq(pci->irq, snd_azf3328_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, (void *)chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto out_err; @@ -1766,9 +1803,11 @@ snd_azf3328_probe(struct pci_dev *pci, c goto out_err; } + card->private_data = chip; + if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_MPU401, - chip->mpu_port, 1, pci->irq, 0, - &chip->rmidi)) < 0) { + chip->mpu_port, MPU401_INFO_INTEGRATED, + pci->irq, 0, &chip->rmidi)) < 0) { snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port); goto out_err; } @@ -1791,6 +1830,8 @@ snd_azf3328_probe(struct pci_dev *pci, c } } + opl3->private_data = chip; + sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, chip->codec_port, chip->irq); @@ -1834,11 +1875,80 @@ snd_azf3328_remove(struct pci_dev *pci) snd_azf3328_dbgcallleave(); } +#ifdef CONFIG_PM +static int +snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_azf3328 *chip = card->private_data; + int reg; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + + snd_pcm_suspend_all(chip->pcm); + + for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++) + chip->saved_regs_mixer[reg] = inw(chip->mixer_port + reg * 2); + + /* make sure to disable master volume etc. to prevent looping sound */ + snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); + snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1); + + for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++) + chip->saved_regs_codec[reg] = inw(chip->codec_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++) + chip->saved_regs_io2[reg] = inw(chip->io2_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++) + chip->saved_regs_mpu[reg] = inw(chip->mpu_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++) + chip->saved_regs_synth[reg] = inw(chip->synth_port + reg * 2); + + pci_set_power_state(pci, PCI_D3hot); + pci_disable_device(pci); + pci_save_state(pci); + return 0; +} + +static int +snd_azf3328_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct snd_azf3328 *chip = card->private_data; + int reg; + + pci_restore_state(pci); + pci_enable_device(pci); + pci_set_power_state(pci, PCI_D0); + pci_set_master(pci); + + for (reg = 0; reg < AZF_IO_SIZE_IO2_PM / 2; reg++) + outw(chip->saved_regs_io2[reg], chip->io2_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; reg++) + outw(chip->saved_regs_mpu[reg], chip->mpu_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_SYNTH_PM / 2; reg++) + outw(chip->saved_regs_synth[reg], chip->synth_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; reg++) + outw(chip->saved_regs_mixer[reg], chip->mixer_port + reg * 2); + for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; reg++) + outw(chip->saved_regs_codec[reg], chip->codec_port + reg * 2); + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + return 0; +} +#endif + + + + static struct pci_driver driver = { .name = "AZF3328", .id_table = snd_azf3328_ids, .probe = snd_azf3328_probe, .remove = __devexit_p(snd_azf3328_remove), +#ifdef CONFIG_PM + .suspend = snd_azf3328_suspend, + .resume = snd_azf3328_resume, +#endif }; static int __init diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h index f489bda..b4f3e3c 100644 --- a/sound/pci/azt3328.h +++ b/sound/pci/azt3328.h @@ -5,6 +5,9 @@ #define __SOUND_AZT3328_H /*** main I/O area port indices ***/ /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ +#define AZF_IO_SIZE_CODEC 0x80 +#define AZF_IO_SIZE_CODEC_PM 0x70 + /* the driver initialisation suggests a layout of 4 main areas: * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??). * And another area from 0x60 to 0x6f (DirectX timer, IRQ management, @@ -87,7 +90,7 @@ #define IDX_IO_REC_DMA_CURRPOS 0x30 /* #define IDX_IO_REC_DMA_CURROFS 0x34 /* PU:0x00000000 */ #define IDX_IO_REC_SOUNDFORMAT 0x36 /* PU:0x0000 */ -/** hmm, what is this I/O area for? MPU401?? (after playback, recording, ???, timer) **/ +/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/ #define IDX_IO_SOMETHING_FLAGS 0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */ /* general */ #define IDX_IO_42H 0x42 /* PU:0x0001 */ @@ -107,7 +110,8 @@ #define IDX_IO_IRQSTATUS 0x64 #define IRQ_UNKNOWN2 0x0080 /* probably unused */ #define IDX_IO_66H 0x66 /* writing 0xffff returns 0x0000 */ #define IDX_IO_SOME_VALUE 0x68 /* this is set to e.g. 0x3ff or 0x300, and writable; maybe some buffer limit, but I couldn't find out more, PU:0x00ff */ -#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated; actually inhibits PCM playback!!! maybe power management?? */ +#define IDX_IO_6AH 0x6A /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); actually inhibits PCM playback!!! maybe power management?? */ + #define IO_6A_PAUSE_PLAYBACK 0x0200 /* bit 9; sure, this pauses playback, but what the heck is this really about?? */ #define IDX_IO_6CH 0x6C #define IDX_IO_6EH 0x6E /* writing 0xffff returns 0x83fe */ /* further I/O indices not saved/restored, so probably not used */ @@ -115,15 +119,25 @@ #define IDX_IO_6EH 0x6E /* writing 0xff /*** I/O 2 area port indices ***/ /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ +#define AZF_IO_SIZE_IO2 0x08 +#define AZF_IO_SIZE_IO2_PM 0x06 + #define IDX_IO2_LEGACY_ADDR 0x04 #define LEGACY_SOMETHING 0x01 /* OPL3?? */ #define LEGACY_JOY 0x08 +#define AZF_IO_SIZE_MPU 0x04 +#define AZF_IO_SIZE_MPU_PM 0x04 + +#define AZF_IO_SIZE_SYNTH 0x08 +#define AZF_IO_SIZE_SYNTH_PM 0x06 /*** mixer I/O area port indices ***/ /* (only 0x22 of 0x40 bytes saved/restored by Windows driver) - * generally spoken: AC97 register index = AZF3328 mixer reg index + 2 - * (in other words: AZF3328 NOT fully AC97 compliant) */ + * UNFORTUNATELY azf3328 is NOT truly AC97 compliant: see main file intro */ +#define AZF_IO_SIZE_MIXER 0x40 +#define AZF_IO_SIZE_MIXER_PM 0x22 + #define MIXER_VOLUME_RIGHT_MASK 0x001f #define MIXER_VOLUME_LEFT_MASK 0x1f00 #define MIXER_MUTE_MASK 0x8000 @@ -156,14 +170,14 @@ #define IDX_MIXER_REC_VOLUME 0x1c #define IDX_MIXER_ADVCTL1 0x1e /* unlisted bits are unmodifiable */ #define MIXER_ADVCTL1_3DWIDTH_MASK 0x000e - #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300 -#define IDX_MIXER_ADVCTL2 0x20 /* resembles AC97_GENERAL_PURPOSE reg! */ + #define MIXER_ADVCTL1_HIFI3D_MASK 0x0300 /* yup, this is missing the high bit that official AC97 contains, plus it doesn't have linear bit value range behaviour but instead acts weirdly (possibly we're dealing with two *different* 3D settings here??) */ +#define IDX_MIXER_ADVCTL2 0x20 /* subset of AC97_GENERAL_PURPOSE reg! */ /* unlisted bits are unmodifiable */ - #define MIXER_ADVCTL2_BIT7 0x0080 /* WaveOut 3D Bypass? mutes WaveOut at LineOut */ - #define MIXER_ADVCTL2_BIT8 0x0100 /* is this Modem Out Select? */ - #define MIXER_ADVCTL2_BIT9 0x0200 /* Mono Select Source? */ - #define MIXER_ADVCTL2_BIT13 0x2000 /* 3D enable? */ - #define MIXER_ADVCTL2_BIT15 0x8000 /* unknown */ + #define MIXER_ADVCTL2_LPBK 0x0080 /* Loopback mode -- Win driver: "WaveOut3DBypass"? mutes WaveOut at LineOut */ + #define MIXER_ADVCTL2_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 -- Win driver: "ModemOutSelect"?? */ + #define MIXER_ADVCTL2_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic; Win driver: "MonoSelectSource"?? */ + #define MIXER_ADVCTL2_3D 0x2000 /* 3D Enhancement 1=on */ + #define MIXER_ADVCTL2_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ #define IDX_MIXER_SOMETHING30H 0x30 /* used, but unknown??? */ diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c index 9ee07d4..4d4277d 100644 --- a/sound/pci/bt87x.c +++ b/sound/pci/bt87x.c @@ -44,7 +44,7 @@ MODULE_SUPPORTED_DEVICE("{{Brooktree,Bt8 static int index[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -2}; /* Exclude the first card */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int digital_rate[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* digital input rate */ +static int digital_rate[SNDRV_CARDS]; /* digital input rate */ static int load_all; /* allow to load the non-whitelisted cards */ module_param_array(index, int, NULL, 0444); @@ -747,7 +747,7 @@ static int __devinit snd_bt87x_create(st snd_bt87x_writel(chip, REG_INT_MASK, 0); snd_bt87x_writel(chip, REG_INT_STAT, MY_INTERRUPTS); - if (request_irq(pci->irq, snd_bt87x_interrupt, SA_INTERRUPT | SA_SHIRQ, + if (request_irq(pci->irq, snd_bt87x_interrupt, IRQF_DISABLED | IRQF_SHARED, "Bt87x audio", chip)) { snd_bt87x_free(chip); snd_printk(KERN_ERR "cannot grab irq\n"); @@ -781,10 +781,12 @@ static struct pci_device_id snd_bt87x_id BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, 0x0070, 0x13eb, 32000), /* Viewcast Osprey 200 */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0xff01, 44100), - /* AVerMedia Studio No. 103, 203, ...? */ - BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000), /* Leadtek Winfast tv 2000xp delux */ BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, 32000), + /* Voodoo TV 200 */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, 32000), + /* AVerMedia Studio No. 103, 203, ...? */ + BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, 48000), { } }; MODULE_DEVICE_TABLE(pci, snd_bt87x_ids); @@ -886,8 +888,9 @@ static int __devinit snd_bt87x_probe(str strcpy(card->driver, "Bt87x"); sprintf(card->shortname, "Brooktree Bt%x", pci->device); - sprintf(card->longname, "%s at %#lx, irq %i", - card->shortname, pci_resource_start(pci, 0), chip->irq); + sprintf(card->longname, "%s at %#llx, irq %i", + card->shortname, (unsigned long long)pci_resource_start(pci, 0), + chip->irq); strcpy(card->mixername, "Bt87x"); err = snd_card_register(card); diff --git a/sound/pci/ca0106/ca0106.h b/sound/pci/ca0106/ca0106.h index c8131ea..9cb66c5 100644 --- a/sound/pci/ca0106/ca0106.h +++ b/sound/pci/ca0106/ca0106.h @@ -537,9 +537,9 @@ #define ADC_TRANWIN_MASK 0x00000070 //Ma #endif #define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux +#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used) #define ADC_MUX_MIC 0x00000002 //Value to select Mic at ADC Mux #define ADC_MUX_LINEIN 0x00000004 //Value to select LineIn at ADC Mux -#define ADC_MUX_PHONE 0x00000001 //Value to select TAD at ADC Mux (Not used) #define ADC_MUX_AUX 0x00000008 //Value to select Aux at ADC Mux #define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */ @@ -604,6 +604,8 @@ struct snd_ca0106 { u32 spdif_bits[4]; /* s/pdif out setup */ int spdif_enable; int capture_source; + int i2c_capture_source; + u8 i2c_capture_volume[4][2]; int capture_mic_line_in; struct snd_dma_buffer buffer; diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c index fd8bfeb..a30c019 100644 --- a/sound/pci/ca0106/ca0106_main.c +++ b/sound/pci/ca0106/ca0106_main.c @@ -186,8 +186,8 @@ static struct snd_ca0106_details ca0106_ /* New Audigy SE. Has a different DAC. */ /* SB0570: * CTRL:CA0106-DAT - * ADC: WM8768GEDS - * DAC: WM8775EDS + * ADC: WM8775EDS + * DAC: WM8768GEDS */ { .serial = 0x100a1102, .name = "Audigy SE [SB0570]", @@ -195,9 +195,14 @@ static struct snd_ca0106_details ca0106_ .i2c_adc = 1, .spi_dac = 1 } , /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */ + /* SB0438 + * CTRL:CA0106-DAT + * ADC: WM8775SEDS + * DAC: CS4382-KQZ + */ { .serial = 0x10091462, .name = "MSI K8N Diamond MB [SB0438]", - .gpio_type = 1, + .gpio_type = 2, .i2c_adc = 1 } , /* Shuttle XPC SD31P which has an onboard Creative Labs * Sound Blaster Live! 24-bit EAX @@ -326,6 +331,7 @@ int snd_ca0106_spi_write(struct snd_ca01 return 0; } +/* The ADC does not support i2c read, so only write is implemented */ int snd_ca0106_i2c_write(struct snd_ca0106 *emu, u32 reg, u32 value) @@ -340,6 +346,7 @@ int snd_ca0106_i2c_write(struct snd_ca01 } tmp = reg << 25 | value << 16; + // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value); /* Not sure what this I2C channel controls. */ /* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */ @@ -348,8 +355,9 @@ int snd_ca0106_i2c_write(struct snd_ca01 for (retry = 0; retry < 10; retry++) { /* Send the data to i2c */ - tmp = snd_ca0106_ptr_read(emu, I2C_A, 0); - tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); + //tmp = snd_ca0106_ptr_read(emu, I2C_A, 0); + //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK); + tmp = 0; tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD); snd_ca0106_ptr_write(emu, I2C_A, 0, tmp); @@ -1181,7 +1189,7 @@ static unsigned int spi_dac_init[] = { 0x02ff, 0x0400, 0x0520, - 0x0600, + 0x0620, /* Set 24 bit. Was 0x0600 */ 0x08ff, 0x0aff, 0x0cff, @@ -1200,6 +1208,22 @@ static unsigned int spi_dac_init[] = { 0x1400, }; +static unsigned int i2c_adc_init[][2] = { + { 0x17, 0x00 }, /* Reset */ + { 0x07, 0x00 }, /* Timeout */ + { 0x0b, 0x22 }, /* Interface control */ + { 0x0c, 0x22 }, /* Master mode control */ + { 0x0d, 0x08 }, /* Powerdown control */ + { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */ + { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */ + { 0x10, 0x7b }, /* ALC Control 1 */ + { 0x11, 0x00 }, /* ALC Control 2 */ + { 0x12, 0x32 }, /* ALC Control 3 */ + { 0x13, 0x00 }, /* Noise gate control */ + { 0x14, 0xa6 }, /* Limiter control */ + { 0x15, ADC_MUX_LINEIN }, /* ADC Mixer control */ +}; + static int __devinit snd_ca0106_create(struct snd_card *card, struct pci_dev *pci, struct snd_ca0106 **rchip) @@ -1244,7 +1268,7 @@ static int __devinit snd_ca0106_create(s } if (request_irq(pci->irq, snd_ca0106_interrupt, - SA_INTERRUPT|SA_SHIRQ, "snd_ca0106", + IRQF_DISABLED|IRQF_SHARED, "snd_ca0106", (void *)chip)) { snd_ca0106_free(chip); printk(KERN_ERR "cannot grab irq\n"); @@ -1361,7 +1385,12 @@ #endif snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */ chip->capture_source = 3; /* Set CAPTURE_SOURCE */ - if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ + if (chip->details->gpio_type == 2) { /* The SB0438 use GPIO differently. */ + /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ + outl(0x0, chip->port+GPIO); + //outl(0x00f0e000, chip->port+GPIO); /* Analog */ + outl(0x005f5301, chip->port+GPIO); /* Analog */ + } else if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */ /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */ outl(0x0, chip->port+GPIO); //outl(0x00f0e000, chip->port+GPIO); /* Analog */ @@ -1379,7 +1408,19 @@ #endif outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */ if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */ - snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ + int size, n; + + size = ARRAY_SIZE(i2c_adc_init); + //snd_printk("I2C:array size=0x%x\n", size); + for (n=0; n < size; n++) { + snd_ca0106_i2c_write(chip, i2c_adc_init[n][0], i2c_adc_init[n][1]); + } + for (n=0; n < 4; n++) { + chip->i2c_capture_volume[n][0]= 0xcf; + chip->i2c_capture_volume[n][1]= 0xcf; + } + chip->i2c_capture_source=2; /* Line in */ + //snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */ } if (chip->details->spi_dac == 1) { /* The SB0570 use SPI to control DAC. */ int size, n; diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c index 06fe055..146eed7 100644 --- a/sound/pci/ca0106/ca0106_mixer.c +++ b/sound/pci/ca0106/ca0106_mixer.c @@ -171,6 +171,76 @@ static int snd_ca0106_capture_source_put return change; } +static int snd_ca0106_i2c_capture_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[6] = { + "Phone", "Mic", "Line in", "Aux" + }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 4; + if (uinfo->value.enumerated.item > 3) + uinfo->value.enumerated.item = 3; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_ca0106_i2c_capture_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->i2c_capture_source; + return 0; +} + +static int snd_ca0106_i2c_capture_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + unsigned int source_id; + unsigned int ngain, ogain; + int change = 0; + u32 source; + /* If the capture source has changed, + * update the capture volume from the cached value + * for the particular source. + */ + source_id = ucontrol->value.enumerated.item[0] ; + change = (emu->i2c_capture_source != source_id); + if (change) { + snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ + ngain = emu->i2c_capture_volume[source_id][0]; /* Left */ + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][0]; /* Left */ + if (ngain != ogain) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff)); + ngain = emu->i2c_capture_volume[source_id][1]; /* Left */ + ogain = emu->i2c_capture_volume[emu->i2c_capture_source][1]; /* Left */ + if (ngain != ogain) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); + source = 1 << source_id; + snd_ca0106_i2c_write(emu, ADC_MUX, source); /* Set source */ + emu->i2c_capture_source = source_id; + } + return change; +} + +static int snd_ca0106_capture_line_in_side_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *texts[2] = { "Side out", "Line in" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + return 0; +} + static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -207,16 +277,16 @@ static int snd_ca0106_capture_mic_line_i if (change) { emu->capture_mic_line_in = val; if (val) { - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */ + //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ tmp = inl(emu->port+GPIO) & ~0x400; tmp = tmp | 0x400; outl(tmp, emu->port+GPIO); - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); + //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC); } else { - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */ + //snd_ca0106_i2c_write(emu, ADC_MUX, 0); /* Mute input */ tmp = inl(emu->port+GPIO) & ~0x400; outl(tmp, emu->port+GPIO); - snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); + //snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN); } } return change; @@ -225,12 +295,22 @@ static int snd_ca0106_capture_mic_line_i static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Mic/Line in Capture", + .name = "Shared Mic/Line in Capture Switch", .info = snd_ca0106_capture_mic_line_in_info, .get = snd_ca0106_capture_mic_line_in_get, .put = snd_ca0106_capture_mic_line_in_put }; +static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out __devinitdata = +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Shared Line in/Side out Capture Switch", + .info = snd_ca0106_capture_line_in_side_out_info, + .get = snd_ca0106_capture_mic_line_in_get, + .put = snd_ca0106_capture_mic_line_in_put +}; + + static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -329,15 +409,81 @@ static int snd_ca0106_volume_put(struct return 1; } +static int snd_ca0106_i2c_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 255; + return 0; +} + +static int snd_ca0106_i2c_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + int source_id; + + source_id = kcontrol->private_value; + + ucontrol->value.integer.value[0] = emu->i2c_capture_volume[source_id][0]; + ucontrol->value.integer.value[1] = emu->i2c_capture_volume[source_id][1]; + return 0; +} + +static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol); + unsigned int ogain; + unsigned int ngain; + int source_id; + int change = 0; + + source_id = kcontrol->private_value; + ogain = emu->i2c_capture_volume[source_id][0]; /* Left */ + ngain = ucontrol->value.integer.value[0]; + if (ngain > 0xff) + return 0; + if (ogain != ngain) { + if (emu->i2c_capture_source == source_id) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCL, ((ngain) & 0xff) ); + emu->i2c_capture_volume[source_id][0] = ucontrol->value.integer.value[0]; + change = 1; + } + ogain = emu->i2c_capture_volume[source_id][1]; /* Right */ + ngain = ucontrol->value.integer.value[1]; + if (ngain > 0xff) + return 0; + if (ogain != ngain) { + if (emu->i2c_capture_source == source_id) + snd_ca0106_i2c_write(emu, ADC_ATTEN_ADCR, ((ngain) & 0xff)); + emu->i2c_capture_volume[source_id][1] = ucontrol->value.integer.value[1]; + change = 1; + } + + return change; +} + #define CA_VOLUME(xname,chid,reg) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = snd_ca0106_volume_info, \ - .get = snd_ca0106_volume_get, \ - .put = snd_ca0106_volume_put, \ + .info = snd_ca0106_volume_info, \ + .get = snd_ca0106_volume_get, \ + .put = snd_ca0106_volume_put, \ .private_value = ((chid) << 8) | (reg) \ } +#define I2C_VOLUME(xname,chid) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_ca0106_i2c_volume_info, \ + .get = snd_ca0106_i2c_volume_get, \ + .put = snd_ca0106_i2c_volume_put, \ + .private_value = chid \ +} + static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = { CA_VOLUME("Analog Front Playback Volume", @@ -361,6 +507,11 @@ static struct snd_kcontrol_new snd_ca010 CA_VOLUME("CAPTURE feedback Playback Volume", 1, CAPTURE_CONTROL), + I2C_VOLUME("Phone Capture Volume", 0), + I2C_VOLUME("Mic Capture Volume", 1), + I2C_VOLUME("Line in Capture Volume", 2), + I2C_VOLUME("Aux Capture Volume", 3), + { .access = SNDRV_CTL_ELEM_ACCESS_READ, .iface = SNDRV_CTL_ELEM_IFACE_PCM, @@ -378,12 +529,19 @@ static struct snd_kcontrol_new snd_ca010 }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", + .name = "Digital Capture Source", .info = snd_ca0106_capture_source_info, .get = snd_ca0106_capture_source_get, .put = snd_ca0106_capture_source_put }, { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_ca0106_i2c_capture_source_info, + .get = snd_ca0106_i2c_capture_source_get, + .put = snd_ca0106_i2c_capture_source_put + }, + { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT), .count = 4, @@ -477,7 +635,10 @@ #endif return err; } if (emu->details->i2c_adc == 1) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); + if (emu->details->gpio_type == 1) + err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)); + else /* gpio_type == 2 */ + err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_line_in_side_out, emu)); if (err < 0) return err; } diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c index 6375727..75ca421 100644 --- a/sound/pci/ca0106/ca0106_proc.c +++ b/sound/pci/ca0106/ca0106_proc.c @@ -431,33 +431,30 @@ int __devinit snd_ca0106_proc_init(struc struct snd_info_entry *entry; if(! snd_card_proc_new(emu->card, "iec958", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_iec958); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958); if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32); entry->c.text.write = snd_ca0106_proc_reg_write32; entry->mode |= S_IWUSR; } if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16); if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read8); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8); if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1); entry->c.text.write = snd_ca0106_proc_reg_write; entry->mode |= S_IWUSR; // entry->private_data = emu; } if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_i2c_write); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_i2c_write); entry->c.text.write = snd_ca0106_proc_i2c_write; entry->mode |= S_IWUSR; // entry->private_data = emu; } if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) - snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read2); + snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2); return 0; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index e5ce2da..03766ad 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2121,7 +2121,7 @@ static struct snd_kcontrol_new snd_cmipc CMIPCI_MIXER_VOL_MONO("Mic Capture Volume", CM_REG_MIXER2, CM_VADMIC_SHIFT, 7), CMIPCI_SB_VOL_MONO("Phone Playback Volume", CM_REG_EXTENT_IND, 5, 7), CMIPCI_DOUBLE("Phone Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 4, 4, 1, 0, 0), - CMIPCI_DOUBLE("PC Speaker Playnack Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), + CMIPCI_DOUBLE("PC Speaker Playback Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 3, 3, 1, 0, 0), CMIPCI_DOUBLE("Mic Boost Capture Switch", CM_REG_EXTENT_IND, CM_REG_EXTENT_IND, 0, 0, 1, 0, 0), }; @@ -2602,7 +2602,7 @@ static void __devinit snd_cmipci_proc_in struct snd_info_entry *entry; if (! snd_card_proc_new(cm->card, "cmipci", &entry)) - snd_info_set_text_ops(entry, cm, 1024, snd_cmipci_proc_read); + snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); } #else /* !CONFIG_PROC_FS */ static inline void snd_cmipci_proc_init(struct cmipci *cm) {} @@ -2862,7 +2862,7 @@ static int __devinit snd_cmipci_create(s cm->iobase = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_cmipci_interrupt, - SA_INTERRUPT|SA_SHIRQ, card->driver, cm)) { + IRQF_DISABLED|IRQF_SHARED, card->driver, cm)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_cmipci_free(cm); return -EBUSY; @@ -2932,7 +2932,7 @@ #endif } integrated_midi = snd_cmipci_read_b(cm, CM_REG_MPU_PCI) != 0xff; - if (integrated_midi) + if (integrated_midi && mpu_port[dev] == 1) iomidi = cm->iobase + CM_REG_MPU_PCI; else { iomidi = mpu_port[dev]; @@ -2981,7 +2981,9 @@ #endif if (iomidi > 0) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI, - iomidi, integrated_midi, + iomidi, + (integrated_midi ? + MPU401_INFO_INTEGRATED : 0), cm->irq, 0, &cm->rmidi)) < 0) { printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi); } diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c index b3c94d8..d180248 100644 --- a/sound/pci/cs4281.c +++ b/sound/pci/cs4281.c @@ -1184,7 +1184,7 @@ static void __devinit snd_cs4281_proc_in struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "cs4281", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_cs4281_proc_read); + snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read); if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; @@ -1379,14 +1379,6 @@ static int __devinit snd_cs4281_create(s chip->ba0_addr = pci_resource_start(pci, 0); chip->ba1_addr = pci_resource_start(pci, 1); - if (request_irq(pci->irq, snd_cs4281_interrupt, SA_INTERRUPT|SA_SHIRQ, - "CS4281", chip)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); - snd_cs4281_free(chip); - return -ENOMEM; - } - chip->irq = pci->irq; - chip->ba0 = ioremap_nocache(chip->ba0_addr, pci_resource_len(pci, 0)); chip->ba1 = ioremap_nocache(chip->ba1_addr, pci_resource_len(pci, 1)); if (!chip->ba0 || !chip->ba1) { @@ -1394,6 +1386,14 @@ static int __devinit snd_cs4281_create(s return -ENOMEM; } + if (request_irq(pci->irq, snd_cs4281_interrupt, IRQF_DISABLED|IRQF_SHARED, + "CS4281", chip)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + snd_cs4281_free(chip); + return -ENOMEM; + } + chip->irq = pci->irq; + tmp = snd_cs4281_chip_init(chip); if (tmp) { snd_cs4281_free(chip); diff --git a/sound/pci/cs46xx/cs46xx.c b/sound/pci/cs46xx/cs46xx.c index 848d772..772dc52 100644 --- a/sound/pci/cs46xx/cs46xx.c +++ b/sound/pci/cs46xx/cs46xx.c @@ -48,8 +48,8 @@ MODULE_SUPPORTED_DEVICE("{{Cirrus Logic, static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int external_amp[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static int thinkpad[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int external_amp[SNDRV_CARDS]; +static int thinkpad[SNDRV_CARDS]; static int mmap_valid[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; module_param_array(index, int, NULL, 0444); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c index 69dbf54..894545e 100644 --- a/sound/pci/cs46xx/cs46xx_lib.c +++ b/sound/pci/cs46xx/cs46xx_lib.c @@ -2877,14 +2877,15 @@ static int snd_cs46xx_free(struct snd_cs if (chip->region.idx[0].resource) snd_cs46xx_hw_stop(chip); + if (chip->irq >= 0) + free_irq(chip->irq, chip); + for (idx = 0; idx < 5; idx++) { struct snd_cs46xx_region *region = &chip->region.idx[idx]; if (region->remap_addr) iounmap(region->remap_addr); release_and_free_resource(region->resource); } - if (chip->irq >= 0) - free_irq(chip->irq, chip); if (chip->active_ctrl) chip->active_ctrl(chip, -chip->amplifier); @@ -3852,7 +3853,7 @@ #endif } } - if (request_irq(pci->irq, snd_cs46xx_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_cs46xx_interrupt, IRQF_DISABLED|IRQF_SHARED, "CS46XX", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_cs46xx_free(chip); diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c index f407d2a..5c9711c 100644 --- a/sound/pci/cs46xx/dsp_spos.c +++ b/sound/pci/cs46xx/dsp_spos.c @@ -767,7 +767,6 @@ int cs46xx_dsp_proc_init (struct snd_car if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) { entry->content = SNDRV_INFO_CONTENT_TEXT; entry->mode = S_IFDIR | S_IRUGO | S_IXUGO; - entry->c.text.read_size = 512; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -784,7 +783,6 @@ int cs46xx_dsp_proc_init (struct snd_car entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_symbol_table_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -797,7 +795,6 @@ int cs46xx_dsp_proc_init (struct snd_car entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_modules_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -810,7 +807,6 @@ int cs46xx_dsp_proc_init (struct snd_car entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -823,7 +819,6 @@ int cs46xx_dsp_proc_init (struct snd_car entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_sample_dump_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -836,7 +831,6 @@ int cs46xx_dsp_proc_init (struct snd_car entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_task_tree_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); @@ -849,7 +843,6 @@ int cs46xx_dsp_proc_init (struct snd_car entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = chip; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 1024; entry->c.text.read = cs46xx_dsp_proc_scb_read; if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c index 2c4ee45..232b337 100644 --- a/sound/pci/cs46xx/dsp_spos_scb_lib.c +++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -180,6 +180,7 @@ static void _dsp_clear_sample_buffer (st void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb) { struct dsp_spos_instance * ins = chip->dsp_spos_instance; + unsigned long flags; /* check integrety */ snd_assert ( (scb->index >= 0 && @@ -194,9 +195,9 @@ #if 0 goto _end); #endif - spin_lock(&scb->lock); + spin_lock_irqsave(&scb->lock, flags); _dsp_unlink_scb (chip,scb); - spin_unlock(&scb->lock); + spin_unlock_irqrestore(&scb->lock, flags); cs46xx_dsp_proc_free_scb_desc(scb); snd_assert (scb->scb_symbol != NULL, return ); @@ -267,7 +268,6 @@ void cs46xx_dsp_proc_register_scb_desc ( entry->private_data = scb_info; entry->mode = S_IFREG | S_IRUGO | S_IWUSR; - entry->c.text.read_size = 512; entry->c.text.read = cs46xx_dsp_proc_scb_info_read; if (snd_info_register(entry) < 0) { diff --git a/sound/pci/cs5535audio/Makefile b/sound/pci/cs5535audio/Makefile index 08d8ee6..2911a8a 100644 --- a/sound/pci/cs5535audio/Makefile +++ b/sound/pci/cs5535audio/Makefile @@ -4,5 +4,9 @@ # snd-cs5535audio-objs := cs5535audio.o cs5535audio_pcm.o +ifdef CONFIG_PM +snd-cs5535audio-objs += cs5535audio_pm.o +endif + # Toplevel Module Dependency obj-$(CONFIG_SND_CS5535AUDIO) += snd-cs5535audio.o diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 2c1213a..c12b24c 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -1,5 +1,5 @@ /* - * Driver for audio on multifunction CS5535 companion device + * Driver for audio on multifunction CS5535/6 companion device * Copyright (C) Jaya Kumar * * Based on Jaroslav Kysela and Takashi Iwai's examples. @@ -40,16 +40,36 @@ #include "cs5535audio.h" #define DRIVER_NAME "cs5535audio" +static char *ac97_quirk; +module_param(ac97_quirk, charp, 0444); +MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds."); + +static struct ac97_quirk ac97_quirks[] __devinitdata = { +#if 0 /* Not yet confirmed if all 5536 boards are HP only */ + { + .subvendor = PCI_VENDOR_ID_AMD, + .subdevice = PCI_DEVICE_ID_AMD_CS5536_AUDIO, + .name = "AMD RDK", + .type = AC97_TUNE_HP_ONLY + }, +#endif + {} +}; static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " DRIVER_NAME); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME); + static struct pci_device_id snd_cs5535audio_ids[] __devinitdata = { - { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) }, {} }; @@ -90,7 +110,8 @@ static unsigned short snd_cs5535audio_co udelay(1); } while (--timeout); if (!timeout) - snd_printk(KERN_ERR "Failure reading cs5535 codec\n"); + snd_printk(KERN_ERR "Failure reading codec reg 0x%x," + "Last value=0x%x\n", reg, val); return (unsigned short) val; } @@ -148,6 +169,8 @@ static int snd_cs5535audio_mixer(struct return err; } + snd_ac97_tune_hardware(cs5535au->ac97, ac97_quirks, ac97_quirk); + return 0; } @@ -298,7 +321,7 @@ static int __devinit snd_cs5535audio_cre cs5535au->port = pci_resource_start(pci, 0); if (request_irq(pci->irq, snd_cs5535audio_interrupt, - SA_INTERRUPT|SA_SHIRQ, "CS5535 Audio", cs5535au)) { + IRQF_DISABLED|IRQF_SHARED, "CS5535 Audio", cs5535au)) { snd_printk("unable to grab IRQ %d\n", pci->irq); err = -EBUSY; goto sndfail; @@ -347,6 +370,8 @@ static int __devinit snd_cs5535audio_pro if ((err = snd_cs5535audio_create(card, pci, &cs5535au)) < 0) goto probefail_out; + card->private_data = cs5535au; + if ((err = snd_cs5535audio_mixer(cs5535au)) < 0) goto probefail_out; @@ -383,6 +408,10 @@ static struct pci_driver driver = { .id_table = snd_cs5535audio_ids, .probe = snd_cs5535audio_probe, .remove = __devexit_p(snd_cs5535audio_remove), +#ifdef CONFIG_PM + .suspend = snd_cs5535audio_suspend, + .resume = snd_cs5535audio_resume, +#endif }; static int __init alsa_card_cs5535audio_init(void) diff --git a/sound/pci/cs5535audio/cs5535audio.h b/sound/pci/cs5535audio/cs5535audio.h index 5e55a1a..4fd1f31 100644 --- a/sound/pci/cs5535audio/cs5535audio.h +++ b/sound/pci/cs5535audio/cs5535audio.h @@ -74,6 +74,8 @@ #define STS_NEW 0x00020000 #define PRM_RDY_STS 0x00800000 #define ACC_CODEC_CNTL_WR_CMD (~0x80000000) #define ACC_CODEC_CNTL_RD_CMD 0x80000000 +#define ACC_CODEC_CNTL_LNK_SHUTDOWN 0x00040000 +#define ACC_CODEC_CNTL_LNK_WRM_RST 0x00020000 #define PRD_JMP 0x2000 #define PRD_EOP 0x4000 #define PRD_EOT 0x8000 @@ -88,6 +90,7 @@ struct cs5535audio_dma_ops { void (*disable_dma)(struct cs5535audio *cs5535au); void (*pause_dma)(struct cs5535audio *cs5535au); void (*setup_prd)(struct cs5535audio *cs5535au, u32 prd_addr); + u32 (*read_prd)(struct cs5535audio *cs5535au); u32 (*read_dma_pntr)(struct cs5535audio *cs5535au); }; @@ -103,11 +106,14 @@ struct cs5535audio_dma { struct snd_pcm_substream *substream; unsigned int buf_addr, buf_bytes; unsigned int period_bytes, periods; + int suspended; + u32 saved_prd; }; struct cs5535audio { struct snd_card *card; struct snd_ac97 *ac97; + struct snd_pcm *pcm; int irq; struct pci_dev *pci; unsigned long port; @@ -117,6 +123,8 @@ struct cs5535audio { struct cs5535audio_dma dmas[NUM_CS5535AUDIO_DMAS]; }; +int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state); +int snd_cs5535audio_resume(struct pci_dev *pci); int __devinit snd_cs5535audio_pcm(struct cs5535audio *cs5535audio); #endif /* __SOUND_CS5535AUDIO_H */ diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c index 60bb82b..5450a9e 100644 --- a/sound/pci/cs5535audio/cs5535audio_pcm.c +++ b/sound/pci/cs5535audio/cs5535audio_pcm.c @@ -43,7 +43,8 @@ static struct snd_pcm_hardware snd_cs553 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_RESUME ), .formats = ( SNDRV_PCM_FMTBIT_S16_LE @@ -142,7 +143,7 @@ static int cs5535audio_build_dma_packets if (dma->periods == periods && dma->period_bytes == period_bytes) return 0; - /* the u32 cast is okay because in snd*create we succesfully told + /* the u32 cast is okay because in snd*create we successfully told pci alloc that we're only 32 bit capable so the uppper will be 0 */ addr = (u32) substream->runtime->dma_addr; desc_addr = (u32) dma->desc_buf.addr; @@ -193,6 +194,11 @@ static void cs5535audio_playback_setup_p cs_writel(cs5535au, ACC_BM0_PRD, prd_addr); } +static u32 cs5535audio_playback_read_prd(struct cs5535audio *cs5535au) +{ + return cs_readl(cs5535au, ACC_BM0_PRD); +} + static u32 cs5535audio_playback_read_dma_pntr(struct cs5535audio *cs5535au) { return cs_readl(cs5535au, ACC_BM0_PNTR); @@ -219,6 +225,11 @@ static void cs5535audio_capture_setup_pr cs_writel(cs5535au, ACC_BM1_PRD, prd_addr); } +static u32 cs5535audio_capture_read_prd(struct cs5535audio *cs5535au) +{ + return cs_readl(cs5535au, ACC_BM1_PRD); +} + static u32 cs5535audio_capture_read_dma_pntr(struct cs5535audio *cs5535au) { return cs_readl(cs5535au, ACC_BM1_PNTR); @@ -285,9 +296,17 @@ static int snd_cs5535audio_trigger(struc case SNDRV_PCM_TRIGGER_START: dma->ops->enable_dma(cs5535au); break; + case SNDRV_PCM_TRIGGER_RESUME: + dma->ops->enable_dma(cs5535au); + dma->suspended = 0; + break; case SNDRV_PCM_TRIGGER_STOP: dma->ops->disable_dma(cs5535au); break; + case SNDRV_PCM_TRIGGER_SUSPEND: + dma->ops->disable_dma(cs5535au); + dma->suspended = 1; + break; default: snd_printk(KERN_ERR "unhandled trigger\n"); err = -EINVAL; @@ -375,6 +394,7 @@ static struct cs5535audio_dma_ops snd_cs .enable_dma = cs5535audio_playback_enable_dma, .disable_dma = cs5535audio_playback_disable_dma, .setup_prd = cs5535audio_playback_setup_prd, + .read_prd = cs5535audio_playback_read_prd, .pause_dma = cs5535audio_playback_pause_dma, .read_dma_pntr = cs5535audio_playback_read_dma_pntr, }; @@ -384,6 +404,7 @@ static struct cs5535audio_dma_ops snd_cs .enable_dma = cs5535audio_capture_enable_dma, .disable_dma = cs5535audio_capture_disable_dma, .setup_prd = cs5535audio_capture_setup_prd, + .read_prd = cs5535audio_capture_read_prd, .pause_dma = cs5535audio_capture_pause_dma, .read_dma_pntr = cs5535audio_capture_read_dma_pntr, }; @@ -413,6 +434,7 @@ int __devinit snd_cs5535audio_pcm(struct snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(cs5535au->pci), 64*1024, 128*1024); + cs5535au->pcm = pcm; return 0; } diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c new file mode 100644 index 0000000..aad0e69 --- /dev/null +++ b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -0,0 +1,123 @@ +/* + * Power management for audio on multifunction CS5535 companion device + * Copyright (C) Jaya Kumar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cs5535audio.h" + +static void snd_cs5535audio_stop_hardware(struct cs5535audio *cs5535au) +{ + /* + we depend on snd_ac97_suspend to tell the + AC97 codec to shutdown. the amd spec suggests + that the LNK_SHUTDOWN be done at the same time + that the codec power-down is issued. instead, + we do it just after rather than at the same + time. excluding codec specific build_ops->suspend + ac97 powerdown hits: + 0x8000 EAPD + 0x4000 Headphone amplifier + 0x0300 ADC & DAC + 0x0400 Analog Mixer powerdown (Vref on) + I am not sure if this is the best that we can do. + The remainder to be investigated are: + - analog mixer (vref off) 0x0800 + - AC-link powerdown 0x1000 + - codec internal clock 0x2000 + */ + + /* set LNK_SHUTDOWN to shutdown AC link */ + cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_SHUTDOWN); + +} + +int snd_cs5535audio_suspend(struct pci_dev *pci, pm_message_t state) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct cs5535audio *cs5535au = card->private_data; + int i; + + snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); + for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { + struct cs5535audio_dma *dma = &cs5535au->dmas[i]; + if (dma && dma->substream && !dma->suspended) + dma->saved_prd = dma->ops->read_prd(cs5535au); + } + snd_pcm_suspend_all(cs5535au->pcm); + snd_ac97_suspend(cs5535au->ac97); + /* save important regs, then disable aclink in hw */ + snd_cs5535audio_stop_hardware(cs5535au); + pci_disable_device(pci); + pci_save_state(pci); + + return 0; +} + +int snd_cs5535audio_resume(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct cs5535audio *cs5535au = card->private_data; + u32 tmp; + int timeout; + int i; + + pci_restore_state(pci); + pci_enable_device(pci); + pci_set_master(pci); + + /* set LNK_WRM_RST to reset AC link */ + cs_writel(cs5535au, ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST); + + timeout = 50; + do { + tmp = cs_readl(cs5535au, ACC_CODEC_STATUS); + if (tmp & PRM_RDY_STS) + break; + udelay(1); + } while (--timeout); + + if (!timeout) + snd_printk(KERN_ERR "Failure getting AC Link ready\n"); + + /* we depend on ac97 to perform the codec power up */ + snd_ac97_resume(cs5535au->ac97); + /* set up rate regs, dma. actual initiation is done in trig */ + for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { + struct cs5535audio_dma *dma = &cs5535au->dmas[i]; + if (dma && dma->substream && dma->suspended) { + dma->substream->ops->prepare(dma->substream); + dma->ops->setup_prd(cs5535au, dma->saved_prd); + } + } + + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} + diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile new file mode 100644 index 0000000..7b576ae --- /dev/null +++ b/sound/pci/echoaudio/Makefile @@ -0,0 +1,30 @@ +# +# Makefile for ALSA Echoaudio soundcard drivers +# Copyright (c) 2003 by Giuliano Pochini +# + +snd-darla20-objs := darla20.o +snd-gina20-objs := gina20.o +snd-layla20-objs := layla20.o +snd-darla24-objs := darla24.o +snd-gina24-objs := gina24.o +snd-layla24-objs := layla24.o +snd-mona-objs := mona.o +snd-mia-objs := mia.o +snd-echo3g-objs := echo3g.o +snd-indigo-objs := indigo.o +snd-indigoio-objs := indigoio.o +snd-indigodj-objs := indigodj.o + +obj-$(CONFIG_SND_DARLA20) += snd-darla20.o +obj-$(CONFIG_SND_GINA20) += snd-gina20.o +obj-$(CONFIG_SND_LAYLA20) += snd-layla20.o +obj-$(CONFIG_SND_DARLA24) += snd-darla24.o +obj-$(CONFIG_SND_GINA24) += snd-gina24.o +obj-$(CONFIG_SND_LAYLA24) += snd-layla24.o +obj-$(CONFIG_SND_MONA) += snd-mona.o +obj-$(CONFIG_SND_MIA) += snd-mia.o +obj-$(CONFIG_SND_ECHO3G) += snd-echo3g.o +obj-$(CONFIG_SND_INDIGO) += snd-indigo.o +obj-$(CONFIG_SND_INDIGOIO) += snd-indigoio.o +obj-$(CONFIG_SND_INDIGODJ) += snd-indigodj.o diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c new file mode 100644 index 0000000..b7108e2 --- /dev/null +++ b/sound/pci/echoaudio/darla20.c @@ -0,0 +1,99 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_DARLA20 +#define ECHOCARD_NAME "Darla20" +#define ECHOCARD_HAS_MONITOR + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 0 */ +#define PX_NUM 10 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 0 */ +#define BX_ANALOG_IN 8 /* 2 */ +#define BX_DIGITAL_IN 10 /* 0 */ +#define BX_NUM 10 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_DARLA20_DSP 0 + +static const struct firmware card_fw[] = { + {0, "darla20_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0}, /* DSP 56301 Darla20 rev.0 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "darla20_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c new file mode 100644 index 0000000..4159e3b --- /dev/null +++ b/sound/pci/echoaudio/darla20_dsp.c @@ -0,0 +1,125 @@ +/*************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Darla20\n")); + snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP]; + chip->spdif_status = GD_SPDIF_STATUS_UNDEF; + chip->clock_state = GD_CLOCK_UNDEF; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + DE_INIT(("init_hw done\n")); + return err; +} + + + +/* The Darla20 has no external clock sources */ +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The Darla20 has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u8 clock_state, spdif_status; + + if (wait_handshake(chip)) + return -EIO; + + switch (rate) { + case 44100: + clock_state = GD_CLOCK_44; + spdif_status = GD_SPDIF_STATUS_44; + break; + case 48000: + clock_state = GD_CLOCK_48; + spdif_status = GD_SPDIF_STATUS_48; + break; + default: + clock_state = GD_CLOCK_NOCHANGE; + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + break; + } + + if (chip->clock_state == clock_state) + clock_state = GD_CLOCK_NOCHANGE; + if (spdif_status == chip->spdif_status) + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->comm_page->gd_clock_state = clock_state; + chip->comm_page->gd_spdif_status = spdif_status; + chip->comm_page->gd_resampler_state = 3; /* magic number - should always be 3 */ + + /* Save the new audio state if it changed */ + if (clock_state != GD_CLOCK_NOCHANGE) + chip->clock_state = clock_state; + if (spdif_status != GD_SPDIF_STATUS_NOCHANGE) + chip->spdif_status = spdif_status; + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); +} diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c new file mode 100644 index 0000000..e59a982 --- /dev/null +++ b/sound/pci/echoaudio/darla24.c @@ -0,0 +1,106 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_DARLA24 +#define ECHOCARD_NAME "Darla24" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_SUPER_INTERLEAVE + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 0 */ +#define PX_NUM 10 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 0 */ +#define BX_ANALOG_IN 8 /* 2 */ +#define BX_DIGITAL_IN 10 /* 0 */ +#define BX_NUM 10 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_DARLA24_DSP 0 + +static const struct firmware card_fw[] = { + {0, "darla24_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0}, /* DSP 56301 Darla24 rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0}, /* DSP 56301 Darla24 rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "darla24_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c new file mode 100644 index 0000000..79938ee --- /dev/null +++ b/sound/pci/echoaudio/darla24_dsp.c @@ -0,0 +1,156 @@ +/*************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Darla24\n")); + snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_ESYNC; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_ESYNC) + clock_bits |= ECHO_CLOCK_BIT_ESYNC; + + return clock_bits; +} + + + +/* The Darla24 has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u8 clock; + + switch (rate) { + case 96000: + clock = GD24_96000; + break; + case 88200: + clock = GD24_88200; + break; + case 48000: + clock = GD24_48000; + break; + case 44100: + clock = GD24_44100; + break; + case 32000: + clock = GD24_32000; + break; + case 22050: + clock = GD24_22050; + break; + case 16000: + clock = GD24_16000; + break; + case 11025: + clock = GD24_11025; + break; + case 8000: + clock = GD24_8000; + break; + default: + DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n", + rate)); + return -EINVAL; + } + + if (wait_handshake(chip)) + return -EIO; + + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + chip->sample_rate = rate; + + /* Override the sample rate if this card is set to Echo sync. */ + if (chip->input_clock == ECHO_CLOCK_ESYNC) + clock = GD24_EXT_SYNC; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */ + chip->comm_page->gd_clock_state = clock; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + snd_assert(clock == ECHO_CLOCK_INTERNAL || + clock == ECHO_CLOCK_ESYNC, return -EINVAL); + chip->input_clock = clock; + return set_sample_rate(chip, chip->sample_rate); +} + diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c new file mode 100644 index 0000000..12099fe --- /dev/null +++ b/sound/pci/echoaudio/echo3g.c @@ -0,0 +1,118 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO3G_FAMILY +#define ECHOCARD_ECHO3G +#define ECHOCARD_NAME "Echo3G" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 +#define ECHOCARD_HAS_MIDI +#define ECHOCARD_HAS_PHANTOM_POWER + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 +#define PX_DIGITAL_OUT chip->px_digital_out +#define PX_ANALOG_IN chip->px_analog_in +#define PX_DIGITAL_IN chip->px_digital_in +#define PX_NUM chip->px_num + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 +#define BX_DIGITAL_OUT chip->bx_digital_out +#define BX_ANALOG_IN chip->bx_analog_in +#define BX_DIGITAL_IN chip->bx_digital_in +#define BX_NUM chip->bx_num + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_ECHO3G_DSP 1 +#define FW_3G_ASIC 2 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "echo3g_dsp.fw"}, + {0, "3g_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0}, /* Echo 3G */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 32000, + .rate_max = 100000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "echo3g_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_3g.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c new file mode 100644 index 0000000..d26a1d1 --- /dev/null +++ b/sound/pci/echoaudio/echo3g_dsp.c @@ -0,0 +1,131 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + +static int load_asic(struct echoaudio *chip); +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int check_asic_status(struct echoaudio *chip); +static int set_sample_rate(struct echoaudio *chip, u32 rate); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_phantom_power(struct echoaudio *chip, char on); +static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, + char force); + +#include + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + local_irq_enable(); + DE_INIT(("init_hw() - Echo3G\n")); + snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->comm_page->e3g_frq_register = + __constant_cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2); + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->has_midi = TRUE; + chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP]; + + /* Load the DSP code and the ASIC on the PCI card and get + what type of external box is attached */ + err = load_firmware(chip); + + if (err < 0) { + return err; + } else if (err == E3G_GINA3G_BOX_TYPE) { + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_ADAT; + chip->card_name = "Gina3G"; + chip->px_digital_out = chip->bx_digital_out = 6; + chip->px_analog_in = chip->bx_analog_in = 14; + chip->px_digital_in = chip->bx_digital_in = 16; + chip->px_num = chip->bx_num = 24; + chip->has_phantom_power = TRUE; + chip->hasnt_input_nominal_level = TRUE; + } else if (err == E3G_LAYLA3G_BOX_TYPE) { + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_ADAT | + ECHO_CLOCK_BIT_WORD; + chip->card_name = "Layla3G"; + chip->px_digital_out = chip->bx_digital_out = 8; + chip->px_analog_in = chip->bx_analog_in = 16; + chip->px_digital_in = chip->bx_digital_in = 24; + chip->px_num = chip->bx_num = 32; + } else { + return -ENODEV; + } + + chip->digital_modes = ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->non_audio_spdif = FALSE; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_phantom_power(chip, 0); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static int set_phantom_power(struct echoaudio *chip, char on) +{ + u32 control_reg = le32_to_cpu(chip->comm_page->control_register); + + if (on) + control_reg |= E3G_PHANTOM_POWER; + else + control_reg &= ~E3G_PHANTOM_POWER; + + chip->phantom_power = on; + return write_control_reg(chip, control_reg, + le32_to_cpu(chip->comm_page->e3g_frq_register), + 0); +} diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c new file mode 100644 index 0000000..27a8dbe --- /dev/null +++ b/sound/pci/echoaudio/echoaudio.c @@ -0,0 +1,2196 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +MODULE_AUTHOR("Giuliano Pochini "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver"); +MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}"); +MODULE_DEVICE_TABLE(pci, snd_echo_ids); + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard."); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard."); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard."); + +static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999}; + +static int get_firmware(const struct firmware **fw_entry, + const struct firmware *frm, struct echoaudio *chip) +{ + int err; + char name[30]; + DE_ACT(("firmware requested: %s\n", frm->data)); + snprintf(name, sizeof(name), "ea/%s", frm->data); + if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0) + snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err); + return err; +} + +static void free_firmware(const struct firmware *fw_entry) +{ + release_firmware(fw_entry); + DE_ACT(("firmware released\n")); +} + + + +/****************************************************************************** + PCM interface +******************************************************************************/ + +static void audiopipe_free(struct snd_pcm_runtime *runtime) +{ + struct audiopipe *pipe = runtime->private_data; + + if (pipe->sgpage.area) + snd_dma_free_pages(&pipe->sgpage); + kfree(pipe); +} + + + +static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask fmt; + + snd_mask_any(&fmt); + +#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + /* >=2 channels cannot be S32_BE */ + if (c->min == 2) { + fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE; + return snd_mask_refine(f, &fmt); + } +#endif + /* > 2 channels cannot be U8 and S32_BE */ + if (c->min > 2) { + fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE); + return snd_mask_refine(f, &fmt); + } + /* Mono is ok with any format */ + return 0; +} + + + +static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval ch; + + snd_interval_any(&ch); + + /* S32_BE is mono (and stereo) only */ + if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) { + ch.min = 1; +#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + ch.max = 2; +#else + ch.max = 1; +#endif + ch.integer = 1; + return snd_interval_refine(c, &ch); + } + /* U8 can be only mono or stereo */ + if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) { + ch.min = 1; + ch.max = 2; + ch.integer = 1; + return snd_interval_refine(c, &ch); + } + /* S16_LE, S24_3LE and S32_LE support any number of channels. */ + return 0; +} + + + +static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask fmt; + u64 fmask; + snd_mask_any(&fmt); + + fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32); + + /* >2 channels must be S16_LE, S24_3LE or S32_LE */ + if (c->min > 2) { + fmask &= SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE; + /* 1 channel must be S32_BE or S32_LE */ + } else if (c->max == 1) + fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE; +#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + /* 2 channels cannot be S32_BE */ + else if (c->min == 2 && c->max == 2) + fmask &= ~SNDRV_PCM_FMTBIT_S32_BE; +#endif + else + return 0; + + fmt.bits[0] &= (u32)fmask; + fmt.bits[1] &= (u32)(fmask >> 32); + return snd_mask_refine(f, &fmt); +} + + + +static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *c = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_interval ch; + u64 fmask; + + snd_interval_any(&ch); + ch.integer = 1; + fmask = f->bits[0] + ((u64)f->bits[1] << 32); + + /* S32_BE is mono (and stereo) only */ + if (fmask == SNDRV_PCM_FMTBIT_S32_BE) { + ch.min = 1; +#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + ch.max = 2; +#else + ch.max = 1; +#endif + /* U8 is stereo only */ + } else if (fmask == SNDRV_PCM_FMTBIT_U8) + ch.min = ch.max = 2; + /* S16_LE and S24_3LE must be at least stereo */ + else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE))) + ch.min = 2; + else + return 0; + + return snd_interval_refine(c, &ch); +} + + + +/* Since the sample rate is a global setting, do allow the user to change the +sample rate only if there is only one pcm device open. */ +static int hw_rule_sample_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct echoaudio *chip = rule->private; + struct snd_interval fixed; + + if (!chip->can_set_rate) { + snd_interval_any(&fixed); + fixed.min = fixed.max = chip->sample_rate; + return snd_interval_refine(rate, &fixed); + } + return 0; +} + + +static int pcm_open(struct snd_pcm_substream *substream, + signed char max_channels) +{ + struct echoaudio *chip; + struct snd_pcm_runtime *runtime; + struct audiopipe *pipe; + int err, i; + + if (max_channels <= 0) + return -EAGAIN; + + chip = snd_pcm_substream_chip(substream); + runtime = substream->runtime; + + if (!(pipe = kmalloc(sizeof(struct audiopipe), GFP_KERNEL))) + return -ENOMEM; + memset(pipe, 0, sizeof(struct audiopipe)); + pipe->index = -1; /* Not configured yet */ + + /* Set up hw capabilities and contraints */ + memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware)); + DE_HWP(("max_channels=%d\n", max_channels)); + pipe->constr.list = channels_list; + pipe->constr.mask = 0; + for (i = 0; channels_list[i] <= max_channels; i++); + pipe->constr.count = i; + if (pipe->hw.channels_max > max_channels) + pipe->hw.channels_max = max_channels; + if (chip->digital_mode == DIGITAL_MODE_ADAT) { + pipe->hw.rate_max = 48000; + pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000; + } + + runtime->hw = pipe->hw; + runtime->private_data = pipe; + runtime->private_free = audiopipe_free; + snd_pcm_set_sync(substream); + + /* Only mono and any even number of channels are allowed */ + if ((err = snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &pipe->constr)) < 0) + return err; + + /* All periods should have the same size */ + if ((err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < 0) + return err; + + /* The hw accesses memory in chunks 32 frames long and they should be + 32-bytes-aligned. It's not a requirement, but it seems that IRQs are + generated with a resolution of 32 frames. Thus we need the following */ + if ((err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + 32)) < 0) + return err; + if ((err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + 32)) < 0) + return err; + + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + hw_rule_sample_rate, chip, + SNDRV_PCM_HW_PARAM_RATE, -1)) < 0) + return err; + + /* Finally allocate a page for the scatter-gather list */ + if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + PAGE_SIZE, &pipe->sgpage)) < 0) { + DE_HWP(("s-g list allocation failed\n")); + return err; + } + + return 0; +} + + + +static int pcm_analog_in_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int err; + + DE_ACT(("pcm_analog_in_open\n")); + if ((err = pcm_open(substream, num_analog_busses_in(chip) - + substream->number)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_capture_channels_by_format, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_capture_format_by_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) + return err; + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; + DE_HWP(("pcm_analog_in_open cs=%d oc=%d r=%d\n", + chip->can_set_rate, atomic_read(&chip->opencount), + chip->sample_rate)); + return 0; +} + + + +static int pcm_analog_out_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int max_channels, err; + +#ifdef ECHOCARD_HAS_VMIXER + max_channels = num_pipes_out(chip); +#else + max_channels = num_analog_busses_out(chip); +#endif + DE_ACT(("pcm_analog_out_open\n")); + if ((err = pcm_open(substream, max_channels - substream->number)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_playback_channels_by_format, + NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) + return err; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_playback_format_by_channels, + NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) + return err; + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; + DE_HWP(("pcm_analog_out_open cs=%d oc=%d r=%d\n", + chip->can_set_rate, atomic_read(&chip->opencount), + chip->sample_rate)); + return 0; +} + + + +#ifdef ECHOCARD_HAS_DIGITAL_IO + +static int pcm_digital_in_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int err, max_channels; + + DE_ACT(("pcm_digital_in_open\n")); + max_channels = num_digital_busses_in(chip) - substream->number; + down(&chip->mode_mutex); + if (chip->digital_mode == DIGITAL_MODE_ADAT) + err = pcm_open(substream, max_channels); + else /* If the card has ADAT, subtract the 6 channels + * that S/PDIF doesn't have + */ + err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT); + + if (err < 0) + goto din_exit; + + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_capture_channels_by_format, NULL, + SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0) + goto din_exit; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_capture_format_by_channels, NULL, + SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0) + goto din_exit; + + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; + +din_exit: + up(&chip->mode_mutex); + return err; +} + + + +#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */ + +static int pcm_digital_out_open(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int err, max_channels; + + DE_ACT(("pcm_digital_out_open\n")); + max_channels = num_digital_busses_out(chip) - substream->number; + down(&chip->mode_mutex); + if (chip->digital_mode == DIGITAL_MODE_ADAT) + err = pcm_open(substream, max_channels); + else /* If the card has ADAT, subtract the 6 channels + * that S/PDIF doesn't have + */ + err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT); + + if (err < 0) + goto dout_exit; + + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_playback_channels_by_format, + NULL, SNDRV_PCM_HW_PARAM_FORMAT, + -1)) < 0) + goto dout_exit; + if ((err = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_playback_format_by_channels, + NULL, SNDRV_PCM_HW_PARAM_CHANNELS, + -1)) < 0) + goto dout_exit; + atomic_inc(&chip->opencount); + if (atomic_read(&chip->opencount) > 1 && chip->rate_set) + chip->can_set_rate=0; +dout_exit: + up(&chip->mode_mutex); + return err; +} + +#endif /* !ECHOCARD_HAS_VMIXER */ + +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +static int pcm_close(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + int oc; + + /* Nothing to do here. Audio is already off and pipe will be + * freed by its callback + */ + DE_ACT(("pcm_close\n")); + + atomic_dec(&chip->opencount); + oc = atomic_read(&chip->opencount); + DE_ACT(("pcm_close oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate, chip->rate_set)); + if (oc < 2) + chip->can_set_rate = 1; + if (oc == 0) + chip->rate_set = 0; + DE_ACT(("pcm_close2 oc=%d cs=%d rs=%d\n", oc, + chip->can_set_rate,chip->rate_set)); + + return 0; +} + + + +/* Channel allocation and scatter-gather list setup */ +static int init_engine(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params, + int pipe_index, int interleave) +{ + struct echoaudio *chip; + int err, per, rest, page, edge, offs; + struct snd_sg_buf *sgbuf; + struct audiopipe *pipe; + + chip = snd_pcm_substream_chip(substream); + pipe = (struct audiopipe *) substream->runtime->private_data; + + /* Sets up che hardware. If it's already initialized, reset and + * redo with the new parameters + */ + spin_lock_irq(&chip->lock); + if (pipe->index >= 0) { + DE_HWP(("hwp_ie free(%d)\n", pipe->index)); + err = free_pipes(chip, pipe); + snd_assert(!err); + chip->substream[pipe->index] = NULL; + } + + err = allocate_pipes(chip, pipe, pipe_index, interleave); + if (err < 0) { + spin_unlock_irq(&chip->lock); + DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n", + pipe_index, err)); + return err; + } + spin_unlock_irq(&chip->lock); + DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index)); + + DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n", + params_buffer_bytes(hw_params), params_periods(hw_params), + params_period_bytes(hw_params))); + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (err < 0) { + snd_printk(KERN_ERR "malloc_pages err=%d\n", err); + spin_lock_irq(&chip->lock); + free_pipes(chip, pipe); + spin_unlock_irq(&chip->lock); + pipe->index = -1; + return err; + } + + sgbuf = snd_pcm_substream_sgbuf(substream); + + DE_HWP(("pcm_hw_params table size=%d pages=%d\n", + sgbuf->size, sgbuf->pages)); + sglist_init(chip, pipe); + edge = PAGE_SIZE; + for (offs = page = per = 0; offs < params_buffer_bytes(hw_params); + per++) { + rest = params_period_bytes(hw_params); + if (offs + rest > params_buffer_bytes(hw_params)) + rest = params_buffer_bytes(hw_params) - offs; + while (rest) { + if (rest <= edge - offs) { + sglist_add_mapping(chip, pipe, + snd_sgbuf_get_addr(sgbuf, offs), + rest); + sglist_add_irq(chip, pipe); + offs += rest; + rest = 0; + } else { + sglist_add_mapping(chip, pipe, + snd_sgbuf_get_addr(sgbuf, offs), + edge - offs); + rest -= edge - offs; + offs = edge; + } + if (offs == edge) { + edge += PAGE_SIZE; + page++; + } + } + } + + /* Close the ring buffer */ + sglist_wrap(chip, pipe); + + /* This stuff is used by the irq handler, so it must be + * initialized before chip->substream + */ + chip->last_period[pipe_index] = 0; + pipe->last_counter = 0; + pipe->position = 0; + smp_wmb(); + chip->substream[pipe_index] = substream; + chip->rate_set = 1; + spin_lock_irq(&chip->lock); + set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den); + spin_unlock_irq(&chip->lock); + DE_HWP(("pcm_hw_params ok\n")); + return 0; +} + + + +static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + + return init_engine(substream, hw_params, px_analog_in(chip) + + substream->number, params_channels(hw_params)); +} + + + +static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + return init_engine(substream, hw_params, substream->number, + params_channels(hw_params)); +} + + + +#ifdef ECHOCARD_HAS_DIGITAL_IO + +static int pcm_digital_in_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + + return init_engine(substream, hw_params, px_digital_in(chip) + + substream->number, params_channels(hw_params)); +} + + + +#ifndef ECHOCARD_HAS_VMIXER /* See the note in snd_echo_new_pcm() */ +static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + + return init_engine(substream, hw_params, px_digital_out(chip) + + substream->number, params_channels(hw_params)); +} +#endif /* !ECHOCARD_HAS_VMIXER */ + +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +static int pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip; + struct audiopipe *pipe; + + chip = snd_pcm_substream_chip(substream); + pipe = (struct audiopipe *) substream->runtime->private_data; + + spin_lock_irq(&chip->lock); + if (pipe->index >= 0) { + DE_HWP(("pcm_hw_free(%d)\n", pipe->index)); + free_pipes(chip, pipe); + chip->substream[pipe->index] = NULL; + pipe->index = -1; + } + spin_unlock_irq(&chip->lock); + + DE_HWP(("pcm_hw_freed\n")); + snd_pcm_lib_free_pages(substream); + return 0; +} + + + +static int pcm_prepare(struct snd_pcm_substream *substream) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct audioformat format; + int pipe_index = ((struct audiopipe *)runtime->private_data)->index; + + DE_HWP(("Prepare rate=%d format=%d channels=%d\n", + runtime->rate, runtime->format, runtime->channels)); + format.interleave = runtime->channels; + format.data_are_bigendian = 0; + format.mono_to_stereo = 0; + switch (runtime->format) { + case SNDRV_PCM_FORMAT_U8: + format.bits_per_sample = 8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + format.bits_per_sample = 16; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + format.bits_per_sample = 24; + break; + case SNDRV_PCM_FORMAT_S32_BE: + format.data_are_bigendian = 1; + case SNDRV_PCM_FORMAT_S32_LE: + format.bits_per_sample = 32; + break; + default: + DE_HWP(("Prepare error: unsupported format %d\n", + runtime->format)); + return -EINVAL; + } + + snd_assert(pipe_index < px_num(chip), return -EINVAL); + snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL); + set_audio_format(chip, pipe_index, &format); + return 0; +} + + + +static int pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct echoaudio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct audiopipe *pipe = runtime->private_data; + int i, err; + u32 channelmask = 0; + struct list_head *pos; + struct snd_pcm_substream *s; + + snd_pcm_group_for_each(pos, substream) { + s = snd_pcm_group_substream_entry(pos); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (s == chip->substream[i]) { + channelmask |= 1 << i; + snd_pcm_trigger_done(s, substream); + } + } + } + + spin_lock(&chip->lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + DE_ACT(("pcm_trigger start\n")); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (channelmask & (1 << i)) { + pipe = chip->substream[i]->runtime->private_data; + switch (pipe->state) { + case PIPE_STATE_STOPPED: + chip->last_period[i] = 0; + pipe->last_counter = 0; + pipe->position = 0; + *pipe->dma_counter = 0; + case PIPE_STATE_PAUSED: + pipe->state = PIPE_STATE_STARTED; + break; + case PIPE_STATE_STARTED: + break; + } + } + } + err = start_transport(chip, channelmask, + chip->pipe_cyclic_mask); + break; + case SNDRV_PCM_TRIGGER_STOP: + DE_ACT(("pcm_trigger stop\n")); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (channelmask & (1 << i)) { + pipe = chip->substream[i]->runtime->private_data; + pipe->state = PIPE_STATE_STOPPED; + } + } + err = stop_transport(chip, channelmask); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + DE_ACT(("pcm_trigger pause\n")); + for (i = 0; i < DSP_MAXPIPES; i++) { + if (channelmask & (1 << i)) { + pipe = chip->substream[i]->runtime->private_data; + pipe->state = PIPE_STATE_PAUSED; + } + } + err = pause_transport(chip, channelmask); + break; + default: + err = -EINVAL; + } + spin_unlock(&chip->lock); + return err; +} + + + +static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct audiopipe *pipe = runtime->private_data; + size_t cnt, bufsize, pos; + + cnt = le32_to_cpu(*pipe->dma_counter); + pipe->position += cnt - pipe->last_counter; + pipe->last_counter = cnt; + bufsize = substream->runtime->buffer_size; + pos = bytes_to_frames(substream->runtime, pipe->position); + + while (pos >= bufsize) { + pipe->position -= frames_to_bytes(substream->runtime, bufsize); + pos -= bufsize; + } + return pos; +} + + + +/* pcm *_ops structures */ +static struct snd_pcm_ops analog_playback_ops = { + .open = pcm_analog_out_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_analog_out_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +static struct snd_pcm_ops analog_capture_ops = { + .open = pcm_analog_in_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_analog_in_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +#ifdef ECHOCARD_HAS_DIGITAL_IO +#ifndef ECHOCARD_HAS_VMIXER +static struct snd_pcm_ops digital_playback_ops = { + .open = pcm_digital_out_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_digital_out_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +#endif /* !ECHOCARD_HAS_VMIXER */ +static struct snd_pcm_ops digital_capture_ops = { + .open = pcm_digital_in_open, + .close = pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = pcm_digital_in_hw_params, + .hw_free = pcm_hw_free, + .prepare = pcm_prepare, + .trigger = pcm_trigger, + .pointer = pcm_pointer, + .page = snd_pcm_sgbuf_ops_page, +}; +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +/* Preallocate memory only for the first substream because it's the most + * used one + */ +static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev) +{ + struct snd_pcm_substream *ss; + int stream, err; + + for (stream = 0; stream < 2; stream++) + for (ss = pcm->streams[stream].substream; ss; ss = ss->next) { + err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG, + dev, + ss->number ? 0 : 128<<10, + 256<<10); + if (err < 0) + return err; + } + return 0; +} + + + +/*<--snd_echo_probe() */ +static int __devinit snd_echo_new_pcm(struct echoaudio *chip) +{ + struct snd_pcm *pcm; + int err; + +#ifdef ECHOCARD_HAS_VMIXER + /* This card has a Vmixer, that is there is no direct mapping from PCM + streams to physical outputs. The user can mix the streams as he wishes + via control interface and it's possible to send any stream to any + output, thus it makes no sense to keep analog and digital outputs + separated */ + + /* PCM#0 Virtual outputs and analog inputs */ + if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip), + num_analog_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->analog_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Analog PCM ok\n")); + +#ifdef ECHOCARD_HAS_DIGITAL_IO + /* PCM#1 Digital inputs, no outputs */ + if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0, + num_digital_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->digital_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Digital PCM ok\n")); +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + +#else /* ECHOCARD_HAS_VMIXER */ + + /* The card can manage substreams formed by analog and digital channels + at the same time, but I prefer to keep analog and digital channels + separated, because that mixed thing is confusing and useless. So we + register two PCM devices: */ + + /* PCM#0 Analog i/o */ + if ((err = snd_pcm_new(chip->card, "Analog PCM", 0, + num_analog_busses_out(chip), + num_analog_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->analog_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Analog PCM ok\n")); + +#ifdef ECHOCARD_HAS_DIGITAL_IO + /* PCM#1 Digital i/o */ + if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, + num_digital_busses_out(chip), + num_digital_busses_in(chip), &pcm)) < 0) + return err; + pcm->private_data = chip; + chip->digital_pcm = pcm; + strcpy(pcm->name, chip->card->shortname); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops); + if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0) + return err; + DE_INIT(("Digital PCM ok\n")); +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + +#endif /* ECHOCARD_HAS_VMIXER */ + + return 0; +} + + + + +/****************************************************************************** + Control interface +******************************************************************************/ + +/******************* PCM output volume *******************/ +static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = num_busses_out(chip); + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = ECHOGAIN_MAXOUT; + return 0; +} + +static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_busses_out(chip); c++) + ucontrol->value.integer.value[c] = chip->output_gain[c]; + return 0; +} + +static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, changed, gain; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_busses_out(chip); c++) { + gain = ucontrol->value.integer.value[c]; + /* Ignore out of range values */ + if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) + continue; + if (chip->output_gain[c] != gain) { + set_output_gain(chip, c, gain); + changed = 1; + } + } + if (changed) + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + return changed; +} + +#ifdef ECHOCARD_HAS_VMIXER +/* On Vmixer cards this one controls the line-out volume */ +static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = { + .name = "Line Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_output_gain_info, + .get = snd_echo_output_gain_get, + .put = snd_echo_output_gain_put, +}; +#else +static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = { + .name = "PCM Playback Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_output_gain_info, + .get = snd_echo_output_gain_get, + .put = snd_echo_output_gain_put, +}; +#endif + + + +#ifdef ECHOCARD_HAS_INPUT_GAIN + +/******************* Analog input volume *******************/ +static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = num_analog_busses_in(chip); + uinfo->value.integer.min = ECHOGAIN_MININP; + uinfo->value.integer.max = ECHOGAIN_MAXINP; + return 0; +} + +static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_analog_busses_in(chip); c++) + ucontrol->value.integer.value[c] = chip->input_gain[c]; + return 0; +} + +static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, gain, changed; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_analog_busses_in(chip); c++) { + gain = ucontrol->value.integer.value[c]; + /* Ignore out of range values */ + if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP) + continue; + if (chip->input_gain[c] != gain) { + set_input_gain(chip, c, gain); + changed = 1; + } + } + if (changed) + update_input_line_level(chip); + spin_unlock_irq(&chip->lock); + return changed; +} + +static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = { + .name = "Line Capture Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_input_gain_info, + .get = snd_echo_input_gain_get, + .put = snd_echo_input_gain_put, +}; + +#endif /* ECHOCARD_HAS_INPUT_GAIN */ + + + +#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL + +/************ Analog output nominal level (+4dBu / -10dBV) ***************/ +static int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = num_analog_busses_out(chip); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_analog_busses_out(chip); c++) + ucontrol->value.integer.value[c] = chip->nominal_level[c]; + return 0; +} + +static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, changed; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_analog_busses_out(chip); c++) { + if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) { + set_nominal_level(chip, c, + ucontrol->value.integer.value[c]); + changed = 1; + } + } + if (changed) + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + return changed; +} + +static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = { + .name = "Line Playback Switch (-10dBV)", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_output_nominal_info, + .get = snd_echo_output_nominal_get, + .put = snd_echo_output_nominal_put, +}; + +#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */ + + + +#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL + +/*************** Analog input nominal level (+4dBu / -10dBV) ***************/ +static int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = num_analog_busses_in(chip); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c; + + chip = snd_kcontrol_chip(kcontrol); + for (c = 0; c < num_analog_busses_in(chip); c++) + ucontrol->value.integer.value[c] = + chip->nominal_level[bx_analog_in(chip) + c]; + return 0; +} + +static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int c, changed; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + for (c = 0; c < num_analog_busses_in(chip); c++) { + if (chip->nominal_level[bx_analog_in(chip) + c] != + ucontrol->value.integer.value[c]) { + set_nominal_level(chip, bx_analog_in(chip) + c, + ucontrol->value.integer.value[c]); + changed = 1; + } + } + if (changed) + update_output_line_level(chip); /* "Output" is not a mistake + * here. + */ + spin_unlock_irq(&chip->lock); + return changed; +} + +static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = { + .name = "Line Capture Switch (-10dBV)", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_input_nominal_info, + .get = snd_echo_input_nominal_get, + .put = snd_echo_input_nominal_put, +}; + +#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */ + + + +#ifdef ECHOCARD_HAS_MONITOR + +/******************* Monitor mixer *******************/ +static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = ECHOGAIN_MAXOUT; + uinfo->dimen.d[0] = num_busses_out(chip); + uinfo->dimen.d[1] = num_busses_in(chip); + return 0; +} + +static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = + chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)] + [ucontrol->id.index % num_busses_in(chip)]; + return 0; +} + +static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int changed, gain; + short out, in; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + out = ucontrol->id.index / num_busses_in(chip); + in = ucontrol->id.index % num_busses_in(chip); + gain = ucontrol->value.integer.value[0]; + if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) + return -EINVAL; + if (chip->monitor_gain[out][in] != gain) { + spin_lock_irq(&chip->lock); + set_monitor_gain(chip, out, in, gain); + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + changed = 1; + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = { + .name = "Monitor Mixer Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_mixer_info, + .get = snd_echo_mixer_get, + .put = snd_echo_mixer_put, +}; + +#endif /* ECHOCARD_HAS_MONITOR */ + + + +#ifdef ECHOCARD_HAS_VMIXER + +/******************* Vmixer *******************/ +static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = ECHOGAIN_MAXOUT; + uinfo->dimen.d[0] = num_busses_out(chip); + uinfo->dimen.d[1] = num_pipes_out(chip); + return 0; +} + +static int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = + chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)] + [ucontrol->id.index % num_pipes_out(chip)]; + return 0; +} + +static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int gain, changed; + short vch, out; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + out = ucontrol->id.index / num_pipes_out(chip); + vch = ucontrol->id.index % num_pipes_out(chip); + gain = ucontrol->value.integer.value[0]; + if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) + return -EINVAL; + if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) { + spin_lock_irq(&chip->lock); + set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]); + update_vmixer_level(chip); + spin_unlock_irq(&chip->lock); + changed = 1; + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = { + .name = "VMixer Volume", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .info = snd_echo_vmixer_info, + .get = snd_echo_vmixer_get, + .put = snd_echo_vmixer_put, +}; + +#endif /* ECHOCARD_HAS_VMIXER */ + + + +#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH + +/******************* Digital mode switch *******************/ +static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *names[4] = { + "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical", + "S/PDIF Cdrom" + }; + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = chip->num_digital_modes; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= chip->num_digital_modes) + uinfo->value.enumerated.item = chip->num_digital_modes - 1; + strcpy(uinfo->value.enumerated.name, names[ + chip->digital_mode_list[uinfo->value.enumerated.item]]); + return 0; +} + +static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int i, mode; + + chip = snd_kcontrol_chip(kcontrol); + mode = chip->digital_mode; + for (i = chip->num_digital_modes - 1; i >= 0; i--) + if (mode == chip->digital_mode_list[i]) { + ucontrol->value.enumerated.item[0] = i; + break; + } + return 0; +} + +static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int changed; + unsigned short emode, dmode; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + + emode = ucontrol->value.enumerated.item[0]; + if (emode >= chip->num_digital_modes) + return -EINVAL; + dmode = chip->digital_mode_list[emode]; + + if (dmode != chip->digital_mode) { + /* mode_mutex is required to make this operation atomic wrt + pcm_digital_*_open() and set_input_clock() functions. */ + down(&chip->mode_mutex); + + /* Do not allow the user to change the digital mode when a pcm + device is open because it also changes the number of channels + and the allowed sample rates */ + if (atomic_read(&chip->opencount)) { + changed = -EAGAIN; + } else { + changed = set_digital_mode(chip, dmode); + /* If we had to change the clock source, report it */ + if (changed > 0 && chip->clock_src_ctl) { + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->clock_src_ctl->id); + DE_ACT(("SDM() =%d\n", changed)); + } + if (changed >= 0) + changed = 1; /* No errors */ + } + up(&chip->mode_mutex); + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = { + .name = "Digital mode Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_digital_mode_info, + .get = snd_echo_digital_mode_get, + .put = snd_echo_digital_mode_put, +}; + +#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */ + + + +#ifdef ECHOCARD_HAS_DIGITAL_IO + +/******************* S/PDIF mode switch *******************/ +static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *names[2] = {"Consumer", "Professional"}; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = 2; + uinfo->count = 1; + if (uinfo->value.enumerated.item) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + names[uinfo->value.enumerated.item]); + return 0; +} + +static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.enumerated.item[0] = !!chip->professional_spdif; + return 0; +} + +static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int mode; + + chip = snd_kcontrol_chip(kcontrol); + mode = !!ucontrol->value.enumerated.item[0]; + if (mode != chip->professional_spdif) { + spin_lock_irq(&chip->lock); + set_professional_spdif(chip, mode); + spin_unlock_irq(&chip->lock); + return 1; + } + return 0; +} + +static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = { + .name = "S/PDIF mode Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_spdif_mode_info, + .get = snd_echo_spdif_mode_get, + .put = snd_echo_spdif_mode_put, +}; + +#endif /* ECHOCARD_HAS_DIGITAL_IO */ + + + +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + +/******************* Select input clock source *******************/ +static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static char *names[8] = { + "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync", + "ESync96", "MTC" + }; + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = chip->num_clock_sources; + uinfo->count = 1; + if (uinfo->value.enumerated.item >= chip->num_clock_sources) + uinfo->value.enumerated.item = chip->num_clock_sources - 1; + strcpy(uinfo->value.enumerated.name, names[ + chip->clock_source_list[uinfo->value.enumerated.item]]); + return 0; +} + +static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int i, clock; + + chip = snd_kcontrol_chip(kcontrol); + clock = chip->input_clock; + + for (i = 0; i < chip->num_clock_sources; i++) + if (clock == chip->clock_source_list[i]) + ucontrol->value.enumerated.item[0] = i; + + return 0; +} + +static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int changed; + unsigned int eclock, dclock; + + changed = 0; + chip = snd_kcontrol_chip(kcontrol); + eclock = ucontrol->value.enumerated.item[0]; + if (eclock >= chip->input_clock_types) + return -EINVAL; + dclock = chip->clock_source_list[eclock]; + if (chip->input_clock != dclock) { + down(&chip->mode_mutex); + spin_lock_irq(&chip->lock); + if ((changed = set_input_clock(chip, dclock)) == 0) + changed = 1; /* no errors */ + spin_unlock_irq(&chip->lock); + up(&chip->mode_mutex); + } + + if (changed < 0) + DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed)); + + return changed; +} + +static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = { + .name = "Sample Clock Source", + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .info = snd_echo_clock_source_info, + .get = snd_echo_clock_source_get, + .put = snd_echo_clock_source_put, +}; + +#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */ + + + +#ifdef ECHOCARD_HAS_PHANTOM_POWER + +/******************* Phantom power switch *******************/ +static int snd_echo_phantom_power_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = chip->phantom_power; + return 0; +} + +static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + int power, changed = 0; + + power = !!ucontrol->value.integer.value[0]; + if (chip->phantom_power != power) { + spin_lock_irq(&chip->lock); + changed = set_phantom_power(chip, power); + spin_unlock_irq(&chip->lock); + if (changed == 0) + changed = 1; /* no errors */ + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = { + .name = "Phantom power Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_phantom_power_info, + .get = snd_echo_phantom_power_get, + .put = snd_echo_phantom_power_put, +}; + +#endif /* ECHOCARD_HAS_PHANTOM_POWER */ + + + +#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE + +/******************* Digital input automute switch *******************/ +static int snd_echo_automute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_automute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = chip->digital_in_automute; + return 0; +} + +static int snd_echo_automute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + int automute, changed = 0; + + automute = !!ucontrol->value.integer.value[0]; + if (chip->digital_in_automute != automute) { + spin_lock_irq(&chip->lock); + changed = set_input_auto_mute(chip, automute); + spin_unlock_irq(&chip->lock); + if (changed == 0) + changed = 1; /* no errors */ + } + return changed; +} + +static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = { + .name = "Digital Capture Switch (automute)", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .info = snd_echo_automute_info, + .get = snd_echo_automute_get, + .put = snd_echo_automute_put, +}; + +#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */ + + + +/******************* VU-meters switch *******************/ +static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + spin_lock_irq(&chip->lock); + set_meters_on(chip, ucontrol->value.integer.value[0]); + spin_unlock_irq(&chip->lock); + return 1; +} + +static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = { + .name = "VU-meters Switch", + .iface = SNDRV_CTL_ELEM_IFACE_CARD, + .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_echo_vumeters_switch_info, + .put = snd_echo_vumeters_switch_put, +}; + + + +/***** Read VU-meters (input, output, analog and digital together) *****/ +static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 96; + uinfo->value.integer.min = ECHOGAIN_MINOUT; + uinfo->value.integer.max = 0; +#ifdef ECHOCARD_HAS_VMIXER + uinfo->dimen.d[0] = 3; /* Out, In, Virt */ +#else + uinfo->dimen.d[0] = 2; /* Out, In */ +#endif + uinfo->dimen.d[1] = 16; /* 16 channels */ + uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */ + return 0; +} + +static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + get_audio_meters(chip, ucontrol->value.integer.value); + return 0; +} + +static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = { + .name = "VU-meters", + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_echo_vumeters_info, + .get = snd_echo_vumeters_get, +}; + + + +/*** Channels info - it exports informations about the number of channels ***/ +static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct echoaudio *chip; + + chip = snd_kcontrol_chip(kcontrol); + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 6; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER; + return 0; +} + +static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct echoaudio *chip; + int detected, clocks, bit, src; + + chip = snd_kcontrol_chip(kcontrol); + ucontrol->value.integer.value[0] = num_busses_in(chip); + ucontrol->value.integer.value[1] = num_analog_busses_in(chip); + ucontrol->value.integer.value[2] = num_busses_out(chip); + ucontrol->value.integer.value[3] = num_analog_busses_out(chip); + ucontrol->value.integer.value[4] = num_pipes_out(chip); + + /* Compute the bitmask of the currently valid input clocks */ + detected = detect_input_clocks(chip); + clocks = 0; + src = chip->num_clock_sources - 1; + for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--) + if (detected & (1 << bit)) + for (; src >= 0; src--) + if (bit == chip->clock_source_list[src]) { + clocks |= 1 << src; + break; + } + ucontrol->value.integer.value[5] = clocks; + + return 0; +} + +static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = { + .name = "Channels info", + .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = snd_echo_channels_info_info, + .get = snd_echo_channels_info_get, +}; + + + + +/****************************************************************************** + IRQ Handler +******************************************************************************/ + +static irqreturn_t snd_echo_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct echoaudio *chip = dev_id; + struct snd_pcm_substream *substream; + int period, ss, st; + + spin_lock(&chip->lock); + st = service_irq(chip); + if (st < 0) { + spin_unlock(&chip->lock); + return IRQ_NONE; + } + /* The hardware doesn't tell us which substream caused the irq, + thus we have to check all running substreams. */ + for (ss = 0; ss < DSP_MAXPIPES; ss++) { + if ((substream = chip->substream[ss])) { + period = pcm_pointer(substream) / + substream->runtime->period_size; + if (period != chip->last_period[ss]) { + chip->last_period[ss] = period; + spin_unlock(&chip->lock); + snd_pcm_period_elapsed(substream); + spin_lock(&chip->lock); + } + } + } + spin_unlock(&chip->lock); + +#ifdef ECHOCARD_HAS_MIDI + if (st > 0 && chip->midi_in) { + snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st); + DE_MID(("rawmidi_iread=%d\n", st)); + } +#endif + return IRQ_HANDLED; +} + + + + +/****************************************************************************** + Module construction / destruction +******************************************************************************/ + +static int snd_echo_free(struct echoaudio *chip) +{ + DE_INIT(("Stop DSP...\n")); + if (chip->comm_page) { + rest_in_peace(chip); + snd_dma_free_pages(&chip->commpage_dma_buf); + } + DE_INIT(("Stopped.\n")); + + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); + + if (chip->dsp_registers) + iounmap(chip->dsp_registers); + + if (chip->iores) + release_and_free_resource(chip->iores); + + DE_INIT(("MMIO freed.\n")); + + pci_disable_device(chip->pci); + + /* release chip data */ + kfree(chip); + DE_INIT(("Chip freed.\n")); + return 0; +} + + + +static int snd_echo_dev_free(struct snd_device *device) +{ + struct echoaudio *chip = device->device_data; + + DE_INIT(("snd_echo_dev_free()...\n")); + return snd_echo_free(chip); +} + + + +/* <--snd_echo_probe() */ +static __devinit int snd_echo_create(struct snd_card *card, + struct pci_dev *pci, + struct echoaudio **rchip) +{ + struct echoaudio *chip; + int err; + size_t sz; + static struct snd_device_ops ops = { + .dev_free = snd_echo_dev_free, + }; + + *rchip = NULL; + + pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0); + + if ((err = pci_enable_device(pci)) < 0) + return err; + pci_set_master(pci); + + /* allocate a chip-specific data */ + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + pci_disable_device(pci); + return -ENOMEM; + } + DE_INIT(("chip=%p\n", chip)); + + spin_lock_init(&chip->lock); + chip->card = card; + chip->pci = pci; + chip->irq = -1; + + /* PCI resource allocation */ + chip->dsp_registers_phys = pci_resource_start(pci, 0); + sz = pci_resource_len(pci, 0); + if (sz > PAGE_SIZE) + sz = PAGE_SIZE; /* We map only the required part */ + + if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz, + ECHOCARD_NAME)) == NULL) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot get memory region\n"); + return -EBUSY; + } + chip->dsp_registers = (volatile u32 __iomem *) + ioremap_nocache(chip->dsp_registers_phys, sz); + + if (request_irq(pci->irq, snd_echo_interrupt, IRQF_DISABLED | IRQF_SHARED, + ECHOCARD_NAME, (void *)chip)) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot grab irq\n"); + return -EBUSY; + } + chip->irq = pci->irq; + DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n", + chip->pci, chip->irq, chip->pci->subsystem_device)); + + /* Create the DSP comm page - this is the area of memory used for most + of the communication with the DSP, which accesses it via bus mastering */ + if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), + sizeof(struct comm_page), + &chip->commpage_dma_buf) < 0) { + snd_echo_free(chip); + snd_printk(KERN_ERR "cannot allocate the comm page\n"); + return -ENOMEM; + } + chip->comm_page_phys = chip->commpage_dma_buf.addr; + chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area; + + err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device); + if (err) { + DE_INIT(("init_hw err=%d\n", err)); + snd_echo_free(chip); + return err; + } + DE_INIT(("Card init OK\n")); + + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { + snd_echo_free(chip); + return err; + } + atomic_set(&chip->opencount, 0); + init_MUTEX(&chip->mode_mutex); + chip->can_set_rate = 1; + *rchip = chip; + /* Init done ! */ + return 0; +} + + + +/* constructor */ +static int __devinit snd_echo_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + static int dev; + struct snd_card *card; + struct echoaudio *chip; + char *dsp; + int i, err; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + DE_INIT(("Echoaudio driver starting...\n")); + i = 0; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + if ((err = snd_echo_create(card, pci, &chip)) < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "Echo_" ECHOCARD_NAME); + strcpy(card->shortname, chip->card_name); + + dsp = "56301"; + if (pci_id->device == 0x3410) + dsp = "56361"; + + sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i", + card->shortname, pci_id->subdevice & 0x000f, dsp, + chip->dsp_registers_phys, chip->irq); + + if ((err = snd_echo_new_pcm(chip)) < 0) { + snd_printk(KERN_ERR "new pcm error %d\n", err); + snd_card_free(card); + return err; + } + +#ifdef ECHOCARD_HAS_MIDI + if (chip->has_midi) { /* Some Mia's do not have midi */ + if ((err = snd_echo_midi_create(card, chip)) < 0) { + snd_printk(KERN_ERR "new midi error %d\n", err); + snd_card_free(card); + return err; + } + } +#endif + +#ifdef ECHOCARD_HAS_VMIXER + snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip); + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0) + goto ctl_error; + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0) + goto ctl_error; +#else + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_INPUT_GAIN + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL + if (!chip->hasnt_input_nominal_level) + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0) + goto ctl_error; +#endif + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0) + goto ctl_error; + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0) + goto ctl_error; + +#ifdef ECHOCARD_HAS_MONITOR + snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip); + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0) + goto ctl_error; +#endif + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0) + goto ctl_error; + +#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH + /* Creates a list of available digital modes */ + chip->num_digital_modes = 0; + for (i = 0; i < 6; i++) + if (chip->digital_modes & (1 << i)) + chip->digital_mode_list[chip->num_digital_modes++] = i; + + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0) + goto ctl_error; +#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */ + +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + /* Creates a list of available clock sources */ + chip->num_clock_sources = 0; + for (i = 0; i < 10; i++) + if (chip->input_clock_types & (1 << i)) + chip->clock_source_list[chip->num_clock_sources++] = i; + + if (chip->num_clock_sources > 1) { + chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip); + if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0) + goto ctl_error; + } +#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */ + +#ifdef ECHOCARD_HAS_DIGITAL_IO + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0) + goto ctl_error; +#endif + +#ifdef ECHOCARD_HAS_PHANTOM_POWER + if (chip->has_phantom_power) + if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0) + goto ctl_error; +#endif + + if ((err = snd_card_register(card)) < 0) { + snd_card_free(card); + goto ctl_error; + } + snd_printk(KERN_INFO "Card registered: %s\n", card->longname); + + pci_set_drvdata(pci, chip); + dev++; + return 0; + +ctl_error: + snd_printk(KERN_ERR "new control error %d\n", err); + snd_card_free(card); + return err; +} + + + +static void __devexit snd_echo_remove(struct pci_dev *pci) +{ + struct echoaudio *chip; + + chip = pci_get_drvdata(pci); + if (chip) + snd_card_free(chip->card); + pci_set_drvdata(pci, NULL); +} + + + +/****************************************************************************** + Everything starts and ends here +******************************************************************************/ + +/* pci_driver definition */ +static struct pci_driver driver = { + .name = "Echoaudio " ECHOCARD_NAME, + .id_table = snd_echo_ids, + .probe = snd_echo_probe, + .remove = __devexit_p(snd_echo_remove), +}; + + + +/* initialization of the module */ +static int __init alsa_card_echo_init(void) +{ + return pci_register_driver(&driver); +} + + + +/* clean up the module */ +static void __exit alsa_card_echo_exit(void) +{ + pci_unregister_driver(&driver); +} + + +module_init(alsa_card_echo_init) +module_exit(alsa_card_echo_exit) diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h new file mode 100644 index 0000000..7e88c96 --- /dev/null +++ b/sound/pci/echoaudio/echoaudio.h @@ -0,0 +1,590 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + **************************************************************************** + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + + **************************************************************************** + + + Here's a block diagram of how most of the cards work: + + +-----------+ + record | |<-------------------- Inputs + <-------| | | + PCI | Transport | | + bus | engine | \|/ + ------->| | +-------+ + play | |--->|monitor|-------> Outputs + +-----------+ | mixer | + +-------+ + + The lines going to and from the PCI bus represent "pipes". A pipe performs + audio transport - moving audio data to and from buffers on the host via + bus mastering. + + The inputs and outputs on the right represent input and output "busses." + A bus is a physical, real connection to the outside world. An example + of a bus would be the 1/4" analog connectors on the back of Layla or + an RCA S/PDIF connector. + + For most cards, there is a one-to-one correspondence between outputs + and busses; that is, each individual pipe is hard-wired to a single bus. + + Cards that work this way are Darla20, Gina20, Layla20, Darla24, Gina24, + Layla24, Mona, and Indigo. + + + Mia has a feature called "virtual outputs." + + + +-----------+ + record | |<----------------------------- Inputs + <-------| | | + PCI | Transport | | + bus | engine | \|/ + ------->| | +------+ +-------+ + play | |-->|vmixer|-->|monitor|-------> Outputs + +-----------+ +------+ | mixer | + +-------+ + + + Obviously, the difference here is the box labeled "vmixer." Vmixer is + short for "virtual output mixer." For Mia, pipes are *not* hard-wired + to a single bus; the vmixer lets you mix any pipe to any bus in any + combination. + + Note, however, that the left-hand side of the diagram is unchanged. + Transport works exactly the same way - the difference is in the mixer stage. + + + Pipes and busses are numbered starting at zero. + + + + Pipe index + ========== + + A number of calls in CEchoGals refer to a "pipe index". A pipe index is + a unique number for a pipe that unambiguously refers to a playback or record + pipe. Pipe indices are numbered starting with analog outputs, followed by + digital outputs, then analog inputs, then digital inputs. + + Take Gina24 as an example: + + Pipe index + + 0-7 Analog outputs (0 .. FirstDigitalBusOut-1) + 8-15 Digital outputs (FirstDigitalBusOut .. NumBussesOut-1) + 16-17 Analog inputs + 18-25 Digital inputs + + + You get the pipe index by calling CEchoGals::OpenAudio; the other transport + functions take the pipe index as a parameter. If you need a pipe index for + some other reason, use the handy Makepipe_index method. + + + Some calls take a CChannelMask parameter; CChannelMask is a handy way to + group pipe indices. + + + + Digital mode switch + =================== + + Some cards (right now, Gina24, Layla24, and Mona) have a Digital Mode Switch + or DMS. Cards with a DMS can be set to one of three mutually exclusive + digital modes: S/PDIF RCA, S/PDIF optical, or ADAT optical. + + This may create some confusion since ADAT optical is 8 channels wide and + S/PDIF is only two channels wide. Gina24, Layla24, and Mona handle this + by acting as if they always have 8 digital outs and ins. If you are in + either S/PDIF mode, the last 6 channels don't do anything - data sent + out these channels is thrown away and you will always record zeros. + + Note that with Gina24, Layla24, and Mona, sample rates above 50 kHz are + only available if you have the card configured for S/PDIF optical or S/PDIF + RCA. + + + + Double speed mode + ================= + + Some of the cards support 88.2 kHz and 96 kHz sampling (Darla24, Gina24, + Layla24, Mona, Mia, and Indigo). For these cards, the driver sometimes has + to worry about "double speed mode"; double speed mode applies whenever the + sampling rate is above 50 kHz. + + For instance, Mona and Layla24 support word clock sync. However, they + actually support two different word clock modes - single speed (below + 50 kHz) and double speed (above 50 kHz). The hardware detects if a single + or double speed word clock signal is present; the generic code uses that + information to determine which mode to use. + + The generic code takes care of all this for you. +*/ + + +#ifndef _ECHOAUDIO_H_ +#define _ECHOAUDIO_H_ + + +#define TRUE 1 +#define FALSE 0 + +#include "echoaudio_dsp.h" + + + +/*********************************************************************** + + PCI configuration space + +***********************************************************************/ + +/* + * PCI vendor ID and device IDs for the hardware + */ +#define VENDOR_ID 0x1057 +#define DEVICE_ID_56301 0x1801 +#define DEVICE_ID_56361 0x3410 +#define SUBVENDOR_ID 0xECC0 + + +/* + * Valid Echo PCI subsystem card IDs + */ +#define DARLA20 0x0010 +#define GINA20 0x0020 +#define LAYLA20 0x0030 +#define DARLA24 0x0040 +#define GINA24 0x0050 +#define LAYLA24 0x0060 +#define MONA 0x0070 +#define MIA 0x0080 +#define INDIGO 0x0090 +#define INDIGO_IO 0x00a0 +#define INDIGO_DJ 0x00b0 +#define ECHO3G 0x0100 + + +/************************************************************************ + + Array sizes and so forth + +***********************************************************************/ + +/* + * Sizes + */ +#define ECHO_MAXAUDIOINPUTS 32 /* Max audio input channels */ +#define ECHO_MAXAUDIOOUTPUTS 32 /* Max audio output channels */ +#define ECHO_MAXAUDIOPIPES 32 /* Max number of input and output + * pipes */ +#define E3G_MAX_OUTPUTS 16 +#define ECHO_MAXMIDIJACKS 1 /* Max MIDI ports */ +#define ECHO_MIDI_QUEUE_SZ 512 /* Max MIDI input queue entries */ +#define ECHO_MTC_QUEUE_SZ 32 /* Max MIDI time code input queue + * entries */ + +/* + * MIDI activity indicator timeout + */ +#define MIDI_ACTIVITY_TIMEOUT_USEC 200000 + + +/**************************************************************************** + + Clocks + +*****************************************************************************/ + +/* + * Clock numbers + */ +#define ECHO_CLOCK_INTERNAL 0 +#define ECHO_CLOCK_WORD 1 +#define ECHO_CLOCK_SUPER 2 +#define ECHO_CLOCK_SPDIF 3 +#define ECHO_CLOCK_ADAT 4 +#define ECHO_CLOCK_ESYNC 5 +#define ECHO_CLOCK_ESYNC96 6 +#define ECHO_CLOCK_MTC 7 +#define ECHO_CLOCK_NUMBER 8 +#define ECHO_CLOCKS 0xffff + +/* + * Clock bit numbers - used to report capabilities and whatever clocks + * are being detected dynamically. + */ +#define ECHO_CLOCK_BIT_INTERNAL (1 << ECHO_CLOCK_INTERNAL) +#define ECHO_CLOCK_BIT_WORD (1 << ECHO_CLOCK_WORD) +#define ECHO_CLOCK_BIT_SUPER (1 << ECHO_CLOCK_SUPER) +#define ECHO_CLOCK_BIT_SPDIF (1 << ECHO_CLOCK_SPDIF) +#define ECHO_CLOCK_BIT_ADAT (1 << ECHO_CLOCK_ADAT) +#define ECHO_CLOCK_BIT_ESYNC (1 << ECHO_CLOCK_ESYNC) +#define ECHO_CLOCK_BIT_ESYNC96 (1 << ECHO_CLOCK_ESYNC96) +#define ECHO_CLOCK_BIT_MTC (1<comm_page->handshake = 0; +} + +static inline u32 get_dsp_register(struct echoaudio *chip, u32 index) +{ + return readl(&chip->dsp_registers[index]); +} + +static inline void set_dsp_register(struct echoaudio *chip, u32 index, + u32 value) +{ + writel(value, &chip->dsp_registers[index]); +} + + +/* Pipe and bus indexes. PX_* and BX_* are defined as chip->px_* and chip->bx_* +for 3G cards because they depend on the external box. They are integer +constants for all other cards. +Never use those defines directly, use the following functions instead. */ + +static inline int px_digital_out(const struct echoaudio *chip) +{ + return PX_DIGITAL_OUT; +} + +static inline int px_analog_in(const struct echoaudio *chip) +{ + return PX_ANALOG_IN; +} + +static inline int px_digital_in(const struct echoaudio *chip) +{ + return PX_DIGITAL_IN; +} + +static inline int px_num(const struct echoaudio *chip) +{ + return PX_NUM; +} + +static inline int bx_digital_out(const struct echoaudio *chip) +{ + return BX_DIGITAL_OUT; +} + +static inline int bx_analog_in(const struct echoaudio *chip) +{ + return BX_ANALOG_IN; +} + +static inline int bx_digital_in(const struct echoaudio *chip) +{ + return BX_DIGITAL_IN; +} + +static inline int bx_num(const struct echoaudio *chip) +{ + return BX_NUM; +} + +static inline int num_pipes_out(const struct echoaudio *chip) +{ + return px_analog_in(chip); +} + +static inline int num_pipes_in(const struct echoaudio *chip) +{ + return px_num(chip) - px_analog_in(chip); +} + +static inline int num_busses_out(const struct echoaudio *chip) +{ + return bx_analog_in(chip); +} + +static inline int num_busses_in(const struct echoaudio *chip) +{ + return bx_num(chip) - bx_analog_in(chip); +} + +static inline int num_analog_busses_out(const struct echoaudio *chip) +{ + return bx_digital_out(chip); +} + +static inline int num_analog_busses_in(const struct echoaudio *chip) +{ + return bx_digital_in(chip) - bx_analog_in(chip); +} + +static inline int num_digital_busses_out(const struct echoaudio *chip) +{ + return num_busses_out(chip) - num_analog_busses_out(chip); +} + +static inline int num_digital_busses_in(const struct echoaudio *chip) +{ + return num_busses_in(chip) - num_analog_busses_in(chip); +} + +/* The monitor array is a one-dimensional array; compute the offset + * into the array */ +static inline int monitor_index(const struct echoaudio *chip, int out, int in) +{ + return out * num_busses_in(chip) + in; +} + + +#ifndef pci_device +#define pci_device(chip) (&chip->pci->dev) +#endif + + +#endif /* _ECHOAUDIO_H_ */ diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c new file mode 100644 index 0000000..9f439ea --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_3g.c @@ -0,0 +1,431 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + + +/* These functions are common for all "3G" cards */ + + +static int check_asic_status(struct echoaudio *chip) +{ + u32 box_status; + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->ext_box_status = + __constant_cpu_to_le32(E3G_ASIC_NOT_LOADED); + chip->asic_loaded = FALSE; + clear_handshake(chip); + send_vector(chip, DSP_VC_TEST_ASIC); + + if (wait_handshake(chip)) { + chip->dsp_code = NULL; + return -EIO; + } + + box_status = le32_to_cpu(chip->comm_page->ext_box_status); + DE_INIT(("box_status=%x\n", box_status)); + if (box_status == E3G_ASIC_NOT_LOADED) + return -ENODEV; + + chip->asic_loaded = TRUE; + return box_status & E3G_BOX_TYPE_MASK; +} + + + +static inline u32 get_frq_reg(struct echoaudio *chip) +{ + return le32_to_cpu(chip->comm_page->e3g_frq_register); +} + + + +/* Most configuration of 3G cards is accomplished by writing the control +register. write_control_reg sends the new control register value to the DSP. */ +static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq, + char force) +{ + if (wait_handshake(chip)) + return -EIO; + + DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq)); + + ctl = cpu_to_le32(ctl); + frq = cpu_to_le32(frq); + + if (ctl != chip->comm_page->control_register || + frq != chip->comm_page->e3g_frq_register || force) { + chip->comm_page->e3g_frq_register = frq; + chip->comm_page->control_register = ctl; + clear_handshake(chip); + return send_vector(chip, DSP_VC_WRITE_CONTROL_REG); + } + + DE_ACT(("WriteControlReg: not written, no change\n")); + return 0; +} + + + +/* Set the digital mode - currently for Gina24, Layla24, Mona, 3G */ +static int set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u8 previous_mode; + int err, i, o; + + /* All audio channels must be closed before changing the digital mode */ + snd_assert(!chip->pipe_alloc_mask, return -EAGAIN); + + snd_assert(chip->digital_modes & (1 << mode), return -EINVAL); + + previous_mode = chip->digital_mode; + err = dsp_set_digital_mode(chip, mode); + + /* If we successfully changed the digital mode from or to ADAT, + * then make sure all output, input and monitor levels are + * updated by the DSP comm object. */ + if (err >= 0 && previous_mode != mode && + (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) { + spin_lock_irq(&chip->lock); + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) + set_monitor_gain(chip, o, i, + chip->monitor_gain[o][i]); + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) + set_input_gain(chip, i, chip->input_gain[i]); + update_input_line_level(chip); +#endif + + for (o = 0; o < num_busses_out(chip); o++) + set_output_gain(chip, o, chip->output_gain[o]); + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + } + + return err; +} + + + +static u32 set_spdif_bits(struct echoaudio *chip, u32 control_reg, u32 rate) +{ + control_reg &= E3G_SPDIF_FORMAT_CLEAR_MASK; + + switch (rate) { + case 32000 : + control_reg |= E3G_SPDIF_SAMPLE_RATE0 | E3G_SPDIF_SAMPLE_RATE1; + break; + case 44100 : + if (chip->professional_spdif) + control_reg |= E3G_SPDIF_SAMPLE_RATE0; + break; + case 48000 : + control_reg |= E3G_SPDIF_SAMPLE_RATE1; + break; + } + + if (chip->professional_spdif) + control_reg |= E3G_SPDIF_PRO_MODE; + + if (chip->non_audio_spdif) + control_reg |= E3G_SPDIF_NOT_AUDIO; + + control_reg |= E3G_SPDIF_24_BIT | E3G_SPDIF_TWO_CHANNEL | + E3G_SPDIF_COPY_PERMIT; + + return control_reg; +} + + + +/* Set the S/PDIF output format */ +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + u32 control_reg; + + control_reg = le32_to_cpu(chip->comm_page->control_register); + chip->professional_spdif = prof; + control_reg = set_spdif_bits(chip, control_reg, chip->sample_rate); + return write_control_reg(chip, control_reg, get_frq_reg(chip), 0); +} + + + +/* detect_input_clocks() returns a bitmask consisting of all the input clocks +currently connected to the hardware; this changes as the user connects and +disconnects clock inputs. You should use this information to determine which +clocks the user is allowed to select. */ +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + * detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD) + clock_bits |= ECHO_CLOCK_BIT_WORD; + + switch(chip->digital_mode) { + case DIGITAL_MODE_SPDIF_RCA: + case DIGITAL_MODE_SPDIF_OPTICAL: + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + break; + case DIGITAL_MODE_ADAT: + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + break; + } + + return clock_bits; +} + + + +static int load_asic(struct echoaudio *chip) +{ + int box_type, err; + + if (chip->asic_loaded) + return 0; + + /* Give the DSP a few milliseconds to settle down */ + mdelay(2); + + err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC, + &card_fw[FW_3G_ASIC]); + if (err < 0) + return err; + + chip->asic_code = &card_fw[FW_3G_ASIC]; + + /* Now give the new ASIC a little time to set up */ + mdelay(2); + /* See if it worked */ + box_type = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + * 48 kHz, internal clock, S/PDIF RCA mode */ + if (box_type >= 0) { + err = write_control_reg(chip, E3G_48KHZ, + E3G_FREQ_REG_DEFAULT, TRUE); + if (err < 0) + return err; + } + + return box_type; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock, base_rate, frq_reg; + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + set_input_clock(chip, chip->input_clock); + return 0; + } + + snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, + return -EINVAL); + + clock = 0; + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= E3G_CLOCK_CLEAR_MASK; + + switch (rate) { + case 96000: + clock = E3G_96KHZ; + break; + case 88200: + clock = E3G_88KHZ; + break; + case 48000: + clock = E3G_48KHZ; + break; + case 44100: + clock = E3G_44KHZ; + break; + case 32000: + clock = E3G_32KHZ; + break; + default: + clock = E3G_CONTINUOUS_CLOCK; + if (rate > 50000) + clock |= E3G_DOUBLE_SPEED_MODE; + break; + } + + control_reg |= clock; + control_reg = set_spdif_bits(chip, control_reg, rate); + + base_rate = rate; + if (base_rate > 50000) + base_rate /= 2; + if (base_rate < 32000) + base_rate = 32000; + + frq_reg = E3G_MAGIC_NUMBER / base_rate - 2; + if (frq_reg > E3G_FREQ_REG_MAX) + frq_reg = E3G_FREQ_REG_MAX; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->sample_rate = rate; + DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg)); + + /* Tell the DSP about it - DSP reads both control reg & freq reg */ + return write_control_reg(chip, control_reg, frq_reg, 0); +} + + + +/* Set the sample clock source to internal, S/PDIF, ADAT */ +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + + DE_ACT(("set_input_clock:\n")); + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + E3G_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Echo3G clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Echo3G clock to SPDIF\n")); + control_reg |= E3G_SPDIF_CLOCK; + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96) + control_reg |= E3G_DOUBLE_SPEED_MODE; + else + control_reg &= ~E3G_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ADAT: + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Echo3G clock to ADAT\n")); + control_reg |= E3G_ADAT_CLOCK; + control_reg &= ~E3G_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_WORD: + DE_ACT(("Set Echo3G clock to WORD\n")); + control_reg |= E3G_WORD_CLOCK; + if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96) + control_reg |= E3G_DOUBLE_SPEED_MODE; + else + control_reg &= ~E3G_DOUBLE_SPEED_MODE; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, get_frq_reg(chip), 1); +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + spin_lock_irq(&chip->lock); + + if (incompatible_clock) { + chip->sample_rate = 48000; + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + } + + /* Clear the current digital mode */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= E3G_DIGITAL_MODE_CLEAR_MASK; + + /* Tweak the control reg */ + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= E3G_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* E3G_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + control_reg |= E3G_ADAT_MODE; + control_reg &= ~E3G_DOUBLE_SPEED_MODE; /* @@ useless */ + break; + } + + err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode)); + return incompatible_clock; +} diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c new file mode 100644 index 0000000..42afa83 --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_dsp.c @@ -0,0 +1,1125 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + +#if PAGE_SIZE < 4096 +#error PAGE_SIZE is < 4k +#endif + +static int restore_dsp_rettings(struct echoaudio *chip); + + +/* Some vector commands involve the DSP reading or writing data to and from the +comm page; if you send one of these commands to the DSP, it will complete the +command and then write a non-zero value to the Handshake field in the +comm page. This function waits for the handshake to show up. */ +static int wait_handshake(struct echoaudio *chip) +{ + int i; + + /* Wait up to 10ms for the handshake from the DSP */ + for (i = 0; i < HANDSHAKE_TIMEOUT; i++) { + /* Look for the handshake value */ + if (chip->comm_page->handshake) { + /*if (i) DE_ACT(("Handshake time: %d\n", i));*/ + return 0; + } + udelay(1); + } + + snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n"); + return -EBUSY; +} + + + +/* Much of the interaction between the DSP and the driver is done via vector +commands; send_vector writes a vector command to the DSP. Typically, this +causes the DSP to read or write fields in the comm page. +PCI posting is not required thanks to the handshake logic. */ +static int send_vector(struct echoaudio *chip, u32 command) +{ + int i; + + wmb(); /* Flush all pending writes before sending the command */ + + /* Wait up to 100ms for the "vector busy" bit to be off */ + for (i = 0; i < VECTOR_BUSY_TIMEOUT; i++) { + if (!(get_dsp_register(chip, CHI32_VECTOR_REG) & + CHI32_VECTOR_BUSY)) { + set_dsp_register(chip, CHI32_VECTOR_REG, command); + /*if (i) DE_ACT(("send_vector time: %d\n", i));*/ + return 0; + } + udelay(1); + } + + DE_ACT((KERN_ERR "timeout on send_vector\n")); + return -EBUSY; +} + + + +/* write_dsp writes a 32-bit value to the DSP; this is used almost +exclusively for loading the DSP. */ +static int write_dsp(struct echoaudio *chip, u32 data) +{ + u32 status, i; + + for (i = 0; i < 10000000; i++) { /* timeout = 10s */ + status = get_dsp_register(chip, CHI32_STATUS_REG); + if ((status & CHI32_STATUS_HOST_WRITE_EMPTY) != 0) { + set_dsp_register(chip, CHI32_DATA_REG, data); + wmb(); /* write it immediately */ + return 0; + } + udelay(1); + cond_resched(); + } + + chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ + DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n")); + return -EIO; +} + + + +/* read_dsp reads a 32-bit value from the DSP; this is used almost +exclusively for loading the DSP and checking the status of the ASIC. */ +static int read_dsp(struct echoaudio *chip, u32 *data) +{ + u32 status, i; + + for (i = 0; i < READ_DSP_TIMEOUT; i++) { + status = get_dsp_register(chip, CHI32_STATUS_REG); + if ((status & CHI32_STATUS_HOST_READ_FULL) != 0) { + *data = get_dsp_register(chip, CHI32_DATA_REG); + return 0; + } + udelay(1); + cond_resched(); + } + + chip->bad_board = TRUE; /* Set TRUE until DSP re-loaded */ + DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n")); + return -EIO; +} + + + +/**************************************************************************** + Firmware loading functions + ****************************************************************************/ + +/* This function is used to read back the serial number from the DSP; +this is triggered by the SET_COMMPAGE_ADDR command. +Only some early Echogals products have serial numbers in the ROM; +the serial number is not used, but you still need to do this as +part of the DSP load process. */ +static int read_sn(struct echoaudio *chip) +{ + int i; + u32 sn[6]; + + for (i = 0; i < 5; i++) { + if (read_dsp(chip, &sn[i])) { + snd_printk(KERN_ERR "Failed to read serial number\n"); + return -EIO; + } + } + DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n", + sn[0], sn[1], sn[2], sn[3], sn[4])); + return 0; +} + + + +#ifndef ECHOCARD_HAS_ASIC +/* This card has no ASIC, just return ok */ +static inline int check_asic_status(struct echoaudio *chip) +{ + chip->asic_loaded = TRUE; + return 0; +} + +#endif /* !ECHOCARD_HAS_ASIC */ + + + +#ifdef ECHOCARD_HAS_ASIC + +/* Load ASIC code - done after the DSP is loaded */ +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic) +{ + const struct firmware *fw; + int err; + u32 i, size; + u8 *code; + + if ((err = get_firmware(&fw, asic, chip)) < 0) { + snd_printk(KERN_WARNING "Firmware not found !\n"); + return err; + } + + code = (u8 *)fw->data; + size = fw->size; + + /* Send the "Here comes the ASIC" command */ + if (write_dsp(chip, cmd) < 0) + goto la_error; + + /* Write length of ASIC file in bytes */ + if (write_dsp(chip, size) < 0) + goto la_error; + + for (i = 0; i < size; i++) { + if (write_dsp(chip, code[i]) < 0) + goto la_error; + } + + DE_INIT(("ASIC loaded\n")); + free_firmware(fw); + return 0; + +la_error: + DE_INIT(("failed on write_dsp\n")); + free_firmware(fw); + return -EIO; +} + +#endif /* ECHOCARD_HAS_ASIC */ + + + +#ifdef DSP_56361 + +/* Install the resident loader for 56361 DSPs; The resident loader is on +the EPROM on the board for 56301 DSP. The resident loader is a tiny little +program that is used to load the real DSP code. */ +static int install_resident_loader(struct echoaudio *chip) +{ + u32 address; + int index, words, i; + u16 *code; + u32 status; + const struct firmware *fw; + + /* 56361 cards only! This check is required by the old 56301-based + Mona and Gina24 */ + if (chip->device_id != DEVICE_ID_56361) + return 0; + + /* Look to see if the resident loader is present. If the resident + loader is already installed, host flag 5 will be on. */ + status = get_dsp_register(chip, CHI32_STATUS_REG); + if (status & CHI32_STATUS_REG_HF5) { + DE_INIT(("Resident loader already installed; status is 0x%x\n", + status)); + return 0; + } + + if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) { + snd_printk(KERN_WARNING "Firmware not found !\n"); + return i; + } + + /* The DSP code is an array of 16 bit words. The array is divided up + into sections. The first word of each section is the size in words, + followed by the section type. + Since DSP addresses and data are 24 bits wide, they each take up two + 16 bit words in the array. + This is a lot like the other loader loop, but it's not a loop, you + don't write the memory type, and you don't write a zero at the end. */ + + /* Set DSP format bits for 24 bit mode */ + set_dsp_register(chip, CHI32_CONTROL_REG, + get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900); + + code = (u16 *)fw->data; + + /* Skip the header section; the first word in the array is the size + of the first section, so the first real section of code is pointed + to by Code[0]. */ + index = code[0]; + + /* Skip the section size, LRS block type, and DSP memory type */ + index += 3; + + /* Get the number of DSP words to write */ + words = code[index++]; + + /* Get the DSP address for this block; 24 bits, so build from two words */ + address = ((u32)code[index] << 16) + code[index + 1]; + index += 2; + + /* Write the count to the DSP */ + if (write_dsp(chip, words)) { + DE_INIT(("install_resident_loader: Failed to write word count!\n")); + goto irl_error; + } + /* Write the DSP address */ + if (write_dsp(chip, address)) { + DE_INIT(("install_resident_loader: Failed to write DSP address!\n")); + goto irl_error; + } + /* Write out this block of code to the DSP */ + for (i = 0; i < words; i++) { + u32 data; + + data = ((u32)code[index] << 16) + code[index + 1]; + if (write_dsp(chip, data)) { + DE_INIT(("install_resident_loader: Failed to write DSP code\n")); + goto irl_error; + } + index += 2; + } + + /* Wait for flag 5 to come up */ + for (i = 0; i < 200; i++) { /* Timeout is 50us * 200 = 10ms */ + udelay(50); + status = get_dsp_register(chip, CHI32_STATUS_REG); + if (status & CHI32_STATUS_REG_HF5) + break; + } + + if (i == 200) { + DE_INIT(("Resident loader failed to set HF5\n")); + goto irl_error; + } + + DE_INIT(("Resident loader successfully installed\n")); + free_firmware(fw); + return 0; + +irl_error: + free_firmware(fw); + return -EIO; +} + +#endif /* DSP_56361 */ + + +static int load_dsp(struct echoaudio *chip, u16 *code) +{ + u32 address, data; + int index, words, i; + + if (chip->dsp_code == code) { + DE_INIT(("DSP is already loaded!\n")); + return 0; + } + chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ + chip->dsp_code = NULL; /* Current DSP code not loaded */ + chip->asic_loaded = FALSE; /* Loading the DSP code will reset the ASIC */ + + DE_INIT(("load_dsp: Set bad_board to TRUE\n")); + + /* If this board requires a resident loader, install it. */ +#ifdef DSP_56361 + if ((i = install_resident_loader(chip)) < 0) + return i; +#endif + + /* Send software reset command */ + if (send_vector(chip, DSP_VC_RESET) < 0) { + DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n")); + return -EIO; + } + /* Delay 10us */ + udelay(10); + + /* Wait 10ms for HF3 to indicate that software reset is complete */ + for (i = 0; i < 1000; i++) { /* Timeout is 10us * 1000 = 10ms */ + if (get_dsp_register(chip, CHI32_STATUS_REG) & + CHI32_STATUS_REG_HF3) + break; + udelay(10); + } + + if (i == 1000) { + DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n")); + return -EIO; + } + + /* Set DSP format bits for 24 bit mode now that soft reset is done */ + set_dsp_register(chip, CHI32_CONTROL_REG, + get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900); + + /* Main loader loop */ + + index = code[0]; + for (;;) { + int block_type, mem_type; + + /* Total Block Size */ + index++; + + /* Block Type */ + block_type = code[index]; + if (block_type == 4) /* We're finished */ + break; + + index++; + + /* Memory Type P=0,X=1,Y=2 */ + mem_type = code[index++]; + + /* Block Code Size */ + words = code[index++]; + if (words == 0) /* We're finished */ + break; + + /* Start Address */ + address = ((u32)code[index] << 16) + code[index + 1]; + index += 2; + + if (write_dsp(chip, words) < 0) { + DE_INIT(("load_dsp: failed to write number of DSP words\n")); + return -EIO; + } + if (write_dsp(chip, address) < 0) { + DE_INIT(("load_dsp: failed to write DSP address\n")); + return -EIO; + } + if (write_dsp(chip, mem_type) < 0) { + DE_INIT(("load_dsp: failed to write DSP memory type\n")); + return -EIO; + } + /* Code */ + for (i = 0; i < words; i++, index+=2) { + data = ((u32)code[index] << 16) + code[index + 1]; + if (write_dsp(chip, data) < 0) { + DE_INIT(("load_dsp: failed to write DSP data\n")); + return -EIO; + } + } + } + + if (write_dsp(chip, 0) < 0) { /* We're done!!! */ + DE_INIT(("load_dsp: Failed to write final zero\n")); + return -EIO; + } + udelay(10); + + for (i = 0; i < 5000; i++) { /* Timeout is 100us * 5000 = 500ms */ + /* Wait for flag 4 - indicates that the DSP loaded OK */ + if (get_dsp_register(chip, CHI32_STATUS_REG) & + CHI32_STATUS_REG_HF4) { + set_dsp_register(chip, CHI32_CONTROL_REG, + get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00); + + if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) { + DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n")); + return -EIO; + } + + if (write_dsp(chip, chip->comm_page_phys) < 0) { + DE_INIT(("load_dsp: Failed to write comm page address\n")); + return -EIO; + } + + /* Get the serial number via slave mode. + This is triggered by the SET_COMMPAGE_ADDR command. + We don't actually use the serial number but we have to + get it as part of the DSP init voodoo. */ + if (read_sn(chip) < 0) { + DE_INIT(("load_dsp: Failed to read serial number\n")); + return -EIO; + } + + chip->dsp_code = code; /* Show which DSP code loaded */ + chip->bad_board = FALSE; /* DSP OK */ + DE_INIT(("load_dsp: OK!\n")); + return 0; + } + udelay(100); + } + + DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n")); + return -EIO; +} + + + +/* load_firmware takes care of loading the DSP and any ASIC code. */ +static int load_firmware(struct echoaudio *chip) +{ + const struct firmware *fw; + int box_type, err; + + snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM); + + /* See if the ASIC is present and working - only if the DSP is already loaded */ + if (chip->dsp_code) { + if ((box_type = check_asic_status(chip)) >= 0) + return box_type; + /* ASIC check failed; force the DSP to reload */ + chip->dsp_code = NULL; + } + + if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0) + return err; + err = load_dsp(chip, (u16 *)fw->data); + free_firmware(fw); + if (err < 0) + return err; + + if ((box_type = load_asic(chip)) < 0) + return box_type; /* error */ + + if ((err = restore_dsp_rettings(chip)) < 0) + return err; + + return box_type; +} + + + +/**************************************************************************** + Mixer functions + ****************************************************************************/ + +#if defined(ECHOCARD_HAS_INPUT_NOMINAL_LEVEL) || \ + defined(ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL) + +/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */ +static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer) +{ + snd_assert(index < num_busses_out(chip) + num_busses_in(chip), + return -EINVAL); + + /* Wait for the handshake (OK even if ASIC is not loaded) */ + if (wait_handshake(chip)) + return -EIO; + + chip->nominal_level[index] = consumer; + + if (consumer) + chip->comm_page->nominal_level_mask |= cpu_to_le32(1 << index); + else + chip->comm_page->nominal_level_mask &= ~cpu_to_le32(1 << index); + + return 0; +} + +#endif /* ECHOCARD_HAS_*_NOMINAL_LEVEL */ + + + +/* Set the gain for a single physical output channel (dB). */ +static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain) +{ + snd_assert(channel < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + /* Save the new value */ + chip->output_gain[channel] = gain; + chip->comm_page->line_out_level[channel] = gain; + return 0; +} + + + +#ifdef ECHOCARD_HAS_MONITOR +/* Set the monitor level from an input bus to an output bus. */ +static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input, + s8 gain) +{ + snd_assert(output < num_busses_out(chip) && + input < num_busses_in(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->monitor_gain[output][input] = gain; + chip->comm_page->monitors[monitor_index(chip, output, input)] = gain; + return 0; +} +#endif /* ECHOCARD_HAS_MONITOR */ + + +/* Tell the DSP to read and update output, nominal & monitor levels in comm page. */ +static int update_output_line_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_OUTVOL); +} + + + +/* Tell the DSP to read and update input levels in comm page */ +static int update_input_line_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_INGAIN); +} + + + +/* set_meters_on turns the meters on or off. If meters are turned on, the DSP +will write the meter and clock detect values to the comm page at about 30Hz */ +static void set_meters_on(struct echoaudio *chip, char on) +{ + if (on && !chip->meters_enabled) { + send_vector(chip, DSP_VC_METERS_ON); + chip->meters_enabled = 1; + } else if (!on && chip->meters_enabled) { + send_vector(chip, DSP_VC_METERS_OFF); + chip->meters_enabled = 0; + memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED, + DSP_MAXPIPES); + memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED, + DSP_MAXPIPES); + } +} + + + +/* Fill out an the given array using the current values in the comm page. +Meters are written in the comm page by the DSP in this order: + Output busses + Input busses + Output pipes (vmixer cards only) + +This function assumes there are no more than 16 in/out busses or pipes +Meters is an array [3][16][2] of long. */ +static void get_audio_meters(struct echoaudio *chip, long *meters) +{ + int i, m, n; + + m = 0; + n = 0; + for (i = 0; i < num_busses_out(chip); i++, m++) { + meters[n++] = chip->comm_page->vu_meter[m]; + meters[n++] = chip->comm_page->peak_meter[m]; + } + for (; n < 32; n++) + meters[n] = 0; + +#ifdef ECHOCARD_ECHO3G + m = E3G_MAX_OUTPUTS; /* Skip unused meters */ +#endif + + for (i = 0; i < num_busses_in(chip); i++, m++) { + meters[n++] = chip->comm_page->vu_meter[m]; + meters[n++] = chip->comm_page->peak_meter[m]; + } + for (; n < 64; n++) + meters[n] = 0; + +#ifdef ECHOCARD_HAS_VMIXER + for (i = 0; i < num_pipes_out(chip); i++, m++) { + meters[n++] = chip->comm_page->vu_meter[m]; + meters[n++] = chip->comm_page->peak_meter[m]; + } +#endif + for (; n < 96; n++) + meters[n] = 0; +} + + + +static int restore_dsp_rettings(struct echoaudio *chip) +{ + int err; + DE_INIT(("restore_dsp_settings\n")); + + if ((err = check_asic_status(chip)) < 0) + return err; + + /* @ Gina20/Darla20 only. Should be harmless for other cards. */ + chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF; + chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF; + chip->comm_page->handshake = 0xffffffff; + + if ((err = set_sample_rate(chip, chip->sample_rate)) < 0) + return err; + + if (chip->meters_enabled) + if (send_vector(chip, DSP_VC_METERS_ON) < 0) + return -EIO; + +#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK + if (set_input_clock(chip, chip->input_clock) < 0) + return -EIO; +#endif + +#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH + if (set_output_clock(chip, chip->output_clock) < 0) + return -EIO; +#endif + + if (update_output_line_level(chip) < 0) + return -EIO; + + if (update_input_line_level(chip) < 0) + return -EIO; + +#ifdef ECHOCARD_HAS_VMIXER + if (update_vmixer_level(chip) < 0) + return -EIO; +#endif + + if (wait_handshake(chip) < 0) + return -EIO; + clear_handshake(chip); + + DE_INIT(("restore_dsp_rettings done\n")); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +/**************************************************************************** + Transport functions + ****************************************************************************/ + +/* set_audio_format() sets the format of the audio data in host memory for +this pipe. Note that _MS_ (mono-to-stereo) playback modes are not used by ALSA +but they are here because they are just mono while capturing */ +static void set_audio_format(struct echoaudio *chip, u16 pipe_index, + const struct audioformat *format) +{ + u16 dsp_format; + + dsp_format = DSP_AUDIOFORM_SS_16LE; + + /* Look for super-interleave (no big-endian and 8 bits) */ + if (format->interleave > 2) { + switch (format->bits_per_sample) { + case 16: + dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE; + break; + case 24: + dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE; + break; + case 32: + dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE; + break; + } + dsp_format |= format->interleave; + } else if (format->data_are_bigendian) { + /* For big-endian data, only 32 bit samples are supported */ + switch (format->interleave) { + case 1: + dsp_format = DSP_AUDIOFORM_MM_32BE; + break; +#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + case 2: + dsp_format = DSP_AUDIOFORM_SS_32BE; + break; +#endif + } + } else if (format->interleave == 1 && + format->bits_per_sample == 32 && !format->mono_to_stereo) { + /* 32 bit little-endian mono->mono case */ + dsp_format = DSP_AUDIOFORM_MM_32LE; + } else { + /* Handle the other little-endian formats */ + switch (format->bits_per_sample) { + case 8: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_8; + else + dsp_format = DSP_AUDIOFORM_MS_8; + break; + default: + case 16: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_16LE; + else + dsp_format = DSP_AUDIOFORM_MS_16LE; + break; + case 24: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_24LE; + else + dsp_format = DSP_AUDIOFORM_MS_24LE; + break; + case 32: + if (format->interleave == 2) + dsp_format = DSP_AUDIOFORM_SS_32LE; + else + dsp_format = DSP_AUDIOFORM_MS_32LE; + break; + } + } + DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format)); + chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format); +} + + + +/* start_transport starts transport for a set of pipes. +The bits 1 in channel_mask specify what pipes to start. Only the bit of the +first channel must be set, regardless its interleave. +Same thing for pause_ and stop_ -trasport below. */ +static int start_transport(struct echoaudio *chip, u32 channel_mask, + u32 cyclic_mask) +{ + DE_ACT(("start_transport %x\n", channel_mask)); + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->cmd_start |= cpu_to_le32(channel_mask); + + if (chip->comm_page->cmd_start) { + clear_handshake(chip); + send_vector(chip, DSP_VC_START_TRANSFER); + if (wait_handshake(chip)) + return -EIO; + /* Keep track of which pipes are transporting */ + chip->active_mask |= channel_mask; + chip->comm_page->cmd_start = 0; + return 0; + } + + DE_ACT(("start_transport: No pipes to start!\n")); + return -EINVAL; +} + + + +static int pause_transport(struct echoaudio *chip, u32 channel_mask) +{ + DE_ACT(("pause_transport %x\n", channel_mask)); + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); + chip->comm_page->cmd_reset = 0; + if (chip->comm_page->cmd_stop) { + clear_handshake(chip); + send_vector(chip, DSP_VC_STOP_TRANSFER); + if (wait_handshake(chip)) + return -EIO; + /* Keep track of which pipes are transporting */ + chip->active_mask &= ~channel_mask; + chip->comm_page->cmd_stop = 0; + chip->comm_page->cmd_reset = 0; + return 0; + } + + DE_ACT(("pause_transport: No pipes to stop!\n")); + return 0; +} + + + +static int stop_transport(struct echoaudio *chip, u32 channel_mask) +{ + DE_ACT(("stop_transport %x\n", channel_mask)); + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask); + chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask); + if (chip->comm_page->cmd_reset) { + clear_handshake(chip); + send_vector(chip, DSP_VC_STOP_TRANSFER); + if (wait_handshake(chip)) + return -EIO; + /* Keep track of which pipes are transporting */ + chip->active_mask &= ~channel_mask; + chip->comm_page->cmd_stop = 0; + chip->comm_page->cmd_reset = 0; + return 0; + } + + DE_ACT(("stop_transport: No pipes to stop!\n")); + return 0; +} + + + +static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index) +{ + return (chip->pipe_alloc_mask & (1 << pipe_index)); +} + + + +/* Stops everything and turns off the DSP. All pipes should be already +stopped and unallocated. */ +static int rest_in_peace(struct echoaudio *chip) +{ + DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask)); + + /* Stops all active pipes (just to be sure) */ + stop_transport(chip, chip->active_mask); + + set_meters_on(chip, FALSE); + +#ifdef ECHOCARD_HAS_MIDI + enable_midi_input(chip, FALSE); +#endif + + /* Go to sleep */ + if (chip->dsp_code) { + /* Make load_firmware do a complete reload */ + chip->dsp_code = NULL; + /* Put the DSP to sleep */ + return send_vector(chip, DSP_VC_GO_COMATOSE); + } + return 0; +} + + + +/* Fills the comm page with default values */ +static int init_dsp_comm_page(struct echoaudio *chip) +{ + /* Check if the compiler added extra padding inside the structure */ + if (offsetof(struct comm_page, midi_output) != 0xbe0) { + DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n")); + return -EPERM; + } + + /* Init all the basic stuff */ + chip->card_name = ECHOCARD_NAME; + chip->bad_board = TRUE; /* Set TRUE until DSP loaded */ + chip->dsp_code = NULL; /* Current DSP code not loaded */ + chip->digital_mode = DIGITAL_MODE_NONE; + chip->input_clock = ECHO_CLOCK_INTERNAL; + chip->output_clock = ECHO_CLOCK_WORD; + chip->asic_loaded = FALSE; + memset(chip->comm_page, 0, sizeof(struct comm_page)); + + /* Init the comm page */ + chip->comm_page->comm_size = + __constant_cpu_to_le32(sizeof(struct comm_page)); + chip->comm_page->handshake = 0xffffffff; + chip->comm_page->midi_out_free_count = + __constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE); + chip->comm_page->sample_rate = __constant_cpu_to_le32(44100); + chip->sample_rate = 44100; + + /* Set line levels so we don't blast any inputs on startup */ + memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE); + memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE); + + return 0; +} + + + +/* This function initializes the several volume controls for busses and pipes. +This MUST be called after the DSP is up and running ! */ +static int init_line_levels(struct echoaudio *chip) +{ + int st, i, o; + + DE_INIT(("init_line_levels\n")); + + /* Mute output busses */ + for (i = 0; i < num_busses_out(chip); i++) + if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_output_line_level(chip))) + return st; + +#ifdef ECHOCARD_HAS_VMIXER + /* Mute the Vmixer */ + for (i = 0; i < num_pipes_out(chip); i++) + for (o = 0; o < num_busses_out(chip); o++) + if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_vmixer_level(chip))) + return st; +#endif /* ECHOCARD_HAS_VMIXER */ + +#ifdef ECHOCARD_HAS_MONITOR + /* Mute the monitor mixer */ + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) + if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_output_line_level(chip))) + return st; +#endif /* ECHOCARD_HAS_MONITOR */ + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) + if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED))) + return st; + if ((st = update_input_line_level(chip))) + return st; +#endif /* ECHOCARD_HAS_INPUT_GAIN */ + + return 0; +} + + + +/* This is low level part of the interrupt handler. +It returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the number +of midi data in the input queue. */ +static int service_irq(struct echoaudio *chip) +{ + int st; + + /* Read the DSP status register and see if this DSP generated this interrupt */ + if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) { + st = 0; +#ifdef ECHOCARD_HAS_MIDI + /* Get and parse midi data if present */ + if (chip->comm_page->midi_input[0]) /* The count is at index 0 */ + st = midi_service_irq(chip); /* Returns how many midi bytes we received */ +#endif + /* Clear the hardware interrupt */ + chip->comm_page->midi_input[0] = 0; + send_vector(chip, DSP_VC_ACK_INT); + return st; + } + return -1; +} + + + + +/****************************************************************************** + Functions for opening and closing pipes + ******************************************************************************/ + +/* allocate_pipes is used to reserve audio pipes for your exclusive use. +The call will fail if some pipes are already allocated. */ +static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe, + int pipe_index, int interleave) +{ + int i; + u32 channel_mask; + char is_cyclic; + + DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave)); + + if (chip->bad_board) + return -EIO; + + is_cyclic = 1; /* This driver uses cyclic buffers only */ + + for (channel_mask = i = 0; i < interleave; i++) + channel_mask |= 1 << (pipe_index + i); + if (chip->pipe_alloc_mask & channel_mask) { + DE_ACT(("allocate_pipes: channel already open\n")); + return -EAGAIN; + } + + chip->comm_page->position[pipe_index] = 0; + chip->pipe_alloc_mask |= channel_mask; + if (is_cyclic) + chip->pipe_cyclic_mask |= channel_mask; + pipe->index = pipe_index; + pipe->interleave = interleave; + pipe->state = PIPE_STATE_STOPPED; + + /* The counter register is where the DSP writes the 32 bit DMA + position for a pipe. The DSP is constantly updating this value as + it moves data. The DMA counter is in units of bytes, not samples. */ + pipe->dma_counter = &chip->comm_page->position[pipe_index]; + *pipe->dma_counter = 0; + DE_ACT(("allocate_pipes: ok\n")); + return pipe_index; +} + + + +static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe) +{ + u32 channel_mask; + int i; + + DE_ACT(("free_pipes: Pipe %d\n", pipe->index)); + snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL); + snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL); + + for (channel_mask = i = 0; i < pipe->interleave; i++) + channel_mask |= 1 << (pipe->index + i); + + chip->pipe_alloc_mask &= ~channel_mask; + chip->pipe_cyclic_mask &= ~channel_mask; + return 0; +} + + + +/****************************************************************************** + Functions for managing the scatter-gather list +******************************************************************************/ + +static int sglist_init(struct echoaudio *chip, struct audiopipe *pipe) +{ + pipe->sglist_head = 0; + memset(pipe->sgpage.area, 0, PAGE_SIZE); + chip->comm_page->sglist_addr[pipe->index].addr = + cpu_to_le32(pipe->sgpage.addr); + return 0; +} + + + +static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe, + dma_addr_t address, size_t length) +{ + int head = pipe->sglist_head; + struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area; + + if (head < MAX_SGLIST_ENTRIES - 1) { + list[head].addr = cpu_to_le32(address); + list[head].size = cpu_to_le32(length); + pipe->sglist_head++; + } else { + DE_ACT(("SGlist: too many fragments\n")); + return -ENOMEM; + } + return 0; +} + + + +static inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe) +{ + return sglist_add_mapping(chip, pipe, 0, 0); +} + + + +static inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe) +{ + return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0); +} diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h new file mode 100644 index 0000000..e55ee00 --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_dsp.h @@ -0,0 +1,694 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + +#ifndef _ECHO_DSP_ +#define _ECHO_DSP_ + + +/**** Echogals: Darla20, Gina20, Layla20, and Darla24 ****/ +#if defined(ECHOGALS_FAMILY) + +#define NUM_ASIC_TESTS 5 +#define READ_DSP_TIMEOUT 1000000L /* one second */ + +/**** Echo24: Gina24, Layla24, Mona, Mia, Mia-midi ****/ +#elif defined(ECHO24_FAMILY) + +#define DSP_56361 /* Some Echo24 cards use the 56361 DSP */ +#define READ_DSP_TIMEOUT 100000L /* .1 second */ + +/**** 3G: Gina3G, Layla3G ****/ +#elif defined(ECHO3G_FAMILY) + +#define DSP_56361 +#define READ_DSP_TIMEOUT 100000L /* .1 second */ +#define MIN_MTC_1X_RATE 32000 + +/**** Indigo: Indigo, Indigo IO, Indigo DJ ****/ +#elif defined(INDIGO_FAMILY) + +#define DSP_56361 +#define READ_DSP_TIMEOUT 100000L /* .1 second */ + +#else + +#error No family is defined + +#endif + + + +/* + * + * Max inputs and outputs + * + */ + +#define DSP_MAXAUDIOINPUTS 16 /* Max audio input channels */ +#define DSP_MAXAUDIOOUTPUTS 16 /* Max audio output channels */ +#define DSP_MAXPIPES 32 /* Max total pipes (input + output) */ + + +/* + * + * These are the offsets for the memory-mapped DSP registers; the DSP base + * address is treated as the start of a u32 array. + */ + +#define CHI32_CONTROL_REG 4 +#define CHI32_STATUS_REG 5 +#define CHI32_VECTOR_REG 6 +#define CHI32_DATA_REG 7 + + +/* + * + * Interesting bits within the DSP registers + * + */ + +#define CHI32_VECTOR_BUSY 0x00000001 +#define CHI32_STATUS_REG_HF3 0x00000008 +#define CHI32_STATUS_REG_HF4 0x00000010 +#define CHI32_STATUS_REG_HF5 0x00000020 +#define CHI32_STATUS_HOST_READ_FULL 0x00000004 +#define CHI32_STATUS_HOST_WRITE_EMPTY 0x00000002 +#define CHI32_STATUS_IRQ 0x00000040 + + +/* + * + * DSP commands sent via slave mode; these are sent to the DSP by write_dsp() + * + */ + +#define DSP_FNC_SET_COMMPAGE_ADDR 0x02 +#define DSP_FNC_LOAD_LAYLA_ASIC 0xa0 +#define DSP_FNC_LOAD_GINA24_ASIC 0xa0 +#define DSP_FNC_LOAD_MONA_PCI_CARD_ASIC 0xa0 +#define DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC 0xa0 +#define DSP_FNC_LOAD_MONA_EXTERNAL_ASIC 0xa1 +#define DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC 0xa1 +#define DSP_FNC_LOAD_3G_ASIC 0xa0 + + +/* + * + * Defines to handle the MIDI input state engine; these are used to properly + * extract MIDI time code bytes and their timestamps from the MIDI input stream. + * + */ + +#define MIDI_IN_STATE_NORMAL 0 +#define MIDI_IN_STATE_TS_HIGH 1 +#define MIDI_IN_STATE_TS_LOW 2 +#define MIDI_IN_STATE_F1_DATA 3 +#define MIDI_IN_SKIP_DATA (-1) + + +/*---------------------------------------------------------------------------- + +Setting the sample rates on Layla24 is somewhat schizophrenic. + +For standard rates, it works exactly like Mona and Gina24. That is, for +8, 11.025, 16, 22.05, 32, 44.1, 48, 88.2, and 96 kHz, you just set the +appropriate bits in the control register and write the control register. + +In order to support MIDI time code sync (and possibly SMPTE LTC sync in +the future), Layla24 also has "continuous sample rate mode". In this mode, +Layla24 can generate any sample rate between 25 and 50 kHz inclusive, or +50 to 100 kHz inclusive for double speed mode. + +To use continuous mode: + +-Set the clock select bits in the control register to 0xe (see the #define + below) + +-Set double-speed mode if you want to use sample rates above 50 kHz + +-Write the control register as you would normally + +-Now, you need to set the frequency register. First, you need to determine the + value for the frequency register. This is given by the following formula: + +frequency_reg = (LAYLA24_MAGIC_NUMBER / sample_rate) - 2 + +Note the #define below for the magic number + +-Wait for the DSP handshake +-Write the frequency_reg value to the .SampleRate field of the comm page +-Send the vector command SET_LAYLA24_FREQUENCY_REG (see vmonkey.h) + +Once you have set the control register up for continuous mode, you can just +write the frequency register to change the sample rate. This could be +used for MIDI time code sync. For MTC sync, the control register is set for +continuous mode. The driver then just keeps writing the +SET_LAYLA24_FREQUENCY_REG command. + +-----------------------------------------------------------------------------*/ + +#define LAYLA24_MAGIC_NUMBER 677376000 +#define LAYLA24_CONTINUOUS_CLOCK 0x000e + + +/* + * + * DSP vector commands + * + */ + +#define DSP_VC_RESET 0x80ff + +#ifndef DSP_56361 + +#define DSP_VC_ACK_INT 0x8073 +#define DSP_VC_SET_VMIXER_GAIN 0x0000 /* Not used, only for compile */ +#define DSP_VC_START_TRANSFER 0x0075 /* Handshke rqd. */ +#define DSP_VC_METERS_ON 0x0079 +#define DSP_VC_METERS_OFF 0x007b +#define DSP_VC_UPDATE_OUTVOL 0x007d /* Handshke rqd. */ +#define DSP_VC_UPDATE_INGAIN 0x007f /* Handshke rqd. */ +#define DSP_VC_ADD_AUDIO_BUFFER 0x0081 /* Handshke rqd. */ +#define DSP_VC_TEST_ASIC 0x00eb +#define DSP_VC_UPDATE_CLOCKS 0x00ef /* Handshke rqd. */ +#define DSP_VC_SET_LAYLA_SAMPLE_RATE 0x00f1 /* Handshke rqd. */ +#define DSP_VC_SET_GD_AUDIO_STATE 0x00f1 /* Handshke rqd. */ +#define DSP_VC_WRITE_CONTROL_REG 0x00f1 /* Handshke rqd. */ +#define DSP_VC_MIDI_WRITE 0x00f5 /* Handshke rqd. */ +#define DSP_VC_STOP_TRANSFER 0x00f7 /* Handshke rqd. */ +#define DSP_VC_UPDATE_FLAGS 0x00fd /* Handshke rqd. */ +#define DSP_VC_GO_COMATOSE 0x00f9 + +#else /* !DSP_56361 */ + +/* Vector commands for families that use either the 56301 or 56361 */ +#define DSP_VC_ACK_INT 0x80F5 +#define DSP_VC_SET_VMIXER_GAIN 0x00DB /* Handshke rqd. */ +#define DSP_VC_START_TRANSFER 0x00DD /* Handshke rqd. */ +#define DSP_VC_METERS_ON 0x00EF +#define DSP_VC_METERS_OFF 0x00F1 +#define DSP_VC_UPDATE_OUTVOL 0x00E3 /* Handshke rqd. */ +#define DSP_VC_UPDATE_INGAIN 0x00E5 /* Handshke rqd. */ +#define DSP_VC_ADD_AUDIO_BUFFER 0x00E1 /* Handshke rqd. */ +#define DSP_VC_TEST_ASIC 0x00ED +#define DSP_VC_UPDATE_CLOCKS 0x00E9 /* Handshke rqd. */ +#define DSP_VC_SET_LAYLA24_FREQUENCY_REG 0x00E9 /* Handshke rqd. */ +#define DSP_VC_SET_LAYLA_SAMPLE_RATE 0x00EB /* Handshke rqd. */ +#define DSP_VC_SET_GD_AUDIO_STATE 0x00EB /* Handshke rqd. */ +#define DSP_VC_WRITE_CONTROL_REG 0x00EB /* Handshke rqd. */ +#define DSP_VC_MIDI_WRITE 0x00E7 /* Handshke rqd. */ +#define DSP_VC_STOP_TRANSFER 0x00DF /* Handshke rqd. */ +#define DSP_VC_UPDATE_FLAGS 0x00FB /* Handshke rqd. */ +#define DSP_VC_GO_COMATOSE 0x00d9 + +#endif /* !DSP_56361 */ + + +/* + * + * Timeouts + * + */ + +#define HANDSHAKE_TIMEOUT 20000 /* send_vector command timeout (20ms) */ +#define VECTOR_BUSY_TIMEOUT 100000 /* 100ms */ +#define MIDI_OUT_DELAY_USEC 2000 /* How long to wait after MIDI fills up */ + + +/* + * + * Flags for .Flags field in the comm page + * + */ + +#define DSP_FLAG_MIDI_INPUT 0x0001 /* Enable MIDI input */ +#define DSP_FLAG_SPDIF_NONAUDIO 0x0002 /* Sets the "non-audio" bit + * in the S/PDIF out status + * bits. Clear this flag for + * audio data; + * set it for AC3 or WMA or + * some such */ +#define DSP_FLAG_PROFESSIONAL_SPDIF 0x0008 /* 1 Professional, 0 Consumer */ + + +/* + * + * Clock detect bits reported by the DSP for Gina20, Layla20, Darla24, and Mia + * + */ + +#define GLDM_CLOCK_DETECT_BIT_WORD 0x0002 +#define GLDM_CLOCK_DETECT_BIT_SUPER 0x0004 +#define GLDM_CLOCK_DETECT_BIT_SPDIF 0x0008 +#define GLDM_CLOCK_DETECT_BIT_ESYNC 0x0010 + + +/* + * + * Clock detect bits reported by the DSP for Gina24, Mona, and Layla24 + * + */ + +#define GML_CLOCK_DETECT_BIT_WORD96 0x0002 +#define GML_CLOCK_DETECT_BIT_WORD48 0x0004 +#define GML_CLOCK_DETECT_BIT_SPDIF48 0x0008 +#define GML_CLOCK_DETECT_BIT_SPDIF96 0x0010 +#define GML_CLOCK_DETECT_BIT_WORD (GML_CLOCK_DETECT_BIT_WORD96 | GML_CLOCK_DETECT_BIT_WORD48) +#define GML_CLOCK_DETECT_BIT_SPDIF (GML_CLOCK_DETECT_BIT_SPDIF48 | GML_CLOCK_DETECT_BIT_SPDIF96) +#define GML_CLOCK_DETECT_BIT_ESYNC 0x0020 +#define GML_CLOCK_DETECT_BIT_ADAT 0x0040 + + +/* + * + * Layla clock numbers to send to DSP + * + */ + +#define LAYLA20_CLOCK_INTERNAL 0 +#define LAYLA20_CLOCK_SPDIF 1 +#define LAYLA20_CLOCK_WORD 2 +#define LAYLA20_CLOCK_SUPER 3 + + +/* + * + * Gina/Darla clock states + * + */ + +#define GD_CLOCK_NOCHANGE 0 +#define GD_CLOCK_44 1 +#define GD_CLOCK_48 2 +#define GD_CLOCK_SPDIFIN 3 +#define GD_CLOCK_UNDEF 0xff + + +/* + * + * Gina/Darla S/PDIF status bits + * + */ + +#define GD_SPDIF_STATUS_NOCHANGE 0 +#define GD_SPDIF_STATUS_44 1 +#define GD_SPDIF_STATUS_48 2 +#define GD_SPDIF_STATUS_UNDEF 0xff + + +/* + * + * Layla20 output clocks + * + */ + +#define LAYLA20_OUTPUT_CLOCK_SUPER 0 +#define LAYLA20_OUTPUT_CLOCK_WORD 1 + + +/**************************************************************************** + + Magic constants for the Darla24 hardware + + ****************************************************************************/ + +#define GD24_96000 0x0 +#define GD24_48000 0x1 +#define GD24_44100 0x2 +#define GD24_32000 0x3 +#define GD24_22050 0x4 +#define GD24_16000 0x5 +#define GD24_11025 0x6 +#define GD24_8000 0x7 +#define GD24_88200 0x8 +#define GD24_EXT_SYNC 0x9 + + +/* + * + * Return values from the DSP when ASIC is loaded + * + */ + +#define ASIC_ALREADY_LOADED 0x1 +#define ASIC_NOT_LOADED 0x0 + + +/* + * + * DSP Audio formats + * + * These are the audio formats that the DSP can transfer + * via input and output pipes. LE means little-endian, + * BE means big-endian. + * + * DSP_AUDIOFORM_MS_8 + * + * 8-bit mono unsigned samples. For playback, + * mono data is duplicated out the left and right channels + * of the output bus. The "MS" part of the name + * means mono->stereo. + * + * DSP_AUDIOFORM_MS_16LE + * + * 16-bit signed little-endian mono samples. Playback works + * like the previous code. + * + * DSP_AUDIOFORM_MS_24LE + * + * 24-bit signed little-endian mono samples. Data is packed + * three bytes per sample; if you had two samples 0x112233 and 0x445566 + * they would be stored in memory like this: 33 22 11 66 55 44. + * + * DSP_AUDIOFORM_MS_32LE + * + * 24-bit signed little-endian mono samples in a 32-bit + * container. In other words, each sample is a 32-bit signed + * integer, where the actual audio data is left-justified + * in the 32 bits and only the 24 most significant bits are valid. + * + * DSP_AUDIOFORM_SS_8 + * DSP_AUDIOFORM_SS_16LE + * DSP_AUDIOFORM_SS_24LE + * DSP_AUDIOFORM_SS_32LE + * + * Like the previous ones, except now with stereo interleaved + * data. "SS" means stereo->stereo. + * + * DSP_AUDIOFORM_MM_32LE + * + * Similar to DSP_AUDIOFORM_MS_32LE, except that the mono + * data is not duplicated out both the left and right outputs. + * This mode is used by the ASIO driver. Here, "MM" means + * mono->mono. + * + * DSP_AUDIOFORM_MM_32BE + * + * Just like DSP_AUDIOFORM_MM_32LE, but now the data is + * in big-endian format. + * + */ + +#define DSP_AUDIOFORM_MS_8 0 /* 8 bit mono */ +#define DSP_AUDIOFORM_MS_16LE 1 /* 16 bit mono */ +#define DSP_AUDIOFORM_MS_24LE 2 /* 24 bit mono */ +#define DSP_AUDIOFORM_MS_32LE 3 /* 32 bit mono */ +#define DSP_AUDIOFORM_SS_8 4 /* 8 bit stereo */ +#define DSP_AUDIOFORM_SS_16LE 5 /* 16 bit stereo */ +#define DSP_AUDIOFORM_SS_24LE 6 /* 24 bit stereo */ +#define DSP_AUDIOFORM_SS_32LE 7 /* 32 bit stereo */ +#define DSP_AUDIOFORM_MM_32LE 8 /* 32 bit mono->mono little-endian */ +#define DSP_AUDIOFORM_MM_32BE 9 /* 32 bit mono->mono big-endian */ +#define DSP_AUDIOFORM_SS_32BE 10 /* 32 bit stereo big endian */ +#define DSP_AUDIOFORM_INVALID 0xFF /* Invalid audio format */ + + +/* + * + * Super-interleave is defined as interleaving by 4 or more. Darla20 and Gina20 + * do not support super interleave. + * + * 16 bit, 24 bit, and 32 bit little endian samples are supported for super + * interleave. The interleave factor must be even. 16 - way interleave is the + * current maximum, so you can interleave by 4, 6, 8, 10, 12, 14, and 16. + * + * The actual format code is derived by taking the define below and or-ing with + * the interleave factor. So, 32 bit interleave by 6 is 0x86 and + * 16 bit interleave by 16 is (0x40 | 0x10) = 0x50. + * + */ + +#define DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE 0x40 +#define DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE 0xc0 +#define DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE 0x80 + + +/* + * + * Gina24, Mona, and Layla24 control register defines + * + */ + +#define GML_CONVERTER_ENABLE 0x0010 +#define GML_SPDIF_PRO_MODE 0x0020 /* Professional S/PDIF == 1, + consumer == 0 */ +#define GML_SPDIF_SAMPLE_RATE0 0x0040 +#define GML_SPDIF_SAMPLE_RATE1 0x0080 +#define GML_SPDIF_TWO_CHANNEL 0x0100 /* 1 == two channels, + 0 == one channel */ +#define GML_SPDIF_NOT_AUDIO 0x0200 +#define GML_SPDIF_COPY_PERMIT 0x0400 +#define GML_SPDIF_24_BIT 0x0800 /* 1 == 24 bit, 0 == 20 bit */ +#define GML_ADAT_MODE 0x1000 /* 1 == ADAT mode, 0 == S/PDIF mode */ +#define GML_SPDIF_OPTICAL_MODE 0x2000 /* 1 == optical mode, 0 == RCA mode */ +#define GML_SPDIF_CDROM_MODE 0x3000 /* 1 == CDROM mode, + * 0 == RCA or optical mode */ +#define GML_DOUBLE_SPEED_MODE 0x4000 /* 1 == double speed, + 0 == single speed */ + +#define GML_DIGITAL_IN_AUTO_MUTE 0x800000 + +#define GML_96KHZ (0x0 | GML_DOUBLE_SPEED_MODE) +#define GML_88KHZ (0x1 | GML_DOUBLE_SPEED_MODE) +#define GML_48KHZ 0x2 +#define GML_44KHZ 0x3 +#define GML_32KHZ 0x4 +#define GML_22KHZ 0x5 +#define GML_16KHZ 0x6 +#define GML_11KHZ 0x7 +#define GML_8KHZ 0x8 +#define GML_SPDIF_CLOCK 0x9 +#define GML_ADAT_CLOCK 0xA +#define GML_WORD_CLOCK 0xB +#define GML_ESYNC_CLOCK 0xC +#define GML_ESYNCx2_CLOCK 0xD + +#define GML_CLOCK_CLEAR_MASK 0xffffbff0 +#define GML_SPDIF_RATE_CLEAR_MASK (~(GML_SPDIF_SAMPLE_RATE0|GML_SPDIF_SAMPLE_RATE1)) +#define GML_DIGITAL_MODE_CLEAR_MASK 0xffffcfff +#define GML_SPDIF_FORMAT_CLEAR_MASK 0xfffff01f + + +/* + * + * Mia sample rate and clock setting constants + * + */ + +#define MIA_32000 0x0040 +#define MIA_44100 0x0042 +#define MIA_48000 0x0041 +#define MIA_88200 0x0142 +#define MIA_96000 0x0141 + +#define MIA_SPDIF 0x00000044 +#define MIA_SPDIF96 0x00000144 + +#define MIA_MIDI_REV 1 /* Must be Mia rev 1 for MIDI support */ + + +/* + * + * 3G register bits + * + */ + +#define E3G_CONVERTER_ENABLE 0x0010 +#define E3G_SPDIF_PRO_MODE 0x0020 /* Professional S/PDIF == 1, + consumer == 0 */ +#define E3G_SPDIF_SAMPLE_RATE0 0x0040 +#define E3G_SPDIF_SAMPLE_RATE1 0x0080 +#define E3G_SPDIF_TWO_CHANNEL 0x0100 /* 1 == two channels, + 0 == one channel */ +#define E3G_SPDIF_NOT_AUDIO 0x0200 +#define E3G_SPDIF_COPY_PERMIT 0x0400 +#define E3G_SPDIF_24_BIT 0x0800 /* 1 == 24 bit, 0 == 20 bit */ +#define E3G_DOUBLE_SPEED_MODE 0x4000 /* 1 == double speed, + 0 == single speed */ +#define E3G_PHANTOM_POWER 0x8000 /* 1 == phantom power on, + 0 == phantom power off */ + +#define E3G_96KHZ (0x0 | E3G_DOUBLE_SPEED_MODE) +#define E3G_88KHZ (0x1 | E3G_DOUBLE_SPEED_MODE) +#define E3G_48KHZ 0x2 +#define E3G_44KHZ 0x3 +#define E3G_32KHZ 0x4 +#define E3G_22KHZ 0x5 +#define E3G_16KHZ 0x6 +#define E3G_11KHZ 0x7 +#define E3G_8KHZ 0x8 +#define E3G_SPDIF_CLOCK 0x9 +#define E3G_ADAT_CLOCK 0xA +#define E3G_WORD_CLOCK 0xB +#define E3G_CONTINUOUS_CLOCK 0xE + +#define E3G_ADAT_MODE 0x1000 +#define E3G_SPDIF_OPTICAL_MODE 0x2000 + +#define E3G_CLOCK_CLEAR_MASK 0xbfffbff0 +#define E3G_DIGITAL_MODE_CLEAR_MASK 0xffffcfff +#define E3G_SPDIF_FORMAT_CLEAR_MASK 0xfffff01f + +/* Clock detect bits reported by the DSP */ +#define E3G_CLOCK_DETECT_BIT_WORD96 0x0001 +#define E3G_CLOCK_DETECT_BIT_WORD48 0x0002 +#define E3G_CLOCK_DETECT_BIT_SPDIF48 0x0004 +#define E3G_CLOCK_DETECT_BIT_ADAT 0x0004 +#define E3G_CLOCK_DETECT_BIT_SPDIF96 0x0008 +#define E3G_CLOCK_DETECT_BIT_WORD (E3G_CLOCK_DETECT_BIT_WORD96|E3G_CLOCK_DETECT_BIT_WORD48) +#define E3G_CLOCK_DETECT_BIT_SPDIF (E3G_CLOCK_DETECT_BIT_SPDIF48|E3G_CLOCK_DETECT_BIT_SPDIF96) + +/* Frequency control register */ +#define E3G_MAGIC_NUMBER 677376000 +#define E3G_FREQ_REG_DEFAULT (E3G_MAGIC_NUMBER / 48000 - 2) +#define E3G_FREQ_REG_MAX 0xffff + +/* 3G external box types */ +#define E3G_GINA3G_BOX_TYPE 0x00 +#define E3G_LAYLA3G_BOX_TYPE 0x10 +#define E3G_ASIC_NOT_LOADED 0xffff +#define E3G_BOX_TYPE_MASK 0xf0 + +#define EXT_3GBOX_NC 0x01 +#define EXT_3GBOX_NOT_SET 0x02 + + +/* + * + * Gina20 & Layla20 have input gain controls for the analog inputs; + * this is the magic number for the hardware that gives you 0 dB at -10. + * + */ + +#define GL20_INPUT_GAIN_MAGIC_NUMBER 0xC8 + + +/* + * + * Defines how much time must pass between DSP load attempts + * + */ + +#define DSP_LOAD_ATTEMPT_PERIOD 1000000L /* One second */ + + +/* + * + * Size of arrays for the comm page. MAX_PLAY_TAPS and MAX_REC_TAPS are + * no longer used, but the sizes must still be right for the DSP to see + * the comm page correctly. + * + */ + +#define MONITOR_ARRAY_SIZE 0x180 +#define VMIXER_ARRAY_SIZE 0x40 +#define MIDI_OUT_BUFFER_SIZE 32 +#define MIDI_IN_BUFFER_SIZE 256 +#define MAX_PLAY_TAPS 168 +#define MAX_REC_TAPS 192 +#define DSP_MIDI_OUT_FIFO_SIZE 64 + + +/* sg_entry is a single entry for the scatter-gather list. The array of struct +sg_entry struct is read by the DSP, so all values must be little-endian. */ + +#define MAX_SGLIST_ENTRIES 512 + +struct sg_entry { + u32 addr; + u32 size; +}; + + +/**************************************************************************** + + The comm page. This structure is read and written by the DSP; the + DSP code is a firm believer in the byte offsets written in the comments + at the end of each line. This structure should not be changed. + + Any reads from or writes to this structure should be in little-endian format. + + ****************************************************************************/ + +struct comm_page { /* Base Length*/ + u32 comm_size; /* size of this object 0x000 4 */ + u32 flags; /* See Appendix A below 0x004 4 */ + u32 unused; /* Unused entry 0x008 4 */ + u32 sample_rate; /* Card sample rate in Hz 0x00c 4 */ + volatile u32 handshake; /* DSP command handshake 0x010 4 */ + u32 cmd_start; /* Chs. to start mask 0x014 4 */ + u32 cmd_stop; /* Chs. to stop mask 0x018 4 */ + u32 cmd_reset; /* Chs. to reset mask 0x01c 4 */ + u16 audio_format[DSP_MAXPIPES]; /* Chs. audio format 0x020 32*2 */ + struct sg_entry sglist_addr[DSP_MAXPIPES]; + /* Chs. Physical sglist addrs 0x060 32*8 */ + volatile u32 position[DSP_MAXPIPES]; + /* Positions for ea. ch. 0x160 32*4 */ + volatile s8 vu_meter[DSP_MAXPIPES]; + /* VU meters 0x1e0 32*1 */ + volatile s8 peak_meter[DSP_MAXPIPES]; + /* Peak meters 0x200 32*1 */ + s8 line_out_level[DSP_MAXAUDIOOUTPUTS]; + /* Output gain 0x220 16*1 */ + s8 line_in_level[DSP_MAXAUDIOINPUTS]; + /* Input gain 0x230 16*1 */ + s8 monitors[MONITOR_ARRAY_SIZE]; + /* Monitor map 0x240 0x180 */ + u32 play_coeff[MAX_PLAY_TAPS]; + /* Gina/Darla play filters - obsolete 0x3c0 168*4 */ + u32 rec_coeff[MAX_REC_TAPS]; + /* Gina/Darla record filters - obsolete 0x660 192*4 */ + volatile u16 midi_input[MIDI_IN_BUFFER_SIZE]; + /* MIDI input data transfer buffer 0x960 256*2 */ + u8 gd_clock_state; /* Chg Gina/Darla clock state 0xb60 1 */ + u8 gd_spdif_status; /* Chg. Gina/Darla S/PDIF state 0xb61 1 */ + u8 gd_resampler_state; /* Should always be 3 0xb62 1 */ + u8 filler2; /* 0xb63 1 */ + u32 nominal_level_mask; /* -10 level enable mask 0xb64 4 */ + u16 input_clock; /* Chg. Input clock state 0xb68 2 */ + u16 output_clock; /* Chg. Output clock state 0xb6a 2 */ + volatile u32 status_clocks; + /* Current Input clock state 0xb6c 4 */ + u32 ext_box_status; /* External box status 0xb70 4 */ + u32 cmd_add_buffer; /* Pipes to add (obsolete) 0xb74 4 */ + volatile u32 midi_out_free_count; + /* # of bytes free in MIDI output FIFO 0xb78 4 */ + u32 unused2; /* Cyclic pipes 0xb7c 4 */ + u32 control_register; + /* Mona, Gina24, Layla24, 3G ctrl reg 0xb80 4 */ + u32 e3g_frq_register; /* 3G frequency register 0xb84 4 */ + u8 filler[24]; /* filler 0xb88 24*1 */ + s8 vmixer[VMIXER_ARRAY_SIZE]; + /* Vmixer levels 0xba0 64*1 */ + u8 midi_output[MIDI_OUT_BUFFER_SIZE]; + /* MIDI output data 0xbe0 32*1 */ +}; + +#endif /* _ECHO_DSP_ */ diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c new file mode 100644 index 0000000..3aa37e7 --- /dev/null +++ b/sound/pci/echoaudio/echoaudio_gml.c @@ -0,0 +1,198 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +/* These functions are common for Gina24, Layla24 and Mona cards */ + + +/* ASIC status check - some cards have one or two ASICs that need to be +loaded. Once that load is complete, this function is called to see if +the load was successful. +If this load fails, it does not necessarily mean that the hardware is +defective - the external box may be disconnected or turned off. */ +static int check_asic_status(struct echoaudio *chip) +{ + u32 asic_status; + + send_vector(chip, DSP_VC_TEST_ASIC); + + /* The DSP will return a value to indicate whether or not the + ASIC is currently loaded */ + if (read_dsp(chip, &asic_status) < 0) { + DE_INIT(("check_asic_status: failed on read_dsp\n")); + chip->asic_loaded = FALSE; + return -EIO; + } + + chip->asic_loaded = (asic_status == ASIC_ALREADY_LOADED); + return chip->asic_loaded ? 0 : -EIO; +} + + + +/* Most configuration of Gina24, Layla24, or Mona is accomplished by writing +the control register. write_control_reg sends the new control register +value to the DSP. */ +static int write_control_reg(struct echoaudio *chip, u32 value, char force) +{ + /* Handle the digital input auto-mute */ + if (chip->digital_in_automute) + value |= GML_DIGITAL_IN_AUTO_MUTE; + else + value &= ~GML_DIGITAL_IN_AUTO_MUTE; + + DE_ACT(("write_control_reg: 0x%x\n", value)); + + /* Write the control register */ + value = cpu_to_le32(value); + if (value != chip->comm_page->control_register || force) { + if (wait_handshake(chip)) + return -EIO; + chip->comm_page->control_register = value; + clear_handshake(chip); + return send_vector(chip, DSP_VC_WRITE_CONTROL_REG); + } + return 0; +} + + + +/* Gina24, Layla24, and Mona support digital input auto-mute. If the digital +input auto-mute is enabled, the DSP will only enable the digital inputs if +the card is syncing to a valid clock on the ADAT or S/PDIF inputs. +If the auto-mute is disabled, the digital inputs are enabled regardless of +what the input clock is set or what is connected. */ +static int set_input_auto_mute(struct echoaudio *chip, int automute) +{ + DE_ACT(("set_input_auto_mute %d\n", automute)); + + chip->digital_in_automute = automute; + + /* Re-set the input clock to the current value - indirectly causes + the auto-mute flag to be sent to the DSP */ + return set_input_clock(chip, chip->input_clock); +} + + + +/* S/PDIF coax / S/PDIF optical / ADAT - switch */ +static int set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u8 previous_mode; + int err, i, o; + + if (chip->bad_board) + return -EIO; + + /* All audio channels must be closed before changing the digital mode */ + snd_assert(!chip->pipe_alloc_mask, return -EAGAIN); + + snd_assert(chip->digital_modes & (1 << mode), return -EINVAL); + + previous_mode = chip->digital_mode; + err = dsp_set_digital_mode(chip, mode); + + /* If we successfully changed the digital mode from or to ADAT, + then make sure all output, input and monitor levels are + updated by the DSP comm object. */ + if (err >= 0 && previous_mode != mode && + (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) { + spin_lock_irq(&chip->lock); + for (o = 0; o < num_busses_out(chip); o++) + for (i = 0; i < num_busses_in(chip); i++) + set_monitor_gain(chip, o, i, + chip->monitor_gain[o][i]); + +#ifdef ECHOCARD_HAS_INPUT_GAIN + for (i = 0; i < num_busses_in(chip); i++) + set_input_gain(chip, i, chip->input_gain[i]); + update_input_line_level(chip); +#endif + + for (o = 0; o < num_busses_out(chip); o++) + set_output_gain(chip, o, chip->output_gain[o]); + update_output_line_level(chip); + spin_unlock_irq(&chip->lock); + } + + return err; +} + + + +/* Set the S/PDIF output format */ +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + u32 control_reg; + int err; + + /* Clear the current S/PDIF flags */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_SPDIF_FORMAT_CLEAR_MASK; + + /* Set the new S/PDIF flags depending on the mode */ + control_reg |= GML_SPDIF_TWO_CHANNEL | GML_SPDIF_24_BIT | + GML_SPDIF_COPY_PERMIT; + if (prof) { + /* Professional mode */ + control_reg |= GML_SPDIF_PRO_MODE; + + switch (chip->sample_rate) { + case 32000: + control_reg |= GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + control_reg |= GML_SPDIF_SAMPLE_RATE0; + break; + case 48000: + control_reg |= GML_SPDIF_SAMPLE_RATE1; + break; + } + } else { + /* Consumer mode */ + switch (chip->sample_rate) { + case 32000: + control_reg |= GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 48000: + control_reg |= GML_SPDIF_SAMPLE_RATE1; + break; + } + } + + if ((err = write_control_reg(chip, control_reg, FALSE))) + return err; + chip->professional_spdif = prof; + DE_ACT(("set_professional_spdif to %s\n", + prof ? "Professional" : "Consumer")); + return 0; +} diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c new file mode 100644 index 0000000..29d6d12 --- /dev/null +++ b/sound/pci/echoaudio/gina20.c @@ -0,0 +1,103 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_GINA20 +#define ECHOCARD_NAME "Gina20" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_INPUT_GAIN +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT FALSE + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 2 */ +#define PX_ANALOG_IN 10 /* 2 */ +#define PX_DIGITAL_IN 12 /* 2 */ +#define PX_NUM 14 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 2 */ +#define BX_ANALOG_IN 10 /* 2 */ +#define BX_DIGITAL_IN 12 /* 2 */ +#define BX_NUM 14 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_GINA20_DSP 0 + +static const struct firmware card_fw[] = { + {0, "gina20_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0}, /* DSP 56301 Gina20 rev.0 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "gina20_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c new file mode 100644 index 0000000..2757c89 --- /dev/null +++ b/sound/pci/echoaudio/gina20_dsp.c @@ -0,0 +1,215 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int update_flags(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Gina20\n")); + snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP]; + chip->spdif_status = GD_SPDIF_STATUS_UNDEF; + chip->clock_state = GD_CLOCK_UNDEF; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + return clock_bits; +} + + + +/* The Gina20 has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u8 clock_state, spdif_status; + + if (wait_handshake(chip)) + return -EIO; + + switch (rate) { + case 44100: + clock_state = GD_CLOCK_44; + spdif_status = GD_SPDIF_STATUS_44; + break; + case 48000: + clock_state = GD_CLOCK_48; + spdif_status = GD_SPDIF_STATUS_48; + break; + default: + clock_state = GD_CLOCK_NOCHANGE; + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + break; + } + + if (chip->clock_state == clock_state) + clock_state = GD_CLOCK_NOCHANGE; + if (spdif_status == chip->spdif_status) + spdif_status = GD_SPDIF_STATUS_NOCHANGE; + + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->comm_page->gd_clock_state = clock_state; + chip->comm_page->gd_spdif_status = spdif_status; + chip->comm_page->gd_resampler_state = 3; /* magic number - should always be 3 */ + + /* Save the new audio state if it changed */ + if (clock_state != GD_CLOCK_NOCHANGE) + chip->clock_state = clock_state; + if (spdif_status != GD_SPDIF_STATUS_NOCHANGE) + chip->spdif_status = spdif_status; + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + DE_ACT(("set_input_clock:\n")); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + /* Reset the audio state to unknown (just in case) */ + chip->clock_state = GD_CLOCK_UNDEF; + chip->spdif_status = GD_SPDIF_STATUS_UNDEF; + set_sample_rate(chip, chip->sample_rate); + chip->input_clock = clock; + DE_ACT(("Set Gina clock to INTERNAL\n")); + break; + case ECHO_CLOCK_SPDIF: + chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN; + chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_NOCHANGE; + clear_handshake(chip); + send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE); + chip->clock_state = GD_CLOCK_SPDIFIN; + DE_ACT(("Set Gina20 clock to SPDIF\n")); + chip->input_clock = clock; + break; + default: + return -EINVAL; + } + + return 0; +} + + + +/* Set input bus gain (one unit is 0.5dB !) */ +static int set_input_gain(struct echoaudio *chip, u16 input, int gain) +{ + snd_assert(input < num_busses_in(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->input_gain[input] = gain; + gain += GL20_INPUT_GAIN_MAGIC_NUMBER; + chip->comm_page->line_in_level[input] = gain; + return 0; +} + + + +/* Tell the DSP to reread the flags from the comm page */ +static int update_flags(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + DE_ACT(("set_professional_spdif %d\n", prof)); + if (prof) + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + chip->professional_spdif = prof; + return update_flags(chip); +} diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c new file mode 100644 index 0000000..e464d72 --- /dev/null +++ b/sound/pci/echoaudio/gina24.c @@ -0,0 +1,123 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_GINA24 +#define ECHOCARD_NAME "Gina24" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 8 */ +#define PX_ANALOG_IN 16 /* 2 */ +#define PX_DIGITAL_IN 18 /* 8 */ +#define PX_NUM 26 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 8 */ +#define BX_ANALOG_IN 16 /* 2 */ +#define BX_DIGITAL_IN 18 /* 8 */ +#define BX_NUM 26 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_GINA24_301_DSP 1 +#define FW_GINA24_361_DSP 2 +#define FW_GINA24_301_ASIC 3 +#define FW_GINA24_361_ASIC 4 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "gina24_301_dsp.fw"}, + {0, "gina24_361_dsp.fw"}, + {0, "gina24_301_asic.fw"}, + {0, "gina24_361_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56301 Gina24 rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56301 Gina24 rev.1 */ + {0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0}, /* DSP 56361 Gina24 rev.0 */ + {0x1057, 0x3410, 0xECC0, 0x0051, 0, 0, 0}, /* DSP 56361 Gina24 rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. + 220 ~= (512 - 1 - (BUFFER_BYTES_MAX / PAGE_SIZE)) / 2 */ +}; + +#include "gina24_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_gml.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c new file mode 100644 index 0000000..144fc56 --- /dev/null +++ b/sound/pci/echoaudio/gina24_dsp.c @@ -0,0 +1,346 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int write_control_reg(struct echoaudio *chip, u32 value, char force); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Gina24\n")); + snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 | + ECHO_CLOCK_BIT_ADAT; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + + /* Gina24 comes in both '301 and '361 flavors */ + if (chip->device_id == DEVICE_ID_56361) { + chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP]; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + } else { + chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP]; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM; + } + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ESYNC) + clock_bits |= ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96; + + return clock_bits; +} + + + +/* Gina24 has an ASIC on the PCI card which must be loaded for anything +interesting to happen. */ +static int load_asic(struct echoaudio *chip) +{ + u32 control_reg; + int err; + const struct firmware *fw; + + if (chip->asic_loaded) + return 1; + + /* Give the DSP a few milliseconds to settle down */ + mdelay(10); + + /* Pick the correct ASIC for '301 or '361 Gina24 */ + if (chip->device_id == DEVICE_ID_56361) + fw = &card_fw[FW_GINA24_361_ASIC]; + else + fw = &card_fw[FW_GINA24_301_ASIC]; + + if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0) + return err; + + chip->asic_code = fw; + + /* Now give the new ASIC a little time to set up */ + mdelay(10); + /* See if it worked */ + err = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + 48 kHz, internal clock, S/PDIF RCA mode */ + if (!err) { + control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; + err = write_control_reg(chip, control_reg, TRUE); + } + DE_INIT(("load_asic() done\n")); + return err; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock; + + snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, + return -EINVAL); + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + clock = 0; + + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK; + + switch (rate) { + case 96000: + clock = GML_96KHZ; + break; + case 88200: + clock = GML_88KHZ; + break; + case 48000: + clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + clock = GML_44KHZ; + /* Professional mode ? */ + if (control_reg & GML_SPDIF_PRO_MODE) + clock |= GML_SPDIF_SAMPLE_RATE0; + break; + case 32000: + clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 22050: + clock = GML_22KHZ; + break; + case 16000: + clock = GML_16KHZ; + break; + case 11025: + clock = GML_11KHZ; + break; + case 8000: + clock = GML_8KHZ; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + control_reg |= clock; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->sample_rate = rate; + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + + return write_control_reg(chip, control_reg, FALSE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + + DE_ACT(("set_input_clock:\n")); + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + GML_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Gina24 clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Gina24 clock to SPDIF\n")); + control_reg |= GML_SPDIF_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ADAT: + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + DE_ACT(("Set Gina24 clock to ADAT\n")); + control_reg |= GML_ADAT_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ESYNC: + DE_ACT(("Set Gina24 clock to ESYNC\n")); + control_reg |= GML_ESYNC_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ESYNC96: + DE_ACT(("Set Gina24 clock to ESYNC96\n")); + control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, TRUE); +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_CDROM: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + spin_lock_irq(&chip->lock); + + if (incompatible_clock) { /* Switch to 48KHz, internal */ + chip->sample_rate = 48000; + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + } + + /* Clear the current digital mode */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_DIGITAL_MODE_CLEAR_MASK; + + /* Tweak the control reg */ + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= GML_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_CDROM: + /* '361 Gina24 cards do not have the S/PDIF CD-ROM mode */ + if (chip->device_id == DEVICE_ID_56301) + control_reg |= GML_SPDIF_CDROM_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* GML_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + control_reg |= GML_ADAT_MODE; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + } + + err = write_control_reg(chip, control_reg, TRUE); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode to %d\n", chip->digital_mode)); + return incompatible_clock; +} diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c new file mode 100644 index 0000000..bfd2467 --- /dev/null +++ b/sound/pci/echoaudio/indigo.c @@ -0,0 +1,104 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define INDIGO_FAMILY +#define ECHOCARD_INDIGO +#define ECHOCARD_NAME "Indigo" +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 0 */ +#define PX_DIGITAL_IN 8 /* 0 */ +#define PX_NUM 8 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 2 */ +#define BX_DIGITAL_OUT 2 /* 0 */ +#define BX_ANALOG_IN 2 /* 0 */ +#define BX_DIGITAL_IN 2 /* 0 */ +#define BX_NUM 2 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_INDIGO_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "indigo_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0}, /* Indigo */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "indigo_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" + diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c new file mode 100644 index 0000000..d6ac773 --- /dev/null +++ b/sound/pci/echoaudio/indigo_dsp.c @@ -0,0 +1,170 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Indigo\n")); + snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + /* Default routing of the virtual channels: all vchannels are routed + to the stereo output */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 0, 2, 0); + set_vmixer_gain(chip, 1, 3, 0); + set_vmixer_gain(chip, 0, 4, 0); + set_vmixer_gain(chip, 1, 5, 0); + set_vmixer_gain(chip, 0, 6, 0); + set_vmixer_gain(chip, 1, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The Indigo has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg; + + switch (rate) { + case 96000: + control_reg = MIA_96000; + break; + case 88200: + control_reg = MIA_88200; + break; + case 48000: + control_reg = MIA_48000; + break; + case 44100: + control_reg = MIA_44100; + break; + case 32000: + control_reg = MIA_32000; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + /* Set the control register if it has changed */ + if (control_reg != le32_to_cpu(chip->comm_page->control_register)) { + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->comm_page->control_register = cpu_to_le32(control_reg); + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); + } + return 0; +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c new file mode 100644 index 0000000..8ed7ff1 --- /dev/null +++ b/sound/pci/echoaudio/indigodj.c @@ -0,0 +1,104 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define INDIGO_FAMILY +#define ECHOCARD_INDIGO_DJ +#define ECHOCARD_NAME "Indigo DJ" +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 0 */ +#define PX_DIGITAL_IN 8 /* 0 */ +#define PX_NUM 8 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 4 */ +#define BX_DIGITAL_OUT 4 /* 0 */ +#define BX_ANALOG_IN 4 /* 0 */ +#define BX_DIGITAL_IN 4 /* 0 */ +#define BX_NUM 4 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_INDIGO_DJ_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "indigo_dj_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0}, /* Indigo DJ*/ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 4, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "indigodj_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" + diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c new file mode 100644 index 0000000..500e150 --- /dev/null +++ b/sound/pci/echoaudio/indigodj_dsp.c @@ -0,0 +1,170 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Indigo DJ\n")); + snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + /* Default routing of the virtual channels: vchannels 0-3 and + vchannels 4-7 are routed to real channels 0-4 */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 2, 2, 0); + set_vmixer_gain(chip, 3, 3, 0); + set_vmixer_gain(chip, 0, 4, 0); + set_vmixer_gain(chip, 1, 5, 0); + set_vmixer_gain(chip, 2, 6, 0); + set_vmixer_gain(chip, 3, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The IndigoDJ has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg; + + switch (rate) { + case 96000: + control_reg = MIA_96000; + break; + case 88200: + control_reg = MIA_88200; + break; + case 48000: + control_reg = MIA_48000; + break; + case 44100: + control_reg = MIA_44100; + break; + case 32000: + control_reg = MIA_32000; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + /* Set the control register if it has changed */ + if (control_reg != le32_to_cpu(chip->comm_page->control_register)) { + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->comm_page->control_register = cpu_to_le32(control_reg); + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); + } + return 0; +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c new file mode 100644 index 0000000..a8788e9 --- /dev/null +++ b/sound/pci/echoaudio/indigoio.c @@ -0,0 +1,105 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define INDIGO_FAMILY +#define ECHOCARD_INDIGO_IO +#define ECHOCARD_NAME "Indigo IO" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 0 */ +#define PX_NUM 10 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 2 */ +#define BX_DIGITAL_OUT 2 /* 0 */ +#define BX_ANALOG_IN 2 /* 2 */ +#define BX_DIGITAL_IN 4 /* 0 */ +#define BX_NUM 4 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_INDIGO_IO_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "indigo_io_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0}, /* Indigo IO*/ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 32000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, +}; + +#include "indigoio_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" + diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c new file mode 100644 index 0000000..f3ad13d --- /dev/null +++ b/sound/pci/echoaudio/indigoio_dsp.c @@ -0,0 +1,141 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Indigo IO\n")); + snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + /* Default routing of the virtual channels: all vchannels are routed + to the stereo output */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 0, 2, 0); + set_vmixer_gain(chip, 1, 3, 0); + set_vmixer_gain(chip, 0, 4, 0); + set_vmixer_gain(chip, 1, 5, 0); + set_vmixer_gain(chip, 0, 6, 0); + set_vmixer_gain(chip, 1, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + return ECHO_CLOCK_BIT_INTERNAL; +} + + + +/* The IndigoIO has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + if (wait_handshake(chip)) + return -EIO; + + chip->sample_rate = rate; + chip->comm_page->sample_rate = cpu_to_le32(rate); + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c new file mode 100644 index 0000000..e503d74 --- /dev/null +++ b/sound/pci/echoaudio/layla20.c @@ -0,0 +1,112 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHOGALS_FAMILY +#define ECHOCARD_LAYLA20 +#define ECHOCARD_NAME "Layla20" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_GAIN +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH +#define ECHOCARD_HAS_MIDI + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 10 */ +#define PX_DIGITAL_OUT 10 /* 2 */ +#define PX_ANALOG_IN 12 /* 8 */ +#define PX_DIGITAL_IN 20 /* 2 */ +#define PX_NUM 22 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 10 */ +#define BX_DIGITAL_OUT 10 /* 2 */ +#define BX_ANALOG_IN 12 /* 8 */ +#define BX_DIGITAL_IN 20 /* 2 */ +#define BX_NUM 22 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_LAYLA20_DSP 0 +#define FW_LAYLA20_ASIC 1 + +static const struct firmware card_fw[] = { + {0, "layla20_dsp.fw"}, + {0, "layla20_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0}, /* DSP 56301 Layla20 rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0}, /* DSP 56301 Layla20 rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 50000, + .channels_min = 1, + .channels_max = 10, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + +#include "layla20_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c new file mode 100644 index 0000000..990c9a6 --- /dev/null +++ b/sound/pci/echoaudio/layla20_dsp.c @@ -0,0 +1,290 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int read_dsp(struct echoaudio *chip, u32 *data); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); +static int update_flags(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Layla20\n")); + snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->has_midi = TRUE; + chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP]; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; + chip->output_clock_types = + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) { + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER) + clock_bits |= ECHO_CLOCK_BIT_SUPER; + else + clock_bits |= ECHO_CLOCK_BIT_WORD; + } + + return clock_bits; +} + + + +/* ASIC status check - some cards have one or two ASICs that need to be +loaded. Once that load is complete, this function is called to see if +the load was successful. +If this load fails, it does not necessarily mean that the hardware is +defective - the external box may be disconnected or turned off. +This routine sometimes fails for Layla20; for Layla20, the loop runs +5 times and succeeds if it wins on three of the loops. */ +static int check_asic_status(struct echoaudio *chip) +{ + u32 asic_status; + int goodcnt, i; + + chip->asic_loaded = FALSE; + for (i = goodcnt = 0; i < 5; i++) { + send_vector(chip, DSP_VC_TEST_ASIC); + + /* The DSP will return a value to indicate whether or not + the ASIC is currently loaded */ + if (read_dsp(chip, &asic_status) < 0) { + DE_ACT(("check_asic_status: failed on read_dsp\n")); + return -EIO; + } + + if (asic_status == ASIC_ALREADY_LOADED) { + if (++goodcnt == 3) { + chip->asic_loaded = TRUE; + return 0; + } + } + } + return -EIO; +} + + + +/* Layla20 has an ASIC in the external box */ +static int load_asic(struct echoaudio *chip) +{ + int err; + + if (chip->asic_loaded) + return 0; + + err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC, + &card_fw[FW_LAYLA20_ASIC]); + if (err < 0) + return err; + + /* Check if ASIC is alive and well. */ + return check_asic_status(chip); +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL); + + /* Only set the clock for internal mode. Do not return failure, + simply treat it as a non-event. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + if (wait_handshake(chip)) + return -EIO; + + DE_ACT(("set_sample_rate(%d)\n", rate)); + chip->sample_rate = rate; + chip->comm_page->sample_rate = cpu_to_le32(rate); + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock_source) +{ + u16 clock; + u32 rate; + + DE_ACT(("set_input_clock:\n")); + rate = 0; + switch (clock_source) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Layla20 clock to INTERNAL\n")); + rate = chip->sample_rate; + clock = LAYLA20_CLOCK_INTERNAL; + break; + case ECHO_CLOCK_SPDIF: + DE_ACT(("Set Layla20 clock to SPDIF\n")); + clock = LAYLA20_CLOCK_SPDIF; + break; + case ECHO_CLOCK_WORD: + DE_ACT(("Set Layla20 clock to WORD\n")); + clock = LAYLA20_CLOCK_WORD; + break; + case ECHO_CLOCK_SUPER: + DE_ACT(("Set Layla20 clock to SUPER\n")); + clock = LAYLA20_CLOCK_SUPER; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Layla24\n", + clock_source)); + return -EINVAL; + } + chip->input_clock = clock_source; + + chip->comm_page->input_clock = cpu_to_le16(clock); + clear_handshake(chip); + send_vector(chip, DSP_VC_UPDATE_CLOCKS); + + if (rate) + set_sample_rate(chip, rate); + + return 0; +} + + + +static int set_output_clock(struct echoaudio *chip, u16 clock) +{ + DE_ACT(("set_output_clock: %d\n", clock)); + switch (clock) { + case ECHO_CLOCK_SUPER: + clock = LAYLA20_OUTPUT_CLOCK_SUPER; + break; + case ECHO_CLOCK_WORD: + clock = LAYLA20_OUTPUT_CLOCK_WORD; + break; + default: + DE_ACT(("set_output_clock wrong clock\n")); + return -EINVAL; + } + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->output_clock = cpu_to_le16(clock); + chip->output_clock = clock; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); +} + + + +/* Set input bus gain (one unit is 0.5dB !) */ +static int set_input_gain(struct echoaudio *chip, u16 input, int gain) +{ + snd_assert(input < num_busses_in(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->input_gain[input] = gain; + gain += GL20_INPUT_GAIN_MAGIC_NUMBER; + chip->comm_page->line_in_level[input] = gain; + return 0; +} + + + +/* Tell the DSP to reread the flags from the comm page */ +static int update_flags(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + DE_ACT(("set_professional_spdif %d\n", prof)); + if (prof) + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + chip->professional_spdif = prof; + return update_flags(chip); +} diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c new file mode 100644 index 0000000..d4581fd --- /dev/null +++ b/sound/pci/echoaudio/layla24.c @@ -0,0 +1,121 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_LAYLA24 +#define ECHOCARD_NAME "Layla24" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 +#define ECHOCARD_HAS_MIDI + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 8 */ +#define PX_ANALOG_IN 16 /* 8 */ +#define PX_DIGITAL_IN 24 /* 8 */ +#define PX_NUM 32 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 8 */ +#define BX_DIGITAL_OUT 8 /* 8 */ +#define BX_ANALOG_IN 16 /* 8 */ +#define BX_DIGITAL_IN 24 /* 8 */ +#define BX_NUM 32 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_LAYLA24_DSP 1 +#define FW_LAYLA24_1_ASIC 2 +#define FW_LAYLA24_2A_ASIC 3 +#define FW_LAYLA24_2S_ASIC 4 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "layla24_dsp.fw"}, + {0, "layla24_1_asic.fw"}, + {0, "layla24_2A_asic.fw"}, + {0, "layla24_2S_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0}, /* DSP 56361 Layla24 rev.0 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 100000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "layla24_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_gml.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c new file mode 100644 index 0000000..7ec5b63 --- /dev/null +++ b/sound/pci/echoaudio/layla24_dsp.c @@ -0,0 +1,394 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int write_control_reg(struct echoaudio *chip, u32 value, char force); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Layla24\n")); + snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->has_midi = TRUE; + chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP]; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD) + clock_bits |= ECHO_CLOCK_BIT_WORD; + + return clock_bits; +} + + + +/* Layla24 has an ASIC on the PCI card and another ASIC in the external box; +both need to be loaded. */ +static int load_asic(struct echoaudio *chip) +{ + int err; + + if (chip->asic_loaded) + return 1; + + DE_INIT(("load_asic\n")); + + /* Give the DSP a few milliseconds to settle down */ + mdelay(10); + + /* Load the ASIC for the PCI card */ + err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC, + &card_fw[FW_LAYLA24_1_ASIC]); + if (err < 0) + return err; + + chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC]; + + /* Now give the new ASIC a little time to set up */ + mdelay(10); + + /* Do the external one */ + err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, + &card_fw[FW_LAYLA24_2S_ASIC]); + if (err < 0) + return FALSE; + + /* Now give the external ASIC a little time to set up */ + mdelay(10); + + /* See if it worked */ + err = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + 48 kHz, internal clock, S/PDIF RCA mode */ + if (!err) + err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ, + TRUE); + + DE_INIT(("load_asic() done\n")); + return err; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock, base_rate; + + snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT, + return -EINVAL); + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + /* Get the control register & clear the appropriate bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK; + + clock = 0; + + switch (rate) { + case 96000: + clock = GML_96KHZ; + break; + case 88200: + clock = GML_88KHZ; + break; + case 48000: + clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + clock = GML_44KHZ; + /* Professional mode */ + if (control_reg & GML_SPDIF_PRO_MODE) + clock |= GML_SPDIF_SAMPLE_RATE0; + break; + case 32000: + clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 22050: + clock = GML_22KHZ; + break; + case 16000: + clock = GML_16KHZ; + break; + case 11025: + clock = GML_11KHZ; + break; + case 8000: + clock = GML_8KHZ; + break; + default: + /* If this is a non-standard rate, then the driver needs to + use Layla24's special "continuous frequency" mode */ + clock = LAYLA24_CONTINUOUS_CLOCK; + if (rate > 50000) { + base_rate = rate >> 1; + control_reg |= GML_DOUBLE_SPEED_MODE; + } else { + base_rate = rate; + } + + if (base_rate < 25000) + base_rate = 25000; + + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = + cpu_to_le32(LAYLA24_MAGIC_NUMBER / base_rate - 2); + + clear_handshake(chip); + send_vector(chip, DSP_VC_SET_LAYLA24_FREQUENCY_REG); + } + + control_reg |= clock; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP ? */ + chip->sample_rate = rate; + DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg)); + + return write_control_reg(chip, control_reg, FALSE); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + GML_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + /* Pick the new clock */ + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Layla24 clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + control_reg |= GML_SPDIF_CLOCK; + /* Layla24 doesn't support 96KHz S/PDIF */ + control_reg &= ~GML_DOUBLE_SPEED_MODE; + DE_ACT(("Set Layla24 clock to SPDIF\n")); + break; + case ECHO_CLOCK_WORD: + control_reg |= GML_WORD_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + DE_ACT(("Set Layla24 clock to WORD\n")); + break; + case ECHO_CLOCK_ADAT: + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + control_reg |= GML_ADAT_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + DE_ACT(("Set Layla24 clock to ADAT\n")); + break; + default: + DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, TRUE); +} + + + +/* Depending on what digital mode you want, Layla24 needs different ASICs +loaded. This function checks the ASIC needed for the new mode and sees +if it matches the one already loaded. */ +static int switch_asic(struct echoaudio *chip, const struct firmware *asic) +{ + s8 *monitors; + + /* Check to see if this is already loaded */ + if (asic != chip->asic_code) { + monitors = kmalloc(MONITOR_ARRAY_SIZE, GFP_KERNEL); + if (! monitors) + return -ENOMEM; + + memcpy(monitors, chip->comm_page->monitors, MONITOR_ARRAY_SIZE); + memset(chip->comm_page->monitors, ECHOGAIN_MUTED, + MONITOR_ARRAY_SIZE); + + /* Load the desired ASIC */ + if (load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC, + asic) < 0) { + memcpy(chip->comm_page->monitors, monitors, + MONITOR_ARRAY_SIZE); + kfree(monitors); + return -EIO; + } + chip->asic_code = asic; + memcpy(chip->comm_page->monitors, monitors, MONITOR_ARRAY_SIZE); + kfree(monitors); + } + + return 0; +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + const struct firmware *asic; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + asic = &card_fw[FW_LAYLA24_2S_ASIC]; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + asic = &card_fw[FW_LAYLA24_2A_ASIC]; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + if (incompatible_clock) { /* Switch to 48KHz, internal */ + chip->sample_rate = 48000; + spin_lock_irq(&chip->lock); + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + spin_unlock_irq(&chip->lock); + } + + /* switch_asic() can sleep */ + if (switch_asic(chip, asic) < 0) + return -EIO; + + spin_lock_irq(&chip->lock); + + /* Tweak the control register */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_DIGITAL_MODE_CLEAR_MASK; + + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= GML_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* GML_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + control_reg |= GML_ADAT_MODE; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + } + + err = write_control_reg(chip, control_reg, TRUE); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode to %d\n", mode)); + return incompatible_clock; +} diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c new file mode 100644 index 0000000..be40c64 --- /dev/null +++ b/sound/pci/echoaudio/mia.c @@ -0,0 +1,117 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_MIA +#define ECHOCARD_NAME "Mia" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_VMIXER +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT FALSE +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 +#define ECHOCARD_HAS_MIDI + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 8 */ +#define PX_DIGITAL_OUT 8 /* 0 */ +#define PX_ANALOG_IN 8 /* 2 */ +#define PX_DIGITAL_IN 10 /* 2 */ +#define PX_NUM 12 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 2 */ +#define BX_DIGITAL_OUT 2 /* 2 */ +#define BX_ANALOG_IN 4 /* 2 */ +#define BX_DIGITAL_IN 6 /* 2 */ +#define BX_NUM 8 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_MIA_DSP 1 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "mia_dsp.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0}, /* DSP 56361 Mia rev.0 */ + {0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0}, /* DSP 56361 Mia rev.1 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "mia_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio.c" +#include "midi.c" diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c new file mode 100644 index 0000000..891c705 --- /dev/null +++ b/sound/pci/echoaudio/mia_dsp.c @@ -0,0 +1,229 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int update_flags(struct echoaudio *chip); +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain); +static int update_vmixer_level(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Mia\n")); + snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->dsp_code_to_load = &card_fw[FW_MIA_DSP]; + /* Since this card has no ASIC, mark it as loaded so everything + works OK */ + chip->asic_loaded = TRUE; + if ((subdevice_id & 0x0000f) == MIA_MIDI_REV) + chip->has_midi = TRUE; + chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL | + ECHO_CLOCK_BIT_SPDIF; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip))) + return err; + + /* Default routing of the virtual channels: vchannels 0-3 go to analog + outputs and vchannels 4-7 go to S/PDIF outputs */ + set_vmixer_gain(chip, 0, 0, 0); + set_vmixer_gain(chip, 1, 1, 0); + set_vmixer_gain(chip, 0, 2, 0); + set_vmixer_gain(chip, 1, 3, 0); + set_vmixer_gain(chip, 2, 4, 0); + set_vmixer_gain(chip, 3, 5, 0); + set_vmixer_gain(chip, 2, 6, 0); + set_vmixer_gain(chip, 3, 7, 0); + err = update_vmixer_level(chip); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + return clock_bits; +} + + + +/* The Mia has no ASIC. Just do nothing */ +static int load_asic(struct echoaudio *chip) +{ + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg; + + switch (rate) { + case 96000: + control_reg = MIA_96000; + break; + case 88200: + control_reg = MIA_88200; + break; + case 48000: + control_reg = MIA_48000; + break; + case 44100: + control_reg = MIA_44100; + break; + case 32000: + control_reg = MIA_32000; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + /* Override the clock setting if this Mia is set to S/PDIF clock */ + if (chip->input_clock == ECHO_CLOCK_SPDIF) + control_reg |= MIA_SPDIF; + + /* Set the control register if it has changed */ + if (control_reg != le32_to_cpu(chip->comm_page->control_register)) { + if (wait_handshake(chip)) + return -EIO; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->comm_page->control_register = cpu_to_le32(control_reg); + chip->sample_rate = rate; + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_CLOCKS); + } + return 0; +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + DE_ACT(("set_input_clock(%d)\n", clock)); + snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF, + return -EINVAL); + + chip->input_clock = clock; + return set_sample_rate(chip, chip->sample_rate); +} + + + +/* This function routes the sound from a virtual channel to a real output */ +static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe, + int gain) +{ + int index; + + snd_assert(pipe < num_pipes_out(chip) && + output < num_busses_out(chip), return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + chip->vmixer_gain[output][pipe] = gain; + index = output * num_pipes_out(chip) + pipe; + chip->comm_page->vmixer[index] = gain; + + DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain)); + return 0; +} + + + +/* Tell the DSP to read and update virtual mixer levels in comm page. */ +static int update_vmixer_level(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_SET_VMIXER_GAIN); +} + + + +/* Tell the DSP to reread the flags from the comm page */ +static int update_flags(struct echoaudio *chip) +{ + if (wait_handshake(chip)) + return -EIO; + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +static int set_professional_spdif(struct echoaudio *chip, char prof) +{ + DE_ACT(("set_professional_spdif %d\n", prof)); + if (prof) + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF); + chip->professional_spdif = prof; + return update_flags(chip); +} + diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c new file mode 100644 index 0000000..e31f0f1 --- /dev/null +++ b/sound/pci/echoaudio/midi.c @@ -0,0 +1,327 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +/****************************************************************************** + MIDI lowlevel code +******************************************************************************/ + +/* Start and stop Midi input */ +static int enable_midi_input(struct echoaudio *chip, char enable) +{ + DE_MID(("enable_midi_input(%d)\n", enable)); + + if (wait_handshake(chip)) + return -EIO; + + if (enable) { + chip->mtc_state = MIDI_IN_STATE_NORMAL; + chip->comm_page->flags |= + __constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT); + } else + chip->comm_page->flags &= + ~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT); + + clear_handshake(chip); + return send_vector(chip, DSP_VC_UPDATE_FLAGS); +} + + + +/* Send a buffer full of MIDI data to the DSP +Returns how many actually written or < 0 on error */ +static int write_midi(struct echoaudio *chip, u8 *data, int bytes) +{ + snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL); + + if (wait_handshake(chip)) + return -EIO; + + /* HF4 indicates that it is safe to write MIDI output data */ + if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4)) + return 0; + + chip->comm_page->midi_output[0] = bytes; + memcpy(&chip->comm_page->midi_output[1], data, bytes); + chip->comm_page->midi_out_free_count = 0; + clear_handshake(chip); + send_vector(chip, DSP_VC_MIDI_WRITE); + DE_MID(("write_midi: %d\n", bytes)); + return bytes; +} + + + +/* Run the state machine for MIDI input data +MIDI time code sync isn't supported by this code right now, but you still need +this state machine to parse the incoming MIDI data stream. Every time the DSP +sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data +stream. The DSP sample position is represented as a 32 bit unsigned value, +with the high 16 bits first, followed by the low 16 bits. Since these aren't +real MIDI bytes, the following logic is needed to skip them. */ +static inline int mtc_process_data(struct echoaudio *chip, short midi_byte) +{ + switch (chip->mtc_state) { + case MIDI_IN_STATE_NORMAL: + if (midi_byte == 0xF1) + chip->mtc_state = MIDI_IN_STATE_TS_HIGH; + break; + case MIDI_IN_STATE_TS_HIGH: + chip->mtc_state = MIDI_IN_STATE_TS_LOW; + return MIDI_IN_SKIP_DATA; + break; + case MIDI_IN_STATE_TS_LOW: + chip->mtc_state = MIDI_IN_STATE_F1_DATA; + return MIDI_IN_SKIP_DATA; + break; + case MIDI_IN_STATE_F1_DATA: + chip->mtc_state = MIDI_IN_STATE_NORMAL; + break; + } + return 0; +} + + + +/* This function is called from the IRQ handler and it reads the midi data +from the DSP's buffer. It returns the number of bytes received. */ +static int midi_service_irq(struct echoaudio *chip) +{ + short int count, midi_byte, i, received; + + /* The count is at index 0, followed by actual data */ + count = le16_to_cpu(chip->comm_page->midi_input[0]); + + snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0); + + /* Get the MIDI data from the comm page */ + i = 1; + received = 0; + for (i = 1; i <= count; i++) { + /* Get the MIDI byte */ + midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]); + + /* Parse the incoming MIDI stream. The incoming MIDI data + consists of MIDI bytes and timestamps for the MIDI time code + 0xF1 bytes. mtc_process_data() is a little state machine that + parses the stream. If you get MIDI_IN_SKIP_DATA back, then + this is a timestamp byte, not a MIDI byte, so don't store it + in the MIDI input buffer. */ + if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA) + continue; + + chip->midi_buffer[received++] = (u8)midi_byte; + } + + return received; +} + + + + +/****************************************************************************** + MIDI interface +******************************************************************************/ + +static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->midi_in = substream; + DE_MID(("rawmidi_iopen\n")); + return 0; +} + + + +static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + if (up != chip->midi_input_enabled) { + spin_lock_irq(&chip->lock); + enable_midi_input(chip, up); + spin_unlock_irq(&chip->lock); + chip->midi_input_enabled = up; + } +} + + + +static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->midi_in = NULL; + DE_MID(("rawmidi_iclose\n")); + return 0; +} + + + +static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->tinuse = 0; + chip->midi_full = 0; + chip->midi_out = substream; + DE_MID(("rawmidi_oopen\n")); + return 0; +} + + + +static void snd_echo_midi_output_write(unsigned long data) +{ + struct echoaudio *chip = (struct echoaudio *)data; + unsigned long flags; + int bytes, sent, time; + unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1]; + + DE_MID(("snd_echo_midi_output_write\n")); + /* No interrupts are involved: we have to check at regular intervals + if the card's output buffer has room for new data. */ + sent = bytes = 0; + spin_lock_irqsave(&chip->lock, flags); + chip->midi_full = 0; + if (chip->midi_out && !snd_rawmidi_transmit_empty(chip->midi_out)) { + bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf, + MIDI_OUT_BUFFER_SIZE - 1); + DE_MID(("Try to send %d bytes...\n", bytes)); + sent = write_midi(chip, buf, bytes); + if (sent < 0) { + snd_printk(KERN_ERR "write_midi() error %d\n", sent); + /* retry later */ + sent = 9000; + chip->midi_full = 1; + } else if (sent > 0) { + DE_MID(("%d bytes sent\n", sent)); + snd_rawmidi_transmit_ack(chip->midi_out, sent); + } else { + /* Buffer is full. DSP's internal buffer is 64 (128 ?) + bytes long. Let's wait until half of them are sent */ + DE_MID(("Full\n")); + sent = 32; + chip->midi_full = 1; + } + } + + /* We restart the timer only if there is some data left to send */ + if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) { + /* The timer will expire slightly after the data has been + sent */ + time = (sent << 3) / 25 + 1; /* 8/25=0.32ms to send a byte */ + mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000); + DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000))); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + + + +static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream, + int up) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + DE_MID(("snd_echo_midi_output_trigger(%d)\n", up)); + spin_lock_irq(&chip->lock); + if (up) { + if (!chip->tinuse) { + init_timer(&chip->timer); + chip->timer.function = snd_echo_midi_output_write; + chip->timer.data = (unsigned long)chip; + chip->tinuse = 1; + } + } else { + if (chip->tinuse) { + del_timer(&chip->timer); + chip->tinuse = 0; + DE_MID(("Timer removed\n")); + } + } + spin_unlock_irq(&chip->lock); + + if (up && !chip->midi_full) + snd_echo_midi_output_write((unsigned long)chip); +} + + + +static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream) +{ + struct echoaudio *chip = substream->rmidi->private_data; + + chip->midi_out = NULL; + DE_MID(("rawmidi_oclose\n")); + return 0; +} + + + +static struct snd_rawmidi_ops snd_echo_midi_input = { + .open = snd_echo_midi_input_open, + .close = snd_echo_midi_input_close, + .trigger = snd_echo_midi_input_trigger, +}; + +static struct snd_rawmidi_ops snd_echo_midi_output = { + .open = snd_echo_midi_output_open, + .close = snd_echo_midi_output_close, + .trigger = snd_echo_midi_output_trigger, +}; + + + +/* <--snd_echo_probe() */ +static int __devinit snd_echo_midi_create(struct snd_card *card, + struct echoaudio *chip) +{ + int err; + + if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1, + &chip->rmidi)) < 0) + return err; + + strcpy(chip->rmidi->name, card->shortname); + chip->rmidi->private_data = chip; + + snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT, + &snd_echo_midi_input); + snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, + &snd_echo_midi_output); + + chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + DE_INIT(("MIDI ok\n")); + return 0; +} diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c new file mode 100644 index 0000000..5dc512a --- /dev/null +++ b/sound/pci/echoaudio/mona.c @@ -0,0 +1,129 @@ +/* + * ALSA driver for Echoaudio soundcards. + * Copyright (C) 2003-2004 Giuliano Pochini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define ECHO24_FAMILY +#define ECHOCARD_MONA +#define ECHOCARD_NAME "Mona" +#define ECHOCARD_HAS_MONITOR +#define ECHOCARD_HAS_ASIC +#define ECHOCARD_HAS_SUPER_INTERLEAVE +#define ECHOCARD_HAS_DIGITAL_IO +#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE +#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH +#define ECHOCARD_HAS_EXTERNAL_CLOCK +#define ECHOCARD_HAS_ADAT 6 +#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32 + +/* Pipe indexes */ +#define PX_ANALOG_OUT 0 /* 6 */ +#define PX_DIGITAL_OUT 6 /* 8 */ +#define PX_ANALOG_IN 14 /* 4 */ +#define PX_DIGITAL_IN 18 /* 8 */ +#define PX_NUM 26 + +/* Bus indexes */ +#define BX_ANALOG_OUT 0 /* 6 */ +#define BX_DIGITAL_OUT 6 /* 8 */ +#define BX_ANALOG_IN 14 /* 4 */ +#define BX_DIGITAL_IN 18 /* 8 */ +#define BX_NUM 26 + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "echoaudio.h" + +#define FW_361_LOADER 0 +#define FW_MONA_301_DSP 1 +#define FW_MONA_361_DSP 2 +#define FW_MONA_301_1_ASIC48 3 +#define FW_MONA_301_1_ASIC96 4 +#define FW_MONA_361_1_ASIC48 5 +#define FW_MONA_361_1_ASIC96 6 +#define FW_MONA_2_ASIC 7 + +static const struct firmware card_fw[] = { + {0, "loader_dsp.fw"}, + {0, "mona_301_dsp.fw"}, + {0, "mona_361_dsp.fw"}, + {0, "mona_301_1_asic_48.fw"}, + {0, "mona_301_1_asic_96.fw"}, + {0, "mona_361_1_asic_48.fw"}, + {0, "mona_361_1_asic_96.fw"}, + {0, "mona_2_asic.fw"} +}; + +static struct pci_device_id snd_echo_ids[] = { + {0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56301 Mona rev.0 */ + {0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56301 Mona rev.1 */ + {0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56301 Mona rev.2 */ + {0x1057, 0x3410, 0xECC0, 0x0070, 0, 0, 0}, /* DSP 56361 Mona rev.0 */ + {0x1057, 0x3410, 0xECC0, 0x0071, 0, 0, 0}, /* DSP 56361 Mona rev.1 */ + {0x1057, 0x3410, 0xECC0, 0x0072, 0, 0, 0}, /* DSP 56361 Mona rev.2 */ + {0,} +}; + +static struct snd_pcm_hardware pcm_hardware_skel = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S32_BE, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = 262144, + .period_bytes_min = 32, + .period_bytes_max = 131072, + .periods_min = 2, + .periods_max = 220, + /* One page (4k) contains 512 instructions. I don't know if the hw + supports lists longer than this. In this case periods_max=220 is a + safe limit to make sure the list never exceeds 512 instructions. */ +}; + + +#include "mona_dsp.c" +#include "echoaudio_dsp.c" +#include "echoaudio_gml.c" +#include "echoaudio.c" diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c new file mode 100644 index 0000000..c0b4bf0 --- /dev/null +++ b/sound/pci/echoaudio/mona_dsp.c @@ -0,0 +1,428 @@ +/**************************************************************************** + + Copyright Echo Digital Audio Corporation (c) 1998 - 2004 + All rights reserved + www.echoaudio.com + + This file is part of Echo Digital Audio's generic driver library. + + Echo Digital Audio's generic driver library is free software; + you can redistribute it and/or modify it under the terms of + the GNU General Public License as published by the Free Software + Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA. + + ************************************************************************* + + Translation from C++ and adaptation for use in ALSA-Driver + were made by Giuliano Pochini + +****************************************************************************/ + + +static int write_control_reg(struct echoaudio *chip, u32 value, char force); +static int set_input_clock(struct echoaudio *chip, u16 clock); +static int set_professional_spdif(struct echoaudio *chip, char prof); +static int set_digital_mode(struct echoaudio *chip, u8 mode); +static int load_asic_generic(struct echoaudio *chip, u32 cmd, + const struct firmware *asic); +static int check_asic_status(struct echoaudio *chip); + + +static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id) +{ + int err; + + DE_INIT(("init_hw() - Mona\n")); + snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV); + + if ((err = init_dsp_comm_page(chip))) { + DE_INIT(("init_hw - could not initialize DSP comm page\n")); + return err; + } + + chip->device_id = device_id; + chip->subdevice_id = subdevice_id; + chip->bad_board = TRUE; + chip->input_clock_types = + ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF | + ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT; + chip->digital_modes = + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA | + ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL | + ECHOCAPS_HAS_DIGITAL_MODE_ADAT; + + /* Mona comes in both '301 and '361 flavors */ + if (chip->device_id == DEVICE_ID_56361) + chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP]; + else + chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP]; + + chip->digital_mode = DIGITAL_MODE_SPDIF_RCA; + chip->professional_spdif = FALSE; + chip->digital_in_automute = TRUE; + + if ((err = load_firmware(chip)) < 0) + return err; + chip->bad_board = FALSE; + + if ((err = init_line_levels(chip)) < 0) + return err; + + err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA); + snd_assert(err >= 0, return err); + err = set_professional_spdif(chip, TRUE); + + DE_INIT(("init_hw done\n")); + return err; +} + + + +static u32 detect_input_clocks(const struct echoaudio *chip) +{ + u32 clocks_from_dsp, clock_bits; + + /* Map the DSP clock detect bits to the generic driver clock + detect bits */ + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + clock_bits = ECHO_CLOCK_BIT_INTERNAL; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF) + clock_bits |= ECHO_CLOCK_BIT_SPDIF; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT) + clock_bits |= ECHO_CLOCK_BIT_ADAT; + + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD) + clock_bits |= ECHO_CLOCK_BIT_WORD; + + return clock_bits; +} + + + +/* Mona has an ASIC on the PCI card and another ASIC in the external box; +both need to be loaded. */ +static int load_asic(struct echoaudio *chip) +{ + u32 control_reg; + int err; + const struct firmware *asic; + + if (chip->asic_loaded) + return 0; + + mdelay(10); + + if (chip->device_id == DEVICE_ID_56361) + asic = &card_fw[FW_MONA_361_1_ASIC48]; + else + asic = &card_fw[FW_MONA_301_1_ASIC48]; + + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic); + if (err < 0) + return err; + + chip->asic_code = asic; + mdelay(10); + + /* Do the external one */ + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC, + &card_fw[FW_MONA_2_ASIC]); + if (err < 0) + return err; + + mdelay(10); + err = check_asic_status(chip); + + /* Set up the control register if the load succeeded - + 48 kHz, internal clock, S/PDIF RCA mode */ + if (!err) { + control_reg = GML_CONVERTER_ENABLE | GML_48KHZ; + err = write_control_reg(chip, control_reg, TRUE); + } + + return err; +} + + + +/* Depending on what digital mode you want, Mona needs different ASICs +loaded. This function checks the ASIC needed for the new mode and sees +if it matches the one already loaded. */ +static int switch_asic(struct echoaudio *chip, char double_speed) +{ + const struct firmware *asic; + int err; + + /* Check the clock detect bits to see if this is + a single-speed clock or a double-speed clock; load + a new ASIC if necessary. */ + if (chip->device_id == DEVICE_ID_56361) { + if (double_speed) + asic = &card_fw[FW_MONA_361_1_ASIC96]; + else + asic = &card_fw[FW_MONA_361_1_ASIC48]; + } else { + if (double_speed) + asic = &card_fw[FW_MONA_301_1_ASIC96]; + else + asic = &card_fw[FW_MONA_301_1_ASIC48]; + } + + if (asic != chip->asic_code) { + /* Load the desired ASIC */ + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, + asic); + if (err < 0) + return err; + chip->asic_code = asic; + } + + return 0; +} + + + +static int set_sample_rate(struct echoaudio *chip, u32 rate) +{ + u32 control_reg, clock; + const struct firmware *asic; + char force_write; + + /* Only set the clock for internal mode. */ + if (chip->input_clock != ECHO_CLOCK_INTERNAL) { + DE_ACT(("set_sample_rate: Cannot set sample rate - " + "clock not set to CLK_CLOCKININTERNAL\n")); + /* Save the rate anyhow */ + chip->comm_page->sample_rate = cpu_to_le32(rate); + chip->sample_rate = rate; + return 0; + } + + /* Now, check to see if the required ASIC is loaded */ + if (rate >= 88200) { + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EINVAL; + if (chip->device_id == DEVICE_ID_56361) + asic = &card_fw[FW_MONA_361_1_ASIC96]; + else + asic = &card_fw[FW_MONA_301_1_ASIC96]; + } else { + if (chip->device_id == DEVICE_ID_56361) + asic = &card_fw[FW_MONA_361_1_ASIC48]; + else + asic = &card_fw[FW_MONA_301_1_ASIC48]; + } + + force_write = 0; + if (asic != chip->asic_code) { + int err; + /* Load the desired ASIC (load_asic_generic() can sleep) */ + spin_unlock_irq(&chip->lock); + err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, + asic); + spin_lock_irq(&chip->lock); + + if (err < 0) + return err; + chip->asic_code = asic; + force_write = 1; + } + + /* Compute the new control register value */ + clock = 0; + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_CLOCK_CLEAR_MASK; + control_reg &= GML_SPDIF_RATE_CLEAR_MASK; + + switch (rate) { + case 96000: + clock = GML_96KHZ; + break; + case 88200: + clock = GML_88KHZ; + break; + case 48000: + clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1; + break; + case 44100: + clock = GML_44KHZ; + /* Professional mode */ + if (control_reg & GML_SPDIF_PRO_MODE) + clock |= GML_SPDIF_SAMPLE_RATE0; + break; + case 32000: + clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 | + GML_SPDIF_SAMPLE_RATE1; + break; + case 22050: + clock = GML_22KHZ; + break; + case 16000: + clock = GML_16KHZ; + break; + case 11025: + clock = GML_11KHZ; + break; + case 8000: + clock = GML_8KHZ; + break; + default: + DE_ACT(("set_sample_rate: %d invalid!\n", rate)); + return -EINVAL; + } + + control_reg |= clock; + + chip->comm_page->sample_rate = cpu_to_le32(rate); /* ignored by the DSP */ + chip->sample_rate = rate; + DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock)); + + return write_control_reg(chip, control_reg, force_write); +} + + + +static int set_input_clock(struct echoaudio *chip, u16 clock) +{ + u32 control_reg, clocks_from_dsp; + int err; + + DE_ACT(("set_input_clock:\n")); + + /* Prevent two simultaneous calls to switch_asic() */ + if (atomic_read(&chip->opencount)) + return -EAGAIN; + + /* Mask off the clock select bits */ + control_reg = le32_to_cpu(chip->comm_page->control_register) & + GML_CLOCK_CLEAR_MASK; + clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks); + + switch (clock) { + case ECHO_CLOCK_INTERNAL: + DE_ACT(("Set Mona clock to INTERNAL\n")); + chip->input_clock = ECHO_CLOCK_INTERNAL; + return set_sample_rate(chip, chip->sample_rate); + case ECHO_CLOCK_SPDIF: + if (chip->digital_mode == DIGITAL_MODE_ADAT) + return -EAGAIN; + spin_unlock_irq(&chip->lock); + err = switch_asic(chip, clocks_from_dsp & + GML_CLOCK_DETECT_BIT_SPDIF96); + spin_lock_irq(&chip->lock); + if (err < 0) + return err; + DE_ACT(("Set Mona clock to SPDIF\n")); + control_reg |= GML_SPDIF_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_WORD: + DE_ACT(("Set Mona clock to WORD\n")); + spin_unlock_irq(&chip->lock); + err = switch_asic(chip, clocks_from_dsp & + GML_CLOCK_DETECT_BIT_WORD96); + spin_lock_irq(&chip->lock); + if (err < 0) + return err; + control_reg |= GML_WORD_CLOCK; + if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96) + control_reg |= GML_DOUBLE_SPEED_MODE; + else + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + case ECHO_CLOCK_ADAT: + DE_ACT(("Set Mona clock to ADAT\n")); + if (chip->digital_mode != DIGITAL_MODE_ADAT) + return -EAGAIN; + control_reg |= GML_ADAT_CLOCK; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + default: + DE_ACT(("Input clock 0x%x not supported for Mona\n", clock)); + return -EINVAL; + } + + chip->input_clock = clock; + return write_control_reg(chip, control_reg, TRUE); +} + + + +static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode) +{ + u32 control_reg; + int err, incompatible_clock; + + /* Set clock to "internal" if it's not compatible with the new mode */ + incompatible_clock = FALSE; + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + case DIGITAL_MODE_SPDIF_RCA: + if (chip->input_clock == ECHO_CLOCK_ADAT) + incompatible_clock = TRUE; + break; + case DIGITAL_MODE_ADAT: + if (chip->input_clock == ECHO_CLOCK_SPDIF) + incompatible_clock = TRUE; + break; + default: + DE_ACT(("Digital mode not supported: %d\n", mode)); + return -EINVAL; + } + + spin_lock_irq(&chip->lock); + + if (incompatible_clock) { /* Switch to 48KHz, internal */ + chip->sample_rate = 48000; + set_input_clock(chip, ECHO_CLOCK_INTERNAL); + } + + /* Clear the current digital mode */ + control_reg = le32_to_cpu(chip->comm_page->control_register); + control_reg &= GML_DIGITAL_MODE_CLEAR_MASK; + + /* Tweak the control reg */ + switch (mode) { + case DIGITAL_MODE_SPDIF_OPTICAL: + control_reg |= GML_SPDIF_OPTICAL_MODE; + break; + case DIGITAL_MODE_SPDIF_RCA: + /* GML_SPDIF_OPTICAL_MODE bit cleared */ + break; + case DIGITAL_MODE_ADAT: + /* If the current ASIC is the 96KHz ASIC, switch the ASIC + and set to 48 KHz */ + if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] || + chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) { + set_sample_rate(chip, 48000); + } + control_reg |= GML_ADAT_MODE; + control_reg &= ~GML_DOUBLE_SPEED_MODE; + break; + } + + err = write_control_reg(chip, control_reg, FALSE); + spin_unlock_irq(&chip->lock); + if (err < 0) + return err; + chip->digital_mode = mode; + + DE_ACT(("set_digital_mode to %d\n", mode)); + return incompatible_clock; +} diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c index 42b11ba..549673e 100644 --- a/sound/pci/emu10k1/emu10k1.c +++ b/sound/pci/emu10k1/emu10k1.c @@ -46,13 +46,13 @@ #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int extin[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static int extout[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int extin[SNDRV_CARDS]; +static int extout[SNDRV_CARDS]; static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4}; static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64}; static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128}; -static int enable_ir[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static uint subsystem[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; /* Force card subsystem model */ +static int enable_ir[SNDRV_CARDS]; +static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard."); diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 6bfa084..d6f135f 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -777,14 +777,6 @@ static int snd_emu10k1_dev_free(struct s static struct snd_emu_chip_details emu_chip_details[] = { /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/ - /* Audigy4 SB0400 */ - {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102, - .driver = "Audigy2", .name = "Audigy 4 [SB0400]", - .id = "Audigy2", - .emu10k2_chip = 1, - .ca0108_chip = 1, - .spk71 = 1, - .ac97_chip = 1} , /* Tested by James@superbug.co.uk 3rd July 2005 */ /* DSP: CA0108-IAT * DAC: CS4382-KQ @@ -799,13 +791,59 @@ static struct snd_emu_chip_details emu_c .ca0108_chip = 1, .spk71 = 1, .ac97_chip = 1} , + /* Audigy4 (Not PRO) SB0610 */ + /* Tested by James@superbug.co.uk 4th April 2006 */ + /* A_IOCFG bits + * Output + * 0: ? + * 1: ? + * 2: ? + * 3: 0 - Digital Out, 1 - Line in + * 4: ? + * 5: ? + * 6: ? + * 7: ? + * Input + * 8: ? + * 9: ? + * A: Green jack sense (Front) + * B: ? + * C: Black jack sense (Rear/Side Right) + * D: Yellow jack sense (Center/LFE/Side Left) + * E: ? + * F: ? + * + * Digital Out/Line in switch using A_IOCFG bit 3 (0x08) + * 0 - Digital Out + * 1 - Line in + */ + /* Mic input not tested. + * Analog CD input not tested + * Digital Out not tested. + * Line in working. + * Audio output 5.1 working. Side outputs not working. + */ + /* DSP: CA10300-IAT LF + * DAC: Cirrus Logic CS4382-KQZ + * ADC: Philips 1361T + * AC97: Sigmatel STAC9750 + * CA0151: None + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10211102, + .driver = "Audigy2", .name = "Audigy 4 [SB0610]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ + .ac97_chip = 1} , /* Audigy 2 ZS Notebook Cardbus card.*/ /* Tested by James@superbug.co.uk 22th December 2005 */ /* Audio output 7.1/Headphones working. * Digital output working. (AC3 not checked, only PCM) * Audio inputs not tested. */ - /* DSP: Tiny2 + /* DSP: Tina2 * DAC: Wolfson WM8768/WM8568 * ADC: Wolfson WM8775 * AC97: None @@ -1195,7 +1233,7 @@ int __devinit snd_emu10k1_create(struct } emu->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_emu10k1_interrupt, SA_INTERRUPT|SA_SHIRQ, "EMU10K1", (void *)emu)) { + if (request_irq(pci->irq, snd_emu10k1_interrupt, IRQF_DISABLED|IRQF_SHARED, "EMU10K1", (void *)emu)) { err = -EBUSY; goto error; } @@ -1421,16 +1459,3 @@ void snd_emu10k1_resume_regs(struct snd_ } } #endif - -/* memory.c */ -EXPORT_SYMBOL(snd_emu10k1_synth_alloc); -EXPORT_SYMBOL(snd_emu10k1_synth_free); -EXPORT_SYMBOL(snd_emu10k1_synth_bzero); -EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); -EXPORT_SYMBOL(snd_emu10k1_memblk_map); -/* voice.c */ -EXPORT_SYMBOL(snd_emu10k1_voice_alloc); -EXPORT_SYMBOL(snd_emu10k1_voice_free); -/* io.c */ -EXPORT_SYMBOL(snd_emu10k1_ptr_read); -EXPORT_SYMBOL(snd_emu10k1_ptr_write); diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c index d51290c..2167279 100644 --- a/sound/pci/emu10k1/emu10k1x.c +++ b/sound/pci/emu10k1/emu10k1x.c @@ -928,7 +928,7 @@ static int __devinit snd_emu10k1x_create } if (request_irq(pci->irq, snd_emu10k1x_interrupt, - SA_INTERRUPT|SA_SHIRQ, "EMU10K1X", + IRQF_DISABLED|IRQF_SHARED, "EMU10K1X", (void *)chip)) { snd_printk(KERN_ERR "emu10k1x: cannot grab irq %d\n", pci->irq); snd_emu10k1x_free(chip); @@ -1055,8 +1055,7 @@ static int __devinit snd_emu10k1x_proc_i struct snd_info_entry *entry; if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read); entry->c.text.write = snd_emu10k1x_proc_reg_write; entry->mode |= S_IWUSR; entry->private_data = emu; diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 2a9d12d..c31f3d0 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -777,6 +777,8 @@ int __devinit snd_emu10k1_mixer(struct s }; static char *audigy_remove_ctls[] = { /* Master/PCM controls on ac97 of Audigy has no effect */ + /* On the Audigy2 the AC97 playback is piped into + * the Philips ADC for 24bit capture */ "PCM Playback Switch", "PCM Playback Volume", "Master Mono Playback Switch", @@ -804,6 +806,47 @@ int __devinit snd_emu10k1_mixer(struct s "AMic Playback Volume", "Mic Playback Volume", NULL }; + static char *audigy_remove_ctls_1361t_adc[] = { + /* On the Audigy2 the AC97 playback is piped into + * the Philips ADC for 24bit capture */ + "PCM Playback Switch", + "PCM Playback Volume", + "Master Mono Playback Switch", + "Master Mono Playback Volume", + "Capture Source", + "Capture Switch", + "Capture Volume", + "Mic Capture Volume", + "Headphone Playback Switch", + "Headphone Playback Volume", + "3D Control - Center", + "3D Control - Depth", + "3D Control - Switch", + "Line2 Playback Volume", + "Line2 Capture Volume", + NULL + }; + static char *audigy_rename_ctls_1361t_adc[] = { + "Master Playback Switch", "Master Capture Switch", + "Master Playback Volume", "Master Capture Volume", + "Wave Master Playback Volume", "Master Playback Volume", + "PC Speaker Playback Switch", "PC Speaker Capture Switch", + "PC Speaker Playback Volume", "PC Speaker Capture Volume", + "Phone Playback Switch", "Phone Capture Switch", + "Phone Playback Volume", "Phone Capture Volume", + "Mic Playback Switch", "Mic Capture Switch", + "Mic Playback Volume", "Mic Capture Volume", + "Line Playback Switch", "Line Capture Switch", + "Line Playback Volume", "Line Capture Volume", + "CD Playback Switch", "CD Capture Switch", + "CD Playback Volume", "CD Capture Volume", + "Aux Playback Switch", "Aux Capture Switch", + "Aux Playback Volume", "Aux Capture Volume", + "Video Playback Switch", "Video Capture Switch", + "Video Playback Volume", "Video Capture Volume", + + NULL + }; if (emu->card_capabilities->ac97_chip) { struct snd_ac97_bus *pbus; @@ -834,7 +877,10 @@ int __devinit snd_emu10k1_mixer(struct s snd_ac97_write_cache(emu->ac97, AC97_MASTER, 0x0000); /* set capture source to mic */ snd_ac97_write_cache(emu->ac97, AC97_REC_SEL, 0x0000); - c = audigy_remove_ctls; + if (emu->card_capabilities->adc_1361t) + c = audigy_remove_ctls_1361t_adc; + else + c = audigy_remove_ctls; } else { /* * Credits for cards based on STAC9758: @@ -863,11 +909,15 @@ int __devinit snd_emu10k1_mixer(struct s } if (emu->audigy) - c = audigy_rename_ctls; + if (emu->card_capabilities->adc_1361t) + c = audigy_rename_ctls_1361t_adc; + else + c = audigy_rename_ctls; else c = emu10k1_rename_ctls; for (; *c; c += 2) rename_ctl(card, c[0], c[1]); + if (emu->card_capabilities->subsystem == 0x20071102) { /* Audigy 4 Pro */ rename_ctl(card, "Line2 Capture Volume", "Line1/Mic Capture Volume"); rename_ctl(card, "Analog Mix Capture Volume", "Line2 Capture Volume"); diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 90f1c52..b939e03 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -532,57 +532,51 @@ int __devinit snd_emu10k1_proc_init(stru struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG if (! snd_card_proc_new(emu->card, "io_regs", &entry)) { - snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read); entry->c.text.write = snd_emu_proc_io_reg_write; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a); entry->c.text.write = snd_emu_proc_ptr_reg_write00; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b); entry->c.text.write = snd_emu_proc_ptr_reg_write00; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a); entry->c.text.write = snd_emu_proc_ptr_reg_write20; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b); entry->c.text.write = snd_emu_proc_ptr_reg_write20; entry->mode |= S_IWUSR; } if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) { - snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20c); - entry->c.text.write_size = 64; + snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c); entry->c.text.write = snd_emu_proc_ptr_reg_write20; entry->mode |= S_IWUSR; } #endif if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read); if (emu->card_capabilities->emu10k2_chip) { if (! snd_card_proc_new(emu->card, "spdif-in", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_spdif_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read); } if (emu->card_capabilities->ca0151_chip) { if (! snd_card_proc_new(emu->card, "capture-rates", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_rates_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read); } if (! snd_card_proc_new(emu->card, "voices", &entry)) - snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read); + snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read); if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; @@ -616,7 +610,6 @@ #endif entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/; - entry->c.text.read_size = 128*1024; entry->c.text.read = snd_emu10k1_proc_acode_read; } return 0; diff --git a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c index ef5304d..029e785 100644 --- a/sound/pci/emu10k1/io.c +++ b/sound/pci/emu10k1/io.c @@ -62,6 +62,8 @@ unsigned int snd_emu10k1_ptr_read(struct } } +EXPORT_SYMBOL(snd_emu10k1_ptr_read); + void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data) { unsigned int regptr; @@ -92,6 +94,8 @@ void snd_emu10k1_ptr_write(struct snd_em } } +EXPORT_SYMBOL(snd_emu10k1_ptr_write); + unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn) diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c index e7ec986..4fcaefe 100644 --- a/sound/pci/emu10k1/memory.c +++ b/sound/pci/emu10k1/memory.c @@ -287,6 +287,8 @@ int snd_emu10k1_memblk_map(struct snd_em return err; } +EXPORT_SYMBOL(snd_emu10k1_memblk_map); + /* * page allocation for DMA */ @@ -387,6 +389,7 @@ snd_emu10k1_synth_alloc(struct snd_emu10 return (struct snd_util_memblk *)blk; } +EXPORT_SYMBOL(snd_emu10k1_synth_alloc); /* * free a synth sample area @@ -409,6 +412,7 @@ snd_emu10k1_synth_free(struct snd_emu10k return 0; } +EXPORT_SYMBOL(snd_emu10k1_synth_free); /* check new allocation range */ static void get_single_page_range(struct snd_util_memhdr *hdr, @@ -540,6 +544,8 @@ int snd_emu10k1_synth_bzero(struct snd_e return 0; } +EXPORT_SYMBOL(snd_emu10k1_synth_bzero); + /* * copy_from_user(blk + offset, data, size) */ @@ -568,3 +574,5 @@ int snd_emu10k1_synth_copy_from_user(str } while (offset < end_offset); return 0; } + +EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user); diff --git a/sound/pci/emu10k1/p17v.h b/sound/pci/emu10k1/p17v.h new file mode 100644 index 0000000..7ddb5be --- /dev/null +++ b/sound/pci/emu10k1/p17v.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) by James Courtier-Dutton + * Driver p17v chips + * Version: 0.01 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/******************************************************************************/ +/* Audigy2Value Tina (P17V) pointer-offset register set, + * accessed through the PTR20 and DATA24 registers */ +/******************************************************************************/ + +/* 00 - 07: Not used */ +#define P17V_PLAYBACK_FIFO_PTR 0x08 /* Current playback fifo pointer + * and number of sound samples in cache. + */ +/* 09 - 12: Not used */ +#define P17V_CAPTURE_FIFO_PTR 0x13 /* Current capture fifo pointer + * and number of sound samples in cache. + */ +/* 14 - 17: Not used */ +#define P17V_PB_CHN_SEL 0x18 /* P17v playback channel select */ +#define P17V_SE_SLOT_SEL_L 0x19 /* Sound Engine slot select low */ +#define P17V_SE_SLOT_SEL_H 0x1a /* Sound Engine slot select high */ +/* 1b - 1f: Not used */ +/* 20 - 2f: Not used */ +/* 30 - 3b: Not used */ +#define P17V_SPI 0x3c /* SPI interface register */ +#define P17V_I2C_ADDR 0x3d /* I2C Address */ +#define P17V_I2C_0 0x3e /* I2C Data */ +#define P17V_I2C_1 0x3f /* I2C Data */ + +#define P17V_START_AUDIO 0x40 /* Start Audio bit */ +/* 41 - 47: Reserved */ +#define P17V_START_CAPTURE 0x48 /* Start Capture bit */ +#define P17V_CAPTURE_FIFO_BASE 0x49 /* Record FIFO base address */ +#define P17V_CAPTURE_FIFO_SIZE 0x4a /* Record FIFO buffer size */ +#define P17V_CAPTURE_FIFO_INDEX 0x4b /* Record FIFO capture index */ +#define P17V_CAPTURE_VOL_H 0x4c /* P17v capture volume control */ +#define P17V_CAPTURE_VOL_L 0x4d /* P17v capture volume control */ +/* 4e - 4f: Not used */ +/* 50 - 5f: Not used */ +#define P17V_SRCSel 0x60 /* SRC48 and SRCMulti sample rate select + * and output select + */ +#define P17V_MIXER_AC97_10K1_VOL_L 0x61 /* 10K to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_10K1_VOL_H 0x62 /* 10K to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_P17V_VOL_L 0x63 /* P17V to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_P17V_VOL_H 0x64 /* P17V to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_SRP_REC_VOL_L 0x65 /* SRP Record to Mixer_AC97 input volume control */ +#define P17V_MIXER_AC97_SRP_REC_VOL_H 0x66 /* SRP Record to Mixer_AC97 input volume control */ +/* 67 - 68: Reserved */ +#define P17V_MIXER_Spdif_10K1_VOL_L 0x69 /* 10K to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_10K1_VOL_H 0x6A /* 10K to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_P17V_VOL_L 0x6B /* P17V to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_P17V_VOL_H 0x6C /* P17V to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_SRP_REC_VOL_L 0x6D /* SRP Record to Mixer_Spdif input volume control */ +#define P17V_MIXER_Spdif_SRP_REC_VOL_H 0x6E /* SRP Record to Mixer_Spdif input volume control */ +/* 6f - 70: Reserved */ +#define P17V_MIXER_I2S_10K1_VOL_L 0x71 /* 10K to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_10K1_VOL_H 0x72 /* 10K to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_P17V_VOL_L 0x73 /* P17V to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_P17V_VOL_H 0x74 /* P17V to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_SRP_REC_VOL_L 0x75 /* SRP Record to Mixer_I2S input volume control */ +#define P17V_MIXER_I2S_SRP_REC_VOL_H 0x76 /* SRP Record to Mixer_I2S input volume control */ +/* 77 - 78: Reserved */ +#define P17V_MIXER_AC97_ENABLE 0x79 /* Mixer AC97 input audio enable */ +#define P17V_MIXER_SPDIF_ENABLE 0x7A /* Mixer SPDIF input audio enable */ +#define P17V_MIXER_I2S_ENABLE 0x7B /* Mixer I2S input audio enable */ +#define P17V_AUDIO_OUT_ENABLE 0x7C /* Audio out enable */ +#define P17V_MIXER_ATT 0x7D /* SRP Mixer Attenuation Select */ +#define P17V_SRP_RECORD_SRR 0x7E /* SRP Record channel source Select */ +#define P17V_SOFT_RESET_SRP_MIXER 0x7F /* SRP and mixer soft reset */ + +#define P17V_AC97_OUT_MASTER_VOL_L 0x80 /* AC97 Output master volume control */ +#define P17V_AC97_OUT_MASTER_VOL_H 0x81 /* AC97 Output master volume control */ +#define P17V_SPDIF_OUT_MASTER_VOL_L 0x82 /* SPDIF Output master volume control */ +#define P17V_SPDIF_OUT_MASTER_VOL_H 0x83 /* SPDIF Output master volume control */ +#define P17V_I2S_OUT_MASTER_VOL_L 0x84 /* I2S Output master volume control */ +#define P17V_I2S_OUT_MASTER_VOL_H 0x85 /* I2S Output master volume control */ +/* 86 - 87: Not used */ +#define P17V_I2S_CHANNEL_SWAP_PHASE_INVERSE 0x88 /* I2S out mono channel swap + * and phase inverse */ +#define P17V_SPDIF_CHANNEL_SWAP_PHASE_INVERSE 0x89 /* SPDIF out mono channel swap + * and phase inverse */ +/* 8A: Not used */ +#define P17V_SRP_P17V_ESR 0x8B /* SRP_P17V estimated sample rate and rate lock */ +#define P17V_SRP_REC_ESR 0x8C /* SRP_REC estimated sample rate and rate lock */ +#define P17V_SRP_BYPASS 0x8D /* srps channel bypass and srps bypass */ +/* 8E - 92: Not used */ +#define P17V_I2S_SRC_SEL 0x93 /* I2SIN mode sel */ + + + + + + diff --git a/sound/pci/emu10k1/tina2.h b/sound/pci/emu10k1/tina2.h index 5c43abf..f2d8eb6 100644 --- a/sound/pci/emu10k1/tina2.h +++ b/sound/pci/emu10k1/tina2.h @@ -1,11 +1,7 @@ /* * Copyright (c) by James Courtier-Dutton - * Driver p16v chips - * Version: 0.21 - * - * - * This code was initally based on code from ALSA's emu10k1x.c which is: - * Copyright (c) by Francisco Moraes + * Driver tina2 chips + * Version: 0.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c index 56ffb7d..94eca82 100644 --- a/sound/pci/emu10k1/voice.c +++ b/sound/pci/emu10k1/voice.c @@ -139,6 +139,8 @@ int snd_emu10k1_voice_alloc(struct snd_e return result; } +EXPORT_SYMBOL(snd_emu10k1_voice_alloc); + int snd_emu10k1_voice_free(struct snd_emu10k1 *emu, struct snd_emu10k1_voice *pvoice) { @@ -153,3 +155,5 @@ int snd_emu10k1_voice_free(struct snd_em spin_unlock_irqrestore(&emu->voice_lock, flags); return 0; } + +EXPORT_SYMBOL(snd_emu10k1_voice_free); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c index ca9e34e..7a985c8 100644 --- a/sound/pci/ens1370.c +++ b/sound/pci/ens1370.c @@ -1915,7 +1915,7 @@ static void __devinit snd_ensoniq_proc_i struct snd_info_entry *entry; if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) - snd_info_set_text_ops(entry, ensoniq, 1024, snd_ensoniq_proc_read); + snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); } /* @@ -2135,7 +2135,7 @@ #endif return err; } ensoniq->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_audiopci_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_audiopci_interrupt, IRQF_DISABLED|IRQF_SHARED, "Ensoniq AudioPCI", ensoniq)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_ensoniq_free(ensoniq); diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index 6f9094c..1113b10 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1429,7 +1429,7 @@ static int es1938_resume(struct pci_dev pci_restore_state(pci); pci_enable_device(pci); request_irq(pci->irq, snd_es1938_interrupt, - SA_INTERRUPT|SA_SHIRQ, "ES1938", chip); + IRQF_DISABLED|IRQF_SHARED, "ES1938", chip); chip->irq = pci->irq; snd_es1938_chip_init(chip); @@ -1544,7 +1544,7 @@ static int __devinit snd_es1938_create(s chip->vc_port = pci_resource_start(pci, 2); chip->mpu_port = pci_resource_start(pci, 3); chip->game_port = pci_resource_start(pci, 4); - if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_DISABLED|IRQF_SHARED, "ES1938", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_es1938_free(chip); @@ -1756,7 +1756,8 @@ static int __devinit snd_es1938_probe(st } } if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) { + chip->mpu_port, MPU401_INFO_INTEGRATED, + chip->irq, 0, &chip->rmidi) < 0) { printk(KERN_ERR "es1938: unable to initialize MPU-401\n"); } else { // this line is vital for MIDI interrupt handling on ess-solo1 diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index 5ff4175..a491c8f 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c @@ -132,7 +132,7 @@ static int enable[SNDRV_CARDS] = SNDRV_D static int total_bufsize[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1024 }; static int pcm_substreams_p[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4 }; static int pcm_substreams_c[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1 }; -static int clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int clock[SNDRV_CARDS]; static int use_pm[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; static int enable_mpu[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; #ifdef SUPPORT_JOYSTICK @@ -2597,7 +2597,7 @@ static int __devinit snd_es1968_create(s return err; } chip->io_port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_es1968_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_DISABLED|IRQF_SHARED, "ESS Maestro", (void*)chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_es1968_free(chip); @@ -2727,7 +2727,8 @@ static int __devinit snd_es1968_probe(st } if (enable_mpu[dev]) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, - chip->io_port + ESM_MPU401_PORT, 1, + chip->io_port + ESM_MPU401_PORT, + MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi)) < 0) { printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n"); } diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c index d72fc28..3aed27e 100644 --- a/sound/pci/fm801.c +++ b/sound/pci/fm801.c @@ -56,7 +56,7 @@ static int enable[SNDRV_CARDS] = SNDRV_D * 3 = MediaForte 64-PCR * High 16-bits are video (radio) device number + 1 */ -static int tea575x_tuner[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; +static int tea575x_tuner[SNDRV_CARDS]; module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for the FM801 soundcard."); @@ -1371,7 +1371,7 @@ static int __devinit snd_fm801_create(st return err; } chip->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_fm801_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED, "FM801", chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq); snd_fm801_free(chip); @@ -1448,7 +1448,8 @@ static int __devinit snd_card_fm801_prob return err; } if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801, - FM801_REG(chip, MPU401_DATA), 1, + FM801_REG(chip, MPU401_DATA), + MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index ddfb5ff..dbacba6 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o +snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o patch_si3054.o patch_atihdmi.o ifdef CONFIG_PROC_FS snd-hda-codec-objs += hda_proc.o endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5bee3b5..23201f3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -86,6 +86,8 @@ unsigned int snd_hda_codec_read(struct h return res; } +EXPORT_SYMBOL(snd_hda_codec_read); + /** * snd_hda_codec_write - send a single command without waiting for response * @codec: the HDA codec @@ -108,6 +110,8 @@ int snd_hda_codec_write(struct hda_codec return err; } +EXPORT_SYMBOL(snd_hda_codec_write); + /** * snd_hda_sequence_write - sequence writes * @codec: the HDA codec @@ -122,6 +126,8 @@ void snd_hda_sequence_write(struct hda_c snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param); } +EXPORT_SYMBOL(snd_hda_sequence_write); + /** * snd_hda_get_sub_nodes - get the range of sub nodes * @codec: the HDA codec @@ -140,6 +146,8 @@ int snd_hda_get_sub_nodes(struct hda_cod return (int)(parm & 0x7fff); } +EXPORT_SYMBOL(snd_hda_get_sub_nodes); + /** * snd_hda_get_connections - get connection list * @codec: the HDA codec @@ -256,6 +264,8 @@ int snd_hda_queue_unsol_event(struct hda return 0; } +EXPORT_SYMBOL(snd_hda_queue_unsol_event); + /* * process queueud unsolicited events */ @@ -384,6 +394,7 @@ int snd_hda_bus_new(struct snd_card *car return 0; } +EXPORT_SYMBOL(snd_hda_bus_new); /* * find a matching codec preset @@ -397,7 +408,9 @@ static const struct hda_codec_preset *fi u32 mask = preset->mask; if (! mask) mask = ~0; - if (preset->id == (codec->vendor_id & mask)) + if (preset->id == (codec->vendor_id & mask) && + (! preset->rev || + preset->rev == codec->revision_id)) return preset; } } @@ -587,6 +600,8 @@ int snd_hda_codec_new(struct hda_bus *bu return 0; } +EXPORT_SYMBOL(snd_hda_codec_new); + /** * snd_hda_codec_setup_stream - set up the codec for streaming * @codec: the CODEC to set up @@ -609,6 +624,7 @@ void snd_hda_codec_setup_stream(struct h snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format); } +EXPORT_SYMBOL(snd_hda_codec_setup_stream); /* * amp access functions @@ -1294,6 +1310,7 @@ int snd_hda_build_controls(struct hda_bu return 0; } +EXPORT_SYMBOL(snd_hda_build_controls); /* * stream formats @@ -1382,6 +1399,8 @@ unsigned int snd_hda_calc_stream_format( return val; } +EXPORT_SYMBOL(snd_hda_calc_stream_format); + /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec @@ -1663,6 +1682,7 @@ int snd_hda_build_pcms(struct hda_bus *b return 0; } +EXPORT_SYMBOL(snd_hda_build_pcms); /** * snd_hda_check_board_config - compare the current codec with the config table @@ -2165,6 +2185,8 @@ int snd_hda_suspend(struct hda_bus *bus, return 0; } +EXPORT_SYMBOL(snd_hda_suspend); + /** * snd_hda_resume - resume the codecs * @bus: the HDA bus @@ -2187,6 +2209,8 @@ int snd_hda_resume(struct hda_bus *bus) return 0; } +EXPORT_SYMBOL(snd_hda_resume); + /** * snd_hda_resume_ctls - resume controls in the new control list * @codec: the HDA codec @@ -2247,25 +2271,6 @@ int snd_hda_resume_spdif_in(struct hda_c #endif /* - * symbols exported for controller modules - */ -EXPORT_SYMBOL(snd_hda_codec_read); -EXPORT_SYMBOL(snd_hda_codec_write); -EXPORT_SYMBOL(snd_hda_sequence_write); -EXPORT_SYMBOL(snd_hda_get_sub_nodes); -EXPORT_SYMBOL(snd_hda_queue_unsol_event); -EXPORT_SYMBOL(snd_hda_bus_new); -EXPORT_SYMBOL(snd_hda_codec_new); -EXPORT_SYMBOL(snd_hda_codec_setup_stream); -EXPORT_SYMBOL(snd_hda_calc_stream_format); -EXPORT_SYMBOL(snd_hda_build_pcms); -EXPORT_SYMBOL(snd_hda_build_controls); -#ifdef CONFIG_PM -EXPORT_SYMBOL(snd_hda_suspend); -EXPORT_SYMBOL(snd_hda_resume); -#endif - -/* * INIT part */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e821d65..025af7c 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -82,6 +82,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6}, "{Intel, ICH8}," "{ATI, SB450}," "{ATI, SB600}," + "{ATI, RS600}," "{VIA, VT8251}," "{VIA, VT8237A}," "{SiS, SIS966}," @@ -167,6 +168,12 @@ #define ULI_NUM_CAPTURE 5 #define ULI_PLAYBACK_INDEX 5 #define ULI_NUM_PLAYBACK 6 +/* ATI HDMI has 1 playback and 0 capture */ +#define ATIHDMI_CAPTURE_INDEX 0 +#define ATIHDMI_NUM_CAPTURE 0 +#define ATIHDMI_PLAYBACK_INDEX 0 +#define ATIHDMI_NUM_PLAYBACK 1 + /* this number is statically defined for simplicity */ #define MAX_AZX_DEV 16 @@ -331,6 +338,7 @@ struct azx { enum { AZX_DRIVER_ICH, AZX_DRIVER_ATI, + AZX_DRIVER_ATIHDMI, AZX_DRIVER_VIA, AZX_DRIVER_SIS, AZX_DRIVER_ULI, @@ -340,6 +348,7 @@ enum { static char *driver_short_names[] __devinitdata = { [AZX_DRIVER_ICH] = "HDA Intel", [AZX_DRIVER_ATI] = "HDA ATI SB", + [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI", [AZX_DRIVER_VIA] = "HDA VIA VT82xx", [AZX_DRIVER_SIS] = "HDA SIS966", [AZX_DRIVER_ULI] = "HDA ULI M5461", @@ -1393,10 +1402,10 @@ static int azx_free(struct azx *chip) msleep(1); } - if (chip->remap_addr) - iounmap(chip->remap_addr); if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); + if (chip->remap_addr) + iounmap(chip->remap_addr); if (chip->bdl.area) snd_dma_free_pages(&chip->bdl); @@ -1477,7 +1486,7 @@ #endif goto errout; } - if (request_irq(pci->irq, azx_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, azx_interrupt, IRQF_DISABLED|IRQF_SHARED, "HDA Intel", (void*)chip)) { snd_printk(KERN_ERR SFX "unable to grab IRQ %d\n", pci->irq); err = -EBUSY; @@ -1495,6 +1504,12 @@ #endif chip->playback_index_offset = ULI_PLAYBACK_INDEX; chip->capture_index_offset = ULI_CAPTURE_INDEX; break; + case AZX_DRIVER_ATIHDMI: + chip->playback_streams = ATIHDMI_NUM_PLAYBACK; + chip->capture_streams = ATIHDMI_NUM_CAPTURE; + chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX; + chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX; + break; default: chip->playback_streams = ICH6_NUM_PLAYBACK; chip->capture_streams = ICH6_NUM_CAPTURE; @@ -1621,6 +1636,7 @@ static struct pci_device_id azx_ids[] __ { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */ { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */ { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */ + { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */ { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */ { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */ { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */ diff --git a/sound/pci/hda/hda_patch.h b/sound/pci/hda/hda_patch.h index acaef3c..0b66879 100644 --- a/sound/pci/hda/hda_patch.h +++ b/sound/pci/hda/hda_patch.h @@ -12,6 +12,8 @@ extern struct hda_codec_preset snd_hda_p extern struct hda_codec_preset snd_hda_preset_sigmatel[]; /* SiLabs 3054/3055 modem codecs */ extern struct hda_codec_preset snd_hda_preset_si3054[]; +/* ATI HDMI codecs */ +extern struct hda_codec_preset snd_hda_preset_atihdmi[]; static const struct hda_codec_preset *hda_preset_tables[] = { snd_hda_preset_realtek, @@ -19,5 +21,6 @@ static const struct hda_codec_preset *hd snd_hda_preset_analog, snd_hda_preset_sigmatel, snd_hda_preset_si3054, + snd_hda_preset_atihdmi, NULL }; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index ca514a6..c2f0fe8 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -182,6 +182,10 @@ static void print_pin_caps(struct snd_in snd_iprintf(buffer, " OUT"); if (caps & AC_PINCAP_HP_DRV) snd_iprintf(buffer, " HP"); + if (caps & AC_PINCAP_EAPD) + snd_iprintf(buffer, " EAPD"); + if (caps & AC_PINCAP_PRES_DETECT) + snd_iprintf(buffer, " Detect"); snd_iprintf(buffer, "\n"); caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0); snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps, @@ -318,7 +322,7 @@ int snd_hda_codec_proc_new(struct hda_co if (err < 0) return err; - snd_info_set_text_ops(entry, codec, 32 * 1024, print_codec_info); + snd_info_set_text_ops(entry, codec, print_codec_info); return 0; } diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 40f000b..33b7d58 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -789,6 +789,8 @@ static struct hda_board_config ad1986a_c { .modelname = "3stack", .config = AD1986A_3STACK }, { .pci_subvendor = 0x10de, .pci_subdevice = 0xcb84, .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */ + { .pci_subvendor = 0x1043, .pci_subdevice = 0x81b3, + .config = AD1986A_3STACK }, /* ASUS P5RD2-VM / P5GPL-X SE */ { .modelname = "laptop", .config = AD1986A_LAPTOP }, { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e, .config = AD1986A_LAPTOP }, /* FSC V2060 */ @@ -797,6 +799,8 @@ static struct hda_board_config ad1986a_c { .pci_subvendor = 0x1043, .pci_subdevice = 0x818f, .config = AD1986A_LAPTOP }, /* ASUS P5GV-MX */ { .modelname = "laptop-eapd", .config = AD1986A_LAPTOP_EAPD }, + { .pci_subvendor = 0x144d, .pci_subdevice = 0xc023, + .config = AD1986A_LAPTOP_EAPD }, /* Samsung X60 Chane */ { .pci_subvendor = 0x144d, .pci_subdevice = 0xc024, .config = AD1986A_LAPTOP_EAPD }, /* Samsung R65-T2300 Charis */ { .pci_subvendor = 0x1043, .pci_subdevice = 0x1153, @@ -809,6 +813,8 @@ static struct hda_board_config ad1986a_c .config = AD1986A_LAPTOP_EAPD }, /* ASUS Z62F */ { .pci_subvendor = 0x103c, .pci_subdevice = 0x30af, .config = AD1986A_LAPTOP_EAPD }, /* HP Compaq Presario B2800 */ + { .pci_subvendor = 0x17aa, .pci_subdevice = 0x2066, + .config = AD1986A_LAPTOP_EAPD }, /* Lenovo 3000 N100-07684JU */ {} }; @@ -963,7 +969,7 @@ static struct snd_kcontrol_new ad1983_mi }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", .info = ad1983_spdif_route_info, .get = ad1983_spdif_route_get, .put = ad1983_spdif_route_put, @@ -1103,7 +1109,7 @@ static struct snd_kcontrol_new ad1981_mi /* identical with AD1983 */ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route", + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", .info = ad1983_spdif_route_info, .get = ad1983_spdif_route_get, .put = ad1983_spdif_route_put, @@ -1329,13 +1335,60 @@ static int ad1981_hp_init(struct hda_cod return 0; } +/* configuration for Lenovo Thinkpad T60 */ +static struct snd_kcontrol_new ad1981_thinkpad_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = ad198x_mux_enum_info, + .get = ad198x_mux_enum_get, + .put = ad198x_mux_enum_put, + }, + /* identical with AD1983 */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source", + .info = ad1983_spdif_route_info, + .get = ad1983_spdif_route_get, + .put = ad1983_spdif_route_put, + }, + { } /* end */ +}; + +static struct hda_input_mux ad1981_thinkpad_capture_source = { + .num_items = 3, + .items = { + { "Mic", 0x0 }, + { "Mix", 0x2 }, + { "CD", 0x4 }, + }, +}; + /* models */ -enum { AD1981_BASIC, AD1981_HP }; +enum { AD1981_BASIC, AD1981_HP, AD1981_THINKPAD }; static struct hda_board_config ad1981_cfg_tbl[] = { { .modelname = "hp", .config = AD1981_HP }, /* All HP models */ { .pci_subvendor = 0x103c, .config = AD1981_HP }, + { .pci_subvendor = 0x30b0, .pci_subdevice = 0x103c, + .config = AD1981_HP }, /* HP nx6320 (reversed SSID, H/W bug) */ + { .modelname = "thinkpad", .config = AD1981_THINKPAD }, + /* Lenovo Thinkpad T60/X60/Z6xx */ + { .pci_subvendor = 0x17aa, .config = AD1981_THINKPAD }, + { .pci_subvendor = 0x1014, .pci_subdevice = 0x0597, + .config = AD1981_THINKPAD }, /* Z60m/t */ { .modelname = "basic", .config = AD1981_BASIC }, {} }; @@ -1381,6 +1434,10 @@ static int patch_ad1981(struct hda_codec codec->patch_ops.init = ad1981_hp_init; codec->patch_ops.unsol_event = ad1981_hp_unsol_event; break; + case AD1981_THINKPAD: + spec->mixers[0] = ad1981_thinkpad_mixers; + spec->input_mux = &ad1981_thinkpad_capture_source; + break; } return 0; diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c new file mode 100644 index 0000000..a27440f --- /dev/null +++ b/sound/pci/hda/patch_atihdmi.c @@ -0,0 +1,165 @@ +/* + * Universal Interface for Intel High Definition Audio Codec + * + * HD audio interface patch for ATI HDMI codecs + * + * Copyright (c) 2006 ATI Technologies Inc. + * + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include "hda_codec.h" +#include "hda_local.h" + +struct atihdmi_spec { + struct hda_multi_out multiout; + + struct hda_pcm pcm_rec; +}; + +static struct hda_verb atihdmi_basic_init[] = { + /* enable digital output on pin widget */ + { 0x03, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {} /* terminator */ +}; + +/* + * Controls + */ +static int atihdmi_build_controls(struct hda_codec *codec) +{ + struct atihdmi_spec *spec = codec->spec; + int err; + + err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + if (err < 0) + return err; + + return 0; +} + +static int atihdmi_init(struct hda_codec *codec) +{ + snd_hda_sequence_write(codec, atihdmi_basic_init); + return 0; +} + +#ifdef CONFIG_PM +/* + * resume + */ +static int atihdmi_resume(struct hda_codec *codec) +{ + atihdmi_init(codec); + snd_hda_resume_spdif_out(codec); + + return 0; +} +#endif + +/* + * Digital out + */ +static int atihdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct atihdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +} + +static int atihdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct atihdmi_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); +} + +static struct hda_pcm_stream atihdmi_pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0x2, /* NID to query formats and rates and setup streams */ + .ops = { + .open = atihdmi_dig_playback_pcm_open, + .close = atihdmi_dig_playback_pcm_close + }, +}; + +static int atihdmi_build_pcms(struct hda_codec *codec) +{ + struct atihdmi_spec *spec = codec->spec; + struct hda_pcm *info = &spec->pcm_rec; + + codec->num_pcms = 1; + codec->pcm_info = info; + + info->name = "ATI HDMI"; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = atihdmi_pcm_digital_playback; + + return 0; +} + +static void atihdmi_free(struct hda_codec *codec) +{ + kfree(codec->spec); +} + +static struct hda_codec_ops atihdmi_patch_ops = { + .build_controls = atihdmi_build_controls, + .build_pcms = atihdmi_build_pcms, + .init = atihdmi_init, + .free = atihdmi_free, +#ifdef CONFIG_PM + .resume = atihdmi_resume, +#endif +}; + +static int patch_atihdmi(struct hda_codec *codec) +{ + struct atihdmi_spec *spec; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + spec->multiout.num_dacs = 0; /* no analog */ + spec->multiout.max_channels = 2; + spec->multiout.dig_out_nid = 0x2; /* NID for copying analog to digital, + * seems to be unused in pure-digital + * case. */ + + codec->patch_ops = atihdmi_patch_ops; + + return 0; +} + +/* + * patch entries + */ +struct hda_codec_preset snd_hda_preset_atihdmi[] = { + { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi }, + {} /* terminator */ +}; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f0e9a9c..18d1052 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -78,6 +78,7 @@ #endif enum { ALC262_BASIC, ALC262_FUJITSU, + ALC262_HP_BPC, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -85,6 +86,7 @@ enum { /* ALC861 models */ enum { ALC861_3ST, + ALC660_3ST, ALC861_3ST_DIG, ALC861_6ST_DIG, ALC861_AUTO, @@ -99,6 +101,17 @@ enum { ALC882_MODEL_LAST, }; +/* ALC883 models */ +enum { + ALC883_3ST_2ch_DIG, + ALC883_3ST_6ch_DIG, + ALC883_3ST_6ch, + ALC883_6ST_DIG, + ALC888_DEMO_BOARD, + ALC883_AUTO, + ALC883_MODEL_LAST, +}; + /* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -108,7 +121,8 @@ struct alc_spec { unsigned int num_mixers; const struct hda_verb *init_verbs[5]; /* initialization verbs - * don't forget NULL termination! + * don't forget NULL + * termination! */ unsigned int num_init_verbs; @@ -163,7 +177,9 @@ struct alc_spec { * configuration template - to be copied to the spec instance */ struct alc_config_preset { - struct snd_kcontrol_new *mixers[5]; /* should be identical size with spec */ + struct snd_kcontrol_new *mixers[5]; /* should be identical size + * with spec + */ const struct hda_verb *init_verbs[5]; unsigned int num_dacs; hda_nid_t *dac_nids; @@ -184,7 +200,8 @@ struct alc_config_preset { /* * input MUX handling */ -static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; @@ -194,7 +211,8 @@ static int alc_mux_enum_info(struct snd_ return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); } -static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; @@ -204,21 +222,24 @@ static int alc_mux_enum_get(struct snd_k return 0; } -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol, - spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]); + spec->adc_nids[adc_idx], + &spec->cur_mux[adc_idx]); } /* * channel mode setting */ -static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; @@ -226,20 +247,24 @@ static int alc_ch_mode_info(struct snd_k spec->num_channel_mode); } -static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, spec->multiout.max_channels); + spec->num_channel_mode, + spec->multiout.max_channels); } -static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode, - spec->num_channel_mode, &spec->multiout.max_channels); + spec->num_channel_mode, + &spec->multiout.max_channels); } /* @@ -290,7 +315,8 @@ #define alc_pin_mode_max(_dir) (alc_pin_ #define alc_pin_mode_n_items(_dir) \ (alc_pin_mode_max(_dir)-alc_pin_mode_min(_dir)+1) -static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_pin_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { unsigned int item_num = uinfo->value.enumerated.item; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; @@ -305,40 +331,46 @@ static int alc_pin_mode_info(struct snd_ return 0; } -static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { unsigned int i; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00); + unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); /* Find enumerated value for current pinctl setting */ i = alc_pin_mode_min(dir); - while (alc_pin_mode_values[i]!=pinctl && i<=alc_pin_mode_max(dir)) + while (alc_pin_mode_values[i] != pinctl && i <= alc_pin_mode_max(dir)) i++; - *valp = i<=alc_pin_mode_max(dir)?i:alc_pin_mode_min(dir); + *valp = i <= alc_pin_mode_max(dir) ? i: alc_pin_mode_min(dir); return 0; } -static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char dir = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; - unsigned int pinctl = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_PIN_WIDGET_CONTROL,0x00); + unsigned int pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, + 0x00); - if (valalc_pin_mode_max(dir)) + if (val < alc_pin_mode_min(dir) || val > alc_pin_mode_max(dir)) val = alc_pin_mode_min(dir); change = pinctl != alc_pin_mode_values[val]; if (change) { /* Set pin mode to that requested */ snd_hda_codec_write(codec,nid,0,AC_VERB_SET_PIN_WIDGET_CONTROL, - alc_pin_mode_values[val]); + alc_pin_mode_values[val]); /* Also enable the retasking pin's input/output as required * for the requested pin mode. Enum values of 2 or less are @@ -351,15 +383,19 @@ static int alc_pin_mode_put(struct snd_k * this turns out to be necessary in the future. */ if (val <= 2) { - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0)); } else { - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_MUTE(0)); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); } } return change; @@ -378,7 +414,8 @@ #define ALC_PIN_MODE(xname, nid, dir) \ * needed for any "production" models. */ #ifdef CONFIG_SND_DEBUG -static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_gpio_data_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -386,33 +423,38 @@ static int alc_gpio_data_info(struct snd uinfo->value.integer.max = 1; return 0; } -static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_gpio_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00); + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, 0x00); *valp = (val & mask) != 0; return 0; } -static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_gpio_data_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; - unsigned int gpio_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_GPIO_DATA,0x00); + unsigned int gpio_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_GPIO_DATA, + 0x00); /* Set/unset the masked GPIO bit(s) as needed */ - change = (val==0?0:mask) != (gpio_data & mask); - if (val==0) + change = (val == 0 ? 0 : mask) != (gpio_data & mask); + if (val == 0) gpio_data &= ~mask; else gpio_data |= mask; - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_GPIO_DATA,gpio_data); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_GPIO_DATA, gpio_data); return change; } @@ -432,7 +474,8 @@ #endif /* CONFIG_SND_DEBUG */ * necessary. */ #ifdef CONFIG_SND_DEBUG -static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_spdif_ctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = 1; @@ -440,33 +483,39 @@ static int alc_spdif_ctrl_info(struct sn uinfo->value.integer.max = 1; return 0; } -static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long *valp = ucontrol->value.integer.value; - unsigned int val = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00); + unsigned int val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT, 0x00); *valp = (val & mask) != 0; return 0; } -static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { signed int change; struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value & 0xffff; unsigned char mask = (kcontrol->private_value >> 16) & 0xff; long val = *ucontrol->value.integer.value; - unsigned int ctrl_data = snd_hda_codec_read(codec,nid,0,AC_VERB_GET_DIGI_CONVERT,0x00); + unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_DIGI_CONVERT, + 0x00); /* Set/unset the masked control bit(s) as needed */ - change = (val==0?0:mask) != (ctrl_data & mask); + change = (val == 0 ? 0 : mask) != (ctrl_data & mask); if (val==0) ctrl_data &= ~mask; else ctrl_data |= mask; - snd_hda_codec_write(codec,nid,0,AC_VERB_SET_DIGI_CONVERT_1,ctrl_data); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, + ctrl_data); return change; } @@ -481,14 +530,17 @@ #endif /* CONFIG_SND_DEBUG */ /* * set up from the preset table */ -static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *preset) +static void setup_preset(struct alc_spec *spec, + const struct alc_config_preset *preset) { int i; for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++) spec->mixers[spec->num_mixers++] = preset->mixers[i]; - for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; i++) - spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i]; + for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i]; + i++) + spec->init_verbs[spec->num_init_verbs++] = + preset->init_verbs[i]; spec->channel_mode = preset->channel_mode; spec->num_channel_mode = preset->num_channel_mode; @@ -517,8 +569,8 @@ static void setup_preset(struct alc_spec * ALC880 3-stack model * * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e) - * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b - * HP = 0x19 + * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, + * F-Mic = 0x1b, HP = 0x19 */ static hda_nid_t alc880_dac_nids[4] = { @@ -662,7 +714,8 @@ static struct snd_kcontrol_new alc880_ca /* * ALC880 5-stack model * - * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd) + * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), + * Side = 0x02 (0xd) * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16 * Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19 */ @@ -700,7 +753,8 @@ static struct hda_channel_mode alc880_fi /* * ALC880 6-stack model * - * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f) + * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), + * Side = 0x05 (0x0f) * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17, * Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b */ @@ -811,7 +865,8 @@ static struct snd_kcontrol_new alc880_w8 * Z710V model * * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d) - * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a + * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), + * Line = 0x1a */ static hda_nid_t alc880_z71v_dac_nids[1] = { @@ -966,7 +1021,8 @@ static int alc_build_controls(struct hda } if (spec->multiout.dig_out_nid) { - err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); + err = snd_hda_create_spdif_out_ctls(codec, + spec->multiout.dig_out_nid); if (err < 0) return err; } @@ -999,8 +1055,8 @@ static struct hda_verb alc880_volume_ini /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback * mixer widget - * Note: PASD motherboards uses the Line In 2 as the input for front panel - * mic (mic 2) + * Note: PASD motherboards uses the Line In 2 as the input for front + * panel mic (mic 2) */ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -1154,8 +1210,8 @@ static struct hda_verb alc880_pin_z71v_i /* * 6-stack pin configuration: - * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19, - * line = 0x1a, HP = 0x1b + * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, + * f-mic = 0x19, line = 0x1a, HP = 0x1b */ static struct hda_verb alc880_pin_6stack_init_verbs[] = { {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */ @@ -1587,8 +1643,8 @@ static int alc880_playback_pcm_prepare(s struct snd_pcm_substream *substream) { struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, - format, substream); + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); } static int alc880_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -1640,7 +1696,8 @@ static int alc880_capture_pcm_cleanup(st { struct alc_spec *spec = codec->spec; - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0); + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], + 0, 0, 0); return 0; } @@ -1822,7 +1879,8 @@ static struct hda_channel_mode alc880_te { 8, NULL }, }; -static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_test_pin_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { static char *texts[] = { "N/A", "Line Out", "HP Out", @@ -1837,7 +1895,8 @@ static int alc_test_pin_ctl_info(struct return 0; } -static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -1863,7 +1922,8 @@ static int alc_test_pin_ctl_get(struct s return 0; } -static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -1881,15 +1941,18 @@ static int alc_test_pin_ctl_put(struct s AC_VERB_GET_PIN_WIDGET_CONTROL, 0); new_ctl = ctls[ucontrol->value.enumerated.item[0]]; if (old_ctl != new_ctl) { - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl); snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000); + (ucontrol->value.enumerated.item[0] >= 3 ? + 0xb080 : 0xb000)); return 1; } return 0; } -static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +static int alc_test_pin_src_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) { static char *texts[] = { "Front", "Surround", "CLFE", "Side" @@ -1903,7 +1966,8 @@ static int alc_test_pin_src_info(struct return 0; } -static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_src_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -1914,7 +1978,8 @@ static int alc_test_pin_src_get(struct s return 0; } -static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = (hda_nid_t)kcontrol->private_value; @@ -2174,6 +2239,7 @@ static struct hda_board_config alc880_cf { .modelname = "lg", .config = ALC880_LG }, { .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG }, + { .pci_subvendor = 0x1854, .pci_subdevice = 0x0068, .config = ALC880_LG }, { .modelname = "lg-lw", .config = ALC880_LG_LW }, { .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW }, @@ -2738,7 +2804,8 @@ static int patch_alc880(struct hda_codec board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl); if (board_config < 0 || board_config >= ALC880_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC880, " + "trying auto-probe from BIOS...\n"); board_config = ALC880_AUTO; } @@ -2749,7 +2816,9 @@ static int patch_alc880(struct hda_codec alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using 3-stack mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using 3-stack mode...\n"); board_config = ALC880_3ST; } } @@ -3105,6 +3174,7 @@ static struct hda_verb alc260_init_verbs { } }; +#if 0 /* should be identical with alc260_init_verbs? */ static struct hda_verb alc260_hp_init_verbs[] = { /* Headphone and output */ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, @@ -3151,6 +3221,7 @@ static struct hda_verb alc260_hp_init_ve {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))}, { } }; +#endif static struct hda_verb alc260_hp_3013_init_verbs[] = { /* Line out and output */ @@ -3822,12 +3893,16 @@ static struct hda_board_config alc260_cf { .modelname = "basic", .config = ALC260_BASIC }, { .pci_subvendor = 0x104d, .pci_subdevice = 0x81bb, .config = ALC260_BASIC }, /* Sony VAIO */ + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cc, + .config = ALC260_BASIC }, /* Sony VAIO VGN-S3HP */ + { .pci_subvendor = 0x104d, .pci_subdevice = 0x81cd, + .config = ALC260_BASIC }, /* Sony VAIO */ { .pci_subvendor = 0x152d, .pci_subdevice = 0x0729, .config = ALC260_BASIC }, /* CTL Travel Master U553W */ { .modelname = "hp", .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3010, .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3011, .config = ALC260_HP }, - { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP }, + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3012, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3013, .config = ALC260_HP_3013 }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, .config = ALC260_HP }, { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, .config = ALC260_HP }, @@ -3862,7 +3937,7 @@ static struct alc_config_preset alc260_p .mixers = { alc260_base_output_mixer, alc260_input_mixer, alc260_capture_alt_mixer }, - .init_verbs = { alc260_hp_init_verbs }, + .init_verbs = { alc260_init_verbs }, .num_dacs = ARRAY_SIZE(alc260_dac_nids), .dac_nids = alc260_dac_nids, .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), @@ -3940,7 +4015,8 @@ static int patch_alc260(struct hda_codec board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl); if (board_config < 0 || board_config >= ALC260_MODEL_LAST) { - snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n"); + snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, " + "trying auto-probe from BIOS...\n"); board_config = ALC260_AUTO; } @@ -3951,7 +4027,9 @@ static int patch_alc260(struct hda_codec alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC260_BASIC; } } @@ -4094,21 +4172,6 @@ static struct snd_kcontrol_new alc882_ba HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 3, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, { } /* end */ }; @@ -4328,9 +4391,12 @@ #define alc882_pcm_digital_capture alc88 static struct hda_board_config alc882_cfg_tbl[] = { { .modelname = "3stack-dig", .config = ALC882_3ST_DIG }, { .modelname = "6stack-dig", .config = ALC882_6ST_DIG }, - { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* MSI */ - { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* Foxconn */ - { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, .config = ALC882_6ST_DIG }, /* ECS */ + { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, + .config = ALC882_6ST_DIG }, /* MSI */ + { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, + .config = ALC882_6ST_DIG }, /* Foxconn */ + { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, + .config = ALC882_6ST_DIG }, /* ECS to Intel*/ { .modelname = "auto", .config = ALC882_AUTO }, {} }; @@ -4342,8 +4408,6 @@ static struct alc_config_preset alc882_p .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, .dig_in_nid = ALC882_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc882_ch_modes), .channel_mode = alc882_ch_modes, @@ -4355,8 +4419,6 @@ static struct alc_config_preset alc882_p .num_dacs = ARRAY_SIZE(alc882_dac_nids), .dac_nids = alc882_dac_nids, .dig_out_nid = ALC882_DIGOUT_NID, - .num_adc_nids = ARRAY_SIZE(alc882_adc_nids), - .adc_nids = alc882_adc_nids, .dig_in_nid = ALC882_DIGIN_NID, .num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes), .channel_mode = alc882_sixstack_modes, @@ -4451,10 +4513,6 @@ static void alc882_auto_init(struct hda_ alc882_auto_init_analog_input(codec); } -/* - * ALC882 Headphone poll in 3.5.1a or 3.5.2 - */ - static int patch_alc882(struct hda_codec *codec) { struct alc_spec *spec; @@ -4469,7 +4527,8 @@ static int patch_alc882(struct hda_codec board_config = snd_hda_check_board_config(codec, alc882_cfg_tbl); if (board_config < 0 || board_config >= ALC882_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC882, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC882, " + "trying auto-probe from BIOS...\n"); board_config = ALC882_AUTO; } @@ -4480,7 +4539,9 @@ static int patch_alc882(struct hda_codec alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC882_3ST_DIG; } } @@ -4521,6 +4582,652 @@ static int patch_alc882(struct hda_codec } /* + * ALC883 support + * + * ALC883 is almost identical with ALC880 but has cleaner and more flexible + * configuration. Each pin widget can choose any input DACs and a mixer. + * Each ADC is connected from a mixer of all inputs. This makes possible + * 6-channel independent captures. + * + * In addition, an independent DAC for the multi-playback (not used in this + * driver yet). + */ +#define ALC883_DIGOUT_NID 0x06 +#define ALC883_DIGIN_NID 0x0a + +static hda_nid_t alc883_dac_nids[4] = { + /* front, rear, clfe, rear_surr */ + 0x02, 0x04, 0x03, 0x05 +}; + +static hda_nid_t alc883_adc_nids[2] = { + /* ADC1-2 */ + 0x08, 0x09, +}; +/* input MUX */ +/* FIXME: should be a matrix-type input source selection */ + +static struct hda_input_mux alc883_capture_source = { + .num_items = 4, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x1 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + }, +}; +#define alc883_mux_enum_info alc_mux_enum_info +#define alc883_mux_enum_get alc_mux_enum_get + +static int alc883_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct alc_spec *spec = codec->spec; + const struct hda_input_mux *imux = spec->input_mux; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 }; + hda_nid_t nid = capture_mixers[adc_idx]; + unsigned int *cur_val = &spec->cur_mux[adc_idx]; + unsigned int i, idx; + + idx = ucontrol->value.enumerated.item[0]; + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (*cur_val == idx && ! codec->in_resume) + return 0; + for (i = 0; i < imux->num_items; i++) { + unsigned int v = (i == idx) ? 0x7000 : 0x7080; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + v | (imux->items[i].index << 8)); + } + *cur_val = idx; + return 1; +} +/* + * 2ch mode + */ +static struct hda_channel_mode alc883_3ST_2ch_modes[1] = { + { 2, NULL } +}; + +/* + * 2ch mode + */ +static struct hda_verb alc883_3ST_ch2_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, + { } /* end */ +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_3ST_ch6_init[] = { + { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, + { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_3ST_6ch_modes[2] = { + { 2, alc883_3ST_ch2_init }, + { 6, alc883_3ST_ch6_init }, +}; + +/* + * 6ch mode + */ +static struct hda_verb alc883_sixstack_ch6_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +/* + * 8ch mode + */ +static struct hda_verb alc883_sixstack_ch8_init[] = { + { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + { } /* end */ +}; + +static struct hda_channel_mode alc883_sixstack_modes[2] = { + { 6, alc883_sixstack_ch6_init }, + { 8, alc883_sixstack_ch8_init }, +}; + +/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 + * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b + */ + +static struct snd_kcontrol_new alc883_base_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), + HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT), + HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT), + HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc883_mux_enum_info, + .get = alc883_mux_enum_get, + .put = alc883_mux_enum_put, + }, + { } /* end */ +}; + +static struct snd_kcontrol_new alc883_chmode_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = alc_ch_mode_info, + .get = alc_ch_mode_get, + .put = alc_ch_mode_put, + }, + { } /* end */ +}; + +static struct hda_verb alc883_init_verbs[] = { + /* ADC1: mute amp left and right */ + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* ADC2: mute amp left and right */ + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Front mixer: unmute input/output amp left and right (volume = 0) */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Rear mixer */ + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* CLFE mixer */ + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + /* Side mixer */ + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* Front Pin: output 0 (0x0c) */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* Rear Pin: output 1 (0x0d) */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* CLFE Pin: output 2 (0x0e) */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* Side Pin: output 3 (0x0f) */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* Mic (rear) pin: input vref at 80% */ + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Front Mic pin: input vref at 80% */ + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line In pin: input */ + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Line-2 In: Headphone output (output 0 - 0x0c) */ + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + /* CD pin widget for input */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + { } +}; + +/* + * generic initialization of ADC, input mixers and output mixers + */ +static struct hda_verb alc883_auto_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for front panel + * mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + /* + * Set up output mixers (0x0c - 0x0f) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + //{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + /* Input mixer2 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + //{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + + { } +}; + +/* capture mixer elements */ +static struct snd_kcontrol_new alc883_capture_mixer[] = { + HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + /* The multiple "Capture Source" controls confuse alsamixer + * So call somewhat different.. + * FIXME: the controls appear in the "playback" view! + */ + /* .name = "Capture Source", */ + .name = "Input Source", + .count = 2, + .info = alc882_mux_enum_info, + .get = alc882_mux_enum_get, + .put = alc882_mux_enum_put, + }, + { } /* end */ +}; + +/* pcm configuration: identiacal with ALC880 */ +#define alc883_pcm_analog_playback alc880_pcm_analog_playback +#define alc883_pcm_analog_capture alc880_pcm_analog_capture +#define alc883_pcm_digital_playback alc880_pcm_digital_playback +#define alc883_pcm_digital_capture alc880_pcm_digital_capture + +/* + * configuration and preset + */ +static struct hda_board_config alc883_cfg_tbl[] = { + { .modelname = "3stack-dig", .config = ALC883_3ST_2ch_DIG }, + { .modelname = "6stack-dig", .config = ALC883_6ST_DIG }, + { .modelname = "6stack-dig-demo", .config = ALC888_DEMO_BOARD }, + { .pci_subvendor = 0x1462, .pci_subdevice = 0x6668, + .config = ALC883_6ST_DIG }, /* MSI */ + { .pci_subvendor = 0x105b, .pci_subdevice = 0x6668, + .config = ALC883_6ST_DIG }, /* Foxconn */ + { .pci_subvendor = 0x1019, .pci_subdevice = 0x6668, + .config = ALC883_3ST_6ch_DIG }, /* ECS to Intel*/ + { .pci_subvendor = 0x108e, .pci_subdevice = 0x534d, + .config = ALC883_3ST_6ch }, + { .modelname = "auto", .config = ALC883_AUTO }, + {} +}; + +static struct alc_config_preset alc883_presets[] = { + [ALC883_3ST_2ch_DIG] = { + .mixers = { alc883_3ST_2ch_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), + .channel_mode = alc883_3ST_2ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch_DIG] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_3ST_6ch] = { + .mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes), + .channel_mode = alc883_3ST_6ch_modes, + .input_mux = &alc883_capture_source, + }, + [ALC883_6ST_DIG] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, + [ALC888_DEMO_BOARD] = { + .mixers = { alc883_base_mixer, alc883_chmode_mixer }, + .init_verbs = { alc883_init_verbs }, + .num_dacs = ARRAY_SIZE(alc883_dac_nids), + .dac_nids = alc883_dac_nids, + .dig_out_nid = ALC883_DIGOUT_NID, + .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), + .adc_nids = alc883_adc_nids, + .dig_in_nid = ALC883_DIGIN_NID, + .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes), + .channel_mode = alc883_sixstack_modes, + .input_mux = &alc883_capture_source, + }, +}; + + +/* + * BIOS auto configuration + */ +static void alc883_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t nid, int pin_type, + int dac_idx) +{ + /* set as output */ + struct alc_spec *spec = codec->spec; + int idx; + + if (spec->multiout.dac_nids[dac_idx] == 0x25) + idx = 4; + else + idx = spec->multiout.dac_nids[dac_idx] - 2; + + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, + pin_type); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE); + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx); + +} + +static void alc883_auto_init_multi_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + alc883_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); + } +} + +static void alc883_auto_init_hp_out(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t pin; + + pin = spec->autocfg.hp_pin; + if (pin) /* connect to front */ + /* use dac 0 */ + alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); +} + +#define alc883_is_input_pin(nid) alc880_is_input_pin(nid) +#define ALC883_PIN_CD_NID ALC880_PIN_CD_NID + +static void alc883_auto_init_analog_input(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < AUTO_PIN_LAST; i++) { + hda_nid_t nid = spec->autocfg.input_pins[i]; + if (alc883_is_input_pin(nid)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, + (i <= AUTO_PIN_FRONT_MIC ? + PIN_VREF80 : PIN_IN)); + if (nid != ALC883_PIN_CD_NID) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_MUTE); + } + } +} + +/* almost identical with ALC880 parser... */ +static int alc883_parse_auto_config(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int err = alc880_parse_auto_config(codec); + + if (err < 0) + return err; + else if (err > 0) + /* hack - override the init verbs */ + spec->init_verbs[0] = alc883_auto_init_verbs; + spec->mixers[spec->num_mixers] = alc883_capture_mixer; + spec->num_mixers++; + return err; +} + +/* additional initialization for auto-configuration model */ +static void alc883_auto_init(struct hda_codec *codec) +{ + alc883_auto_init_multi_out(codec); + alc883_auto_init_hp_out(codec); + alc883_auto_init_analog_input(codec); +} + +static int patch_alc883(struct hda_codec *codec) +{ + struct alc_spec *spec; + int err, board_config; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + + board_config = snd_hda_check_board_config(codec, alc883_cfg_tbl); + if (board_config < 0 || board_config >= ALC883_MODEL_LAST) { + printk(KERN_INFO "hda_codec: Unknown model for ALC883, " + "trying auto-probe from BIOS...\n"); + board_config = ALC883_AUTO; + } + + if (board_config == ALC883_AUTO) { + /* automatic parse from the BIOS config */ + err = alc883_parse_auto_config(codec); + if (err < 0) { + alc_free(codec); + return err; + } else if (! err) { + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); + board_config = ALC883_3ST_2ch_DIG; + } + } + + if (board_config != ALC883_AUTO) + setup_preset(spec, &alc883_presets[board_config]); + + spec->stream_name_analog = "ALC883 Analog"; + spec->stream_analog_playback = &alc883_pcm_analog_playback; + spec->stream_analog_capture = &alc883_pcm_analog_capture; + + spec->stream_name_digital = "ALC883 Digital"; + spec->stream_digital_playback = &alc883_pcm_digital_playback; + spec->stream_digital_capture = &alc883_pcm_digital_capture; + + spec->adc_nids = alc883_adc_nids; + spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids); + + codec->patch_ops = alc_patch_ops; + if (board_config == ALC883_AUTO) + spec->init_hook = alc883_auto_init; + + return 0; +} + +/* * ALC262 support */ @@ -4554,6 +5261,28 @@ static struct snd_kcontrol_new alc262_ba { } /* end */ }; +static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = { + HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT), + HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("PC Beep Playback Volume", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_MUTE("PC Beep Playback Switch", 0x0b, 0x05, HDA_INPUT), + HDA_CODEC_VOLUME("AUX IN Playback Volume", 0x0b, 0x06, HDA_INPUT), + HDA_CODEC_MUTE("AUX IN Playback Switch", 0x0b, 0x06, HDA_INPUT), + { } /* end */ +}; + #define alc262_capture_mixer alc882_capture_mixer #define alc262_capture_alt_mixer alc882_capture_alt_mixer @@ -4657,6 +5386,17 @@ static struct hda_input_mux alc262_fujit }, }; +static struct hda_input_mux alc262_HP_capture_source = { + .num_items = 5, + .items = { + { "Mic", 0x0 }, + { "Front Mic", 0x3 }, + { "Line", 0x2 }, + { "CD", 0x4 }, + { "AUX IN", 0x6 }, + }, +}; + /* mute/unmute internal speaker according to the hp jack and mute state */ static void alc262_fujitsu_automute(struct hda_codec *codec, int force) { @@ -4880,6 +5620,93 @@ static struct hda_verb alc262_volume_ini { } }; +static struct hda_verb alc262_HP_BPC_init_verbs[] = { + /* + * Unmute ADC0-2 and set the default input to mic-in + */ + {0x07, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback + * mixer widget + * Note: PASD motherboards uses the Line In 2 as the input for front panel + * mic (mic 2) + */ + /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(6)}, + + /* + * Set up output mixers (0x0c - 0x0e) + */ + /* set vol=0 to output mixers */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, + + /* set up input amps for analog loopback */ + /* Amp Indices: DAC = 0, mixer = 1 */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + + {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0}, + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, + + {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, + + {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, + + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24}, + {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, + + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 }, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 }, + + + /* FIXME: use matrix-type input source selection */ + /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ + /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */ + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* Input mixer2 */ + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + /* Input mixer3 */ + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))}, + {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))}, + + { } +}; + /* pcm configuration: identiacal with ALC880 */ #define alc262_pcm_analog_playback alc880_pcm_analog_playback #define alc262_pcm_analog_capture alc880_pcm_analog_capture @@ -4940,7 +5767,16 @@ static void alc262_auto_init(struct hda_ static struct hda_board_config alc262_cfg_tbl[] = { { .modelname = "basic", .config = ALC262_BASIC }, { .modelname = "fujitsu", .config = ALC262_FUJITSU }, - { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU }, + { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, + .config = ALC262_FUJITSU }, + { .pci_subvendor = 0x103c, .pci_subdevice = 0x208c, + .config = ALC262_HP_BPC }, /* xw4400 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3014, + .config = ALC262_HP_BPC }, /* xw6400 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x3015, + .config = ALC262_HP_BPC }, /* xw8400 */ + { .pci_subvendor = 0x103c, .pci_subdevice = 0x12fe, + .config = ALC262_HP_BPC }, /* xw9400 */ { .modelname = "auto", .config = ALC262_AUTO }, {} }; @@ -4968,6 +5804,16 @@ static struct alc_config_preset alc262_p .input_mux = &alc262_fujitsu_capture_source, .unsol_event = alc262_fujitsu_unsol_event, }, + [ALC262_HP_BPC] = { + .mixers = { alc262_HP_BPC_mixer }, + .init_verbs = { alc262_HP_BPC_init_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_HP_capture_source, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -4993,8 +5839,10 @@ #if 0 #endif board_config = snd_hda_check_board_config(codec, alc262_cfg_tbl); + if (board_config < 0 || board_config >= ALC262_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC262, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC262, " + "trying auto-probe from BIOS...\n"); board_config = ALC262_AUTO; } @@ -5005,7 +5853,9 @@ #endif alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC262_BASIC; } } @@ -5046,7 +5896,6 @@ #endif return 0; } - /* * ALC861 channel source setting (2/6 channel selection for 3-stack) */ @@ -5061,9 +5910,11 @@ static struct hda_verb alc861_threestack /* set pin widget 18h (mic1/2) for input, for mic also enable the vref */ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, //mic - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, //line in + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/ +#endif { } /* end */ }; /* @@ -5077,11 +5928,13 @@ static struct hda_verb alc861_threestack { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 }, { 0x0c, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, //mic - { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, //line in + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 }, +#if 0 + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8)) }, /*mic*/ + { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8)) }, /*line in*/ +#endif { } /* end */ }; @@ -5365,6 +6218,11 @@ static hda_nid_t alc861_dac_nids[4] = { 0x03, 0x06, 0x05, 0x04 }; +static hda_nid_t alc660_dac_nids[3] = { + /* front, clfe, surround */ + 0x03, 0x05, 0x06 +}; + static hda_nid_t alc861_adc_nids[1] = { /* ADC0-2 */ 0x08, @@ -5617,7 +6475,10 @@ static void alc861_auto_init(struct hda_ */ static struct hda_board_config alc861_cfg_tbl[] = { { .modelname = "3stack", .config = ALC861_3ST }, - { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, .config = ALC861_3ST }, + { .pci_subvendor = 0x8086, .pci_subdevice = 0xd600, + .config = ALC861_3ST }, + { .pci_subvendor = 0x1043, .pci_subdevice = 0x81e7, + .config = ALC660_3ST }, { .modelname = "3stack-dig", .config = ALC861_3ST_DIG }, { .modelname = "6stack-dig", .config = ALC861_6ST_DIG }, { .modelname = "auto", .config = ALC861_AUTO }, @@ -5660,6 +6521,17 @@ static struct alc_config_preset alc861_p .adc_nids = alc861_adc_nids, .input_mux = &alc861_capture_source, }, + [ALC660_3ST] = { + .mixers = { alc861_3ST_mixer }, + .init_verbs = { alc861_threestack_init_verbs }, + .num_dacs = ARRAY_SIZE(alc660_dac_nids), + .dac_nids = alc660_dac_nids, + .num_channel_mode = ARRAY_SIZE(alc861_threestack_modes), + .channel_mode = alc861_threestack_modes, + .num_adc_nids = ARRAY_SIZE(alc861_adc_nids), + .adc_nids = alc861_adc_nids, + .input_mux = &alc861_capture_source, + }, }; @@ -5676,8 +6548,10 @@ static int patch_alc861(struct hda_codec codec->spec = spec; board_config = snd_hda_check_board_config(codec, alc861_cfg_tbl); + if (board_config < 0 || board_config >= ALC861_MODEL_LAST) { - printk(KERN_INFO "hda_codec: Unknown model for ALC861, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: Unknown model for ALC861, " + "trying auto-probe from BIOS...\n"); board_config = ALC861_AUTO; } @@ -5688,7 +6562,9 @@ static int patch_alc861(struct hda_codec alc_free(codec); return err; } else if (! err) { - printk(KERN_INFO "hda_codec: Cannot set up configuration from BIOS. Using base mode...\n"); + printk(KERN_INFO + "hda_codec: Cannot set up configuration " + "from BIOS. Using base mode...\n"); board_config = ALC861_3ST_DIG; } } @@ -5719,8 +6595,12 @@ struct hda_codec_preset snd_hda_preset_r { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, - { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 }, + { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 }, - { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, + { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 }, + { .id = 0x10ec0861, .rev = 0x100300, .name = "ALC861", + .patch = patch_alc861 }, + { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", + .patch = patch_alc861 }, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 8c440fb..fb4bed0 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -41,6 +41,10 @@ #define STAC_UNSOL_ENABLE (AC_USRSP_EN #define STAC_REF 0 #define STAC_D945GTP3 1 #define STAC_D945GTP5 2 +#define STAC_MACMINI 3 +#define STAC_D965_2112 4 +#define STAC_D965_284B 5 +#define STAC_922X_MODELS 6 /* number of 922x models */ struct sigmatel_spec { struct snd_kcontrol_new *mixers[4]; @@ -52,6 +56,7 @@ struct sigmatel_spec { unsigned int mic_switch: 1; unsigned int alt_switch: 1; unsigned int hp_detect: 1; + unsigned int gpio_mute: 1; /* playback */ struct hda_multi_out multiout; @@ -105,10 +110,24 @@ static hda_nid_t stac922x_adc_nids[2] = 0x06, 0x07, }; +static hda_nid_t stac9227_adc_nids[2] = { + 0x07, 0x08, +}; + +#if 0 +static hda_nid_t d965_2112_dac_nids[3] = { + 0x02, 0x03, 0x05, +}; +#endif + static hda_nid_t stac922x_mux_nids[2] = { 0x12, 0x13, }; +static hda_nid_t stac9227_mux_nids[2] = { + 0x15, 0x16, +}; + static hda_nid_t stac927x_adc_nids[3] = { 0x07, 0x08, 0x09 }; @@ -171,6 +190,24 @@ static struct hda_verb stac922x_core_ini {} }; +static struct hda_verb stac9227_core_init[] = { + /* set master volume and direct control */ + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* unmute node 0x1b */ + { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + {} +}; + +static struct hda_verb d965_2112_core_init[] = { + /* set master volume and direct control */ + { 0x16, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* unmute node 0x1b */ + { 0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000}, + /* select node 0x03 as DAC */ + { 0x0b, AC_VERB_SET_CONNECT_SEL, 0x01}, + {} +}; + static struct hda_verb stac927x_core_init[] = { /* set master volume and direct control */ { 0x24, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, @@ -210,6 +247,21 @@ static struct snd_kcontrol_new stac922x_ { } /* end */ }; +/* This needs to be generated dynamically based on sequence */ +static struct snd_kcontrol_new stac9227_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .count = 1, + .info = stac92xx_mux_enum_info, + .get = stac92xx_mux_enum_get, + .put = stac92xx_mux_enum_put, + }, + HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static snd_kcontrol_new_t stac927x_mixer[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -289,10 +341,17 @@ static unsigned int d945gtp5_pin_configs 0x02a19320, 0x40000100, }; -static unsigned int *stac922x_brd_tbl[] = { - ref922x_pin_configs, - d945gtp3_pin_configs, - d945gtp5_pin_configs, +static unsigned int d965_2112_pin_configs[10] = { + 0x0221401f, 0x40000100, 0x40000100, 0x01014011, + 0x01a19021, 0x01813024, 0x01452130, 0x40000100, + 0x02a19320, 0x40000100, +}; + +static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = { + [STAC_REF] = ref922x_pin_configs, + [STAC_D945GTP3] = d945gtp3_pin_configs, + [STAC_D945GTP5] = d945gtp5_pin_configs, + [STAC_D965_2112] = d965_2112_pin_configs, }; static struct hda_board_config stac922x_cfg_tbl[] = { @@ -324,6 +383,15 @@ static struct hda_board_config stac922x_ { .pci_subvendor = PCI_VENDOR_ID_INTEL, .pci_subdevice = 0x0417, .config = STAC_D945GTP5 }, /* Intel D975XBK - 5 Stack */ + { .pci_subvendor = 0x8384, + .pci_subdevice = 0x7680, + .config = STAC_MACMINI }, /* Apple Mac Mini (early 2006) */ + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x2112, + .config = STAC_D965_2112 }, + { .pci_subvendor = PCI_VENDOR_ID_INTEL, + .pci_subdevice = 0x284b, + .config = STAC_D965_284B }, {} /* terminator */ }; @@ -707,7 +775,8 @@ static int stac92xx_add_dyn_out_pins(str * A and B is not supported. */ /* fill in the dac_nids table from the parsed pin configuration */ -static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg) +static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) { struct sigmatel_spec *spec = codec->spec; hda_nid_t nid; @@ -726,10 +795,13 @@ static int stac92xx_auto_fill_dac_nids(s } /* add playback controls from the parsed DAC table */ -static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, const struct auto_pin_cfg *cfg) +static int stac92xx_auto_create_multi_out_ctls(struct sigmatel_spec *spec, + const struct auto_pin_cfg *cfg) { char name[32]; - static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" }; + static const char *chname[4] = { + "Front", "Surround", NULL /*CLFE*/, "Side" + }; hda_nid_t nid; int i, err; @@ -841,6 +913,19 @@ static int stac92xx_auto_create_analog_i } } + if (imux->num_items == 1) { + /* + * Set the current input for the muxes. + * The STAC9221 has two input muxes with identical source + * NID lists. Hopefully this won't get confused. + */ + for (i = 0; i < spec->num_muxes; i++) { + snd_hda_codec_write(codec, spec->mux_nids[i], 0, + AC_VERB_SET_CONNECT_SEL, + imux->items[0].index); + } + } + return 0; } @@ -874,10 +959,12 @@ static int stac92xx_parse_auto_config(st return err; if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */ + if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) return err; - if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) - return err; + if (spec->multiout.num_dacs == 0) + if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + return err; if ((err = stac92xx_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || (err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg)) < 0 || @@ -946,6 +1033,45 @@ static int stac9200_parse_auto_config(st return 1; } +/* + * Early 2006 Intel Macintoshes with STAC9220X5 codecs seem to have a + * funky external mute control using GPIO pins. + */ + +static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted) +{ + unsigned int gpiostate, gpiomask, gpiodir; + + gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + + if (!muted) + gpiostate |= (1 << pin); + else + gpiostate &= ~(1 << pin); + + gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_MASK, 0); + gpiomask |= (1 << pin); + + gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DIRECTION, 0); + gpiodir |= (1 << pin); + + /* AppleHDA seems to do this -- WTF is this verb?? */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_MASK, gpiomask); + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); + + msleep(1); + + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); +} + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -982,6 +1108,11 @@ static int stac92xx_init(struct hda_code stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, AC_PINCTL_IN_EN); + if (spec->gpio_mute) { + stac922x_gpio_mute(codec, 0, 0); + stac922x_gpio_mute(codec, 1, 0); + } + return 0; } @@ -1131,8 +1262,9 @@ static int patch_stac922x(struct hda_cod codec->spec = spec; spec->board_config = snd_hda_check_board_config(codec, stac922x_cfg_tbl); if (spec->board_config < 0) - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, using BIOS defaults\n"); - else { + snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, " + "using BIOS defaults\n"); + else if (stac922x_brd_tbl[spec->board_config] != NULL) { spec->num_pins = 10; spec->pin_nids = stac922x_pin_nids; spec->pin_configs = stac922x_brd_tbl[spec->board_config]; @@ -1147,6 +1279,25 @@ static int patch_stac922x(struct hda_cod spec->mixer = stac922x_mixer; spec->multiout.dac_nids = spec->dac_nids; + + switch (spec->board_config) { + case STAC_D965_2112: + spec->adc_nids = stac9227_adc_nids; + spec->mux_nids = stac9227_mux_nids; +#if 0 + spec->multiout.dac_nids = d965_2112_dac_nids; + spec->multiout.num_dacs = ARRAY_SIZE(d965_2112_dac_nids); +#endif + spec->init = d965_2112_core_init; + spec->mixer = stac9227_mixer; + break; + case STAC_D965_284B: + spec->adc_nids = stac9227_adc_nids; + spec->mux_nids = stac9227_mux_nids; + spec->init = stac9227_core_init; + spec->mixer = stac9227_mixer; + break; + } err = stac92xx_parse_auto_config(codec, 0x08, 0x09); if (err < 0) { @@ -1154,6 +1305,9 @@ static int patch_stac922x(struct hda_cod return err; } + if (spec->board_config == STAC_MACMINI) + spec->gpio_mute = 1; + codec->patch_ops = stac92xx_patch_ops; return 0; @@ -1262,13 +1416,13 @@ static int vaio_master_sw_put(struct snd int change; change = snd_hda_codec_amp_update(codec, 0x02, 0, HDA_OUTPUT, 0, - 0x80, valp[0] & 0x80); + 0x80, (valp[0] ? 0 : 0x80)); change |= snd_hda_codec_amp_update(codec, 0x02, 1, HDA_OUTPUT, 0, - 0x80, valp[1] & 0x80); + 0x80, (valp[1] ? 0 : 0x80)); snd_hda_codec_amp_update(codec, 0x05, 0, HDA_OUTPUT, 0, - 0x80, valp[0] & 0x80); + 0x80, (valp[0] ? 0 : 0x80)); snd_hda_codec_amp_update(codec, 0x05, 1, HDA_OUTPUT, 0, - 0x80, valp[1] & 0x80); + 0x80, (valp[1] ? 0 : 0x80)); return change; } @@ -1370,6 +1524,12 @@ struct hda_codec_preset snd_hda_preset_s { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x }, { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x }, { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x }, + { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac922x }, + { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac922x }, + { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac922x }, + { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac922x }, + { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac922x }, + { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac922x }, { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x }, { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x }, { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x }, diff --git a/sound/pci/ice1712/aureon.c b/sound/pci/ice1712/aureon.c index 336dc48..ca74f5b 100644 --- a/sound/pci/ice1712/aureon.c +++ b/sound/pci/ice1712/aureon.c @@ -1281,9 +1281,15 @@ static int aureon_set_headphone_amp(stru tmp2 = tmp = snd_ice1712_gpio_read(ice); if (enable) - tmp |= AUREON_HP_SEL; + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) + tmp |= AUREON_HP_SEL; + else + tmp |= PRODIGY_HP_SEL; else - tmp &= ~ AUREON_HP_SEL; + if (ice->eeprom.subvendor != VT1724_SUBDEVICE_PRODIGY71LT) + tmp &= ~ AUREON_HP_SEL; + else + tmp &= ~ PRODIGY_HP_SEL; if (tmp != tmp2) { snd_ice1712_gpio_write(ice, tmp); return 1; @@ -2079,16 +2085,16 @@ static unsigned char prodigy71_eeprom[] }; static unsigned char prodigy71lt_eeprom[] __devinitdata = { - 0x0b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ + 0x4b, /* SYSCINF: clock 512, spdif-in/ADC, 4DACs */ 0x80, /* ACLINK: I2S */ 0xfc, /* I2S: vol, 96k, 24bit, 192k */ - 0xc3, /* SPDUF: out-en, out-int */ - 0x00, /* GPIO_DIR */ - 0x07, /* GPIO_DIR1 */ - 0x00, /* GPIO_DIR2 */ - 0xff, /* GPIO_MASK */ - 0xf8, /* GPIO_MASK1 */ - 0xff, /* GPIO_MASK2 */ + 0xc3, /* SPDIF: out-en, out-int, spdif-in */ + 0xff, /* GPIO_DIR */ + 0xff, /* GPIO_DIR1 */ + 0x5f, /* GPIO_DIR2 */ + 0x00, /* GPIO_MASK */ + 0x00, /* GPIO_MASK1 */ + 0x00, /* GPIO_MASK2 */ 0x00, /* GPIO_STATE */ 0x00, /* GPIO_STATE1 */ 0x00, /* GPIO_STATE2 */ diff --git a/sound/pci/ice1712/aureon.h b/sound/pci/ice1712/aureon.h index 98a6752..3b7bea6 100644 --- a/sound/pci/ice1712/aureon.h +++ b/sound/pci/ice1712/aureon.h @@ -58,5 +58,6 @@ #define AUREON_AC97_DATA_MASK 0xFF #define PRODIGY_WM_CS (1 << 8) #define PRODIGY_SPI_MOSI (1 << 10) #define PRODIGY_SPI_CLK (1 << 9) +#define PRODIGY_HP_SEL (1 << 5) #endif /* __SOUND_AUREON_H */ diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c index 2c529e7..b135389 100644 --- a/sound/pci/ice1712/ews.c +++ b/sound/pci/ice1712/ews.c @@ -1031,6 +1031,9 @@ struct snd_ice1712_card_info snd_ice1712 .model = "dmx6fire", .chip_init = snd_ice1712_ews_init, .build_controls = snd_ice1712_ews_add_controls, + .mpu401_1_name = "MIDI-Front DMX6fire", + .mpu401_2_name = "Wavetable DMX6fire", + .mpu401_2_info_flags = MPU401_INFO_OUTPUT, }, { } /* terminator */ }; diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c index c56793b..89a06de 100644 --- a/sound/pci/ice1712/ice1712.c +++ b/sound/pci/ice1712/ice1712.c @@ -61,7 +61,6 @@ #include #include #include #include -#include #include #include @@ -1596,7 +1595,7 @@ static void __devinit snd_ice1712_proc_i struct snd_info_entry *entry; if (! snd_card_proc_new(ice->card, "ice1712", &entry)) - snd_info_set_text_ops(entry, ice, 1024, snd_ice1712_proc_read); + snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read); } /* @@ -2398,13 +2397,14 @@ static int __devinit snd_ice1712_chip_in udelay(200); outb(ICE1712_NATIVE, ICEREG(ice, CONTROL)); udelay(200); - if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && !ice->dxr_enable) { - /* Limit active ADCs and DACs to 6; */ - /* Note: DXR extension not supported */ - pci_write_config_byte(ice->pci, 0x60, 0x2a); - } else { - pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]); - } + if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && + !ice->dxr_enable) + /* Set eeprom value to limit active ADCs and DACs to 6; + * Also disable AC97 as no hardware in standard 6fire card/box + * Note: DXR extensions are not currently supported + */ + ice->eeprom.data[ICE_EEP1_CODEC] = 0x3a; + pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]); pci_write_config_byte(ice->pci, 0x61, ice->eeprom.data[ICE_EEP1_ACLINK]); pci_write_config_byte(ice->pci, 0x62, ice->eeprom.data[ICE_EEP1_I2SID]); pci_write_config_byte(ice->pci, 0x63, ice->eeprom.data[ICE_EEP1_SPDIF]); @@ -2606,7 +2606,7 @@ static int __devinit snd_ice1712_create( ice->dmapath_port = pci_resource_start(pci, 2); ice->profi_port = pci_resource_start(pci, 3); - if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_ice1712_interrupt, IRQF_DISABLED|IRQF_SHARED, "ICE1712", ice)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_ice1712_free(ice); @@ -2737,21 +2737,38 @@ static int __devinit snd_ice1712_probe(s if (! c->no_mpu401) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, - ICEREG(ice, MPU1_CTRL), 1, + ICEREG(ice, MPU1_CTRL), + (c->mpu401_1_info_flags | + MPU401_INFO_INTEGRATED), ice->irq, 0, &ice->rmidi[0])) < 0) { snd_card_free(card); return err; } - - if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) + if (c->mpu401_1_name) + /* Prefered name available in card_info */ + snprintf(ice->rmidi[0]->name, + sizeof(ice->rmidi[0]->name), + "%s %d", c->mpu401_1_name, card->number); + + if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) { + /* 2nd port used */ if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712, - ICEREG(ice, MPU2_CTRL), 1, + ICEREG(ice, MPU2_CTRL), + (c->mpu401_2_info_flags | + MPU401_INFO_INTEGRATED), ice->irq, 0, &ice->rmidi[1])) < 0) { snd_card_free(card); return err; } + if (c->mpu401_2_name) + /* Prefered name available in card_info */ + snprintf(ice->rmidi[1]->name, + sizeof(ice->rmidi[1]->name), + "%s %d", c->mpu401_2_name, + card->number); + } } snd_ice1712_set_input_clock_source(ice, 0); diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h index 053f8e5..ce27eac 100644 --- a/sound/pci/ice1712/ice1712.h +++ b/sound/pci/ice1712/ice1712.h @@ -29,6 +29,7 @@ #include #include #include #include +#include /* @@ -495,6 +496,10 @@ struct snd_ice1712_card_info { int (*chip_init)(struct snd_ice1712 *); int (*build_controls)(struct snd_ice1712 *); unsigned int no_mpu401: 1; + unsigned int mpu401_1_info_flags; + unsigned int mpu401_2_info_flags; + const char *mpu401_1_name; + const char *mpu401_2_name; unsigned int eeprom_size; unsigned char *eeprom_data; }; diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c index b1c007e..ad69ed7 100644 --- a/sound/pci/ice1712/ice1724.c +++ b/sound/pci/ice1712/ice1724.c @@ -1293,7 +1293,7 @@ static void __devinit snd_vt1724_proc_in struct snd_info_entry *entry; if (! snd_card_proc_new(ice->card, "ice1724", &entry)) - snd_info_set_text_ops(entry, ice, 1024, snd_vt1724_proc_read); + snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read); } /* @@ -2253,7 +2253,7 @@ static int __devinit snd_vt1724_create(s ice->profi_port = pci_resource_start(pci, 1); if (request_irq(pci->irq, snd_vt1724_interrupt, - SA_INTERRUPT|SA_SHIRQ, "ICE1724", ice)) { + IRQF_DISABLED|IRQF_SHARED, "ICE1724", ice)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_vt1724_free(ice); return -EIO; @@ -2388,7 +2388,8 @@ static int __devinit snd_vt1724_probe(st if (! c->no_mpu401) { if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712, - ICEREG1724(ice, MPU_CTRL), 1, + ICEREG1724(ice, MPU_CTRL), + MPU401_INFO_INTEGRATED, ice->irq, 0, &ice->rmidi[0])) < 0) { snd_card_free(card); diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c index d23fb3f..0efcad9 100644 --- a/sound/pci/ice1712/pontis.c +++ b/sound/pci/ice1712/pontis.c @@ -680,9 +680,8 @@ static void wm_proc_init(struct snd_ice1 { struct snd_info_entry *entry; if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) { - snd_info_set_text_ops(entry, ice, 1024, wm_proc_regs_read); + snd_info_set_text_ops(entry, ice, wm_proc_regs_read); entry->mode |= S_IWUSR; - entry->c.text.write_size = 1024; entry->c.text.write = wm_proc_regs_write; } } @@ -705,9 +704,8 @@ static void cs_proc_regs_read(struct snd static void cs_proc_init(struct snd_ice1712 *ice) { struct snd_info_entry *entry; - if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) { - snd_info_set_text_ops(entry, ice, 1024, cs_proc_regs_read); - } + if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) + snd_info_set_text_ops(entry, ice, cs_proc_regs_read); } diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c index b5754b3..fec9440 100644 --- a/sound/pci/ice1712/revo.c +++ b/sound/pci/ice1712/revo.c @@ -87,12 +87,25 @@ static void revo_set_rate_val(struct snd * initialize the chips on M-Audio Revolution cards */ +static unsigned int revo71_num_stereo_front[] = {2}; +static char *revo71_channel_names_front[] = {"PCM Playback Volume"}; + +static unsigned int revo71_num_stereo_surround[] = {1, 1, 2, 2}; +static char *revo71_channel_names_surround[] = {"PCM Center Playback Volume", "PCM LFE Playback Volume", + "PCM Side Playback Volume", "PCM Rear Playback Volume"}; + +static unsigned int revo51_num_stereo[] = {2, 1, 1, 2}; +static char *revo51_channel_names[] = {"PCM Playback Volume", "PCM Center Playback Volume", + "PCM LFE Playback Volume", "PCM Rear Playback Volume"}; + static struct snd_akm4xxx akm_revo_front __devinitdata = { .type = SND_AK4381, .num_dacs = 2, .ops = { .set_rate_val = revo_set_rate_val - } + }, + .num_stereo = revo71_num_stereo_front, + .channel_names = revo71_channel_names_front }; static struct snd_ak4xxx_private akm_revo_front_priv __devinitdata = { @@ -113,7 +126,9 @@ static struct snd_akm4xxx akm_revo_surro .num_dacs = 6, .ops = { .set_rate_val = revo_set_rate_val - } + }, + .num_stereo = revo71_num_stereo_surround, + .channel_names = revo71_channel_names_surround }; static struct snd_ak4xxx_private akm_revo_surround_priv __devinitdata = { @@ -133,7 +148,9 @@ static struct snd_akm4xxx akm_revo51 __d .num_dacs = 6, .ops = { .set_rate_val = revo_set_rate_val - } + }, + .num_stereo = revo51_num_stereo, + .channel_names = revo51_channel_names }; static struct snd_ak4xxx_private akm_revo51_priv __devinitdata = { diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 0df7602..5634bc3 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -66,7 +66,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel,82801AA static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -static int ac97_clock = 0; +static int ac97_clock; static char *ac97_quirk; static int buggy_semaphore; static int buggy_irq = -1; /* auto-check */ @@ -1807,6 +1807,12 @@ static struct ac97_quirk ac97_quirks[] _ }, { .subvendor = 0x1028, + .subdevice = 0x014e, + .name = "Dell D800", /* STAC9750/51 */ + .type = AC97_TUNE_HP_ONLY + }, + { + .subvendor = 0x1028, .subdevice = 0x0163, .name = "Dell Unknown", /* STAC9750/51 */ .type = AC97_TUNE_HP_ONLY @@ -2469,7 +2475,7 @@ static int intel8x0_resume(struct pci_de pci_restore_state(pci); pci_enable_device(pci); pci_set_master(pci); - request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, + request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, chip); chip->irq = pci->irq; synchronize_irq(chip->irq); @@ -2645,7 +2651,7 @@ static void __devinit snd_intel8x0_proc_ struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0_proc_read); + snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); } #else #define snd_intel8x0_proc_init(x) @@ -2842,7 +2848,7 @@ static int __devinit snd_intel8x0_create /* request irq after initializaing int_sta_mask, etc */ if (request_irq(pci->irq, snd_intel8x0_interrupt, - SA_INTERRUPT|SA_SHIRQ, card->shortname, chip)) { + IRQF_DISABLED|IRQF_SHARED, card->shortname, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_intel8x0_free(chip); return -EBUSY; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c index 720635f..f28e273 100644 --- a/sound/pci/intel8x0m.c +++ b/sound/pci/intel8x0m.c @@ -59,7 +59,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel,82801AA static int index = -2; /* Exclude the first card */ static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ -static int ac97_clock = 0; +static int ac97_clock; module_param(index, int, 0444); MODULE_PARM_DESC(index, "Index value for Intel i8x0 modemcard."); @@ -1092,7 +1092,7 @@ static void __devinit snd_intel8x0m_proc struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "intel8x0m", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_intel8x0m_proc_read); + snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read); } #else /* !CONFIG_PROC_FS */ #define snd_intel8x0m_proc_init(chip) @@ -1185,7 +1185,7 @@ static int __devinit snd_intel8x0m_creat } port_inited: - if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_intel8x0_interrupt, IRQF_DISABLED|IRQF_SHARED, card->shortname, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_intel8x0_free(chip); diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c index e39fad1..2b4ce00 100644 --- a/sound/pci/korg1212/korg1212.c +++ b/sound/pci/korg1212/korg1212.c @@ -2085,7 +2085,7 @@ static void __devinit snd_korg1212_proc_ struct snd_info_entry *entry; if (! snd_card_proc_new(korg1212->card, "korg1212", &entry)) - snd_info_set_text_ops(entry, korg1212, 1024, snd_korg1212_proc_read); + snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read); } static int @@ -2237,7 +2237,7 @@ static int __devinit snd_korg1212_create } err = request_irq(pci->irq, snd_korg1212_interrupt, - SA_INTERRUPT|SA_SHIRQ, + IRQF_DISABLED|IRQF_SHARED, "korg1212", korg1212); if (err) { diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 1928e06..828eab5 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c @@ -2760,7 +2760,7 @@ snd_m3_create(struct snd_card *card, str tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); - if (request_irq(pci->irq, snd_m3_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_m3_interrupt, IRQF_DISABLED|IRQF_SHARED, card->driver, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_m3_free(chip); @@ -2861,7 +2861,8 @@ snd_m3_probe(struct pci_dev *pci, const #if 0 /* TODO: not supported yet */ /* TODO enable MIDI IRQ and I/O */ err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401, - chip->iobase + MPU401_DATA_PORT, 1, + chip->iobase + MPU401_DATA_PORT, + MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi); if (err < 0) printk(KERN_WARNING "maestro3: no MIDI support.\n"); diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c index 09cc078..a4aaa7b 100644 --- a/sound/pci/mixart/mixart.c +++ b/sound/pci/mixart/mixart.c @@ -1244,7 +1244,6 @@ static void __devinit snd_mixart_proc_in /* text interface to read perf and temp meters */ if (! snd_card_proc_new(chip->card, "board_info", &entry)) { entry->private_data = chip; - entry->c.text.read_size = 1024; entry->c.text.read = snd_mixart_proc_read; } @@ -1320,7 +1319,7 @@ static int __devinit snd_mixart_probe(st pci_resource_len(pci, i)); } - if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) { + if (request_irq(pci->irq, snd_mixart_interrupt, IRQF_DISABLED|IRQF_SHARED, CARD_NAME, (void *)mgr)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_mixart_free(mgr); return -EBUSY; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index b92d660..56d7282 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c @@ -465,7 +465,7 @@ static int snd_nm256_acquire_irq(struct { mutex_lock(&chip->irq_mutex); if (chip->irq < 0) { - if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(chip->pci->irq, chip->interrupt, IRQF_DISABLED|IRQF_SHARED, chip->card->driver, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->pci->irq); mutex_unlock(&chip->irq_mutex); diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index dafa223..ae980e1 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -1150,9 +1150,9 @@ static void __devinit pcxhr_proc_init(st struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "info", &entry)) - snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_info); + snd_info_set_text_ops(entry, chip, pcxhr_proc_info); if (! snd_card_proc_new(chip->card, "sync", &entry)) - snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_sync); + snd_info_set_text_ops(entry, chip, pcxhr_proc_sync); } /* end of proc interface */ @@ -1250,7 +1250,7 @@ static int __devinit pcxhr_probe(struct mgr->pci = pci; mgr->irq = -1; - if (request_irq(pci->irq, pcxhr_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, pcxhr_interrupt, IRQF_DISABLED|IRQF_SHARED, card_name, mgr)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); pcxhr_free(mgr); diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index d8cc985..5501a08 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -1836,11 +1836,11 @@ static int snd_riptide_free(struct snd_r UNSET_GRESET(cif->hwport); kfree(chip->cif); } + if (chip->irq >= 0) + free_irq(chip->irq, chip); if (chip->fw_entry) release_firmware(chip->fw_entry); release_and_free_resource(chip->res_port); - if (chip->irq >= 0) - free_irq(chip->irq, chip); kfree(chip); return 0; } @@ -1892,7 +1892,7 @@ snd_riptide_create(struct snd_card *card UNSET_AIE(hwport); if (request_irq - (pci->irq, snd_riptide_interrupt, SA_INTERRUPT | SA_SHIRQ, + (pci->irq, snd_riptide_interrupt, IRQF_DISABLED | IRQF_SHARED, "RIPTIDE", chip)) { snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n", pci->irq); @@ -1992,7 +1992,7 @@ static void __devinit snd_riptide_proc_i struct snd_info_entry *entry; if (!snd_card_proc_new(chip->card, "riptide", &entry)) - snd_info_set_text_ops(entry, chip, 4096, snd_riptide_proc_read); + snd_info_set_text_ops(entry, chip, snd_riptide_proc_read); } static int __devinit snd_riptide_mixer(struct snd_riptide *chip) diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c index 55b1d48..2e24b68 100644 --- a/sound/pci/rme32.c +++ b/sound/pci/rme32.c @@ -1368,18 +1368,18 @@ static int __devinit snd_rme32_create(st return err; rme32->port = pci_resource_start(rme32->pci, 0); - if (request_irq(pci->irq, snd_rme32_interrupt, SA_INTERRUPT | SA_SHIRQ, "RME32", (void *) rme32)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); - return -EBUSY; - } - rme32->irq = pci->irq; - if ((rme32->iobase = ioremap_nocache(rme32->port, RME32_IO_SIZE)) == 0) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme32->port, rme32->port + RME32_IO_SIZE - 1); return -ENOMEM; } + if (request_irq(pci->irq, snd_rme32_interrupt, IRQF_DISABLED | IRQF_SHARED, "RME32", (void *) rme32)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme32->irq = pci->irq; + /* read the card's revision number */ pci_read_config_byte(pci, 8, &rme32->rev); @@ -1578,7 +1578,7 @@ static void __devinit snd_rme32_proc_ini struct snd_info_entry *entry; if (! snd_card_proc_new(rme32->card, "rme32", &entry)) - snd_info_set_text_ops(entry, rme32, 1024, snd_rme32_proc_read); + snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read); } /* diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c index 3c1bc53..fde0f3e 100644 --- a/sound/pci/rme96.c +++ b/sound/pci/rme96.c @@ -1151,6 +1151,25 @@ static struct snd_pcm_hw_constraint_list .mask = 0 }; +static void +rme96_set_buffer_size_constraint(struct rme96 *rme96, + struct snd_pcm_runtime *runtime) +{ + unsigned int size; + + snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, + RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); + if ((size = rme96->playback_periodsize) != 0 || + (size = rme96->capture_periodsize) != 0) + snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + size, size); + else + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, + &hw_constraints_period_bytes); +} + static int snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream) { @@ -1180,8 +1199,7 @@ snd_rme96_playback_spdif_open(struct snd runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + rme96_set_buffer_size_constraint(rme96, runtime); rme96->wcreg_spdif_stream = rme96->wcreg_spdif; rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; @@ -1219,9 +1237,7 @@ snd_rme96_capture_spdif_open(struct snd_ rme96->capture_substream = substream; spin_unlock_irq(&rme96->lock); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); - + rme96_set_buffer_size_constraint(rme96, runtime); return 0; } @@ -1254,8 +1270,7 @@ snd_rme96_playback_adat_open(struct snd_ runtime->hw.rate_min = rate; runtime->hw.rate_max = rate; } - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + rme96_set_buffer_size_constraint(rme96, runtime); return 0; } @@ -1291,8 +1306,7 @@ snd_rme96_capture_adat_open(struct snd_p rme96->capture_substream = substream; spin_unlock_irq(&rme96->lock); - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, RME96_BUFFER_SIZE, RME96_BUFFER_SIZE); - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_period_bytes); + rme96_set_buffer_size_constraint(rme96, runtime); return 0; } @@ -1569,17 +1583,17 @@ snd_rme96_create(struct rme96 *rme96) return err; rme96->port = pci_resource_start(rme96->pci, 0); - if (request_irq(pci->irq, snd_rme96_interrupt, SA_INTERRUPT|SA_SHIRQ, "RME96", (void *)rme96)) { - snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); - return -EBUSY; - } - rme96->irq = pci->irq; - if ((rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE)) == 0) { snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1); return -ENOMEM; } + if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_DISABLED|IRQF_SHARED, "RME96", (void *)rme96)) { + snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); + return -EBUSY; + } + rme96->irq = pci->irq; + /* read the card's revision number */ pci_read_config_byte(pci, 8, &rme96->rev); @@ -1805,7 +1819,7 @@ snd_rme96_proc_init(struct rme96 *rme96) struct snd_info_entry *entry; if (! snd_card_proc_new(rme96->card, "rme96", &entry)) - snd_info_set_text_ops(entry, rme96, 1024, snd_rme96_proc_read); + snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read); } /* diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index 61f82f0..99cf862 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -389,7 +389,7 @@ #define HDSP_DMA_AREA_KILOBYTES (HDSP_DM /* use hotplug firmeare loader? */ #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE) -#ifndef HDSP_USE_HWDEP_LOADER +#if !defined(HDSP_USE_HWDEP_LOADER) && !defined(CONFIG_SND_HDSP) #define HDSP_FW_LOADER #endif #endif @@ -3169,9 +3169,10 @@ snd_hdsp_proc_read(struct snd_info_entry char *clock_source; int x; - if (hdsp_check_for_iobox (hdsp)) + if (hdsp_check_for_iobox (hdsp)) { snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n"); return; + } if (hdsp_check_for_firmware(hdsp, 0)) { if (hdsp->state & HDSP_FirmwareCached) { @@ -3470,7 +3471,7 @@ static void __devinit snd_hdsp_proc_init struct snd_info_entry *entry; if (! snd_card_proc_new(hdsp->card, "hdsp", &entry)) - snd_info_set_text_ops(entry, hdsp, 1024, snd_hdsp_proc_read); + snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read); } static void snd_hdsp_free_buffers(struct hdsp *hdsp) @@ -4911,7 +4912,7 @@ static int __devinit snd_hdsp_create(str return -EBUSY; } - if (request_irq(pci->irq, snd_hdsp_interrupt, SA_INTERRUPT|SA_SHIRQ, "hdsp", (void *)hdsp)) { + if (request_irq(pci->irq, snd_hdsp_interrupt, IRQF_DISABLED|IRQF_SHARED, "hdsp", (void *)hdsp)) { snd_printk(KERN_ERR "Hammerfall-DSP: unable to use IRQ %d\n", pci->irq); return -EBUSY; } diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 722b9e6..7d03ae0 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -2489,7 +2489,7 @@ static void __devinit snd_hdspm_proc_ini struct snd_info_entry *entry; if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) - snd_info_set_text_ops(entry, hdspm, 1024, + snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_read); } @@ -3497,7 +3497,7 @@ static int __devinit snd_hdspm_create(st hdspm->port + io_extent - 1); if (request_irq(pci->irq, snd_hdspm_interrupt, - SA_INTERRUPT | SA_SHIRQ, "hdspm", + IRQF_DISABLED | IRQF_SHARED, "hdspm", (void *) hdspm)) { snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq); return -EBUSY; diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c index 75d6406..9534e18 100644 --- a/sound/pci/rme9652/rme9652.c +++ b/sound/pci/rme9652/rme9652.c @@ -41,7 +41,7 @@ #include static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int precise_ptr[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 0 }; /* Enable precise pointer */ +static int precise_ptr[SNDRV_CARDS]; /* Enable precise pointer */ module_param_array(index, int, NULL, 0444); MODULE_PARM_DESC(index, "Index value for RME Digi9652 (Hammerfall) soundcard."); @@ -1787,7 +1787,7 @@ static void __devinit snd_rme9652_proc_i struct snd_info_entry *entry; if (! snd_card_proc_new(rme9652->card, "rme9652", &entry)) - snd_info_set_text_ops(entry, rme9652, 1024, snd_rme9652_proc_read); + snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read); } static void snd_rme9652_free_buffers(struct snd_rme9652 *rme9652) @@ -2500,7 +2500,7 @@ static int __devinit snd_rme9652_create( return -EBUSY; } - if (request_irq(pci->irq, snd_rme9652_interrupt, SA_INTERRUPT|SA_SHIRQ, "rme9652", (void *)rme9652)) { + if (request_irq(pci->irq, snd_rme9652_interrupt, IRQF_DISABLED|IRQF_SHARED, "rme9652", (void *)rme9652)) { snd_printk(KERN_ERR "unable to request IRQ %d\n", pci->irq); return -EBUSY; } diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c index 91f8bf3..c430341 100644 --- a/sound/pci/sonicvibes.c +++ b/sound/pci/sonicvibes.c @@ -54,8 +54,8 @@ #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */ -static int reverb[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; -static int mge[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 0}; +static int reverb[SNDRV_CARDS]; +static int mge[SNDRV_CARDS]; static unsigned int dmaio = 0x7a00; /* DDMA i/o address */ module_param_array(index, int, NULL, 0444); @@ -1144,7 +1144,7 @@ static void __devinit snd_sonicvibes_pro struct snd_info_entry *entry; if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry)) - snd_info_set_text_ops(entry, sonic, 1024, snd_sonicvibes_proc_read); + snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read); } /* @@ -1257,7 +1257,7 @@ static int __devinit snd_sonicvibes_crea sonic->midi_port = pci_resource_start(pci, 3); sonic->game_port = pci_resource_start(pci, 4); - if (request_irq(pci->irq, snd_sonicvibes_interrupt, SA_INTERRUPT|SA_SHIRQ, "S3 SonicVibes", (void *)sonic)) { + if (request_irq(pci->irq, snd_sonicvibes_interrupt, IRQF_DISABLED|IRQF_SHARED, "S3 SonicVibes", (void *)sonic)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_sonicvibes_free(sonic); return -EBUSY; @@ -1441,10 +1441,10 @@ static int __devinit snd_sonic_probe(str strcpy(card->driver, "SonicVibes"); strcpy(card->shortname, "S3 SonicVibes"); - sprintf(card->longname, "%s rev %i at 0x%lx, irq %i", + sprintf(card->longname, "%s rev %i at 0x%llx, irq %i", card->shortname, sonic->revision, - pci_resource_start(pci, 1), + (unsigned long long)pci_resource_start(pci, 1), sonic->irq); if ((err = snd_sonicvibes_pcm(sonic, 0, NULL)) < 0) { @@ -1456,7 +1456,7 @@ static int __devinit snd_sonic_probe(str return err; } if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES, - sonic->midi_port, 1, + sonic->midi_port, MPU401_INFO_INTEGRATED, sonic->irq, 0, &midi_uart)) < 0) { snd_card_free(card); diff --git a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c index 9624a5f..5629b7e 100644 --- a/sound/pci/trident/trident.c +++ b/sound/pci/trident/trident.c @@ -148,7 +148,8 @@ static int __devinit snd_trident_probe(s } if (trident->device != TRIDENT_DEVICE_ID_SI7018 && (err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE, - trident->midi_port, 1, + trident->midi_port, + MPU401_INFO_INTEGRATED, trident->irq, 0, &trident->rmidi)) < 0) { snd_card_free(card); return err; diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 52178b8..4930cc6 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -306,6 +306,8 @@ void snd_trident_start_voice(struct snd_ outl(mask, TRID_REG(trident, reg)); } +EXPORT_SYMBOL(snd_trident_start_voice); + /*--------------------------------------------------------------------------- void snd_trident_stop_voice(struct snd_trident * trident, unsigned int voice) @@ -328,6 +330,8 @@ void snd_trident_stop_voice(struct snd_t outl(mask, TRID_REG(trident, reg)); } +EXPORT_SYMBOL(snd_trident_stop_voice); + /*--------------------------------------------------------------------------- int snd_trident_allocate_pcm_channel(struct snd_trident *trident) @@ -502,6 +506,8 @@ #if 0 #endif } +EXPORT_SYMBOL(snd_trident_write_voice_regs); + /*--------------------------------------------------------------------------- snd_trident_write_cso_reg @@ -3332,7 +3338,7 @@ static void __devinit snd_trident_proc_i if (trident->device == TRIDENT_DEVICE_ID_SI7018) s = "sis7018"; if (! snd_card_proc_new(trident->card, s, &entry)) - snd_info_set_text_ops(entry, trident, 1024, snd_trident_proc_read); + snd_info_set_text_ops(entry, trident, snd_trident_proc_read); } static int snd_trident_dev_free(struct snd_device *device) @@ -3593,7 +3599,7 @@ int __devinit snd_trident_create(struct } trident->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_trident_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_trident_interrupt, IRQF_DISABLED|IRQF_SHARED, "Trident Audio", trident)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_trident_free(trident); @@ -3884,6 +3890,8 @@ struct snd_trident_voice *snd_trident_al return NULL; } +EXPORT_SYMBOL(snd_trident_alloc_voice); + void snd_trident_free_voice(struct snd_trident * trident, struct snd_trident_voice *voice) { unsigned long flags; @@ -3912,6 +3920,8 @@ void snd_trident_free_voice(struct snd_t private_free(voice); } +EXPORT_SYMBOL(snd_trident_free_voice); + static void snd_trident_clear_voices(struct snd_trident * trident, unsigned short v_min, unsigned short v_max) { unsigned int i, val, mask[2] = { 0, 0 }; @@ -3993,13 +4003,3 @@ int snd_trident_resume(struct pci_dev *p return 0; } #endif /* CONFIG_PM */ - -EXPORT_SYMBOL(snd_trident_alloc_voice); -EXPORT_SYMBOL(snd_trident_free_voice); -EXPORT_SYMBOL(snd_trident_start_voice); -EXPORT_SYMBOL(snd_trident_stop_voice); -EXPORT_SYMBOL(snd_trident_write_voice_regs); -/* trident_memory.c symbols */ -EXPORT_SYMBOL(snd_trident_synth_alloc); -EXPORT_SYMBOL(snd_trident_synth_free); -EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff --git a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c index 46c6982..aff3f87 100644 --- a/sound/pci/trident/trident_memory.c +++ b/sound/pci/trident/trident_memory.c @@ -349,6 +349,7 @@ snd_trident_synth_alloc(struct snd_tride return blk; } +EXPORT_SYMBOL(snd_trident_synth_alloc); /* * free a synth sample area @@ -365,6 +366,7 @@ snd_trident_synth_free(struct snd_triden return 0; } +EXPORT_SYMBOL(snd_trident_synth_free); /* * reset TLB entry and free kernel page @@ -486,3 +488,4 @@ int snd_trident_synth_copy_from_user(str return 0; } +EXPORT_SYMBOL(snd_trident_synth_copy_from_user); diff --git a/sound/pci/trident/trident_synth.c b/sound/pci/trident/trident_synth.c index cc7af8b..9b7dee8 100644 --- a/sound/pci/trident/trident_synth.c +++ b/sound/pci/trident/trident_synth.c @@ -914,7 +914,9 @@ static int snd_trident_synth_create_port &callbacks, SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE, SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE | - SNDRV_SEQ_PORT_TYPE_SYNTH, + SNDRV_SEQ_PORT_TYPE_SYNTH | + SNDRV_SEQ_PORT_TYPE_HARDWARE | + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER, 16, 0, name); if (p->chset->port < 0) { diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 39daf62..37bd5eb 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1775,6 +1775,12 @@ static struct ac97_quirk ac97_quirks[] = .name = "Targa Traveller 811", .type = AC97_TUNE_HP_ONLY, }, + { + .subvendor = 0x161f, + .subdevice = 0x2032, + .name = "m680x", + .type = AC97_TUNE_HP_ONLY, /* http://launchpad.net/bugs/38546 */ + }, { } /* terminator */ }; @@ -1973,7 +1979,7 @@ #endif pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg); if (chip->mpu_res) { if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A, - mpu_port, 1, + mpu_port, MPU401_INFO_INTEGRATED, chip->irq, 0, &chip->rmidi) < 0) { printk(KERN_WARNING "unable to initialize MPU-401" " at 0x%lx, skipping\n", mpu_port); @@ -2015,7 +2021,7 @@ static void __devinit snd_via82xx_proc_i struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "via82xx", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read); + snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); } /* @@ -2275,7 +2281,7 @@ static int __devinit snd_via82xx_create( if (request_irq(pci->irq, chip_type == TYPE_VIA8233 ? snd_via8233_interrupt : snd_via686_interrupt, - SA_INTERRUPT|SA_SHIRQ, + IRQF_DISABLED|IRQF_SHARED, card->driver, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_via82xx_free(chip); @@ -2365,7 +2371,7 @@ static int __devinit check_dxs_list(stru { .subvendor = 0x1462, .subdevice = 0x0470, .action = VIA_DXS_SRC }, /* MSI KT880 Delta-FSR */ { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */ { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */ - { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */ + { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_SRC }, /* MSI K8T Neo2-FI */ { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */ { .subvendor = 0x1462, .subdevice = 0x7142, .action = VIA_DXS_ENABLE }, /* MSI K8MM-V */ { .subvendor = 0x1462, .subdevice = 0xb012, .action = VIA_DXS_SRC }, /* P4M800/VIA8237R */ diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c index ef97e50..c1ede6c 100644 --- a/sound/pci/via82xx_modem.c +++ b/sound/pci/via82xx_modem.c @@ -929,7 +929,7 @@ static void __devinit snd_via82xx_proc_i struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "via82xx", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_via82xx_proc_read); + snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); } /* @@ -1118,7 +1118,7 @@ static int __devinit snd_via82xx_create( return err; } chip->port = pci_resource_start(pci, 0); - if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_via82xx_interrupt, IRQF_DISABLED|IRQF_SHARED, card->driver, chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_via82xx_free(chip); diff --git a/sound/pci/vx222/vx222.c b/sound/pci/vx222/vx222.c index 0f1ebb0..7deda25 100644 --- a/sound/pci/vx222/vx222.c +++ b/sound/pci/vx222/vx222.c @@ -162,7 +162,7 @@ static int __devinit snd_vx222_create(st for (i = 0; i < 2; i++) vx->port[i] = pci_resource_start(pci, i + 1); - if (request_irq(pci->irq, snd_vx_irq_handler, SA_INTERRUPT|SA_SHIRQ, + if (request_irq(pci->irq, snd_vx_irq_handler, IRQF_DISABLED|IRQF_SHARED, CARD_NAME, (void *) chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_vx222_free(chip); diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c index 65ebf5f..26aa775 100644 --- a/sound/pci/ymfpci/ymfpci.c +++ b/sound/pci/ymfpci/ymfpci.c @@ -308,7 +308,8 @@ static int __devinit snd_card_ymfpci_pro } if (chip->mpu_res) { if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI, - mpu_port[dev], 1, + mpu_port[dev], + MPU401_INFO_INTEGRATED, pci->irq, 0, &chip->rawmidi)) < 0) { printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]); legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */ diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c index 8ac5ab5..a55b5fd 100644 --- a/sound/pci/ymfpci/ymfpci_main.c +++ b/sound/pci/ymfpci/ymfpci_main.c @@ -1919,7 +1919,7 @@ static int __devinit snd_ymfpci_proc_ini struct snd_info_entry *entry; if (! snd_card_proc_new(card, "ymfpci", &entry)) - snd_info_set_text_ops(entry, chip, 1024, snd_ymfpci_proc_read); + snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); return 0; } @@ -2288,7 +2288,7 @@ int __devinit snd_ymfpci_create(struct s snd_ymfpci_free(chip); return -EBUSY; } - if (request_irq(pci->irq, snd_ymfpci_interrupt, SA_INTERRUPT|SA_SHIRQ, "YMFPCI", (void *) chip)) { + if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_DISABLED|IRQF_SHARED, "YMFPCI", (void *) chip)) { snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq); snd_ymfpci_free(chip); return -EBUSY; diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c index bd0d70f..1dfe29b 100644 --- a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c +++ b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c @@ -144,7 +144,7 @@ static void pdacf_proc_init(struct snd_p struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry)) - snd_info_set_text_ops(entry, chip, 1024, pdacf_proc_read); + snd_info_set_text_ops(entry, chip, pdacf_proc_read); } struct snd_pdacf *snd_pdacf_create(struct snd_card *card) diff --git a/sound/pcmcia/vx/vxp_ops.c b/sound/pcmcia/vx/vxp_ops.c index 7f82f61..1ee0918 100644 --- a/sound/pcmcia/vx/vxp_ops.c +++ b/sound/pcmcia/vx/vxp_ops.c @@ -202,7 +202,7 @@ static int vxp_load_xilinx_binary(struct c |= (int)vx_inb(chip, RXM) << 8; c |= vx_inb(chip, RXL); - snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%x\n", c, fw->size); + snd_printdd(KERN_DEBUG "xilinx: dsp size received 0x%x, orig 0x%Zx\n", c, fw->size); vx_outb(chip, ICR, ICR_HF0); diff --git a/sound/pcmcia/vx/vxpocket.c b/sound/pcmcia/vx/vxpocket.c index 7e0cda2..cafe664 100644 --- a/sound/pcmcia/vx/vxpocket.c +++ b/sound/pcmcia/vx/vxpocket.c @@ -261,7 +261,7 @@ static int vxpocket_config(struct pcmcia link->dev_node = &vxp->node; kfree(parse); - return 9; + return 0; cs_failed: cs_error(link, last_fn, last_ret); diff --git a/sound/ppc/Makefile b/sound/ppc/Makefile index d6ba995..4d95c65 100644 --- a/sound/ppc/Makefile +++ b/sound/ppc/Makefile @@ -3,7 +3,7 @@ # Makefile for ALSA # Copyright (c) 2001 by Jaroslav Kysela # -snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o toonie.o keywest.o beep.o +snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o # Toplevel Module Dependency obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index f0794ef..6414306 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -867,8 +867,6 @@ static int __init snd_pmac_detect(struct unsigned int *prop, l; struct macio_chip* macio; - u32 layout_id = 0; - if (!machine_is(powermac)) return -ENODEV; @@ -929,8 +927,14 @@ static int __init snd_pmac_detect(struct if (prop && *prop < 16) chip->subframe = *prop; prop = (unsigned int *) get_property(sound, "layout-id", NULL); - if (prop) - layout_id = *prop; + if (prop) { + /* partly deprecate snd-powermac, for those machines + * that have a layout-id property for now */ + printk(KERN_INFO "snd-powermac no longer handles any " + "machines with a layout-id property " + "in the device-tree, use snd-aoa.\n"); + return -ENODEV; + } /* This should be verified on older screamers */ if (device_is_compatible(sound, "screamer")) { chip->model = PMAC_SCREAMER; @@ -963,38 +967,6 @@ static int __init snd_pmac_detect(struct chip->freq_table = tumbler_freqs; chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ } - if (device_is_compatible(sound, "AOAKeylargo") || - device_is_compatible(sound, "AOAbase") || - device_is_compatible(sound, "AOAK2")) { - /* For now, only support very basic TAS3004 based machines with - * single frequency until proper i2s control is implemented - */ - switch(layout_id) { - case 0x24: - case 0x29: - case 0x33: - case 0x46: - case 0x48: - case 0x50: - case 0x5c: - chip->num_freqs = ARRAY_SIZE(tumbler_freqs); - chip->model = PMAC_SNAPPER; - chip->can_byte_swap = 0; /* FIXME: check this */ - chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */ - break; - case 0x3a: - chip->num_freqs = ARRAY_SIZE(tumbler_freqs); - chip->model = PMAC_TOONIE; - chip->can_byte_swap = 0; /* FIXME: check this */ - chip->control_mask = MASK_IEPC | 0x11;/* disable IEE */ - break; - default: - printk(KERN_ERR "snd: Unknown layout ID 0x%x\n", - layout_id); - return -ENODEV; - - } - } prop = (unsigned int *)get_property(sound, "device-id", NULL); if (prop) chip->device_id = *prop; @@ -1148,6 +1120,7 @@ int __init snd_pmac_new(struct snd_card struct snd_pmac *chip; struct device_node *np; int i, err; + unsigned int irq; unsigned long ctrl_addr, txdma_addr, rxdma_addr; static struct snd_device_ops ops = { .dev_free = snd_pmac_dev_free, @@ -1181,10 +1154,6 @@ int __init snd_pmac_new(struct snd_card if (chip->is_k2) { static char *rnames[] = { "Sound Control", "Sound DMA" }; - if (np->n_intrs < 3) { - err = -ENODEV; - goto __error; - } for (i = 0; i < 2; i ++) { if (of_address_to_resource(np->parent, i, &chip->rsrc[i])) { @@ -1198,9 +1167,10 @@ int __init snd_pmac_new(struct snd_card chip->rsrc[i].start + 1, rnames[i]) == NULL) { printk(KERN_ERR "snd: can't request rsrc " - " %d (%s: 0x%08lx:%08lx)\n", - i, rnames[i], chip->rsrc[i].start, - chip->rsrc[i].end); + " %d (%s: 0x%016llx:%016llx)\n", + i, rnames[i], + (unsigned long long)chip->rsrc[i].start, + (unsigned long long)chip->rsrc[i].end); err = -ENODEV; goto __error; } @@ -1212,10 +1182,6 @@ int __init snd_pmac_new(struct snd_card } else { static char *rnames[] = { "Sound Control", "Sound Tx DMA", "Sound Rx DMA" }; - if (np->n_intrs < 3) { - err = -ENODEV; - goto __error; - } for (i = 0; i < 3; i ++) { if (of_address_to_resource(np, i, &chip->rsrc[i])) { @@ -1229,9 +1195,10 @@ int __init snd_pmac_new(struct snd_card chip->rsrc[i].start + 1, rnames[i]) == NULL) { printk(KERN_ERR "snd: can't request rsrc " - " %d (%s: 0x%08lx:%08lx)\n", - i, rnames[i], chip->rsrc[i].start, - chip->rsrc[i].end); + " %d (%s: 0x%016llx:%016llx)\n", + i, rnames[i], + (unsigned long long)chip->rsrc[i].start, + (unsigned long long)chip->rsrc[i].end); err = -ENODEV; goto __error; } @@ -1246,28 +1213,30 @@ int __init snd_pmac_new(struct snd_card chip->playback.dma = ioremap(txdma_addr, 0x100); chip->capture.dma = ioremap(rxdma_addr, 0x100); if (chip->model <= PMAC_BURGUNDY) { - if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0, + irq = irq_of_parse_and_map(np, 0); + if (request_irq(irq, snd_pmac_ctrl_intr, 0, "PMac", (void*)chip)) { - snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[0].line); + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", + irq); err = -EBUSY; goto __error; } - chip->irq = np->intrs[0].line; + chip->irq = irq; } - if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0, - "PMac Output", (void*)chip)) { - snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[1].line); + irq = irq_of_parse_and_map(np, 1); + if (request_irq(irq, snd_pmac_tx_intr, 0, "PMac Output", (void*)chip)){ + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq); err = -EBUSY; goto __error; } - chip->tx_irq = np->intrs[1].line; - if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0, - "PMac Input", (void*)chip)) { - snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[2].line); + chip->tx_irq = irq; + irq = irq_of_parse_and_map(np, 2); + if (request_irq(irq, snd_pmac_rx_intr, 0, "PMac Input", (void*)chip)) { + snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", irq); err = -EBUSY; goto __error; } - chip->rx_irq = np->intrs[2].line; + chip->rx_irq = irq; snd_pmac_sound_feature(chip, 1); diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h index 3a9bd4d..8394e66 100644 --- a/sound/ppc/pmac.h +++ b/sound/ppc/pmac.h @@ -85,7 +85,7 @@ struct pmac_stream { enum snd_pmac_model { PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, - PMAC_SNAPPER, PMAC_TOONIE + PMAC_SNAPPER }; struct snd_pmac { @@ -188,7 +188,6 @@ int snd_pmac_burgundy_init(struct snd_pm int snd_pmac_daca_init(struct snd_pmac *chip); int snd_pmac_tumbler_init(struct snd_pmac *chip); int snd_pmac_tumbler_post_init(void); -int snd_pmac_toonie_init(struct snd_pmac *chip); /* i2c functions */ struct pmac_keywest { diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c index f4902a2..fa9a44a 100644 --- a/sound/ppc/powermac.c +++ b/sound/ppc/powermac.c @@ -94,13 +94,6 @@ static int __init snd_pmac_probe(struct if ( snd_pmac_tumbler_init(chip) < 0 || snd_pmac_tumbler_post_init() < 0) goto __error; break; - case PMAC_TOONIE: - strcpy(card->driver, "PMac Toonie"); - strcpy(card->shortname, "PowerMac Toonie"); - strcpy(card->longname, card->shortname); - if ((err = snd_pmac_toonie_init(chip)) < 0) - goto __error; - break; case PMAC_AWACS: case PMAC_SCREAMER: name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS"; @@ -188,11 +181,15 @@ static int __init alsa_card_pmac_init(vo if ((err = platform_driver_register(&snd_pmac_driver)) < 0) return err; device = platform_device_register_simple(SND_PMAC_DRIVER, -1, NULL, 0); - if (IS_ERR(device)) { - platform_driver_unregister(&snd_pmac_driver); - return PTR_ERR(device); - } - return 0; + if (!IS_ERR(device)) { + if (platform_get_drvdata(device)) + return 0; + platform_device_unregister(device); + err = -ENODEV; + } else + err = PTR_ERR(device); + platform_driver_unregister(&snd_pmac_driver); + return err; } diff --git a/sound/ppc/toonie.c b/sound/ppc/toonie.c deleted file mode 100644 index 1ac7c85..0000000 --- a/sound/ppc/toonie.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Mac Mini "toonie" mixer control - * - * Copyright (c) 2005 by Benjamin Herrenschmidt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pmac.h" - -#undef DEBUG - -#ifdef DEBUG -#define DBG(fmt...) printk(fmt) -#else -#define DBG(fmt...) -#endif - -struct pmac_gpio { - unsigned int addr; - u8 active_val; - u8 inactive_val; - u8 active_state; -}; - -struct pmac_toonie -{ - struct pmac_gpio hp_detect_gpio; - struct pmac_gpio hp_mute_gpio; - struct pmac_gpio amp_mute_gpio; - int hp_detect_irq; - int auto_mute_notify; - struct work_struct detect_work; -}; - - -/* - * gpio access - */ -#define do_gpio_write(gp, val) \ - pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val) -#define do_gpio_read(gp) \ - pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0) -#define tumbler_gpio_free(gp) /* NOP */ - -static void write_audio_gpio(struct pmac_gpio *gp, int active) -{ - if (! gp->addr) - return; - active = active ? gp->active_val : gp->inactive_val; - do_gpio_write(gp, active); - DBG("(I) gpio %x write %d\n", gp->addr, active); -} - -static int check_audio_gpio(struct pmac_gpio *gp) -{ - int ret; - - if (! gp->addr) - return 0; - - ret = do_gpio_read(gp); - - return (ret & 0xd) == (gp->active_val & 0xd); -} - -static int read_audio_gpio(struct pmac_gpio *gp) -{ - int ret; - if (! gp->addr) - return 0; - ret = ((do_gpio_read(gp) & 0x02) !=0); - return ret == gp->active_state; -} - - -enum { TOONIE_MUTE_HP, TOONIE_MUTE_AMP }; - -static int toonie_get_mute_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - struct pmac_toonie *mix = chip->mixer_data; - struct pmac_gpio *gp; - - if (mix == NULL) - return -ENODEV; - switch(kcontrol->private_value) { - case TOONIE_MUTE_HP: - gp = &mix->hp_mute_gpio; - break; - case TOONIE_MUTE_AMP: - gp = &mix->amp_mute_gpio; - break; - default: - return -EINVAL; - } - ucontrol->value.integer.value[0] = !check_audio_gpio(gp); - return 0; -} - -static int toonie_put_mute_switch(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_pmac *chip = snd_kcontrol_chip(kcontrol); - struct pmac_toonie *mix = chip->mixer_data; - struct pmac_gpio *gp; - int val; - - if (chip->update_automute && chip->auto_mute) - return 0; /* don't touch in the auto-mute mode */ - - if (mix == NULL) - return -ENODEV; - - switch(kcontrol->private_value) { - case TOONIE_MUTE_HP: - gp = &mix->hp_mute_gpio; - break; - case TOONIE_MUTE_AMP: - gp = &mix->amp_mute_gpio; - break; - default: - return -EINVAL; - } - val = ! check_audio_gpio(gp); - if (val != ucontrol->value.integer.value[0]) { - write_audio_gpio(gp, ! ucontrol->value.integer.value[0]); - return 1; - } - return 0; -} - -static struct snd_kcontrol_new toonie_hp_sw __initdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Headphone Playback Switch", - .info = snd_pmac_boolean_mono_info, - .get = toonie_get_mute_switch, - .put = toonie_put_mute_switch, - .private_value = TOONIE_MUTE_HP, -}; -static struct snd_kcontrol_new toonie_speaker_sw __initdata = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PC Speaker Playback Switch", - .info = snd_pmac_boolean_mono_info, - .get = toonie_get_mute_switch, - .put = toonie_put_mute_switch, - .private_value = TOONIE_MUTE_AMP, -}; - -/* - * auto-mute stuffs - */ -static int toonie_detect_headphone(struct snd_pmac *chip) -{ - struct pmac_toonie *mix = chip->mixer_data; - int detect = 0; - - if (mix->hp_detect_gpio.addr) - detect |= read_audio_gpio(&mix->hp_detect_gpio); - return detect; -} - -static void toonie_check_mute(struct snd_pmac *chip, struct pmac_gpio *gp, int val, - int do_notify, struct snd_kcontrol *sw) -{ - if (check_audio_gpio(gp) != val) { - write_audio_gpio(gp, val); - if (do_notify) - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &sw->id); - } -} - -static void toonie_detect_handler(void *self) -{ - struct snd_pmac *chip = (struct snd_pmac *) self; - struct pmac_toonie *mix; - int headphone; - - if (!chip) - return; - - mix = chip->mixer_data; - snd_assert(mix, return); - - headphone = toonie_detect_headphone(chip); - - DBG("headphone: %d, lineout: %d\n", headphone, lineout); - - if (headphone) { - /* unmute headphone/lineout & mute speaker */ - toonie_check_mute(chip, &mix->hp_mute_gpio, 0, - mix->auto_mute_notify, chip->master_sw_ctl); - toonie_check_mute(chip, &mix->amp_mute_gpio, 1, - mix->auto_mute_notify, chip->speaker_sw_ctl); - } else { - /* unmute speaker, mute others */ - toonie_check_mute(chip, &mix->amp_mute_gpio, 0, - mix->auto_mute_notify, chip->speaker_sw_ctl); - toonie_check_mute(chip, &mix->hp_mute_gpio, 1, - mix->auto_mute_notify, chip->master_sw_ctl); - } - if (mix->auto_mute_notify) { - snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, - &chip->hp_detect_ctl->id); - } -} - -static void toonie_update_automute(struct snd_pmac *chip, int do_notify) -{ - if (chip->auto_mute) { - struct pmac_toonie *mix; - mix = chip->mixer_data; - snd_assert(mix, return); - mix->auto_mute_notify = do_notify; - schedule_work(&mix->detect_work); - } -} - -/* interrupt - headphone plug changed */ -static irqreturn_t toonie_hp_intr(int irq, void *devid, struct pt_regs *regs) -{ - struct snd_pmac *chip = devid; - - if (chip->update_automute && chip->initialized) { - chip->update_automute(chip, 1); - return IRQ_HANDLED; - } - return IRQ_NONE; -} - -/* look for audio gpio device */ -static int find_audio_gpio(const char *name, const char *platform, - struct pmac_gpio *gp) -{ - struct device_node *np; - u32 *base, addr; - - if (! (np = find_devices("gpio"))) - return -ENODEV; - - for (np = np->child; np; np = np->sibling) { - char *property = get_property(np, "audio-gpio", NULL); - if (property && strcmp(property, name) == 0) - break; - if (device_is_compatible(np, name)) - break; - } - if (np == NULL) - return -ENODEV; - - base = (u32 *)get_property(np, "AAPL,address", NULL); - if (! base) { - base = (u32 *)get_property(np, "reg", NULL); - if (!base) { - DBG("(E) cannot find address for device %s !\n", name); - return -ENODEV; - } - addr = *base; - if (addr < 0x50) - addr += 0x50; - } else - addr = *base; - - gp->addr = addr & 0x0000ffff; - - /* Try to find the active state, default to 0 ! */ - base = (u32 *)get_property(np, "audio-gpio-active-state", NULL); - if (base) { - gp->active_state = *base; - gp->active_val = (*base) ? 0x5 : 0x4; - gp->inactive_val = (*base) ? 0x4 : 0x5; - } else { - u32 *prop = NULL; - gp->active_state = 0; - gp->active_val = 0x4; - gp->inactive_val = 0x5; - /* Here are some crude hacks to extract the GPIO polarity and - * open collector informations out of the do-platform script - * as we don't yet have an interpreter for these things - */ - if (platform) - prop = (u32 *)get_property(np, platform, NULL); - if (prop) { - if (prop[3] == 0x9 && prop[4] == 0x9) { - gp->active_val = 0xd; - gp->inactive_val = 0xc; - } - if (prop[3] == 0x1 && prop[4] == 0x1) { - gp->active_val = 0x5; - gp->inactive_val = 0x4; - } - } - } - - DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n", - name, gp->addr, gp->active_state); - - return (np->n_intrs > 0) ? np->intrs[0].line : 0; -} - -static void toonie_cleanup(struct snd_pmac *chip) -{ - struct pmac_toonie *mix = chip->mixer_data; - if (! mix) - return; - if (mix->hp_detect_irq >= 0) - free_irq(mix->hp_detect_irq, chip); - kfree(mix); - chip->mixer_data = NULL; -} - -int __init snd_pmac_toonie_init(struct snd_pmac *chip) -{ - struct pmac_toonie *mix; - - mix = kmalloc(sizeof(*mix), GFP_KERNEL); - if (! mix) - return -ENOMEM; - - chip->mixer_data = mix; - chip->mixer_free = toonie_cleanup; - - find_audio_gpio("headphone-mute", NULL, &mix->hp_mute_gpio); - find_audio_gpio("amp-mute", NULL, &mix->amp_mute_gpio); - mix->hp_detect_irq = find_audio_gpio("headphone-detect", - NULL, &mix->hp_detect_gpio); - - strcpy(chip->card->mixername, "PowerMac Toonie"); - - chip->master_sw_ctl = snd_ctl_new1(&toonie_hp_sw, chip); - snd_ctl_add(chip->card, chip->master_sw_ctl); - - chip->speaker_sw_ctl = snd_ctl_new1(&toonie_speaker_sw, chip); - snd_ctl_add(chip->card, chip->speaker_sw_ctl); - - INIT_WORK(&mix->detect_work, toonie_detect_handler, (void *)chip); - - if (mix->hp_detect_irq >= 0) { - snd_pmac_add_automute(chip); - - chip->detect_headphone = toonie_detect_headphone; - chip->update_automute = toonie_update_automute; - toonie_update_automute(chip, 0); - - if (request_irq(mix->hp_detect_irq, toonie_hp_intr, 0, - "Sound Headphone Detection", chip) < 0) - mix->hp_detect_irq = -1; - } - - return 0; -} - diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 70e4ebc..692c611 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1121,7 +1121,7 @@ static long tumbler_find_device(const ch DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n", device, gp->addr, gp->active_state); - return (node->n_intrs > 0) ? node->intrs[0].line : 0; + return irq_of_parse_and_map(node, 0); } /* reset audio */ @@ -1264,16 +1264,16 @@ static int __init tumbler_init(struct sn &mix->line_mute, 1); irq = tumbler_find_device("headphone-detect", NULL, &mix->hp_detect, 0); - if (irq < 0) + if (irq <= NO_IRQ) irq = tumbler_find_device("headphone-detect", NULL, &mix->hp_detect, 1); - if (irq < 0) + if (irq <= NO_IRQ) irq = tumbler_find_device("keywest-gpio15", NULL, &mix->hp_detect, 1); mix->headphone_irq = irq; irq = tumbler_find_device("line-output-detect", NULL, &mix->line_detect, 0); - if (irq < 0) + if (irq <= NO_IRQ) irq = tumbler_find_device("line-output-detect", NULL, &mix->line_detect, 1); mix->lineout_irq = irq; diff --git a/sound/sound_core.c b/sound/sound_core.c index 6f84972..62d4d0c 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -34,7 +34,6 @@ * locking at some point in 2.3.x. */ -#include #include #include #include @@ -44,7 +43,6 @@ #include #include #include #include -#include #include #define SOUND_STEP 16 @@ -172,8 +170,6 @@ static int sound_insert_unit(struct soun else sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP); - devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor), - S_IFCHR | mode, s->name); class_device_create(sound_class, NULL, MKDEV(SOUND_MAJOR, s->unit_minor), dev, s->name+6); return r; @@ -197,7 +193,6 @@ static void sound_remove_unit(struct sou p = __sound_remove_unit(list, unit); spin_unlock(&sound_loader_lock); if (p) { - devfs_remove(p->name); class_device_destroy(sound_class, MKDEV(SOUND_MAJOR, p->unit_minor)); kfree(p); } @@ -570,7 +565,6 @@ static void __exit cleanup_soundcore(voi /* We have nothing to really do here - we know the lists must be empty */ unregister_chrdev(SOUND_MAJOR, "sound"); - devfs_remove("sound"); class_destroy(sound_class); } @@ -580,7 +574,6 @@ static int __init init_soundcore(void) printk(KERN_ERR "soundcore: sound device already in use.\n"); return -EBUSY; } - devfs_mk_dir ("sound"); sound_class = class_create(THIS_MODULE, "sound"); if (IS_ERR(sound_class)) return PTR_ERR(sound_class); diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index 5549334..2bd8e40 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -46,6 +46,7 @@ #include #include #include #include +#include static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ @@ -335,7 +336,6 @@ #define AMD7930_FLAG_CAPTURE 0x00000002 int pgain; int mgain; - struct sbus_dev *sdev; unsigned int irq; unsigned int regs_size; struct snd_amd7930 *next; @@ -946,11 +946,9 @@ static struct snd_device_ops snd_amd7930 }; static int __init snd_amd7930_create(struct snd_card *card, - struct sbus_dev *sdev, struct resource *rp, unsigned int reg_size, - struct linux_prom_irqs *irq_prop, - int dev, + int irq, int dev, struct snd_amd7930 **ramd) { unsigned long flags; @@ -964,7 +962,6 @@ static int __init snd_amd7930_create(str spin_lock_init(&amd->lock); amd->card = card; - amd->sdev = sdev; amd->regs_size = reg_size; amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930"); @@ -975,15 +972,14 @@ static int __init snd_amd7930_create(str amd7930_idle(amd); - if (request_irq(irq_prop->pri, snd_amd7930_interrupt, - SA_INTERRUPT | SA_SHIRQ, "amd7930", amd)) { - snd_printk("amd7930-%d: Unable to grab IRQ %s\n", - dev, - __irq_itoa(irq_prop->pri)); + if (request_irq(irq, snd_amd7930_interrupt, + IRQF_DISABLED | IRQF_SHARED, "amd7930", amd)) { + snd_printk("amd7930-%d: Unable to grab IRQ %d\n", + dev, irq); snd_amd7930_free(amd); return -EBUSY; } - amd->irq = irq_prop->pri; + amd->irq = irq; amd7930_enable_ints(amd); @@ -1017,60 +1013,35 @@ static int __init snd_amd7930_create(str return 0; } -static int __init amd7930_attach(int prom_node, struct sbus_dev *sdev) +static int __init amd7930_attach_common(struct resource *rp, int irq) { - static int dev; - struct linux_prom_registers reg_prop; - struct linux_prom_irqs irq_prop; - struct resource res, *rp; + static int dev_num; struct snd_card *card; struct snd_amd7930 *amd; int err; - if (dev >= SNDRV_CARDS) + if (dev_num >= SNDRV_CARDS) return -ENODEV; - if (!enable[dev]) { - dev++; + if (!enable[dev_num]) { + dev_num++; return -ENOENT; } - err = prom_getproperty(prom_node, "intr", - (char *) &irq_prop, sizeof(irq_prop)); - if (err < 0) { - snd_printk("amd7930-%d: Firmware node lacks IRQ property.\n", dev); - return -ENODEV; - } - - err = prom_getproperty(prom_node, "reg", - (char *) ®_prop, sizeof(reg_prop)); - if (err < 0) { - snd_printk("amd7930-%d: Firmware node lacks register property.\n", dev); - return -ENODEV; - } - - if (sdev) { - rp = &sdev->resource[0]; - } else { - rp = &res; - rp->start = reg_prop.phys_addr; - rp->end = rp->start + reg_prop.reg_size - 1; - rp->flags = IORESOURCE_IO | (reg_prop.which_io & 0xff); - } - - card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0); + card = snd_card_new(index[dev_num], id[dev_num], THIS_MODULE, 0); if (card == NULL) return -ENOMEM; strcpy(card->driver, "AMD7930"); strcpy(card->shortname, "Sun AMD7930"); - sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s", + sprintf(card->longname, "%s at 0x%02lx:0x%08Lx, irq %d", card->shortname, rp->flags & 0xffL, - rp->start, - __irq_itoa(irq_prop.pri)); + (unsigned long long)rp->start, + irq); - if ((err = snd_amd7930_create(card, sdev, rp, reg_prop.reg_size, - &irq_prop, dev, &amd)) < 0) + if ((err = snd_amd7930_create(card, rp, + (rp->end - rp->start) + 1, + irq, dev_num, &amd)) < 0) goto out_err; if ((err = snd_amd7930_pcm(amd)) < 0) @@ -1085,7 +1056,8 @@ static int __init amd7930_attach(int pro amd->next = amd7930_list; amd7930_list = amd; - dev++; + dev_num++; + return 0; out_err: @@ -1093,29 +1065,71 @@ out_err: return err; } -static int __init amd7930_init(void) +static int __init amd7930_obio_attach(struct device_node *dp) +{ + struct linux_prom_registers *regs; + struct linux_prom_irqs *irqp; + struct resource res, *rp; + int len; + + irqp = of_get_property(dp, "intr", &len); + if (!irqp) { + snd_printk("%s: Firmware node lacks IRQ property.\n", + dp->full_name); + return -ENODEV; + } + + regs = of_get_property(dp, "reg", &len); + if (!regs) { + snd_printk("%s: Firmware node lacks register property.\n", + dp->full_name); + return -ENODEV; + } + + rp = &res; + rp->start = regs->phys_addr; + rp->end = rp->start + regs->reg_size - 1; + rp->flags = IORESOURCE_IO | (regs->which_io & 0xff); + + return amd7930_attach_common(rp, irqp->pri); +} + +static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match) { - struct sbus_bus *sbus; - struct sbus_dev *sdev; - int node, found; + struct sbus_dev *sdev = to_sbus_device(&dev->dev); - found = 0; + return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]); +} + +static struct of_device_id amd7930_match[] = { + { + .name = "audio", + }, + {}, +}; + +static struct of_platform_driver amd7930_sbus_driver = { + .name = "audio", + .match_table = amd7930_match, + .probe = amd7930_sbus_probe, +}; + +static int __init amd7930_init(void) +{ + struct device_node *dp; /* Try to find the sun4c "audio" node first. */ - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "audio"); - if (node && amd7930_attach(node, NULL) == 0) - found++; + dp = of_find_node_by_path("/"); + dp = dp->child; + while (dp) { + if (!strcmp(dp->name, "audio")) + amd7930_obio_attach(dp); - /* Probe each SBUS for amd7930 chips. */ - for_all_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, "audio")) { - if (amd7930_attach(sdev->prom_node, sdev) == 0) - found++; - } + dp = dp->sibling; } - return (found > 0) ? 0 : -EIO; + /* Probe each SBUS for amd7930 chips. */ + return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type); } static void __exit amd7930_exit(void) @@ -1131,6 +1145,8 @@ static void __exit amd7930_exit(void) } amd7930_list = NULL; + + of_unregister_driver(&amd7930_sbus_driver); } module_init(amd7930_init); diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 8804f26..9a06c3b 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -8,7 +8,6 @@ * Copyright (c) by Jaroslav Kysela */ -#include #include #include #include @@ -2002,10 +2001,9 @@ static int __init snd_cs4231_sbus_create chip->c_dma.preallocate = sbus_dma_preallocate; if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt, - SA_SHIRQ, "cs4231", chip)) { - snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %s\n", - dev, - __irq_itoa(sdev->irqs[0])); + IRQF_SHARED, "cs4231", chip)) { + snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n", + dev, sdev->irqs[0]); snd_cs4231_sbus_free(chip); return -EBUSY; } @@ -2038,11 +2036,11 @@ static int __init cs4231_sbus_attach(str if (err) return err; - sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s", + sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d", card->shortname, rp->flags & 0xffL, - rp->start, - __irq_itoa(sdev->irqs[0])); + (unsigned long long)rp->start, + sdev->irqs[0]); if ((err = snd_cs4231_sbus_create(card, sdev, dev, &cp)) < 0) { snd_card_free(card); @@ -2244,10 +2242,10 @@ static int __init cs4231_ebus_attach(str if (err) return err; - sprintf(card->longname, "%s at 0x%lx, irq %s", + sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, edev->resource[0].start, - __irq_itoa(edev->irqs[0])); + edev->irqs[0]); if ((err = snd_cs4231_ebus_create(card, edev, dev, &chip)) < 0) { snd_card_free(card); @@ -2285,15 +2283,14 @@ #ifdef EBUS_SUPPORT for_each_ebusdev(edev, ebus) { int match = 0; - if (!strcmp(edev->prom_name, "SUNW,CS4231")) { + if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) { match = 1; - } else if (!strcmp(edev->prom_name, "audio")) { - char compat[16]; + } else if (!strcmp(edev->prom_node->name, "audio")) { + char *compat; - prom_getstring(edev->prom_node, "compatible", - compat, sizeof(compat)); - compat[15] = '\0'; - if (!strcmp(compat, "SUNW,CS4231")) + compat = of_get_property(edev->prom_node, + "compatible", NULL); + if (compat && !strcmp(compat, "SUNW,CS4231")) match = 1; } diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index 2164b7d..f3ae6e2 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -46,7 +46,7 @@ * * I've tried to stick to the following function naming conventions: * snd_* ALSA stuff - * cs4215_* CS4215 codec specfic stuff + * cs4215_* CS4215 codec specific stuff * dbri_* DBRI high-level stuff * other DBRI low-level stuff */ @@ -92,7 +92,7 @@ #define D_MM (1<<3) #define D_USR (1<<4) #define D_DESC (1<<5) -static int dbri_debug = 0; +static int dbri_debug; module_param(dbri_debug, int, 0644); MODULE_PARM_DESC(dbri_debug, "Debug value for Sun DBRI soundcard."); @@ -593,7 +593,7 @@ #define DBRI_STREAMNO(substream) \ /* Return a pointer to dbri_streaminfo */ #define DBRI_STREAM(dbri, substream) &dbri->stream_info[DBRI_STREAMNO(substream)] -static struct snd_dbri *dbri_list = NULL; /* All DBRI devices */ +static struct snd_dbri *dbri_list; /* All DBRI devices */ /* * Short data pipes transmit LSB first. The CS4215 receives MSB first. Grrr. @@ -2521,11 +2521,11 @@ void snd_dbri_proc(struct snd_dbri * dbr struct snd_info_entry *entry; if (! snd_card_proc_new(dbri->card, "regs", &entry)) - snd_info_set_text_ops(entry, dbri, 1024, dbri_regs_read); + snd_info_set_text_ops(entry, dbri, dbri_regs_read); #ifdef DBRI_DEBUG if (! snd_card_proc_new(dbri->card, "debug", &entry)) { - snd_info_set_text_ops(entry, dbri, 4096, dbri_debug_read); + snd_info_set_text_ops(entry, dbri, dbri_debug_read); entry->mode = S_IFREG | S_IRUGO; /* Readable only. */ } #endif @@ -2569,7 +2569,7 @@ static int __init snd_dbri_create(struct return -EIO; } - err = request_irq(dbri->irq, snd_dbri_interrupt, SA_SHIRQ, + err = request_irq(dbri->irq, snd_dbri_interrupt, IRQF_SHARED, "DBRI audio", dbri); if (err) { printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); @@ -2645,9 +2645,9 @@ static int __init dbri_attach(int prom_n strcpy(card->driver, "DBRI"); strcpy(card->shortname, "Sun DBRI"); rp = &sdev->resource[0]; - sprintf(card->longname, "%s at 0x%02lx:0x%08lx, irq %s", + sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d", card->shortname, - rp->flags & 0xffL, rp->start, __irq_itoa(irq.pri)); + rp->flags & 0xffL, (unsigned long long)rp->start, irq.pri); if ((err = snd_dbri_create(card, sdev, &irq, dev)) < 0) { snd_card_free(card); diff --git a/sound/synth/emux/emux.c b/sound/synth/emux/emux.c index fc733bb..573e370 100644 --- a/sound/synth/emux/emux.c +++ b/sound/synth/emux/emux.c @@ -63,6 +63,7 @@ #endif return 0; } +EXPORT_SYMBOL(snd_emux_new); /* */ @@ -136,6 +137,7 @@ #endif return 0; } +EXPORT_SYMBOL(snd_emux_register); /* */ @@ -171,18 +173,8 @@ #endif return 0; } - -EXPORT_SYMBOL(snd_emux_new); -EXPORT_SYMBOL(snd_emux_register); EXPORT_SYMBOL(snd_emux_free); -EXPORT_SYMBOL(snd_emux_terminate_all); -EXPORT_SYMBOL(snd_emux_lock_voice); -EXPORT_SYMBOL(snd_emux_unlock_voice); - -/* soundfont.c */ -EXPORT_SYMBOL(snd_sf_linear_to_log); - /* * INIT part diff --git a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c index 1ba68ce..58b9601 100644 --- a/sound/synth/emux/emux_proc.c +++ b/sound/synth/emux/emux_proc.c @@ -119,7 +119,6 @@ void snd_emux_proc_init(struct snd_emux entry->content = SNDRV_INFO_CONTENT_TEXT; entry->private_data = emu; - entry->c.text.read_size = 1024; entry->c.text.read = snd_emux_proc_info_read; if (snd_info_register(entry) < 0) snd_info_free_entry(entry); diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index 8f00f07..d176cc0 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c @@ -55,7 +55,8 @@ #define DEFAULT_MIDI_TYPE (SNDRV_SEQ_POR SNDRV_SEQ_PORT_TYPE_MIDI_GM |\ SNDRV_SEQ_PORT_TYPE_MIDI_GS |\ SNDRV_SEQ_PORT_TYPE_MIDI_XG |\ - SNDRV_SEQ_PORT_TYPE_DIRECT_SAMPLE) + SNDRV_SEQ_PORT_TYPE_HARDWARE |\ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) /* * Initialise the EMUX Synth by creating a client and registering diff --git a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c index 24705d1..3733118 100644 --- a/sound/synth/emux/emux_synth.c +++ b/sound/synth/emux/emux_synth.c @@ -434,6 +434,7 @@ snd_emux_terminate_all(struct snd_emux * spin_unlock_irqrestore(&emu->voice_lock, flags); } +EXPORT_SYMBOL(snd_emux_terminate_all); /* * Terminate all voices associated with the given port @@ -951,6 +952,8 @@ void snd_emux_lock_voice(struct snd_emux spin_unlock_irqrestore(&emu->voice_lock, flags); } +EXPORT_SYMBOL(snd_emux_lock_voice); + /* */ void snd_emux_unlock_voice(struct snd_emux *emu, int voice) @@ -965,3 +968,5 @@ void snd_emux_unlock_voice(struct snd_em voice, emu->voices[voice].state); spin_unlock_irqrestore(&emu->voice_lock, flags); } + +EXPORT_SYMBOL(snd_emux_unlock_voice); diff --git a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c index 32c2716..455e535 100644 --- a/sound/synth/emux/soundfont.c +++ b/sound/synth/emux/soundfont.c @@ -195,7 +195,7 @@ snd_soundfont_load(struct snd_sf_list *s break; case SNDRV_SFNT_REMOVE_INFO: /* patch must be opened */ - if (sflist->currsf) { + if (!sflist->currsf) { snd_printk("soundfont: remove_info: patch not opened\n"); rc = -EINVAL; } else { @@ -810,6 +810,9 @@ snd_sf_linear_to_log(unsigned int amount return v; } +EXPORT_SYMBOL(snd_sf_linear_to_log); + + #define OFFSET_MSEC 653117 /* base = 1000 */ #define OFFSET_ABSCENT 851781 /* base = 8176 */ #define OFFSET_SAMPLERATE 1011119 /* base = 44100 */ @@ -1485,4 +1488,3 @@ snd_soundfont_remove_unlocked(struct snd unlock_preset(sflist); return 0; } - diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c index 4e614ac..d32d83d 100644 --- a/sound/usb/usbaudio.c +++ b/sound/usb/usbaudio.c @@ -2138,7 +2138,7 @@ static void proc_pcm_format_add(struct s sprintf(name, "stream%d", stream->pcm_index); if (! snd_card_proc_new(card, name, &entry)) - snd_info_set_text_ops(entry, stream, 1024, proc_pcm_format_read); + snd_info_set_text_ops(entry, stream, proc_pcm_format_read); } #else @@ -2627,9 +2627,10 @@ static int parse_audio_endpoints(struct if (!csep && altsd->bNumEndpoints >= 2) csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT); if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { - snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", + snd_printk(KERN_WARNING "%d:%u:%d : no or invalid" + " class specific endpoint descriptor\n", dev->devnum, iface_no, altno); - continue; + csep = NULL; } fp = kmalloc(sizeof(*fp), GFP_KERNEL); @@ -2648,7 +2649,7 @@ static int parse_audio_endpoints(struct if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) * (fp->maxpacksize & 0x7ff); - fp->attributes = csep[3]; + fp->attributes = csep ? csep[3] : 0; /* some quirks for attributes here */ @@ -2980,7 +2981,7 @@ static int create_ua1000_quirk(struct sn return -ENXIO; alts = &iface->altsetting[1]; altsd = get_iface_desc(alts); - if (alts->extralen != 11 || alts->extra[1] != CS_AUDIO_INTERFACE || + if (alts->extralen != 11 || alts->extra[1] != USB_DT_CS_INTERFACE || altsd->bNumEndpoints != 1) return -ENXIO; @@ -3095,6 +3096,32 @@ static int snd_usb_audigy2nx_boot_quirk( } /* + * C-Media CM106/CM106+ have four 16-bit internal registers that are nicely + * documented in the device's data sheet. + */ +static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 value) +{ + u8 buf[4]; + buf[0] = 0x20; + buf[1] = value & 0xff; + buf[2] = (value >> 8) & 0xff; + buf[3] = reg; + return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT, + 0, 0, &buf, 4, 1000); +} + +static int snd_usb_cm106_boot_quirk(struct usb_device *dev) +{ + /* + * Enable line-out driver mode, set headphone source to front + * channels, enable stereo mic. + */ + return snd_usb_cm106_write_int_reg(dev, 2, 0x8004); +} + + +/* * Setup quirks */ #define AUDIOPHILE_SET 0x01 /* if set, parse device_setup */ @@ -3197,9 +3224,9 @@ static void snd_usb_audio_create_proc(st { struct snd_info_entry *entry; if (! snd_card_proc_new(chip->card, "usbbus", &entry)) - snd_info_set_text_ops(entry, chip, 1024, proc_audio_usbbus_read); + snd_info_set_text_ops(entry, chip, proc_audio_usbbus_read); if (! snd_card_proc_new(chip->card, "usbid", &entry)) - snd_info_set_text_ops(entry, chip, 1024, proc_audio_usbid_read); + snd_info_set_text_ops(entry, chip, proc_audio_usbid_read); } /* @@ -3364,6 +3391,12 @@ static void *snd_usb_audio_probe(struct goto __err_val; } + /* C-Media CM106 / Turtle Beach Audio Advantage Roadie */ + if (id == USB_ID(0x10f5, 0x0200)) { + if (snd_usb_cm106_boot_quirk(dev) < 0) + goto __err_val; + } + /* * found a config. now register to ALSA */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 8873352..0f4b2b8 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -30,13 +30,6 @@ #define USB_SUBCLASS_AUDIO_STREAMING 0x0 #define USB_SUBCLASS_MIDI_STREAMING 0x03 #define USB_SUBCLASS_VENDOR_SPEC 0xff -#define CS_AUDIO_UNDEFINED 0x20 -#define CS_AUDIO_DEVICE 0x21 -#define CS_AUDIO_CONFIGURATION 0x22 -#define CS_AUDIO_STRING 0x23 -#define CS_AUDIO_INTERFACE 0x24 -#define CS_AUDIO_ENDPOINT 0x25 - #define HEADER 0x01 #define INPUT_TERMINAL 0x02 #define OUTPUT_TERMINAL 0x03 diff --git a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c index 2b9d940..5105b6b 100644 --- a/sound/usb/usbmidi.c +++ b/sound/usb/usbmidi.c @@ -48,6 +48,7 @@ #include #include #include #include +#include #include "usbaudio.h" @@ -1010,97 +1011,157 @@ static struct snd_rawmidi_substream *snd * "(product) MIDI (n)" schema because they aren't external MIDI ports, * such as internal control or synthesizer ports. */ -static struct { +static struct port_info { u32 id; - int port; - const char *name_format; -} snd_usbmidi_port_names[] = { + short int port; + short int voices; + const char *name; + unsigned int seq_flags; +} snd_usbmidi_port_info[] = { +#define PORT_INFO(vendor, product, num, name_, voices_, flags) \ + { .id = USB_ID(vendor, product), \ + .port = num, .voices = voices_, \ + .name = name_, .seq_flags = flags } +#define EXTERNAL_PORT(vendor, product, num, name) \ + PORT_INFO(vendor, product, num, name, 0, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_PORT) +#define CONTROL_PORT(vendor, product, num, name) \ + PORT_INFO(vendor, product, num, name, 0, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE) +#define ROLAND_SYNTH_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ + SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) +#define SOUNDCANVAS_PORT(vendor, product, num, name, voices) \ + PORT_INFO(vendor, product, num, name, voices, \ + SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GM2 | \ + SNDRV_SEQ_PORT_TYPE_MIDI_GS | \ + SNDRV_SEQ_PORT_TYPE_MIDI_XG | \ + SNDRV_SEQ_PORT_TYPE_MIDI_MT32 | \ + SNDRV_SEQ_PORT_TYPE_HARDWARE | \ + SNDRV_SEQ_PORT_TYPE_SYNTHESIZER) /* Roland UA-100 */ - { USB_ID(0x0582, 0x0000), 2, "%s Control" }, + CONTROL_PORT(0x0582, 0x0000, 2, "%s Control"), /* Roland SC-8850 */ - { USB_ID(0x0582, 0x0003), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0003), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0003), 2, "%s Part C" }, - { USB_ID(0x0582, 0x0003), 3, "%s Part D" }, - { USB_ID(0x0582, 0x0003), 4, "%s MIDI 1" }, - { USB_ID(0x0582, 0x0003), 5, "%s MIDI 2" }, + SOUNDCANVAS_PORT(0x0582, 0x0003, 0, "%s Part A", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 1, "%s Part B", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 2, "%s Part C", 128), + SOUNDCANVAS_PORT(0x0582, 0x0003, 3, "%s Part D", 128), + EXTERNAL_PORT(0x0582, 0x0003, 4, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0003, 5, "%s MIDI 2"), /* Roland U-8 */ - { USB_ID(0x0582, 0x0004), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0004), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x0004, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0004, 1, "%s Control"), /* Roland SC-8820 */ - { USB_ID(0x0582, 0x0007), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0007), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0007), 2, "%s MIDI" }, + SOUNDCANVAS_PORT(0x0582, 0x0007, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x0007, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x0007, 2, "%s MIDI"), /* Roland SK-500 */ - { USB_ID(0x0582, 0x000b), 0, "%s Part A" }, - { USB_ID(0x0582, 0x000b), 1, "%s Part B" }, - { USB_ID(0x0582, 0x000b), 2, "%s MIDI" }, + SOUNDCANVAS_PORT(0x0582, 0x000b, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x000b, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x000b, 2, "%s MIDI"), /* Roland SC-D70 */ - { USB_ID(0x0582, 0x000c), 0, "%s Part A" }, - { USB_ID(0x0582, 0x000c), 1, "%s Part B" }, - { USB_ID(0x0582, 0x000c), 2, "%s MIDI" }, + SOUNDCANVAS_PORT(0x0582, 0x000c, 0, "%s Part A", 64), + SOUNDCANVAS_PORT(0x0582, 0x000c, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x000c, 2, "%s MIDI"), /* Edirol UM-880 */ - { USB_ID(0x0582, 0x0014), 8, "%s Control" }, + CONTROL_PORT(0x0582, 0x0014, 8, "%s Control"), /* Edirol SD-90 */ - { USB_ID(0x0582, 0x0016), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0016), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0016), 2, "%s MIDI 1" }, - { USB_ID(0x0582, 0x0016), 3, "%s MIDI 2" }, + ROLAND_SYNTH_PORT(0x0582, 0x0016, 0, "%s Part A", 128), + ROLAND_SYNTH_PORT(0x0582, 0x0016, 1, "%s Part B", 128), + EXTERNAL_PORT(0x0582, 0x0016, 2, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0016, 3, "%s MIDI 2"), /* Edirol UM-550 */ - { USB_ID(0x0582, 0x0023), 5, "%s Control" }, + CONTROL_PORT(0x0582, 0x0023, 5, "%s Control"), /* Edirol SD-20 */ - { USB_ID(0x0582, 0x0027), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0027), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0027), 2, "%s MIDI" }, + ROLAND_SYNTH_PORT(0x0582, 0x0027, 0, "%s Part A", 64), + ROLAND_SYNTH_PORT(0x0582, 0x0027, 1, "%s Part B", 64), + EXTERNAL_PORT(0x0582, 0x0027, 2, "%s MIDI"), /* Edirol SD-80 */ - { USB_ID(0x0582, 0x0029), 0, "%s Part A" }, - { USB_ID(0x0582, 0x0029), 1, "%s Part B" }, - { USB_ID(0x0582, 0x0029), 2, "%s MIDI 1" }, - { USB_ID(0x0582, 0x0029), 3, "%s MIDI 2" }, + ROLAND_SYNTH_PORT(0x0582, 0x0029, 0, "%s Part A", 128), + ROLAND_SYNTH_PORT(0x0582, 0x0029, 1, "%s Part B", 128), + EXTERNAL_PORT(0x0582, 0x0029, 2, "%s MIDI 1"), + EXTERNAL_PORT(0x0582, 0x0029, 3, "%s MIDI 2"), /* Edirol UA-700 */ - { USB_ID(0x0582, 0x002b), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x002b), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x002b, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x002b, 1, "%s Control"), /* Roland VariOS */ - { USB_ID(0x0582, 0x002f), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x002f), 1, "%s External MIDI" }, - { USB_ID(0x0582, 0x002f), 2, "%s Sync" }, + EXTERNAL_PORT(0x0582, 0x002f, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x002f, 1, "%s External MIDI"), + EXTERNAL_PORT(0x0582, 0x002f, 2, "%s Sync"), /* Edirol PCR */ - { USB_ID(0x0582, 0x0033), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0033), 1, "%s 1" }, - { USB_ID(0x0582, 0x0033), 2, "%s 2" }, + EXTERNAL_PORT(0x0582, 0x0033, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x0033, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x0033, 2, "%s 2"), /* BOSS GS-10 */ - { USB_ID(0x0582, 0x003b), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x003b), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x003b, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x003b, 1, "%s Control"), /* Edirol UA-1000 */ - { USB_ID(0x0582, 0x0044), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0044), 1, "%s Control" }, + EXTERNAL_PORT(0x0582, 0x0044, 0, "%s MIDI"), + CONTROL_PORT(0x0582, 0x0044, 1, "%s Control"), /* Edirol UR-80 */ - { USB_ID(0x0582, 0x0048), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x0048), 1, "%s 1" }, - { USB_ID(0x0582, 0x0048), 2, "%s 2" }, + EXTERNAL_PORT(0x0582, 0x0048, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x0048, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x0048, 2, "%s 2"), /* Edirol PCR-A */ - { USB_ID(0x0582, 0x004d), 0, "%s MIDI" }, - { USB_ID(0x0582, 0x004d), 1, "%s 1" }, - { USB_ID(0x0582, 0x004d), 2, "%s 2" }, + EXTERNAL_PORT(0x0582, 0x004d, 0, "%s MIDI"), + EXTERNAL_PORT(0x0582, 0x004d, 1, "%s 1"), + EXTERNAL_PORT(0x0582, 0x004d, 2, "%s 2"), /* Edirol UM-3EX */ - { USB_ID(0x0582, 0x009a), 3, "%s Control" }, + CONTROL_PORT(0x0582, 0x009a, 3, "%s Control"), /* M-Audio MidiSport 8x8 */ - { USB_ID(0x0763, 0x1031), 8, "%s Control" }, - { USB_ID(0x0763, 0x1033), 8, "%s Control" }, + CONTROL_PORT(0x0763, 0x1031, 8, "%s Control"), + CONTROL_PORT(0x0763, 0x1033, 8, "%s Control"), /* MOTU Fastlane */ - { USB_ID(0x07fd, 0x0001), 0, "%s MIDI A" }, - { USB_ID(0x07fd, 0x0001), 1, "%s MIDI B" }, + EXTERNAL_PORT(0x07fd, 0x0001, 0, "%s MIDI A"), + EXTERNAL_PORT(0x07fd, 0x0001, 1, "%s MIDI B"), /* Emagic Unitor8/AMT8/MT4 */ - { USB_ID(0x086a, 0x0001), 8, "%s Broadcast" }, - { USB_ID(0x086a, 0x0002), 8, "%s Broadcast" }, - { USB_ID(0x086a, 0x0003), 4, "%s Broadcast" }, + EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"), + EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"), + EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"), }; +static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_info); ++i) { + if (snd_usbmidi_port_info[i].id == umidi->chip->usb_id && + snd_usbmidi_port_info[i].port == number) + return &snd_usbmidi_port_info[i]; + } + return NULL; +} + +static void snd_usbmidi_get_port_info(struct snd_rawmidi *rmidi, int number, + struct snd_seq_port_info *seq_port_info) +{ + struct snd_usb_midi *umidi = rmidi->private_data; + struct port_info *port_info; + + /* TODO: read port flags from descriptors */ + port_info = find_port_info(umidi, number); + if (port_info) { + seq_port_info->type = port_info->seq_flags; + seq_port_info->midi_voices = port_info->voices; + } +} + static void snd_usbmidi_init_substream(struct snd_usb_midi* umidi, int stream, int number, struct snd_rawmidi_substream ** rsubstream) { - int i; + struct port_info *port_info; const char *name_format; struct snd_rawmidi_substream *substream = snd_usbmidi_find_substream(umidi, stream, number); @@ -1110,14 +1171,8 @@ static void snd_usbmidi_init_substream(s } /* TODO: read port name from jack descriptor */ - name_format = "%s MIDI %d"; - for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) { - if (snd_usbmidi_port_names[i].id == umidi->chip->usb_id && - snd_usbmidi_port_names[i].port == number) { - name_format = snd_usbmidi_port_names[i].name_format; - break; - } - } + port_info = find_port_info(umidi, number); + name_format = port_info ? port_info->name : "%s MIDI %d"; snprintf(substream->name, sizeof(substream->name), name_format, umidi->chip->card->shortname, number + 1); @@ -1358,7 +1413,7 @@ static int snd_usbmidi_detect_yamaha(str for (cs_desc = hostif->extra; cs_desc < hostif->extra + hostif->extralen && cs_desc[0] >= 2; cs_desc += cs_desc[0]) { - if (cs_desc[1] == CS_AUDIO_INTERFACE) { + if (cs_desc[1] == USB_DT_CS_INTERFACE) { if (cs_desc[2] == MIDI_IN_JACK) endpoint->in_cables = (endpoint->in_cables << 1) | 1; else if (cs_desc[2] == MIDI_OUT_JACK) @@ -1457,6 +1512,10 @@ static int snd_usbmidi_create_endpoints_ return 0; } +static struct snd_rawmidi_global_ops snd_usbmidi_ops = { + .get_port_info = snd_usbmidi_get_port_info, +}; + static int snd_usbmidi_create_rawmidi(struct snd_usb_midi* umidi, int out_ports, int in_ports) { @@ -1472,6 +1531,7 @@ static int snd_usbmidi_create_rawmidi(st rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->ops = &snd_usbmidi_ops; rmidi->private_data = umidi; rmidi->private_free = snd_usbmidi_rawmidi_free; snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops); diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c index ce86283..491e975 100644 --- a/sound/usb/usbmixer.c +++ b/sound/usb/usbmixer.c @@ -46,6 +46,27 @@ #include "usbaudio.h" /* ignore error from controls - for debugging */ /* #define IGNORE_CTL_ERROR */ +/* + * Sound Blaster remote control configuration + * + * format of remote control data: + * Extigy: xx 00 + * Audigy 2 NX: 06 80 xx 00 00 00 + * Live! 24-bit: 06 80 xx yy 22 83 + */ +static const struct rc_config { + u32 usb_id; + u8 offset; + u8 length; + u8 packet_length; + u8 mute_mixer_id; + u32 mute_code; +} rc_configs[] = { + { USB_ID(0x041e, 0x3000), 0, 1, 2, 18, 0x0013 }, /* Extigy */ + { USB_ID(0x041e, 0x3020), 2, 1, 6, 18, 0x0013 }, /* Audigy 2 NX */ + { USB_ID(0x041e, 0x3040), 2, 2, 6, 2, 0x6e91 }, /* Live! 24-bit */ +}; + struct usb_mixer_interface { struct snd_usb_audio *chip; unsigned int ctrlif; @@ -55,11 +76,7 @@ struct usb_mixer_interface { struct usb_mixer_elem_info **id_elems; /* array[256], indexed by unit id */ /* Sound Blaster remote control stuff */ - enum { - RC_NONE, - RC_EXTIGY, - RC_AUDIGY2NX, - } rc_type; + const struct rc_config *rc_cfg; unsigned long rc_hwdep_open; u32 rc_code; wait_queue_head_t rc_waitq; @@ -1647,7 +1664,7 @@ static void snd_usb_mixer_notify_id(stru static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer, int unitid) { - if (mixer->rc_type == RC_NONE) + if (!mixer->rc_cfg) return; /* unit ids specific to Extigy/Audigy 2 NX: */ switch (unitid) { @@ -1732,20 +1749,19 @@ static void snd_usb_soundblaster_remote_ struct pt_regs *regs) { struct usb_mixer_interface *mixer = urb->context; - /* - * format of remote control data: - * Extigy: xx 00 - * Audigy 2 NX: 06 80 xx 00 00 00 - */ - int offset = mixer->rc_type == RC_EXTIGY ? 0 : 2; + const struct rc_config *rc = mixer->rc_cfg; u32 code; - if (urb->status < 0 || urb->actual_length <= offset) + if (urb->status < 0 || urb->actual_length < rc->packet_length) return; - code = mixer->rc_buffer[offset]; + + code = mixer->rc_buffer[rc->offset]; + if (rc->length == 2) + code |= mixer->rc_buffer[rc->offset + 1] << 8; + /* the Mute button actually changes the mixer control */ - if (code == 13) - snd_usb_mixer_notify_id(mixer, 18); + if (code == rc->mute_code) + snd_usb_mixer_notify_id(mixer, rc->mute_mixer_id); mixer->rc_code = code; wmb(); wake_up(&mixer->rc_waitq); @@ -1801,21 +1817,17 @@ static unsigned int snd_usb_sbrc_hwdep_p static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer) { struct snd_hwdep *hwdep; - int err, len; + int err, len, i; - switch (mixer->chip->usb_id) { - case USB_ID(0x041e, 0x3000): - mixer->rc_type = RC_EXTIGY; - len = 2; - break; - case USB_ID(0x041e, 0x3020): - mixer->rc_type = RC_AUDIGY2NX; - len = 6; - break; - default: + for (i = 0; i < ARRAY_SIZE(rc_configs); ++i) + if (rc_configs[i].usb_id == mixer->chip->usb_id) + break; + if (i >= ARRAY_SIZE(rc_configs)) return 0; - } + mixer->rc_cfg = &rc_configs[i]; + len = mixer->rc_cfg->packet_length; + init_waitqueue_head(&mixer->rc_waitq); err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep); if (err < 0) @@ -1998,7 +2010,7 @@ #endif if ((err = snd_audigy2nx_controls_create(mixer)) < 0) goto _error; if (!snd_card_proc_new(chip->card, "audigy2nx", &entry)) - snd_info_set_text_ops(entry, mixer, 1024, + snd_info_set_text_ops(entry, mixer, snd_audigy2nx_proc_read); } diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index fe67a92..88b72b5 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -632,7 +632,7 @@ static int usX2Y_pcms_lock_check(struct for (s = 0; s < 2; ++s) { struct snd_pcm_substream *substream; substream = pcm->streams[s].substream; - if (substream && substream->ffile != NULL) + if (SUBSTREAM_BUSY(substream)) err = -EBUSY; } } diff --git a/usr/Makefile b/usr/Makefile index 19d74e6..e938242 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -21,8 +21,7 @@ ramfs-input := $(if $(filter-out "",$(CO $(CONFIG_INITRAMFS_SOURCE),-d) ramfs-args := \ $(if $(CONFIG_INITRAMFS_ROOT_UID), -u $(CONFIG_INITRAMFS_ROOT_UID)) \ - $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) \ - $(ramfs-input) + $(if $(CONFIG_INITRAMFS_ROOT_GID), -g $(CONFIG_INITRAMFS_ROOT_GID)) # .initramfs_data.cpio.gz.d is used to identify all files included # in initramfs and to detect if any files are added/removed.